smtp: use the upload buffer size for scratch buffer malloc
[platform/upstream/curl.git] / lib / socks.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2017, 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   timediff_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     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_user,
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_user) {
210     size_t plen = strlen(proxy_user);
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_user, 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[2] << 8) | (unsigned char)socksreq[3]),
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[2] << 8) | (unsigned char)socksreq[3]),
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[2] << 8) | (unsigned char)socksreq[3]),
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[2] << 8) | (unsigned char)socksreq[3]),
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_user,
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   int idx;
379   ssize_t actualread;
380   ssize_t written;
381   int result;
382   CURLcode code;
383   curl_socket_t sock = conn->sock[sockindex];
384   struct Curl_easy *data = conn->data;
385   timediff_t timeout;
386   bool socks5_resolve_local =
387     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
388   const size_t hostname_len = strlen(hostname);
389   ssize_t len = 0;
390   const unsigned long auth = data->set.socks5auth;
391   bool allow_gssapi = FALSE;
392
393   if(conn->bits.httpproxy)
394     infof(conn->data, "SOCKS5: connecting to HTTP proxy %s port %d\n",
395           hostname, remote_port);
396
397   /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
398   if(!socks5_resolve_local && hostname_len > 255) {
399     infof(conn->data, "SOCKS5: server resolving disabled for hostnames of "
400           "length > 255 [actual len=%zu]\n", hostname_len);
401     socks5_resolve_local = TRUE;
402   }
403
404   /* get timeout */
405   timeout = Curl_timeleft(data, NULL, TRUE);
406
407   if(timeout < 0) {
408     /* time-out, bail out, go home */
409     failf(data, "Connection time-out");
410     return CURLE_OPERATION_TIMEDOUT;
411   }
412
413   (void)curlx_nonblock(sock, TRUE);
414
415   /* wait until socket gets connected */
416   result = SOCKET_WRITABLE(sock, timeout);
417
418   if(-1 == result) {
419     failf(conn->data, "SOCKS5: no connection here");
420     return CURLE_COULDNT_CONNECT;
421   }
422   if(0 == result) {
423     failf(conn->data, "SOCKS5: connection timeout");
424     return CURLE_OPERATION_TIMEDOUT;
425   }
426
427   if(result & CURL_CSELECT_ERR) {
428     failf(conn->data, "SOCKS5: error occurred during connection");
429     return CURLE_COULDNT_CONNECT;
430   }
431
432   if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
433     infof(conn->data,
434         "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu\n",
435         auth);
436   if(!(auth & CURLAUTH_BASIC))
437     /* disable username/password auth */
438     proxy_user = NULL;
439 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
440   if(auth & CURLAUTH_GSSAPI)
441     allow_gssapi = TRUE;
442 #endif
443
444   idx = 0;
445   socksreq[idx++] = 5;   /* version */
446   idx++;                 /* reserve for the number of authentication methods */
447   socksreq[idx++] = 0;   /* no authentication */
448   if(allow_gssapi)
449     socksreq[idx++] = 1; /* GSS-API */
450   if(proxy_user)
451     socksreq[idx++] = 2; /* username/password */
452   /* write the number of authentication methods */
453   socksreq[1] = (unsigned char) (idx - 2);
454
455   (void)curlx_nonblock(sock, FALSE);
456
457   infof(data, "SOCKS5 communication to %s:%d\n", hostname, remote_port);
458
459   code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
460                           &written);
461   if(code || (written != (2 + (int)socksreq[1]))) {
462     failf(data, "Unable to send initial SOCKS5 request.");
463     return CURLE_COULDNT_CONNECT;
464   }
465
466   (void)curlx_nonblock(sock, TRUE);
467
468   result = SOCKET_READABLE(sock, timeout);
469
470   if(-1 == result) {
471     failf(conn->data, "SOCKS5 nothing to read");
472     return CURLE_COULDNT_CONNECT;
473   }
474   if(0 == result) {
475     failf(conn->data, "SOCKS5 read timeout");
476     return CURLE_OPERATION_TIMEDOUT;
477   }
478
479   if(result & CURL_CSELECT_ERR) {
480     failf(conn->data, "SOCKS5 read error occurred");
481     return CURLE_RECV_ERROR;
482   }
483
484   (void)curlx_nonblock(sock, FALSE);
485
486   result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
487   if(result || (actualread != 2)) {
488     failf(data, "Unable to receive initial SOCKS5 response.");
489     return CURLE_COULDNT_CONNECT;
490   }
491
492   if(socksreq[0] != 5) {
493     failf(data, "Received invalid version in initial SOCKS5 response.");
494     return CURLE_COULDNT_CONNECT;
495   }
496   if(socksreq[1] == 0) {
497     /* Nothing to do, no authentication needed */
498     ;
499   }
500 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
501   else if(allow_gssapi && (socksreq[1] == 1)) {
502     code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
503     if(code) {
504       failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
505       return CURLE_COULDNT_CONNECT;
506     }
507   }
508 #endif
509   else if(socksreq[1] == 2) {
510     /* Needs user name and password */
511     size_t proxy_user_len, proxy_password_len;
512     if(proxy_user && proxy_password) {
513       proxy_user_len = strlen(proxy_user);
514       proxy_password_len = strlen(proxy_password);
515     }
516     else {
517       proxy_user_len = 0;
518       proxy_password_len = 0;
519     }
520
521     /*   username/password request looks like
522      * +----+------+----------+------+----------+
523      * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
524      * +----+------+----------+------+----------+
525      * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
526      * +----+------+----------+------+----------+
527      */
528     len = 0;
529     socksreq[len++] = 1;    /* username/pw subnegotiation version */
530     socksreq[len++] = (unsigned char) proxy_user_len;
531     if(proxy_user && proxy_user_len)
532       memcpy(socksreq + len, proxy_user, proxy_user_len);
533     len += proxy_user_len;
534     socksreq[len++] = (unsigned char) proxy_password_len;
535     if(proxy_password && proxy_password_len)
536       memcpy(socksreq + len, proxy_password, proxy_password_len);
537     len += proxy_password_len;
538
539     code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
540     if(code || (len != written)) {
541       failf(data, "Failed to send SOCKS5 sub-negotiation request.");
542       return CURLE_COULDNT_CONNECT;
543     }
544
545     result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
546     if(result || (actualread != 2)) {
547       failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
548       return CURLE_COULDNT_CONNECT;
549     }
550
551     /* ignore the first (VER) byte */
552     if(socksreq[1] != 0) { /* status */
553       failf(data, "User was rejected by the SOCKS5 server (%d %d).",
554             socksreq[0], socksreq[1]);
555       return CURLE_COULDNT_CONNECT;
556     }
557
558     /* Everything is good so far, user was authenticated! */
559   }
560   else {
561     /* error */
562     if(!allow_gssapi && (socksreq[1] == 1)) {
563       failf(data,
564             "SOCKS5 GSSAPI per-message authentication is not supported.");
565       return CURLE_COULDNT_CONNECT;
566     }
567     if(socksreq[1] == 255) {
568       if(!proxy_user || !*proxy_user) {
569         failf(data,
570               "No authentication method was acceptable. (It is quite likely"
571               " that the SOCKS5 server wanted a username/password, since none"
572               " was supplied to the server on this connection.)");
573       }
574       else {
575         failf(data, "No authentication method was acceptable.");
576       }
577       return CURLE_COULDNT_CONNECT;
578     }
579     else {
580       failf(data,
581             "Undocumented SOCKS5 mode attempted to be used by server.");
582       return CURLE_COULDNT_CONNECT;
583     }
584   }
585
586   /* Authentication is complete, now specify destination to the proxy */
587   len = 0;
588   socksreq[len++] = 5; /* version (SOCKS5) */
589   socksreq[len++] = 1; /* connect */
590   socksreq[len++] = 0; /* must be zero */
591
592   if(!socks5_resolve_local) {
593     socksreq[len++] = 3; /* ATYP: domain name = 3 */
594     socksreq[len++] = (char) hostname_len; /* address length */
595     memcpy(&socksreq[len], hostname, hostname_len); /* address str w/o NULL */
596     len += hostname_len;
597   }
598   else {
599     struct Curl_dns_entry *dns;
600     Curl_addrinfo *hp = NULL;
601     int rc = Curl_resolv(conn, hostname, remote_port, &dns);
602
603     if(rc == CURLRESOLV_ERROR)
604       return CURLE_COULDNT_RESOLVE_HOST;
605
606     if(rc == CURLRESOLV_PENDING) {
607       /* this requires that we're in "wait for resolve" state */
608       code = Curl_resolver_wait_resolv(conn, &dns);
609       if(code)
610         return code;
611     }
612
613     /*
614      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
615      * returns a Curl_addrinfo pointer that may not always look the same.
616      */
617     if(dns)
618       hp = dns->addr;
619     if(hp) {
620       int i;
621       char buf[64];
622       Curl_printable_address(hp, buf, sizeof(buf));
623
624       if(hp->ai_family == AF_INET) {
625         struct sockaddr_in *saddr_in;
626         socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
627
628         saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
629         for(i = 0; i < 4; i++) {
630           socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
631         }
632
633         infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", buf);
634       }
635 #ifdef ENABLE_IPV6
636       else if(hp->ai_family == AF_INET6) {
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