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