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