Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.haxx.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  ***************************************************************************/
     22 
     23 #include "curl_setup.h"
     24 
     25 #if !defined(CURL_DISABLE_PROXY)
     26 
     27 #ifdef HAVE_NETINET_IN_H
     28 #include <netinet/in.h>
     29 #endif
     30 #ifdef HAVE_ARPA_INET_H
     31 #include <arpa/inet.h>
     32 #endif
     33 
     34 #include "urldata.h"
     35 #include "sendf.h"
     36 #include "strequal.h"
     37 #include "select.h"
     38 #include "connect.h"
     39 #include "timeval.h"
     40 #include "socks.h"
     41 
     42 /* The last #include file should be: */
     43 #include "memdebug.h"
     44 
     45 /*
     46  * Helper read-from-socket functions. Does the same as Curl_read() but it
     47  * blocks until all bytes amount of buffersize will be read. No more, no less.
     48  *
     49  * This is STUPID BLOCKING behaviour which we frown upon, but right now this
     50  * is what we have...
     51  */
     52 int Curl_blockread_all(struct connectdata *conn, /* connection data */
     53                        curl_socket_t sockfd,     /* read from this socket */
     54                        char *buf,                /* store read data here */
     55                        ssize_t buffersize,       /* max amount to read */
     56                        ssize_t *n)               /* amount bytes read */
     57 {
     58   ssize_t nread;
     59   ssize_t allread = 0;
     60   int result;
     61   long timeleft;
     62   *n = 0;
     63   for(;;) {
     64     timeleft = Curl_timeleft(conn->data, NULL, TRUE);
     65     if(timeleft < 0) {
     66       /* we already got the timeout */
     67       result = CURLE_OPERATION_TIMEDOUT;
     68       break;
     69     }
     70     if(Curl_socket_ready(sockfd, CURL_SOCKET_BAD, timeleft) <= 0) {
     71       result = ~CURLE_OK;
     72       break;
     73     }
     74     result = Curl_read_plain(sockfd, buf, buffersize, &nread);
     75     if(CURLE_AGAIN == result)
     76       continue;
     77     else if(result)
     78       break;
     79 
     80     if(buffersize == nread) {
     81       allread += nread;
     82       *n = allread;
     83       result = CURLE_OK;
     84       break;
     85     }
     86     if(!nread) {
     87       result = ~CURLE_OK;
     88       break;
     89     }
     90 
     91     buffersize -= nread;
     92     buf += nread;
     93     allread += nread;
     94   }
     95   return result;
     96 }
     97 
     98 /*
     99 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
    100 * destination server.
    101 *
    102 * Reference :
    103 *   http://socks.permeo.com/protocol/socks4.protocol
    104 *
    105 * Note :
    106 *   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
    107 *   Nonsupport "Identification Protocol (RFC1413)"
    108 */
    109 CURLcode Curl_SOCKS4(const char *proxy_name,
    110                      const char *hostname,
    111                      int remote_port,
    112                      int sockindex,
    113                      struct connectdata *conn,
    114                      bool protocol4a)
    115 {
    116 #define SOCKS4REQLEN 262
    117   unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
    118                                            id */
    119   int result;
    120   CURLcode code;
    121   curl_socket_t sock = conn->sock[sockindex];
    122   struct Curl_easy *data = conn->data;
    123 
    124   if(Curl_timeleft(data, NULL, TRUE) < 0) {
    125     /* time-out, bail out, go home */
    126     failf(data, "Connection time-out");
    127     return CURLE_OPERATION_TIMEDOUT;
    128   }
    129 
    130   (void)curlx_nonblock(sock, FALSE);
    131 
    132   infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port);
    133 
    134   /*
    135    * Compose socks4 request
    136    *
    137    * Request format
    138    *
    139    *     +----+----+----+----+----+----+----+----+----+----+....+----+
    140    *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
    141    *     +----+----+----+----+----+----+----+----+----+----+....+----+
    142    * # of bytes:  1    1      2              4           variable       1
    143    */
    144 
    145   socksreq[0] = 4; /* version (SOCKS4) */
    146   socksreq[1] = 1; /* connect */
    147   socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
    148   socksreq[3] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
    149 
    150   /* DNS resolve only for SOCKS4, not SOCKS4a */
    151   if(!protocol4a) {
    152     struct Curl_dns_entry *dns;
    153     Curl_addrinfo *hp=NULL;
    154     int rc;
    155 
    156     rc = Curl_resolv(conn, hostname, remote_port, &dns);
    157 
    158     if(rc == CURLRESOLV_ERROR)
    159       return CURLE_COULDNT_RESOLVE_PROXY;
    160 
    161     if(rc == CURLRESOLV_PENDING)
    162       /* ignores the return code, but 'dns' remains NULL on failure */
    163       (void)Curl_resolver_wait_resolv(conn, &dns);
    164 
    165     /*
    166      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
    167      * returns a Curl_addrinfo pointer that may not always look the same.
    168      */
    169     if(dns)
    170       hp=dns->addr;
    171     if(hp) {
    172       char buf[64];
    173       unsigned short ip[4];
    174       Curl_printable_address(hp, buf, sizeof(buf));
    175 
    176       if(4 == sscanf(buf, "%hu.%hu.%hu.%hu",
    177                      &ip[0], &ip[1], &ip[2], &ip[3])) {
    178         /* Set DSTIP */
    179         socksreq[4] = (unsigned char)ip[0];
    180         socksreq[5] = (unsigned char)ip[1];
    181         socksreq[6] = (unsigned char)ip[2];
    182         socksreq[7] = (unsigned char)ip[3];
    183       }
    184       else
    185         hp = NULL; /* fail! */
    186 
    187       infof(data, "SOCKS4 connect to %s (locally resolved)\n", buf);
    188 
    189       Curl_resolv_unlock(data, dns); /* not used anymore from now on */
    190 
    191     }
    192     if(!hp) {
    193       failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
    194             hostname);
    195       return CURLE_COULDNT_RESOLVE_HOST;
    196     }
    197   }
    198 
    199   /*
    200    * This is currently not supporting "Identification Protocol (RFC1413)".
    201    */
    202   socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
    203   if(proxy_name) {
    204     size_t plen = strlen(proxy_name);
    205     if(plen >= sizeof(socksreq) - 8) {
    206       failf(data, "Too long SOCKS proxy name, can't use!\n");
    207       return CURLE_COULDNT_CONNECT;
    208     }
    209     /* copy the proxy name WITH trailing zero */
    210     memcpy(socksreq + 8, proxy_name, plen+1);
    211   }
    212 
    213   /*
    214    * Make connection
    215    */
    216   {
    217     ssize_t actualread;
    218     ssize_t written;
    219     ssize_t hostnamelen = 0;
    220     int packetsize = 9 +
    221       (int)strlen((char*)socksreq + 8); /* size including NUL */
    222 
    223     /* If SOCKS4a, set special invalid IP address 0.0.0.x */
    224     if(protocol4a) {
    225       socksreq[4] = 0;
    226       socksreq[5] = 0;
    227       socksreq[6] = 0;
    228       socksreq[7] = 1;
    229       /* If still enough room in buffer, also append hostname */
    230       hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
    231       if(packetsize + hostnamelen <= SOCKS4REQLEN)
    232         strcpy((char*)socksreq + packetsize, hostname);
    233       else
    234         hostnamelen = 0; /* Flag: hostname did not fit in buffer */
    235     }
    236 
    237     /* Send request */
    238     code = Curl_write_plain(conn, sock, (char *)socksreq,
    239                             packetsize + hostnamelen,
    240                             &written);
    241     if(code || (written != packetsize + hostnamelen)) {
    242       failf(data, "Failed to send SOCKS4 connect request.");
    243       return CURLE_COULDNT_CONNECT;
    244     }
    245     if(protocol4a && hostnamelen == 0) {
    246       /* SOCKS4a with very long hostname - send that name separately */
    247       hostnamelen = (ssize_t)strlen(hostname) + 1;
    248       code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen,
    249                               &written);
    250       if(code || (written != hostnamelen)) {
    251         failf(data, "Failed to send SOCKS4 connect request.");
    252         return CURLE_COULDNT_CONNECT;
    253       }
    254     }
    255 
    256     packetsize = 8; /* receive data size */
    257 
    258     /* Receive response */
    259     result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
    260                                 &actualread);
    261     if(result || (actualread != packetsize)) {
    262       failf(data, "Failed to receive SOCKS4 connect request ack.");
    263       return CURLE_COULDNT_CONNECT;
    264     }
    265 
    266     /*
    267      * Response format
    268      *
    269      *     +----+----+----+----+----+----+----+----+
    270      *     | VN | CD | DSTPORT |      DSTIP        |
    271      *     +----+----+----+----+----+----+----+----+
    272      * # of bytes:  1    1      2              4
    273      *
    274      * VN is the version of the reply code and should be 0. CD is the result
    275      * code with one of the following values:
    276      *
    277      * 90: request granted
    278      * 91: request rejected or failed
    279      * 92: request rejected because SOCKS server cannot connect to
    280      *     identd on the client
    281      * 93: request rejected because the client program and identd
    282      *     report different user-ids
    283      */
    284 
    285     /* wrong version ? */
    286     if(socksreq[0] != 0) {
    287       failf(data,
    288             "SOCKS4 reply has wrong version, version should be 4.");
    289       return CURLE_COULDNT_CONNECT;
    290     }
    291 
    292     /* Result */
    293     switch(socksreq[1]) {
    294     case 90:
    295       infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":"");
    296       break;
    297     case 91:
    298       failf(data,
    299             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
    300             ", request rejected or failed.",
    301             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
    302             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
    303             (((unsigned char)socksreq[8] << 8) | (unsigned char)socksreq[9]),
    304             (unsigned char)socksreq[1]);
    305       return CURLE_COULDNT_CONNECT;
    306     case 92:
    307       failf(data,
    308             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
    309             ", request rejected because SOCKS server cannot connect to "
    310             "identd on the client.",
    311             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
    312             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
    313             (((unsigned char)socksreq[8] << 8) | (unsigned char)socksreq[9]),
    314             (unsigned char)socksreq[1]);
    315       return CURLE_COULDNT_CONNECT;
    316     case 93:
    317       failf(data,
    318             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
    319             ", request rejected because the client program and identd "
    320             "report different user-ids.",
    321             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
    322             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
    323             (((unsigned char)socksreq[8] << 8) | (unsigned char)socksreq[9]),
    324             (unsigned char)socksreq[1]);
    325       return CURLE_COULDNT_CONNECT;
    326     default:
    327       failf(data,
    328             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
    329             ", Unknown.",
    330             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
    331             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
    332             (((unsigned char)socksreq[8] << 8) | (unsigned char)socksreq[9]),
    333             (unsigned char)socksreq[1]);
    334       return CURLE_COULDNT_CONNECT;
    335     }
    336   }
    337 
    338   (void)curlx_nonblock(sock, TRUE);
    339 
    340   return CURLE_OK; /* Proxy was successful! */
    341 }
    342 
    343 /*
    344  * This function logs in to a SOCKS5 proxy and sends the specifics to the final
    345  * destination server.
    346  */
    347 CURLcode Curl_SOCKS5(const char *proxy_name,
    348                      const char *proxy_password,
    349                      const char *hostname,
    350                      int remote_port,
    351                      int sockindex,
    352                      struct connectdata *conn)
    353 {
    354   /*
    355     According to the RFC1928, section "6.  Replies". This is what a SOCK5
    356     replies:
    357 
    358         +----+-----+-------+------+----------+----------+
    359         |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    360         +----+-----+-------+------+----------+----------+
    361         | 1  |  1  | X'00' |  1   | Variable |    2     |
    362         +----+-----+-------+------+----------+----------+
    363 
    364     Where:
    365 
    366     o  VER    protocol version: X'05'
    367     o  REP    Reply field:
    368     o  X'00' succeeded
    369   */
    370 
    371   unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
    372   ssize_t actualread;
    373   ssize_t written;
    374   int result;
    375   CURLcode code;
    376   curl_socket_t sock = conn->sock[sockindex];
    377   struct Curl_easy *data = conn->data;
    378   long timeout;
    379   bool socks5_resolve_local = (conn->proxytype == CURLPROXY_SOCKS5)?TRUE:FALSE;
    380   const size_t hostname_len = strlen(hostname);
    381   ssize_t len = 0;
    382 
    383   /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
    384   if(!socks5_resolve_local && hostname_len > 255) {
    385     infof(conn->data, "SOCKS5: server resolving disabled for hostnames of "
    386           "length > 255 [actual len=%zu]\n", hostname_len);
    387     socks5_resolve_local = TRUE;
    388   }
    389 
    390   /* get timeout */
    391   timeout = Curl_timeleft(data, NULL, TRUE);
    392 
    393   if(timeout < 0) {
    394     /* time-out, bail out, go home */
    395     failf(data, "Connection time-out");
    396     return CURLE_OPERATION_TIMEDOUT;
    397   }
    398 
    399   (void)curlx_nonblock(sock, TRUE);
    400 
    401   /* wait until socket gets connected */
    402   result = Curl_socket_ready(CURL_SOCKET_BAD, sock, timeout);
    403 
    404   if(-1 == result) {
    405     failf(conn->data, "SOCKS5: no connection here");
    406     return CURLE_COULDNT_CONNECT;
    407   }
    408   else if(0 == result) {
    409     failf(conn->data, "SOCKS5: connection timeout");
    410     return CURLE_OPERATION_TIMEDOUT;
    411   }
    412 
    413   if(result & CURL_CSELECT_ERR) {
    414     failf(conn->data, "SOCKS5: error occurred during connection");
    415     return CURLE_COULDNT_CONNECT;
    416   }
    417 
    418   socksreq[0] = 5; /* version */
    419 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    420   socksreq[1] = (char)(proxy_name ? 3 : 2); /* number of methods (below) */
    421   socksreq[2] = 0; /* no authentication */
    422   socksreq[3] = 1; /* GSS-API */
    423   socksreq[4] = 2; /* username/password */
    424 #else
    425   socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
    426   socksreq[2] = 0; /* no authentication */
    427   socksreq[3] = 2; /* username/password */
    428 #endif
    429 
    430   (void)curlx_nonblock(sock, FALSE);
    431 
    432   code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
    433                           &written);
    434   if(code || (written != (2 + (int)socksreq[1]))) {
    435     failf(data, "Unable to send initial SOCKS5 request.");
    436     return CURLE_COULDNT_CONNECT;
    437   }
    438 
    439   (void)curlx_nonblock(sock, TRUE);
    440 
    441   result = Curl_socket_ready(sock, CURL_SOCKET_BAD, timeout);
    442 
    443   if(-1 == result) {
    444     failf(conn->data, "SOCKS5 nothing to read");
    445     return CURLE_COULDNT_CONNECT;
    446   }
    447   else if(0 == result) {
    448     failf(conn->data, "SOCKS5 read timeout");
    449     return CURLE_OPERATION_TIMEDOUT;
    450   }
    451 
    452   if(result & CURL_CSELECT_ERR) {
    453     failf(conn->data, "SOCKS5 read error occurred");
    454     return CURLE_RECV_ERROR;
    455   }
    456 
    457   (void)curlx_nonblock(sock, FALSE);
    458 
    459   result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
    460   if(result || (actualread != 2)) {
    461     failf(data, "Unable to receive initial SOCKS5 response.");
    462     return CURLE_COULDNT_CONNECT;
    463   }
    464 
    465   if(socksreq[0] != 5) {
    466     failf(data, "Received invalid version in initial SOCKS5 response.");
    467     return CURLE_COULDNT_CONNECT;
    468   }
    469   if(socksreq[1] == 0) {
    470     /* Nothing to do, no authentication needed */
    471     ;
    472   }
    473 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    474   else if(socksreq[1] == 1) {
    475     code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
    476     if(code) {
    477       failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
    478       return CURLE_COULDNT_CONNECT;
    479     }
    480   }
    481 #endif
    482   else if(socksreq[1] == 2) {
    483     /* Needs user name and password */
    484     size_t proxy_name_len, proxy_password_len;
    485     if(proxy_name && proxy_password) {
    486       proxy_name_len = strlen(proxy_name);
    487       proxy_password_len = strlen(proxy_password);
    488     }
    489     else {
    490       proxy_name_len = 0;
    491       proxy_password_len = 0;
    492     }
    493 
    494     /*   username/password request looks like
    495      * +----+------+----------+------+----------+
    496      * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
    497      * +----+------+----------+------+----------+
    498      * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
    499      * +----+------+----------+------+----------+
    500      */
    501     len = 0;
    502     socksreq[len++] = 1;    /* username/pw subnegotiation version */
    503     socksreq[len++] = (unsigned char) proxy_name_len;
    504     if(proxy_name && proxy_name_len)
    505       memcpy(socksreq + len, proxy_name, proxy_name_len);
    506     len += proxy_name_len;
    507     socksreq[len++] = (unsigned char) proxy_password_len;
    508     if(proxy_password && proxy_password_len)
    509       memcpy(socksreq + len, proxy_password, proxy_password_len);
    510     len += proxy_password_len;
    511 
    512     code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
    513     if(code || (len != written)) {
    514       failf(data, "Failed to send SOCKS5 sub-negotiation request.");
    515       return CURLE_COULDNT_CONNECT;
    516     }
    517 
    518     result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
    519     if(result || (actualread != 2)) {
    520       failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
    521       return CURLE_COULDNT_CONNECT;
    522     }
    523 
    524     /* ignore the first (VER) byte */
    525     if(socksreq[1] != 0) { /* status */
    526       failf(data, "User was rejected by the SOCKS5 server (%d %d).",
    527             socksreq[0], socksreq[1]);
    528       return CURLE_COULDNT_CONNECT;
    529     }
    530 
    531     /* Everything is good so far, user was authenticated! */
    532   }
    533   else {
    534     /* error */
    535 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    536     if(socksreq[1] == 255) {
    537 #else
    538     if(socksreq[1] == 1) {
    539       failf(data,
    540             "SOCKS5 GSSAPI per-message authentication is not supported.");
    541       return CURLE_COULDNT_CONNECT;
    542     }
    543     else if(socksreq[1] == 255) {
    544 #endif
    545       if(!proxy_name || !*proxy_name) {
    546         failf(data,
    547               "No authentication method was acceptable. (It is quite likely"
    548               " that the SOCKS5 server wanted a username/password, since none"
    549               " was supplied to the server on this connection.)");
    550       }
    551       else {
    552         failf(data, "No authentication method was acceptable.");
    553       }
    554       return CURLE_COULDNT_CONNECT;
    555     }
    556     else {
    557       failf(data,
    558             "Undocumented SOCKS5 mode attempted to be used by server.");
    559       return CURLE_COULDNT_CONNECT;
    560     }
    561   }
    562 
    563   /* Authentication is complete, now specify destination to the proxy */
    564   len = 0;
    565   socksreq[len++] = 5; /* version (SOCKS5) */
    566   socksreq[len++] = 1; /* connect */
    567   socksreq[len++] = 0; /* must be zero */
    568 
    569   if(!socks5_resolve_local) {
    570     socksreq[len++] = 3; /* ATYP: domain name = 3 */
    571     socksreq[len++] = (char) hostname_len; /* address length */
    572     memcpy(&socksreq[len], hostname, hostname_len); /* address str w/o NULL */
    573     len += hostname_len;
    574   }
    575   else {
    576     struct Curl_dns_entry *dns;
    577     Curl_addrinfo *hp = NULL;
    578     int rc = Curl_resolv(conn, hostname, remote_port, &dns);
    579 
    580     if(rc == CURLRESOLV_ERROR)
    581       return CURLE_COULDNT_RESOLVE_HOST;
    582 
    583     if(rc == CURLRESOLV_PENDING) {
    584       /* this requires that we're in "wait for resolve" state */
    585       code = Curl_resolver_wait_resolv(conn, &dns);
    586       if(code)
    587         return code;
    588     }
    589 
    590     /*
    591      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
    592      * returns a Curl_addrinfo pointer that may not always look the same.
    593      */
    594     if(dns)
    595       hp=dns->addr;
    596     if(hp) {
    597       struct sockaddr_in *saddr_in;
    598 #ifdef ENABLE_IPV6
    599       struct sockaddr_in6 *saddr_in6;
    600 #endif
    601       int i;
    602 
    603       if(hp->ai_family == AF_INET) {
    604         socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
    605 
    606         saddr_in = (struct sockaddr_in*)(void*)hp->ai_addr;
    607         for(i = 0; i < 4; i++) {
    608           socksreq[len++] = ((unsigned char*)&saddr_in->sin_addr.s_addr)[i];
    609           infof(data, "%d\n", socksreq[len-1]);
    610         }
    611       }
    612 #ifdef ENABLE_IPV6
    613       else if(hp->ai_family == AF_INET6) {
    614         socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
    615 
    616         saddr_in6 = (struct sockaddr_in6*)(void*)hp->ai_addr;
    617         for(i = 0; i < 16; i++) {
    618           socksreq[len++] = ((unsigned char*)&saddr_in6->sin6_addr.s6_addr)[i];
    619         }
    620       }
    621 #endif
    622       else
    623         hp = NULL; /* fail! */
    624 
    625       Curl_resolv_unlock(data, dns); /* not used anymore from now on */
    626     }
    627     if(!hp) {
    628       failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
    629             hostname);
    630       return CURLE_COULDNT_RESOLVE_HOST;
    631     }
    632   }
    633 
    634   socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
    635   socksreq[len++] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
    636 
    637 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    638   if(conn->socks5_gssapi_enctype) {
    639     failf(data, "SOCKS5 GSS-API protection not yet implemented.");
    640   }
    641   else
    642 #endif
    643     code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
    644 
    645   if(code || (len != written)) {
    646     failf(data, "Failed to send SOCKS5 connect request.");
    647     return CURLE_COULDNT_CONNECT;
    648   }
    649 
    650   len = 10; /* minimum packet size is 10 */
    651 
    652 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    653   if(conn->socks5_gssapi_enctype) {
    654     failf(data, "SOCKS5 GSS-API protection not yet implemented.");
    655   }
    656   else
    657 #endif
    658     result = Curl_blockread_all(conn, sock, (char *)socksreq,
    659                                 len, &actualread);
    660 
    661   if(result || (len != actualread)) {
    662     failf(data, "Failed to receive SOCKS5 connect request ack.");
    663     return CURLE_COULDNT_CONNECT;
    664   }
    665 
    666   if(socksreq[0] != 5) { /* version */
    667     failf(data,
    668           "SOCKS5 reply has wrong version, version should be 5.");
    669     return CURLE_COULDNT_CONNECT;
    670   }
    671   if(socksreq[1] != 0) { /* Anything besides 0 is an error */
    672     if(socksreq[3] == 1) {
    673       failf(data,
    674             "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
    675             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
    676             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
    677             (((unsigned char)socksreq[8] << 8) | (unsigned char)socksreq[9]),
    678             (unsigned char)socksreq[1]);
    679     }
    680     else if(socksreq[3] == 3) {
    681       failf(data,
    682             "Can't complete SOCKS5 connection to %s:%d. (%d)",
    683             hostname,
    684             (((unsigned char)socksreq[8] << 8) | (unsigned char)socksreq[9]),
    685             (unsigned char)socksreq[1]);
    686     }
    687     else if(socksreq[3] == 4) {
    688       failf(data,
    689             "Can't complete SOCKS5 connection to %02x%02x:%02x%02x:"
    690             "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%d. (%d)",
    691             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
    692             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
    693             (unsigned char)socksreq[8], (unsigned char)socksreq[9],
    694             (unsigned char)socksreq[10], (unsigned char)socksreq[11],
    695             (unsigned char)socksreq[12], (unsigned char)socksreq[13],
    696             (unsigned char)socksreq[14], (unsigned char)socksreq[15],
    697             (unsigned char)socksreq[16], (unsigned char)socksreq[17],
    698             (unsigned char)socksreq[18], (unsigned char)socksreq[19],
    699             (((unsigned char)socksreq[8] << 8) | (unsigned char)socksreq[9]),
    700             (unsigned char)socksreq[1]);
    701     }
    702     return CURLE_COULDNT_CONNECT;
    703   }
    704 
    705   /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
    706      1928, so the reply packet should be read until the end to avoid errors at
    707      subsequent protocol level.
    708 
    709     +----+-----+-------+------+----------+----------+
    710     |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    711     +----+-----+-------+------+----------+----------+
    712     | 1  |  1  | X'00' |  1   | Variable |    2     |
    713     +----+-----+-------+------+----------+----------+
    714 
    715      ATYP:
    716      o  IP v4 address: X'01', BND.ADDR = 4 byte
    717      o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
    718      o  IP v6 address: X'04', BND.ADDR = 16 byte
    719      */
    720 
    721   /* Calculate real packet size */
    722   if(socksreq[3] == 3) {
    723     /* domain name */
    724     int addrlen = (int) socksreq[4];
    725     len = 5 + addrlen + 2;
    726   }
    727   else if(socksreq[3] == 4) {
    728     /* IPv6 */
    729     len = 4 + 16 + 2;
    730   }
    731 
    732   /* At this point we already read first 10 bytes */
    733 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    734   if(!conn->socks5_gssapi_enctype) {
    735     /* decrypt_gssapi_blockread already read the whole packet */
    736 #endif
    737     if(len > 10) {
    738       len -= 10;
    739       result = Curl_blockread_all(conn, sock, (char *)&socksreq[10],
    740                                   len, &actualread);
    741       if(result || (len != actualread)) {
    742         failf(data, "Failed to receive SOCKS5 connect request ack.");
    743         return CURLE_COULDNT_CONNECT;
    744       }
    745     }
    746 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    747   }
    748 #endif
    749 
    750   (void)curlx_nonblock(sock, TRUE);
    751   return CURLE_OK; /* Proxy was successful! */
    752 }
    753 
    754 #endif /* CURL_DISABLE_PROXY */
    755 
    756