tnetwork.c - vaccinewars - be a doctor and try to vaccinate the world
git clone git://src.adamsgaard.dk/vaccinewars
Log
Files
Refs
README
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 
    25 #endif
    26 
    27 #ifdef NETWORKING
    28 
    29 #ifdef CYGWIN
    30 #include            /* For network functions */
    31 #include             /* For datatypes such as BOOL */
    32 #include "winmain.h"
    33 #else
    34 #include           /* For size_t etc. */
    35 #include          /* For struct sockaddr etc. */
    36 #include          /* For struct sockaddr_in etc. */
    37 #include           /* For socklen_t */
    38 #include                 /* For getpwuid */
    39 #include              /* For memcpy, strlen etc. */
    40 #ifdef HAVE_UNISTD_H
    41 #include              /* For close(), various types and
    42                                  * constants */
    43 #endif
    44 #ifdef HAVE_FCNTL_H
    45 #include               /* For fcntl() */
    46 #endif
    47 #include               /* For gethostbyname() */
    48 #endif /* CYGWIN */
    49 
    50 #include 
    51 #include               /* For errno and Unix error codes */
    52 #include              /* For exit() and atoi() */
    53 #include               /* 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, ¶m);
   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 */