URI:
       tnetwork.c - vaccinewars - be a doctor and try to vaccinate the world
  HTML git clone git://src.adamsgaard.dk/vaccinewars
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tnetwork.c (43581B)
       ---
            1 /************************************************************************
            2  * network.c      Low-level networking routines                         *
            3  * Copyright (C)  1998-2021  Ben Webb                                   *
            4  *                Email: benwebb@users.sf.net                           *
            5  *                WWW: https://dopewars.sourceforge.io/                 *
            6  *                                                                      *
            7  * This program is free software; you can redistribute it and/or        *
            8  * modify it under the terms of the GNU General Public License          *
            9  * as published by the Free Software Foundation; either version 2       *
           10  * of the License, or (at your option) any later version.               *
           11  *                                                                      *
           12  * This program is distributed in the hope that it will be useful,      *
           13  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
           14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
           15  * GNU General Public License for more details.                         *
           16  *                                                                      *
           17  * You should have received a copy of the GNU General Public License    *
           18  * along with this program; if not, write to the Free Software          *
           19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,               *
           20  *                   MA  02111-1307, USA.                               *
           21  ************************************************************************/
           22 
           23 #ifdef HAVE_CONFIG_H
           24 #include <config.h>
           25 #endif
           26 
           27 #ifdef NETWORKING
           28 
           29 #ifdef CYGWIN
           30 #include <winsock2.h>           /* For network functions */
           31 #include <windows.h>            /* For datatypes such as BOOL */
           32 #include "winmain.h"
           33 #else
           34 #include <sys/types.h>          /* For size_t etc. */
           35 #include <sys/socket.h>         /* For struct sockaddr etc. */
           36 #include <netinet/in.h>         /* For struct sockaddr_in etc. */
           37 #include <arpa/inet.h>          /* For socklen_t */
           38 #include <pwd.h>                /* For getpwuid */
           39 #include <string.h>             /* For memcpy, strlen etc. */
           40 #ifdef HAVE_UNISTD_H
           41 #include <unistd.h>             /* For close(), various types and
           42                                  * constants */
           43 #endif
           44 #ifdef HAVE_FCNTL_H
           45 #include <fcntl.h>              /* For fcntl() */
           46 #endif
           47 #include <netdb.h>              /* For gethostbyname() */
           48 #endif /* CYGWIN */
           49 
           50 #include <glib.h>
           51 #include <errno.h>              /* For errno and Unix error codes */
           52 #include <stdlib.h>             /* For exit() and atoi() */
           53 #include <stdio.h>              /* For perror() */
           54 
           55 #include "error.h"
           56 #include "network.h"
           57 #include "nls.h"
           58 
           59 /* Maximum sizes (in bytes) of read and write buffers - connections should
           60  * be dropped if either buffer is filled */
           61 #define MAXREADBUF   (32768)
           62 #define MAXWRITEBUF  (65536)
           63 
           64 /* SOCKS5 authentication method codes */
           65 typedef enum {
           66   SM_NOAUTH = 0,                /* No authentication required */
           67   SM_GSSAPI = 1,                /* GSSAPI */
           68   SM_USERPASSWD = 2             /* Username/password authentication */
           69 } SocksMethods;
           70 
           71 static gboolean StartSocksNegotiation(NetworkBuffer *NetBuf,
           72                                       gchar *RemoteHost,
           73                                       unsigned RemotePort);
           74 static gboolean StartConnect(int *fd, const gchar *bindaddr, gchar *RemoteHost,
           75                              unsigned RemotePort, gboolean *doneOK,
           76                              LastError **error);
           77 
           78 #ifdef CYGWIN
           79 
           80 void StartNetworking()
           81 {
           82   WSADATA wsaData;
           83   LastError *error;
           84   GString *errstr;
           85 
           86   if (WSAStartup(MAKEWORD(1, 0), &wsaData) != 0) {
           87     error = NewError(ET_WINSOCK, WSAGetLastError(), NULL);
           88     errstr = g_string_new("");
           89     g_string_assign_error(errstr, error);
           90     g_log(NULL, G_LOG_LEVEL_CRITICAL, _("Cannot initialize WinSock (%s)!"),
           91           errstr->str);
           92     g_string_free(errstr, TRUE);
           93     FreeError(error);
           94     exit(EXIT_FAILURE);
           95   }
           96 }
           97 
           98 void StopNetworking()
           99 {
          100   WSACleanup();
          101 }
          102 
          103 void SetReuse(SOCKET sock)
          104 {
          105   BOOL i = TRUE;
          106 
          107   if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&i,
          108                  sizeof(i)) == -1) {
          109     perror("setsockopt");
          110     exit(EXIT_FAILURE);
          111   }
          112 }
          113 
          114 void SetBlocking(SOCKET sock, gboolean blocking)
          115 {
          116   unsigned long param;
          117 
          118   param = blocking ? 0 : 1;
          119   ioctlsocket(sock, FIONBIO, &param);
          120 }
          121 
          122 #else
          123 
          124 void StartNetworking()
          125 {
          126 }
          127 
          128 void StopNetworking()
          129 {
          130 }
          131 
          132 void SetReuse(int sock)
          133 {
          134   int i = 1;
          135 
          136   if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) == -1) {
          137     perror("setsockopt");
          138     exit(EXIT_FAILURE);
          139   }
          140 }
          141 
          142 void SetBlocking(int sock, gboolean blocking)
          143 {
          144   fcntl(sock, F_SETFL, blocking ? 0 : O_NONBLOCK);
          145 }
          146 
          147 #endif /* CYGWIN */
          148 
          149 static gboolean FinishConnect(int fd, LastError **error);
          150 
          151 static void NetBufCallBack(NetworkBuffer *NetBuf, gboolean CallNow)
          152 {
          153   if (NetBuf && NetBuf->CallBack) {
          154     (*NetBuf->CallBack) (NetBuf, NetBuf->status != NBS_PRECONNECT,
          155                          (NetBuf->status == NBS_CONNECTED
          156                           && NetBuf->WriteBuf.DataPresent)
          157                          || (NetBuf->status == NBS_SOCKSCONNECT
          158                              && NetBuf->negbuf.DataPresent)
          159                          || NetBuf->WaitConnect, TRUE, CallNow);
          160   }
          161 }
          162 
          163 static void NetBufCallBackStop(NetworkBuffer *NetBuf)
          164 {
          165   if (NetBuf && NetBuf->CallBack) {
          166     (*NetBuf->CallBack) (NetBuf, FALSE, FALSE, FALSE, FALSE);
          167   }
          168 }
          169 
          170 static void InitConnBuf(ConnBuf *buf)
          171 {
          172   buf->Data = NULL;
          173   buf->Length = 0;
          174   buf->DataPresent = 0;
          175 }
          176 
          177 static void FreeConnBuf(ConnBuf *buf)
          178 {
          179   g_free(buf->Data);
          180   InitConnBuf(buf);
          181 }
          182 
          183 /* 
          184  * Initializes the passed network buffer, ready for use. Messages sent
          185  * or received on the buffered connection will be terminated by the
          186  * given character, and if they end in "StripChar" it will be removed
          187  * before the messages are sent or received.
          188  */
          189 void InitNetworkBuffer(NetworkBuffer *NetBuf, char Terminator,
          190                        char StripChar, SocksServer *socks)
          191 {
          192   NetBuf->fd = -1;
          193   NetBuf->ioch = NULL;
          194   NetBuf->InputTag = 0;
          195   NetBuf->CallBack = NULL;
          196   NetBuf->CallBackData = NULL;
          197   NetBuf->Terminator = Terminator;
          198   NetBuf->StripChar = StripChar;
          199   InitConnBuf(&NetBuf->ReadBuf);
          200   InitConnBuf(&NetBuf->WriteBuf);
          201   InitConnBuf(&NetBuf->negbuf);
          202   NetBuf->WaitConnect = FALSE;
          203   NetBuf->status = NBS_PRECONNECT;
          204   NetBuf->socks = socks;
          205   NetBuf->host = NULL;
          206   NetBuf->userpasswd = NULL;
          207   NetBuf->error = NULL;
          208 }
          209 
          210 void SetNetworkBufferCallBack(NetworkBuffer *NetBuf, NBCallBack CallBack,
          211                               gpointer CallBackData)
          212 {
          213   NetBufCallBackStop(NetBuf);
          214   NetBuf->CallBack = CallBack;
          215   NetBuf->CallBackData = CallBackData;
          216   NetBufCallBack(NetBuf, FALSE);
          217 }
          218 
          219 /* 
          220  * Sets the function used to obtain a username and password for SOCKS5
          221  * username/password authentication.
          222  */
          223 void SetNetworkBufferUserPasswdFunc(NetworkBuffer *NetBuf,
          224                                     NBUserPasswd userpasswd, gpointer data)
          225 {
          226   NetBuf->userpasswd = userpasswd;
          227   NetBuf->userpasswddata = data;
          228 }
          229 
          230 /* 
          231  * Sets up the given network buffer to handle data being sent/received
          232  * through the given socket.
          233  */
          234 void BindNetworkBufferToSocket(NetworkBuffer *NetBuf, int fd)
          235 {
          236   NetBuf->fd = fd;
          237 #ifdef CYGIN
          238   NetBuf->ioch = g_io_channel_win32_new_socket(fd);
          239 #else
          240   NetBuf->ioch = g_io_channel_unix_new(fd);
          241 #endif
          242 
          243   SetBlocking(fd, FALSE);       /* We only deal with non-blocking sockets */
          244   NetBuf->status = NBS_CONNECTED;       /* Assume the socket is connected */
          245 }
          246 
          247 /* 
          248  * Returns TRUE if the pointer is to a valid network buffer, and it's
          249  * connected to an active socket.
          250  */
          251 gboolean IsNetworkBufferActive(NetworkBuffer *NetBuf)
          252 {
          253   return (NetBuf && NetBuf->fd >= 0);
          254 }
          255 
          256 gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,
          257                                    const gchar *bindaddr,
          258                                    gchar *RemoteHost, unsigned RemotePort)
          259 {
          260   gchar *realhost;
          261   unsigned realport;
          262   gboolean doneOK;
          263 
          264   ShutdownNetworkBuffer(NetBuf);
          265 
          266   if (NetBuf->socks) {
          267     realhost = NetBuf->socks->name;
          268     realport = NetBuf->socks->port;
          269   } else {
          270     realhost = RemoteHost;
          271     realport = RemotePort;
          272   }
          273 
          274   if (StartConnect(&NetBuf->fd, bindaddr, realhost, realport, &doneOK,
          275                    &NetBuf->error)) {
          276 #ifdef CYGIN
          277     NetBuf->ioch = g_io_channel_win32_new_socket(NetBuf->fd);
          278 #else
          279     NetBuf->ioch = g_io_channel_unix_new(NetBuf->fd);
          280 #endif
          281     /* If we connected immediately, then set status, otherwise signal that 
          282      * we're waiting for the connect to complete */
          283     if (doneOK) {
          284       NetBuf->status = NetBuf->socks ? NBS_SOCKSCONNECT : NBS_CONNECTED;
          285       NetBuf->sockstat = NBSS_METHODS;
          286     } else {
          287       NetBuf->WaitConnect = TRUE;
          288     }
          289 
          290     if (NetBuf->socks
          291         && !StartSocksNegotiation(NetBuf, RemoteHost, RemotePort)) {
          292       return FALSE;
          293     }
          294 
          295     /* Notify the owner if necessary to check for the connection
          296      * completing and/or for data to be writeable */
          297     NetBufCallBack(NetBuf, FALSE);
          298 
          299     return TRUE;
          300   } else {
          301     return FALSE;
          302   }
          303 }
          304 
          305 /* 
          306  * Frees the network buffer's data structures (leaving it in the
          307  * 'initialized' state) and closes the accompanying socket.
          308  */
          309 void ShutdownNetworkBuffer(NetworkBuffer *NetBuf)
          310 {
          311   NetBufCallBackStop(NetBuf);
          312 
          313   if (NetBuf->fd >= 0) {
          314     CloseSocket(NetBuf->fd);
          315     g_io_channel_unref(NetBuf->ioch);
          316     NetBuf->fd = -1;
          317   }
          318 
          319   FreeConnBuf(&NetBuf->ReadBuf);
          320   FreeConnBuf(&NetBuf->WriteBuf);
          321   FreeConnBuf(&NetBuf->negbuf);
          322 
          323   FreeError(NetBuf->error);
          324   NetBuf->error = NULL;
          325 
          326   g_free(NetBuf->host);
          327 
          328   InitNetworkBuffer(NetBuf, NetBuf->Terminator, NetBuf->StripChar,
          329                     NetBuf->socks);
          330 }
          331 
          332 /* 
          333  * Updates the sets of read and write file descriptors to monitor
          334  * input to/output from the given network buffer. MaxSock is updated
          335  * with the highest-numbered file descriptor (plus 1) for use in a
          336  * later select() call.
          337  */
          338 void SetSelectForNetworkBuffer(NetworkBuffer *NetBuf, fd_set *readfds,
          339                                fd_set *writefds, fd_set *errorfds,
          340                                int *MaxSock)
          341 {
          342   if (!NetBuf || NetBuf->fd <= 0)
          343     return;
          344   FD_SET(NetBuf->fd, readfds);
          345   if (errorfds)
          346     FD_SET(NetBuf->fd, errorfds);
          347   if (NetBuf->fd >= *MaxSock)
          348     *MaxSock = NetBuf->fd + 1;
          349   if ((NetBuf->status == NBS_CONNECTED && NetBuf->WriteBuf.DataPresent)
          350       || (NetBuf->status == NBS_SOCKSCONNECT && NetBuf->negbuf.DataPresent)
          351       || NetBuf->WaitConnect) {
          352     FD_SET(NetBuf->fd, writefds);
          353   }
          354 }
          355 
          356 typedef enum {
          357   SEC_5FAILURE = 1,
          358   SEC_5RULESET = 2,
          359   SEC_5NETDOWN = 3,
          360   SEC_5UNREACH = 4,
          361   SEC_5CONNREF = 5,
          362   SEC_5TTLEXPIRED = 6,
          363   SEC_5COMMNOSUPP = 7,
          364   SEC_5ADDRNOSUPP = 8,
          365 
          366   SEC_REJECT = 91,
          367   SEC_NOIDENTD = 92,
          368   SEC_IDMISMATCH = 93,
          369 
          370   SEC_UNKNOWN = 200,
          371   SEC_AUTHFAILED,
          372   SEC_USERCANCEL,
          373   SEC_ADDRTYPE,
          374   SEC_REPLYVERSION,
          375   SEC_VERSION,
          376   SEC_NOMETHODS
          377 } SocksErrorCode;
          378 
          379 static ErrTable SocksErrStr[] = {
          380   /* SOCKS version 5 error messages */
          381   {SEC_5FAILURE, N_("SOCKS server general failure")},
          382   {SEC_5RULESET, N_("Connection denied by SOCKS ruleset")},
          383   {SEC_5NETDOWN, N_("SOCKS: Network unreachable")},
          384   {SEC_5UNREACH, N_("SOCKS: Host unreachable")},
          385   {SEC_5CONNREF, N_("SOCKS: Connection refused")},
          386   {SEC_5TTLEXPIRED, N_("SOCKS: TTL expired")},
          387   {SEC_5COMMNOSUPP, N_("SOCKS: Command not supported")},
          388   {SEC_5ADDRNOSUPP, N_("SOCKS: Address type not supported")},
          389   {SEC_NOMETHODS, N_("SOCKS server rejected all offered methods")},
          390   {SEC_ADDRTYPE, N_("Unknown SOCKS address type returned")},
          391   {SEC_AUTHFAILED, N_("SOCKS authentication failed")},
          392   {SEC_USERCANCEL, N_("SOCKS authentication canceled by user")},
          393 
          394   /* SOCKS version 4 error messages */
          395   {SEC_REJECT, N_("SOCKS: Request rejected or failed")},
          396   {SEC_NOIDENTD, N_("SOCKS: Rejected - unable to contact identd")},
          397   {SEC_IDMISMATCH,
          398    N_("SOCKS: Rejected - identd reports different user-id")},
          399 
          400   /* SOCKS errors due to protocol violations */
          401   {SEC_UNKNOWN, N_("Unknown SOCKS reply code")},
          402   {SEC_REPLYVERSION, N_("Unknown SOCKS reply version code")},
          403   {SEC_VERSION, N_("Unknown SOCKS server version")},
          404   {0, NULL}
          405 };
          406 
          407 static void SocksAppendError(GString *str, LastError *error)
          408 {
          409   LookupErrorCode(str, error->code, SocksErrStr, _("SOCKS error code %d"));
          410 }
          411 
          412 static ErrorType ETSocks = { SocksAppendError, NULL };
          413 
          414 static gboolean Socks5UserPasswd(NetworkBuffer *NetBuf)
          415 {
          416   if (!NetBuf->userpasswd) {
          417     SetError(&NetBuf->error, &ETSocks, SEC_NOMETHODS, NULL);
          418     return FALSE;
          419   } else {
          420     /* Request a username and password (the callback function should in
          421      * turn call SendSocks5UserPasswd when it's done) */
          422     NetBuf->sockstat = NBSS_USERPASSWD;
          423     (*NetBuf->userpasswd) (NetBuf, NetBuf->userpasswddata);
          424     return TRUE;
          425   }
          426 }
          427 
          428 void SendSocks5UserPasswd(NetworkBuffer *NetBuf, gchar *user,
          429                           gchar *password)
          430 {
          431   gchar *addpt;
          432   guint addlen;
          433   ConnBuf *conn;
          434 
          435   if (!user || !password || !user[0] || !password[0]) {
          436     SetError(&NetBuf->error, &ETSocks, SEC_USERCANCEL, NULL);
          437     NetBufCallBack(NetBuf, TRUE);
          438     return;
          439   }
          440   conn = &NetBuf->negbuf;
          441   addlen = 3 + strlen(user) + strlen(password);
          442   addpt = ExpandWriteBuffer(conn, addlen, &NetBuf->error);
          443   if (!addpt || strlen(user) > 255 || strlen(password) > 255) {
          444     SetError(&NetBuf->error, ET_CUSTOM, E_FULLBUF, NULL);
          445     NetBufCallBack(NetBuf, TRUE);
          446     return;
          447   }
          448   addpt[0] = 1;                 /* Subnegotiation version code */
          449   addpt[1] = strlen(user);
          450   strcpy(&addpt[2], user);
          451   addpt[2 + strlen(user)] = strlen(password);
          452   strcpy(&addpt[3 + strlen(user)], password);
          453 
          454   CommitWriteBuffer(NetBuf, conn, addpt, addlen);
          455 }
          456 
          457 static gboolean Socks5Connect(NetworkBuffer *NetBuf)
          458 {
          459   gchar *addpt;
          460   guint addlen, hostlen;
          461   ConnBuf *conn;
          462   unsigned short int netport;
          463 
          464   conn = &NetBuf->negbuf;
          465   g_assert(NetBuf->host);
          466   hostlen = strlen(NetBuf->host);
          467   if (hostlen > 255)
          468     return FALSE;
          469 
          470   netport = htons(NetBuf->port);
          471   g_assert(sizeof(netport) == 2);
          472 
          473   addlen = hostlen + 7;
          474   addpt = ExpandWriteBuffer(conn, addlen, &NetBuf->error);
          475   if (!addpt)
          476     return FALSE;
          477   addpt[0] = 5;                 /* SOCKS version 5 */
          478   addpt[1] = 1;                 /* CONNECT */
          479   addpt[2] = 0;                 /* reserved - must be zero */
          480   addpt[3] = 3;                 /* Address type - FQDN */
          481   addpt[4] = hostlen;           /* Length of address */
          482   strcpy(&addpt[5], NetBuf->host);
          483   memcpy(&addpt[5 + hostlen], &netport, sizeof(netport));
          484 
          485   NetBuf->sockstat = NBSS_CONNECT;
          486 
          487   CommitWriteBuffer(NetBuf, conn, addpt, addlen);
          488 
          489   return TRUE;
          490 }
          491 
          492 static gboolean HandleSocksReply(NetworkBuffer *NetBuf)
          493 {
          494   gchar *data;
          495   guchar addrtype;
          496   guint replylen;
          497   gboolean retval = TRUE;
          498 
          499   if (NetBuf->socks->version == 5) {
          500     if (NetBuf->sockstat == NBSS_METHODS) {
          501       data = GetWaitingData(NetBuf, 2);
          502       if (data) {
          503         retval = FALSE;
          504         if (data[0] != 5) {
          505           SetError(&NetBuf->error, &ETSocks, SEC_VERSION, NULL);
          506         } else if (data[1] == SM_NOAUTH) {
          507           retval = Socks5Connect(NetBuf);
          508         } else if (data[1] == SM_USERPASSWD) {
          509           retval = Socks5UserPasswd(NetBuf);
          510         } else {
          511           SetError(&NetBuf->error, &ETSocks, SEC_NOMETHODS, NULL);
          512         }
          513         g_free(data);
          514       }
          515     } else if (NetBuf->sockstat == NBSS_USERPASSWD) {
          516       data = GetWaitingData(NetBuf, 2);
          517       if (data) {
          518         retval = FALSE;
          519         if (data[1] != 0) {
          520           SetError(&NetBuf->error, &ETSocks, SEC_AUTHFAILED, NULL);
          521         } else {
          522           retval = Socks5Connect(NetBuf);
          523         }
          524         g_free(data);
          525       }
          526     } else if (NetBuf->sockstat == NBSS_CONNECT) {
          527       data = PeekWaitingData(NetBuf, 5);
          528       if (data) {
          529         retval = FALSE;
          530         addrtype = data[3];
          531         if (data[0] != 5) {
          532           SetError(&NetBuf->error, &ETSocks, SEC_VERSION, NULL);
          533         } else if (data[1] > 8) {
          534           SetError(&NetBuf->error, &ETSocks, SEC_UNKNOWN, NULL);
          535         } else if (data[1] != 0) {
          536           SetError(&NetBuf->error, &ETSocks, data[1], NULL);
          537         } else if (addrtype != 1 && addrtype != 3 && addrtype != 4) {
          538           SetError(&NetBuf->error, &ETSocks, SEC_ADDRTYPE, NULL);
          539         } else {
          540           retval = TRUE;
          541           replylen = 6;
          542           if (addrtype == 1)
          543             replylen += 4;      /* IPv4 address */
          544           else if (addrtype == 4)
          545             replylen += 16;     /* IPv6 address */
          546           else
          547             replylen += data[4];        /* FQDN */
          548           data = GetWaitingData(NetBuf, replylen);
          549           if (data) {
          550             NetBuf->status = NBS_CONNECTED;
          551             g_free(data);
          552             NetBufCallBack(NetBuf, FALSE);      /* status has changed */
          553           }
          554         }
          555       }
          556     }
          557     return retval;
          558   } else {
          559     data = GetWaitingData(NetBuf, 8);
          560     if (data) {
          561       retval = FALSE;
          562       if (data[0] != 0) {
          563         SetError(&NetBuf->error, &ETSocks, SEC_REPLYVERSION, NULL);
          564       } else {
          565         if (data[1] == 90) {
          566           NetBuf->status = NBS_CONNECTED;
          567           NetBufCallBack(NetBuf, FALSE);        /* status has changed */
          568           retval = TRUE;
          569         } else if (data[1] >= SEC_REJECT && data[1] <= SEC_IDMISMATCH) {
          570           SetError(&NetBuf->error, &ETSocks, data[1], NULL);
          571         } else {
          572           SetError(&NetBuf->error, &ETSocks, SEC_UNKNOWN, NULL);
          573         }
          574       }
          575       g_free(data);
          576     }
          577     return retval;
          578   }
          579 }
          580 
          581 /* 
          582  * Reads and writes data if the network connection is ready. Sets the
          583  * various OK variables to TRUE if no errors occurred in the relevant
          584  * operations, and returns TRUE if data was read and is waiting for
          585  * processing.
          586  */
          587 static gboolean DoNetworkBufferStuff(NetworkBuffer *NetBuf,
          588                                      gboolean ReadReady,
          589                                      gboolean WriteReady,
          590                                      gboolean ErrorReady, gboolean *ReadOK,
          591                                      gboolean *WriteOK, gboolean *ErrorOK)
          592 {
          593   gboolean DataWaiting = FALSE, ConnectDone = FALSE;
          594   gboolean retval;
          595 
          596   *ReadOK = *WriteOK = *ErrorOK = TRUE;
          597 
          598   if (ErrorReady || NetBuf->error)
          599     *ErrorOK = FALSE;
          600   else if (NetBuf->WaitConnect) {
          601     if (WriteReady) {
          602       retval = FinishConnect(NetBuf->fd, &NetBuf->error);
          603       ConnectDone = TRUE;
          604       NetBuf->WaitConnect = FALSE;
          605 
          606       if (retval) {
          607         if (NetBuf->socks) {
          608           NetBuf->status = NBS_SOCKSCONNECT;
          609           NetBuf->sockstat = NBSS_METHODS;
          610         } else {
          611           NetBuf->status = NBS_CONNECTED;
          612         }
          613       } else {
          614         NetBuf->status = NBS_PRECONNECT;
          615         *WriteOK = FALSE;
          616       }
          617     }
          618   } else {
          619     if (WriteReady)
          620       *WriteOK = WriteDataToWire(NetBuf);
          621 
          622     if (ReadReady) {
          623       *ReadOK = ReadDataFromWire(NetBuf);
          624       if (NetBuf->ReadBuf.DataPresent > 0 &&
          625           NetBuf->status == NBS_SOCKSCONNECT) {
          626         if (!HandleSocksReply(NetBuf)
          627             || NetBuf->error) { /* From SendSocks5UserPasswd, possibly */
          628           *ErrorOK = FALSE;
          629         }
          630       }
          631       if (NetBuf->ReadBuf.DataPresent > 0 &&
          632           NetBuf->status != NBS_SOCKSCONNECT) {
          633         DataWaiting = TRUE;
          634       }
          635     }
          636   }
          637 
          638   if (!(*ErrorOK && *WriteOK && *ReadOK)) {
          639     /* We don't want to check the socket any more */
          640     NetBufCallBackStop(NetBuf);
          641     /* If there were errors, then the socket is now useless - so close it */
          642     CloseSocket(NetBuf->fd);
          643     NetBuf->fd = -1;
          644   } else if (ConnectDone) {
          645     /* If we just connected, then no need to listen for write-ready status
          646      * any more */
          647     NetBufCallBack(NetBuf, FALSE);
          648   } else if (WriteReady
          649              && ((NetBuf->status == NBS_CONNECTED
          650                   && NetBuf->WriteBuf.DataPresent == 0)
          651                  || (NetBuf->status == NBS_SOCKSCONNECT
          652                      && NetBuf->negbuf.DataPresent == 0))) {
          653     /* If we wrote out everything, then tell the owner so that the socket
          654      * no longer needs to be checked for write-ready status */
          655     NetBufCallBack(NetBuf, FALSE);
          656   }
          657 
          658   return DataWaiting;
          659 }
          660 
          661 /* 
          662  * Responds to a select() call by reading/writing data as necessary.
          663  * If any data were read, TRUE is returned. "DoneOK" is set TRUE
          664  * unless a fatal error (i.e. the connection was broken) occurred.
          665  */
          666 gboolean RespondToSelect(NetworkBuffer *NetBuf, fd_set *readfds,
          667                          fd_set *writefds, fd_set *errorfds,
          668                          gboolean *DoneOK)
          669 {
          670   gboolean ReadOK, WriteOK, ErrorOK;
          671   gboolean DataWaiting = FALSE;
          672 
          673   *DoneOK = TRUE;
          674   if (!NetBuf || NetBuf->fd <= 0)
          675     return DataWaiting;
          676   DataWaiting = DoNetworkBufferStuff(NetBuf, FD_ISSET(NetBuf->fd, readfds),
          677                                      FD_ISSET(NetBuf->fd, writefds),
          678                                      errorfds ? FD_ISSET(NetBuf->fd,
          679                                                          errorfds) : FALSE,
          680                                      &ReadOK, &WriteOK, &ErrorOK);
          681   *DoneOK = (WriteOK && ErrorOK && ReadOK);
          682   return DataWaiting;
          683 }
          684 
          685 gboolean NetBufHandleNetwork(NetworkBuffer *NetBuf, gboolean ReadReady,
          686                              gboolean WriteReady, gboolean ErrorReady,
          687                              gboolean *DoneOK)
          688 {
          689   gboolean ReadOK, WriteOK, ErrorOK;
          690   gboolean DataWaiting = FALSE;
          691 
          692   *DoneOK = TRUE;
          693   if (!NetBuf || NetBuf->fd <= 0)
          694     return DataWaiting;
          695 
          696   DataWaiting = DoNetworkBufferStuff(NetBuf, ReadReady, WriteReady, ErrorReady,
          697                                      &ReadOK, &WriteOK, &ErrorOK);
          698 
          699   *DoneOK = (WriteOK && ErrorOK && ReadOK);
          700   return DataWaiting;
          701 }
          702 
          703 /* 
          704  * Returns the number of complete (terminated) messages waiting in the
          705  * given network buffer. This is the number of times that
          706  * GetWaitingMessage() can be safely called without it returning NULL.
          707  */
          708 gint CountWaitingMessages(NetworkBuffer *NetBuf)
          709 {
          710   ConnBuf *conn;
          711   gint i, msgs = 0;
          712 
          713   if (NetBuf->status != NBS_CONNECTED)
          714     return 0;
          715 
          716   conn = &NetBuf->ReadBuf;
          717 
          718   if (conn->Data)
          719     for (i = 0; i < conn->DataPresent; i++) {
          720       if (conn->Data[i] == NetBuf->Terminator)
          721         msgs++;
          722     }
          723   return msgs;
          724 }
          725 
          726 gchar *PeekWaitingData(NetworkBuffer *NetBuf, int numbytes)
          727 {
          728   ConnBuf *conn;
          729 
          730   conn = &NetBuf->ReadBuf;
          731   if (!conn->Data || conn->DataPresent < numbytes)
          732     return NULL;
          733   else
          734     return conn->Data;
          735 }
          736 
          737 gchar *GetWaitingData(NetworkBuffer *NetBuf, int numbytes)
          738 {
          739   ConnBuf *conn;
          740   gchar *data;
          741 
          742   conn = &NetBuf->ReadBuf;
          743   if (!conn->Data || conn->DataPresent < numbytes)
          744     return NULL;
          745 
          746   data = g_new(gchar, numbytes);
          747   memcpy(data, conn->Data, numbytes);
          748 
          749   memmove(&conn->Data[0], &conn->Data[numbytes],
          750           conn->DataPresent - numbytes);
          751   conn->DataPresent -= numbytes;
          752 
          753   return data;
          754 }
          755 
          756 /* 
          757  * Reads a complete (terminated) message from the network buffer. The
          758  * message is removed from the buffer, and returned as a null-terminated
          759  * string (the network terminator is removed). If no complete message is
          760  * waiting, NULL is returned. The string is dynamically allocated, and
          761  * so must be g_free'd by the caller.
          762  */
          763 gchar *GetWaitingMessage(NetworkBuffer *NetBuf)
          764 {
          765   ConnBuf *conn;
          766   int MessageLen;
          767   char *SepPt;
          768   gchar *NewMessage;
          769 
          770   conn = &NetBuf->ReadBuf;
          771   if (!conn->Data || !conn->DataPresent || NetBuf->status != NBS_CONNECTED) {
          772     return NULL;
          773   }
          774   SepPt = memchr(conn->Data, NetBuf->Terminator, conn->DataPresent);
          775   if (!SepPt)
          776     return NULL;
          777   *SepPt = '\0';
          778   MessageLen = SepPt - conn->Data + 1;
          779   SepPt--;
          780   if (NetBuf->StripChar && *SepPt == NetBuf->StripChar)
          781     *SepPt = '\0';
          782   NewMessage = g_new(gchar, MessageLen);
          783 
          784   memcpy(NewMessage, conn->Data, MessageLen);
          785   if (MessageLen < conn->DataPresent) {
          786     memmove(&conn->Data[0], &conn->Data[MessageLen],
          787             conn->DataPresent - MessageLen);
          788   }
          789   conn->DataPresent -= MessageLen;
          790   return NewMessage;
          791 }
          792 
          793 /* 
          794  * Reads any waiting data on the given network buffer's TCP/IP connection
          795  * into the read buffer. Returns FALSE if the connection was closed, or
          796  * if the read buffer's maximum size was reached.
          797  */
          798 gboolean ReadDataFromWire(NetworkBuffer *NetBuf)
          799 {
          800   ConnBuf *conn;
          801   int CurrentPosition, BytesRead;
          802 
          803   conn = &NetBuf->ReadBuf;
          804   CurrentPosition = conn->DataPresent;
          805   while (1) {
          806     if (CurrentPosition >= conn->Length) {
          807       if (conn->Length == MAXREADBUF) {
          808         SetError(&NetBuf->error, ET_CUSTOM, E_FULLBUF, NULL);
          809         return FALSE;           /* drop connection */
          810       }
          811       if (conn->Length == 0)
          812         conn->Length = 256;
          813       else
          814         conn->Length *= 2;
          815       if (conn->Length > MAXREADBUF)
          816         conn->Length = MAXREADBUF;
          817       conn->Data = g_realloc(conn->Data, conn->Length);
          818     }
          819     BytesRead = recv(NetBuf->fd, &conn->Data[CurrentPosition],
          820                      conn->Length - CurrentPosition, 0);
          821     if (BytesRead == SOCKET_ERROR) {
          822 #ifdef CYGWIN
          823       int Error = WSAGetLastError();
          824 
          825       if (Error == WSAEWOULDBLOCK)
          826         break;
          827       else {
          828         SetError(&NetBuf->error, ET_WINSOCK, Error, NULL);
          829         return FALSE;
          830       }
          831 #else
          832       if (errno == EAGAIN)
          833         break;
          834       else if (errno != EINTR) {
          835         SetError(&NetBuf->error, ET_ERRNO, errno, NULL);
          836         return FALSE;
          837       }
          838 #endif
          839     } else if (BytesRead == 0) {
          840       return FALSE;
          841     } else {
          842       CurrentPosition += BytesRead;
          843       conn->DataPresent = CurrentPosition;
          844     }
          845   }
          846   return TRUE;
          847 }
          848 
          849 gchar *ExpandWriteBuffer(ConnBuf *conn, int numbytes, LastError **error)
          850 {
          851   int newlen;
          852 
          853   newlen = conn->DataPresent + numbytes;
          854   if (newlen > conn->Length) {
          855     conn->Length *= 2;
          856     conn->Length = MAX(conn->Length, newlen);
          857     if (conn->Length > MAXWRITEBUF)
          858       conn->Length = MAXWRITEBUF;
          859     if (newlen > conn->Length) {
          860       if (error)
          861         SetError(error, ET_CUSTOM, E_FULLBUF, NULL);
          862       return NULL;
          863     }
          864     conn->Data = g_realloc(conn->Data, conn->Length);
          865   }
          866 
          867   return (&conn->Data[conn->DataPresent]);
          868 }
          869 
          870 void CommitWriteBuffer(NetworkBuffer *NetBuf, ConnBuf *conn,
          871                        gchar *addpt, guint addlen)
          872 {
          873   conn->DataPresent += addlen;
          874 
          875   /* If the buffer was empty before, we may need to tell the owner to
          876    * check the socket for write-ready status */
          877   if (NetBuf && addpt == conn->Data)
          878     NetBufCallBack(NetBuf, FALSE);
          879 }
          880 
          881 /* 
          882  * Writes the null-terminated string "data" to the network buffer, ready
          883  * to be sent to the wire when the network connection becomes free. The
          884  * message is automatically terminated. Fails to write the message without
          885  * error if the buffer reaches its maximum size (although this error will
          886  * be detected when an attempt is made to write the buffer to the wire).
          887  */
          888 void QueueMessageForSend(NetworkBuffer *NetBuf, gchar *data)
          889 {
          890   gchar *addpt;
          891   guint addlen;
          892   ConnBuf *conn;
          893 
          894   conn = &NetBuf->WriteBuf;
          895 
          896   if (!data)
          897     return;
          898   addlen = strlen(data) + 1;
          899   addpt = ExpandWriteBuffer(conn, addlen, NULL);
          900   if (!addpt)
          901     return;
          902 
          903   memcpy(addpt, data, addlen);
          904   addpt[addlen - 1] = NetBuf->Terminator;
          905 
          906   CommitWriteBuffer(NetBuf, conn, addpt, addlen);
          907 }
          908 
          909 static void SetNetworkError(LastError **error) {
          910 #ifdef CYGWIN
          911   SetError(error, ET_WINSOCK, WSAGetLastError(), NULL);
          912 #else
          913   SetError(error, ET_HERRNO, h_errno, NULL);
          914 #endif
          915 }
          916 
          917 static struct hostent *LookupHostname(const gchar *host, LastError **error)
          918 {
          919   struct hostent *he;
          920 
          921   if ((he = gethostbyname(host)) == NULL && error) {
          922     SetNetworkError(error);
          923   }
          924   return he;
          925 }
          926 
          927 gboolean StartSocksNegotiation(NetworkBuffer *NetBuf, gchar *RemoteHost,
          928                                unsigned RemotePort)
          929 {
          930   guint num_methods;
          931   ConnBuf *conn;
          932   struct hostent *he;
          933   gchar *addpt;
          934   guint addlen, i;
          935   struct in_addr *haddr;
          936   unsigned short int netport;
          937   gchar *username = NULL;
          938 
          939 #ifdef CYGWIN
          940   DWORD bufsize;
          941 #else
          942   struct passwd *pwd;
          943 #endif
          944 
          945   conn = &NetBuf->negbuf;
          946 
          947   if (NetBuf->socks->version == 5) {
          948     num_methods = 1;
          949     if (NetBuf->userpasswd)
          950       num_methods++;
          951     addlen = 2 + num_methods;
          952     addpt = ExpandWriteBuffer(conn, addlen, &NetBuf->error);
          953     if (!addpt)
          954       return FALSE;
          955     addpt[0] = 5;               /* SOCKS version 5 */
          956     addpt[1] = num_methods;
          957     i = 2;
          958     addpt[i++] = SM_NOAUTH;
          959     if (NetBuf->userpasswd)
          960       addpt[i++] = SM_USERPASSWD;
          961 
          962     g_free(NetBuf->host);
          963     NetBuf->host = g_strdup(RemoteHost);
          964     NetBuf->port = RemotePort;
          965 
          966     CommitWriteBuffer(NetBuf, conn, addpt, addlen);
          967 
          968     return TRUE;
          969   }
          970 
          971   he = LookupHostname(RemoteHost, &NetBuf->error);
          972   if (!he)
          973     return FALSE;
          974 
          975   if (NetBuf->socks->user && NetBuf->socks->user[0]) {
          976     username = g_strdup(NetBuf->socks->user);
          977   } else {
          978 #ifdef CYGWIN
          979     bufsize = 0;
          980     WNetGetUser(NULL, username, &bufsize);
          981     if (GetLastError() != ERROR_MORE_DATA) {
          982       SetError(&NetBuf->error, ET_WIN32, GetLastError(), NULL);
          983       return FALSE;
          984     } else {
          985       username = g_malloc(bufsize);
          986       if (WNetGetUser(NULL, username, &bufsize) != NO_ERROR) {
          987         SetError(&NetBuf->error, ET_WIN32, GetLastError(), NULL);
          988         return FALSE;
          989       }
          990     }
          991 #else
          992     if (NetBuf->socks->numuid) {
          993       username = g_strdup_printf("%d", getuid());
          994     } else {
          995       pwd = getpwuid(getuid());
          996       if (!pwd || !pwd->pw_name)
          997         return FALSE;
          998       username = g_strdup(pwd->pw_name);
          999     }
         1000 #endif
         1001   }
         1002   addlen = 9 + strlen(username);
         1003 
         1004   haddr = (struct in_addr *)he->h_addr;
         1005   g_assert(sizeof(struct in_addr) == 4);
         1006 
         1007   netport = htons(RemotePort);
         1008   g_assert(sizeof(netport) == 2);
         1009 
         1010   addpt = ExpandWriteBuffer(conn, addlen, &NetBuf->error);
         1011   if (!addpt)
         1012     return FALSE;
         1013 
         1014   addpt[0] = 4;                 /* SOCKS version */
         1015   addpt[1] = 1;                 /* CONNECT */
         1016   memcpy(&addpt[2], &netport, sizeof(netport));
         1017   memcpy(&addpt[4], haddr, sizeof(struct in_addr));
         1018   strcpy(&addpt[8], username);
         1019   g_free(username);
         1020   addpt[addlen - 1] = '\0';
         1021 
         1022   CommitWriteBuffer(NetBuf, conn, addpt, addlen);
         1023 
         1024   return TRUE;
         1025 }
         1026 
         1027 static gboolean WriteBufToWire(NetworkBuffer *NetBuf, ConnBuf *conn)
         1028 {
         1029   int CurrentPosition, BytesSent;
         1030 
         1031   if (!conn->Data || !conn->DataPresent)
         1032     return TRUE;
         1033   if (conn->Length == MAXWRITEBUF) {
         1034     SetError(&NetBuf->error, ET_CUSTOM, E_FULLBUF, NULL);
         1035     return FALSE;
         1036   }
         1037   CurrentPosition = 0;
         1038   while (CurrentPosition < conn->DataPresent) {
         1039     BytesSent = send(NetBuf->fd, &conn->Data[CurrentPosition],
         1040                      conn->DataPresent - CurrentPosition, 0);
         1041     if (BytesSent == SOCKET_ERROR) {
         1042 #ifdef CYGWIN
         1043       int Error = WSAGetLastError();
         1044 
         1045       if (Error == WSAEWOULDBLOCK)
         1046         break;
         1047       else {
         1048         SetError(&NetBuf->error, ET_WINSOCK, Error, NULL);
         1049         return FALSE;
         1050       }
         1051 #else
         1052       if (errno == EAGAIN)
         1053         break;
         1054       else if (errno != EINTR) {
         1055         SetError(&NetBuf->error, ET_ERRNO, errno, NULL);
         1056         return FALSE;
         1057       }
         1058 #endif
         1059     } else {
         1060       CurrentPosition += BytesSent;
         1061     }
         1062   }
         1063   if (CurrentPosition > 0 && CurrentPosition < conn->DataPresent) {
         1064     memmove(&conn->Data[0], &conn->Data[CurrentPosition],
         1065             conn->DataPresent - CurrentPosition);
         1066   }
         1067   conn->DataPresent -= CurrentPosition;
         1068   return TRUE;
         1069 }
         1070 
         1071 /* 
         1072  * Writes any waiting data in the network buffer to the wire. Returns
         1073  * TRUE on success, or FALSE if the buffer's maximum length is
         1074  * reached, or the remote end has closed the connection.
         1075  */
         1076 gboolean WriteDataToWire(NetworkBuffer *NetBuf)
         1077 {
         1078   if (NetBuf->status == NBS_SOCKSCONNECT) {
         1079     return WriteBufToWire(NetBuf, &NetBuf->negbuf);
         1080   } else {
         1081     return WriteBufToWire(NetBuf, &NetBuf->WriteBuf);
         1082   }
         1083 }
         1084 
         1085 static size_t MetaConnWriteFunc(void *contents, size_t size, size_t nmemb,
         1086                                 void *userp)
         1087 {
         1088   size_t realsize = size * nmemb;
         1089   CurlConnection *conn = (CurlConnection *)userp;
         1090  
         1091   conn->data = g_realloc(conn->data, conn->data_size + realsize + 1);
         1092   memcpy(&(conn->data[conn->data_size]), contents, realsize);
         1093   conn->data_size += realsize;
         1094   conn->data[conn->data_size] = 0;
         1095  
         1096   return realsize;
         1097 }
         1098 
         1099 static size_t MetaConnHeaderFunc(char *contents, size_t size, size_t nmemb,
         1100                                  void *userp)
         1101 {
         1102   size_t realsize = size * nmemb;
         1103   CurlConnection *conn = (CurlConnection *)userp;
         1104 
         1105   gchar *str = g_strchomp(g_strndup(contents, realsize));
         1106   g_ptr_array_add(conn->headers, (gpointer)str);
         1107   return realsize;
         1108 }
         1109 
         1110 void CurlInit(CurlConnection *conn)
         1111 {
         1112   curl_global_init(CURL_GLOBAL_DEFAULT);
         1113   conn->multi = curl_multi_init();
         1114   conn->h = curl_easy_init();
         1115   conn->running = FALSE;
         1116   conn->Terminator = '\n';
         1117   conn->StripChar = '\r';
         1118   conn->data_size = 0;
         1119   conn->headers = NULL;
         1120   conn->timer_cb = NULL;
         1121   conn->socket_cb = NULL;
         1122 }
         1123 
         1124 void CloseCurlConnection(CurlConnection *conn)
         1125 {
         1126   if (conn->running) {
         1127     curl_multi_remove_handle(conn->multi, conn->h);
         1128     g_free(conn->data);
         1129     conn->data_size = 0;
         1130     conn->running = FALSE;
         1131     g_ptr_array_free(conn->headers, TRUE);
         1132     conn->headers = NULL;
         1133   }
         1134 }
         1135 
         1136 void CurlCleanup(CurlConnection *conn)
         1137 {
         1138   if (conn->running) {
         1139     CloseCurlConnection(conn);
         1140   }
         1141   curl_easy_cleanup(conn->h);
         1142   curl_multi_cleanup(conn->multi);
         1143   curl_global_cleanup();
         1144 }
         1145 
         1146 gboolean HandleCurlMultiReturn(CurlConnection *conn, CURLMcode mres,
         1147                                GError **err)
         1148 {
         1149   struct CURLMsg *m;
         1150   if (mres != CURLM_OK && mres != CURLM_CALL_MULTI_PERFORM) {
         1151     CloseCurlConnection(conn);
         1152     g_set_error_literal(err, DOPE_CURLM_ERROR, mres, curl_multi_strerror(mres));
         1153     return FALSE;
         1154   }
         1155 
         1156   do {
         1157     int msgq = 0;
         1158     m = curl_multi_info_read(conn->multi, &msgq);
         1159     if (m && m->msg == CURLMSG_DONE && m->data.result != CURLE_OK) {
         1160       CloseCurlConnection(conn);
         1161       g_set_error_literal(err, DOPE_CURL_ERROR, m->data.result,
         1162                           curl_easy_strerror(m->data.result));
         1163       return FALSE;
         1164     }
         1165   } while(m);
         1166   
         1167   return TRUE;
         1168 }
         1169 
         1170 gboolean CurlConnectionPerform(CurlConnection *conn, int *still_running,
         1171                                GError **err)
         1172 {
         1173   CURLMcode mres = curl_multi_perform(conn->multi, still_running);
         1174   return HandleCurlMultiReturn(conn, mres, err);
         1175 }
         1176 
         1177 gboolean CurlConnectionSocketAction(CurlConnection *conn, curl_socket_t fd,
         1178                                     int action, int *still_running,
         1179                                     GError **err)
         1180 {
         1181   CURLMcode mres = curl_multi_socket_action(conn->multi, fd, action,
         1182                                             still_running);
         1183   return HandleCurlMultiReturn(conn, mres, err);
         1184 }
         1185 
         1186 GQuark dope_curl_error_quark(void)
         1187 {
         1188   return g_quark_from_static_string("dope-curl-error-quark");
         1189 }
         1190 
         1191 GQuark dope_curlm_error_quark(void)
         1192 {
         1193   return g_quark_from_static_string("dope-curlm-error-quark");
         1194 }
         1195 
         1196 gboolean CurlEasySetopt1(CURL *curl, CURLoption option, void *arg, GError **err)
         1197 {
         1198   CURLcode res = curl_easy_setopt(curl, option, arg);
         1199   if (res == CURLE_OK) {
         1200     return TRUE;
         1201   } else {
         1202     g_set_error_literal(err, DOPE_CURL_ERROR, res, curl_easy_strerror(res));
         1203     return FALSE;
         1204   }
         1205 }
         1206 
         1207 #ifdef CYGWIN
         1208 /* Set the path to TLS CA certificates. Without this, curl connections
         1209    to the metaserver may fail on Windows as it cannot verify the
         1210    certificate.
         1211  */
         1212 static gboolean SetCaInfo(CurlConnection *conn, GError **err)
         1213 {
         1214   gchar *bindir, *cainfo;
         1215   gboolean ret;
         1216 
         1217   /* Point to a .crt file in the same directory as dopewars.exe */
         1218   bindir = GetBinaryDir();
         1219   cainfo = g_strdup_printf("%s\\ca-bundle.crt", bindir);
         1220   g_free(bindir);
         1221 
         1222   ret = CurlEasySetopt1(conn->h, CURLOPT_CAINFO, cainfo, err);
         1223   g_free(cainfo);
         1224   return ret;
         1225 }
         1226 #endif
         1227 
         1228 gboolean OpenCurlConnection(CurlConnection *conn, char *URL, char *body,
         1229                             GError **err)
         1230 {
         1231   /* If the previous connect hung for so long that it's still active, then
         1232    * break the connection before we start a new one */
         1233   if (conn->running) {
         1234     CloseCurlConnection(conn);
         1235   }
         1236 
         1237   if (conn->h) {
         1238     int still_running;
         1239     CURLMcode mres;
         1240     if (body && !CurlEasySetopt1(conn->h, CURLOPT_COPYPOSTFIELDS, body, err)) {
         1241       return FALSE;
         1242     }
         1243 
         1244     if (!CurlEasySetopt1(conn->h, CURLOPT_URL, URL, err)
         1245         || !CurlEasySetopt1(conn->h, CURLOPT_WRITEFUNCTION, MetaConnWriteFunc,
         1246                             err)
         1247         || !CurlEasySetopt1(conn->h, CURLOPT_WRITEDATA, conn, err)
         1248         || !CurlEasySetopt1(conn->h, CURLOPT_HEADERFUNCTION,
         1249                             MetaConnHeaderFunc, err)
         1250 #ifdef CYGWIN
         1251         || !SetCaInfo(conn, err)
         1252 #endif
         1253         || !CurlEasySetopt1(conn->h, CURLOPT_HEADERDATA, conn, err)) {
         1254       return FALSE;
         1255     }
         1256 
         1257     mres = curl_multi_add_handle(conn->multi, conn->h);
         1258     if (mres != CURLM_OK && mres != CURLM_CALL_MULTI_PERFORM) {
         1259       g_set_error_literal(err, DOPE_CURLM_ERROR, mres,
         1260                           curl_multi_strerror(mres));
         1261       return FALSE;
         1262     }
         1263     conn->data = g_malloc(1);
         1264     conn->data_size = 0;
         1265     conn->headers = g_ptr_array_new_with_free_func(g_free);
         1266     conn->running = TRUE;
         1267     if (conn->timer_cb) {
         1268       /* If we set a callback, we must not do _perform, but wait for the cb */
         1269       return TRUE;
         1270     } else {
         1271       return CurlConnectionPerform(conn, &still_running, err);
         1272     }
         1273   } else {
         1274     g_set_error_literal(err, DOPE_CURLM_ERROR, 0, _("Could not init curl"));
         1275     return FALSE;
         1276   }
         1277   return TRUE;
         1278 }
         1279 
         1280 char *CurlNextLine(CurlConnection *conn, char *ch)
         1281 {
         1282   char *sep_pt;
         1283   if (!ch) return NULL;
         1284   sep_pt = strchr(ch, conn->Terminator);
         1285   if (sep_pt) {
         1286     *sep_pt = '\0';
         1287     if (sep_pt > ch && sep_pt[-1] == conn->StripChar) {
         1288       sep_pt[-1] = '\0';
         1289     }
         1290     sep_pt++;
         1291   }
         1292   return sep_pt;
         1293 }
         1294 
         1295 /* Information associated with a specific socket */
         1296 typedef struct _SockData {
         1297   GIOChannel *ch;
         1298   guint ev;
         1299 } SockData;
         1300 
         1301 static int timer_function(CURLM *multi, long timeout_ms, void *userp)
         1302 {
         1303   CurlConnection *g = userp;
         1304 
         1305   if (g->timer_event) {
         1306     dp_g_source_remove(g->timer_event);
         1307     g->timer_event = 0;
         1308   }
         1309 
         1310   /* -1 means we should just delete our timer. */
         1311   if (timeout_ms >= 0) {
         1312     g->timer_event = dp_g_timeout_add(timeout_ms, g->timer_cb, g);
         1313   }
         1314   return 0;
         1315 }
         1316 
         1317 /* Clean up the SockData structure */
         1318 static void remsock(SockData *f)
         1319 {
         1320   if (!f) {
         1321     return;
         1322   }
         1323   if (f->ev) {
         1324     dp_g_source_remove(f->ev);
         1325   }
         1326   g_io_channel_unref(f->ch);
         1327   g_free(f);
         1328 }
         1329 
         1330 /* Assign information to a SockData structure */
         1331 static void setsock(SockData *f, curl_socket_t s, CURL *e, int act,
         1332                     CurlConnection *g)
         1333 {
         1334   GIOCondition kind =
         1335     ((act & CURL_POLL_IN) ? G_IO_IN : 0) |
         1336     ((act & CURL_POLL_OUT) ? G_IO_OUT : 0);
         1337 
         1338   if (f->ev) {
         1339     dp_g_source_remove(f->ev);
         1340   }
         1341   f->ev = dp_g_io_add_watch(f->ch, kind, g->socket_cb, g);
         1342 }
         1343 
         1344 /* Initialize a new SockData structure */
         1345 static void addsock(curl_socket_t s, CURL *easy, int action, CurlConnection *g)
         1346 {
         1347   SockData *fdp = g_malloc0(sizeof(SockData));
         1348 
         1349 #ifdef CYGIN
         1350   fdp->ch = g_io_channel_win32_new_socket(s);
         1351 #else
         1352   fdp->ch = g_io_channel_unix_new(s);
         1353 #endif
         1354   setsock(fdp, s, easy, action, g);
         1355   curl_multi_assign(g->multi, s, fdp);
         1356 }
         1357 
         1358 static int socket_function(CURL *easy, curl_socket_t s, int  what, void *userp,
         1359                            void *socketp)
         1360 {
         1361   CurlConnection *g = userp;
         1362   SockData *fdp = socketp;
         1363   if (what == CURL_POLL_REMOVE) {
         1364     remsock(fdp);
         1365   } else if (!fdp) {
         1366     addsock(s, easy, what, g);
         1367   } else {
         1368     setsock(fdp, s, easy, what, g);
         1369   }
         1370   return 0;
         1371 }
         1372 
         1373 void SetCurlCallback(CurlConnection *conn, GSourceFunc timer_cb,
         1374                      GIOFunc socket_cb)
         1375 {
         1376   conn->timer_event = 0;
         1377   conn->timer_cb = timer_cb;
         1378   conn->socket_cb = socket_cb;
         1379 
         1380   curl_multi_setopt(conn->multi, CURLMOPT_TIMERFUNCTION, timer_function);
         1381   curl_multi_setopt(conn->multi, CURLMOPT_TIMERDATA, conn);
         1382   curl_multi_setopt(conn->multi, CURLMOPT_SOCKETFUNCTION, socket_function);
         1383   curl_multi_setopt(conn->multi, CURLMOPT_SOCKETDATA, conn);
         1384 }
         1385 
         1386 int CreateTCPSocket(LastError **error)
         1387 {
         1388   int fd;
         1389 
         1390   fd = socket(AF_INET, SOCK_STREAM, 0);
         1391 
         1392   if (fd == SOCKET_ERROR && error) {
         1393     SetNetworkError(error);
         1394   }
         1395 
         1396   return fd;
         1397 }
         1398 
         1399 gboolean BindTCPSocket(int sock, const gchar *addr, unsigned port,
         1400                        LastError **error)
         1401 {
         1402   struct sockaddr_in bindaddr;
         1403   int retval;
         1404   struct hostent *he;
         1405 
         1406   bindaddr.sin_family = AF_INET;
         1407   bindaddr.sin_port = htons(port);
         1408   if (addr && addr[0]) {
         1409     he = LookupHostname(addr, error);
         1410     if (!he) {
         1411       return FALSE;
         1412     }
         1413     bindaddr.sin_addr = *((struct in_addr *)he->h_addr);
         1414   } else {
         1415     bindaddr.sin_addr.s_addr = INADDR_ANY;
         1416   }
         1417   memset(bindaddr.sin_zero, 0, sizeof(bindaddr.sin_zero));
         1418 
         1419   retval =
         1420       bind(sock, (struct sockaddr *)&bindaddr, sizeof(struct sockaddr));
         1421 
         1422   if (retval == SOCKET_ERROR && error) {
         1423     SetNetworkError(error);
         1424   }
         1425 
         1426   return (retval != SOCKET_ERROR);
         1427 }
         1428 
         1429 gboolean StartConnect(int *fd, const gchar *bindaddr, gchar *RemoteHost,
         1430                       unsigned RemotePort, gboolean *doneOK, LastError **error)
         1431 {
         1432   struct sockaddr_in ClientAddr;
         1433   struct hostent *he;
         1434 
         1435   if (doneOK)
         1436     *doneOK = FALSE;
         1437   he = LookupHostname(RemoteHost, error);
         1438   if (!he)
         1439     return FALSE;
         1440 
         1441   *fd = CreateTCPSocket(error);
         1442   if (*fd == SOCKET_ERROR)
         1443     return FALSE;
         1444 
         1445   if (bindaddr && bindaddr[0] && !BindTCPSocket(*fd, bindaddr, 0, error)) {
         1446     return FALSE;
         1447   }
         1448 
         1449   ClientAddr.sin_family = AF_INET;
         1450   ClientAddr.sin_port = htons(RemotePort);
         1451   ClientAddr.sin_addr = *((struct in_addr *)he->h_addr);
         1452   memset(ClientAddr.sin_zero, 0, sizeof(ClientAddr.sin_zero));
         1453 
         1454   SetBlocking(*fd, FALSE);
         1455 
         1456   if (connect(*fd, (struct sockaddr *)&ClientAddr,
         1457               sizeof(struct sockaddr)) == SOCKET_ERROR) {
         1458 #ifdef CYGWIN
         1459     int errcode = WSAGetLastError();
         1460 
         1461     if (errcode == WSAEWOULDBLOCK)
         1462       return TRUE;
         1463     else if (error)
         1464       SetError(error, ET_WINSOCK, errcode, NULL);
         1465 #else
         1466     if (errno == EINPROGRESS)
         1467       return TRUE;
         1468     else if (error)
         1469       SetError(error, ET_ERRNO, errno, NULL);
         1470 #endif
         1471     CloseSocket(*fd);
         1472     *fd = -1;
         1473     return FALSE;
         1474   } else {
         1475     if (doneOK)
         1476       *doneOK = TRUE;
         1477   }
         1478   return TRUE;
         1479 }
         1480 
         1481 gboolean FinishConnect(int fd, LastError **error)
         1482 {
         1483   int errcode;
         1484 
         1485 #ifdef CYGWIN
         1486   errcode = WSAGetLastError();
         1487   if (errcode == 0)
         1488     return TRUE;
         1489   else {
         1490     if (error) {
         1491       SetError(error, ET_WINSOCK, errcode, NULL);
         1492     }
         1493     return FALSE;
         1494   }
         1495 #else
         1496 #ifdef HAVE_SOCKLEN_T
         1497   socklen_t optlen;
         1498 #else
         1499   int optlen;
         1500 #endif
         1501 
         1502   optlen = sizeof(errcode);
         1503   if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &optlen) == -1) {
         1504     errcode = errno;
         1505   }
         1506   if (errcode == 0)
         1507     return TRUE;
         1508   else {
         1509     if (error) {
         1510       SetError(error, ET_ERRNO, errcode, NULL);
         1511     }
         1512     return FALSE;
         1513   }
         1514 #endif /* CYGWIN */
         1515 }
         1516 
         1517 static void AddB64char(GString *str, int c)
         1518 {
         1519   if (c < 0)
         1520     return;
         1521   else if (c < 26)
         1522     g_string_append_c(str, c + 'A');
         1523   else if (c < 52)
         1524     g_string_append_c(str, c - 26 + 'a');
         1525   else if (c < 62)
         1526     g_string_append_c(str, c - 52 + '0');
         1527   else if (c == 62)
         1528     g_string_append_c(str, '+');
         1529   else
         1530     g_string_append_c(str, '/');
         1531 }
         1532 
         1533 /* 
         1534  * Adds the plain text string "unenc" to the end of the GString "str",
         1535  * using the Base64 encoding scheme.
         1536  */
         1537 void AddB64Enc(GString *str, gchar *unenc)
         1538 {
         1539   guint i;
         1540   long value = 0;
         1541 
         1542   if (!unenc || !str)
         1543     return;
         1544   for (i = 0; i < strlen(unenc); i++) {
         1545     value <<= 8;
         1546     value |= (unsigned char)unenc[i];
         1547     if (i % 3 == 2) {
         1548       AddB64char(str, (value >> 18) & 0x3F);
         1549       AddB64char(str, (value >> 12) & 0x3F);
         1550       AddB64char(str, (value >> 6) & 0x3F);
         1551       AddB64char(str, value & 0x3F);
         1552       value = 0;
         1553     }
         1554   }
         1555   if (i % 3 == 1) {
         1556     AddB64char(str, (value >> 2) & 0x3F);
         1557     AddB64char(str, (value << 4) & 0x3F);
         1558     g_string_append(str, "==");
         1559   } else if (i % 3 == 2) {
         1560     AddB64char(str, (value >> 10) & 0x3F);
         1561     AddB64char(str, (value >> 4) & 0x3F);
         1562     AddB64char(str, (value << 2) & 0x3F);
         1563     g_string_append_c(str, '=');
         1564   }
         1565 }
         1566 
         1567 #endif /* NETWORKING */