7b5740be425f707b3e6bc6dba99fcde4c374259a
[external/curl.git] / lib / socks.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2010, 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 http://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 "setup.h"
24
25 #if !defined(CURL_DISABLE_PROXY) || defined(USE_WINDOWS_SSPI)
26 #include <string.h>
27
28 #ifdef HAVE_STDLIB_H
29 #include <stdlib.h>
30 #endif
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
33 #endif
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37 #ifdef HAVE_ARPA_INET_H
38 #include <arpa/inet.h>
39 #endif
40
41 #include "urldata.h"
42 #include "sendf.h"
43 #include "strequal.h"
44 #include "select.h"
45 #include "connect.h"
46 #include "timeval.h"
47 #include "socks.h"
48
49 /* The last #include file should be: */
50 #include "memdebug.h"
51
52 /*
53  * Helper read-from-socket functions. Does the same as Curl_read() but it
54  * blocks until all bytes amount of buffersize will be read. No more, no less.
55  *
56  * This is STUPID BLOCKING behaviour which we frown upon, but right now this
57  * is what we have...
58  */
59 int Curl_blockread_all(struct connectdata *conn, /* connection data */
60                        curl_socket_t sockfd,     /* read from this socket */
61                        char *buf,                /* store read data here */
62                        ssize_t buffersize,       /* max amount to read */
63                        ssize_t *n,               /* amount bytes read */
64                        long conn_timeout)        /* timeout for data wait
65                                                     relative to
66                                                     conn->created */
67 {
68   ssize_t nread;
69   ssize_t allread = 0;
70   int result;
71   struct timeval tvnow;
72   long conntime;
73   *n = 0;
74   for(;;) {
75     tvnow = Curl_tvnow();
76     /* calculating how long connection is establishing */
77     conntime = Curl_tvdiff(tvnow, conn->created);
78     if(conntime > conn_timeout) {
79       /* we already got the timeout */
80       result = CURLE_OPERATION_TIMEDOUT;
81       break;
82     }
83     if(Curl_socket_ready(sockfd, CURL_SOCKET_BAD,
84                    (int)(conn_timeout - conntime)) <= 0) {
85       result = ~CURLE_OK;
86       break;
87     }
88     result = Curl_read_plain(sockfd, buf, buffersize, &nread);
89     if(CURLE_AGAIN == result)
90       continue;
91     else if(result)
92       break;
93
94     if(buffersize == nread) {
95       allread += nread;
96       *n = allread;
97       result = CURLE_OK;
98       break;
99     }
100     if(!nread) {
101       result = ~CURLE_OK;
102       break;
103     }
104
105     buffersize -= nread;
106     buf += nread;
107     allread += nread;
108   }
109   return result;
110 }
111
112 /*
113 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
114 * destination server.
115 *
116 * Reference :
117 *   http://socks.permeo.com/protocol/socks4.protocol
118 *
119 * Note :
120 *   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
121 *   Nonsupport "Identification Protocol (RFC1413)"
122 */
123 CURLcode Curl_SOCKS4(const char *proxy_name,
124                      const char *hostname,
125                      int remote_port,
126                      int sockindex,
127                      struct connectdata *conn,
128                      bool protocol4a)
129 {
130 #define SOCKS4REQLEN 262
131   unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
132                                            id */
133   int result;
134   CURLcode code;
135   curl_socket_t sock = conn->sock[sockindex];
136   long timeout;
137   struct SessionHandle *data = conn->data;
138
139   /* get timeout */
140   timeout = Curl_timeleft(conn, NULL, TRUE);
141
142   if(timeout < 0) {
143     /* time-out, bail out, go home */
144     failf(data, "Connection time-out");
145     return CURLE_OPERATION_TIMEDOUT;
146   }
147
148   curlx_nonblock(sock, FALSE);
149
150   /*
151    * Compose socks4 request
152    *
153    * Request format
154    *
155    *     +----+----+----+----+----+----+----+----+----+----+....+----+
156    *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
157    *     +----+----+----+----+----+----+----+----+----+----+....+----+
158    * # of bytes:  1    1      2              4           variable       1
159    */
160
161   socksreq[0] = 4; /* version (SOCKS4) */
162   socksreq[1] = 1; /* connect */
163   *((unsigned short*)&socksreq[2]) = htons((unsigned short)remote_port);
164
165   /* DNS resolve only for SOCKS4, not SOCKS4a */
166   if (!protocol4a) {
167     struct Curl_dns_entry *dns;
168     Curl_addrinfo *hp=NULL;
169     int rc;
170
171     rc = Curl_resolv(conn, hostname, remote_port, &dns);
172
173     if(rc == CURLRESOLV_ERROR)
174       return CURLE_COULDNT_RESOLVE_PROXY;
175
176     if(rc == CURLRESOLV_PENDING)
177       /* ignores the return code, but 'dns' remains NULL on failure */
178       (void)Curl_wait_for_resolv(conn, &dns);
179
180     /*
181      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
182      * returns a Curl_addrinfo pointer that may not always look the same.
183      */
184     if(dns)
185       hp=dns->addr;
186     if(hp) {
187       char buf[64];
188       unsigned short ip[4];
189       Curl_printable_address(hp, buf, sizeof(buf));
190
191       if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
192                       &ip[0], &ip[1], &ip[2], &ip[3])) {
193         /* Set DSTIP */
194         socksreq[4] = (unsigned char)ip[0];
195         socksreq[5] = (unsigned char)ip[1];
196         socksreq[6] = (unsigned char)ip[2];
197         socksreq[7] = (unsigned char)ip[3];
198       }
199       else
200         hp = NULL; /* fail! */
201
202       Curl_resolv_unlock(data, dns); /* not used anymore from now on */
203
204     }
205     if(!hp) {
206       failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
207             hostname);
208       return CURLE_COULDNT_RESOLVE_HOST;
209     }
210   }
211
212   /*
213    * This is currently not supporting "Identification Protocol (RFC1413)".
214    */
215   socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
216   if(proxy_name)
217     strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
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 != CURLE_OK) || (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 != CURLE_OK) || (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, timeout);
267     if((result != CURLE_OK) || (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     {
301     case 90:
302       if (protocol4a)
303         infof(data, "SOCKS4a request granted.\n");
304       else
305         infof(data, "SOCKS4 request granted.\n");
306       break;
307     case 91:
308       failf(data,
309             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
310             ", request rejected or failed.",
311             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
312             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
313             (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
314             socksreq[1]);
315       return CURLE_COULDNT_CONNECT;
316     case 92:
317       failf(data,
318             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
319             ", request rejected because SOCKS server cannot connect to "
320             "identd on the client.",
321             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
322             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
323             (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
324             socksreq[1]);
325       return CURLE_COULDNT_CONNECT;
326     case 93:
327       failf(data,
328             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
329             ", request rejected because the client program and identd "
330             "report different user-ids.",
331             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
332             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
333             (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
334             socksreq[1]);
335       return CURLE_COULDNT_CONNECT;
336     default:
337       failf(data,
338             "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
339             ", Unknown.",
340             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
341             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
342             (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
343             socksreq[1]);
344       return CURLE_COULDNT_CONNECT;
345     }
346   }
347
348   curlx_nonblock(sock, TRUE);
349
350   return CURLE_OK; /* Proxy was successful! */
351 }
352
353 /*
354  * This function logs in to a SOCKS5 proxy and sends the specifics to the final
355  * destination server.
356  */
357 CURLcode Curl_SOCKS5(const char *proxy_name,
358                      const char *proxy_password,
359                      const char *hostname,
360                      int remote_port,
361                      int sockindex,
362                      struct connectdata *conn)
363 {
364   /*
365     According to the RFC1928, section "6.  Replies". This is what a SOCK5
366     replies:
367
368         +----+-----+-------+------+----------+----------+
369         |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
370         +----+-----+-------+------+----------+----------+
371         | 1  |  1  | X'00' |  1   | Variable |    2     |
372         +----+-----+-------+------+----------+----------+
373
374     Where:
375
376     o  VER    protocol version: X'05'
377     o  REP    Reply field:
378     o  X'00' succeeded
379   */
380
381   unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
382   ssize_t actualread;
383   ssize_t written;
384   int result;
385   CURLcode code;
386   curl_socket_t sock = conn->sock[sockindex];
387   struct SessionHandle *data = conn->data;
388   long timeout;
389   bool socks5_resolve_local = (bool)(data->set.proxytype == CURLPROXY_SOCKS5);
390   const size_t hostname_len = strlen(hostname);
391   ssize_t packetsize = 0;
392
393   /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
394   if(!socks5_resolve_local && hostname_len > 255)
395   {
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(conn, 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   curlx_nonblock(sock, TRUE);
411
412   /* wait until socket gets connected */
413   result = Curl_socket_ready(CURL_SOCKET_BAD, sock, (int)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 occured 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; /* gssapi */
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   curlx_nonblock(sock, FALSE);
442
443   code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
444                           &written);
445   if((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
446     failf(data, "Unable to send initial SOCKS5 request.");
447     return CURLE_COULDNT_CONNECT;
448   }
449
450   curlx_nonblock(sock, TRUE);
451
452   result = Curl_socket_ready(sock, CURL_SOCKET_BAD, (int)timeout);
453
454   if(-1 == result) {
455     failf(conn->data, "SOCKS5 nothing to read");
456     return CURLE_COULDNT_CONNECT;
457   }
458   else if(0 == result) {
459     failf(conn->data, "SOCKS5 read timeout");
460     return CURLE_OPERATION_TIMEDOUT;
461   }
462
463   if(result & CURL_CSELECT_ERR) {
464     failf(conn->data, "SOCKS5 read error occured");
465     return CURLE_RECV_ERROR;
466   }
467
468   curlx_nonblock(sock, FALSE);
469
470   result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
471                             timeout);
472   if((result != CURLE_OK) || (actualread != 2)) {
473     failf(data, "Unable to receive initial SOCKS5 response.");
474     return CURLE_COULDNT_CONNECT;
475   }
476
477   if(socksreq[0] != 5) {
478     failf(data, "Received invalid version in initial SOCKS5 response.");
479     return CURLE_COULDNT_CONNECT;
480   }
481   if(socksreq[1] == 0) {
482     /* Nothing to do, no authentication needed */
483     ;
484   }
485 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
486   else if(socksreq[1] == 1) {
487     code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
488     if(code != CURLE_OK) {
489       failf(data, "Unable to negotiate SOCKS5 gssapi context.");
490       return CURLE_COULDNT_CONNECT;
491     }
492   }
493 #endif
494   else if(socksreq[1] == 2) {
495     /* Needs user name and password */
496     size_t userlen, pwlen;
497     int len;
498     if(proxy_name && proxy_password) {
499       userlen = strlen(proxy_name);
500       pwlen = strlen(proxy_password);
501     }
502     else {
503       userlen = 0;
504       pwlen = 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) userlen;
517     if(proxy_name && userlen)
518       memcpy(socksreq + len, proxy_name, userlen);
519     len += (int)userlen;
520     socksreq[len++] = (unsigned char) pwlen;
521     if(proxy_password && pwlen)
522       memcpy(socksreq + len, proxy_password, pwlen);
523     len += (int)pwlen;
524
525     code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
526     if((code != CURLE_OK) || (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                          timeout);
533     if((result != CURLE_OK) || (actualread != 2)) {
534       failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
535       return CURLE_COULDNT_CONNECT;
536     }
537
538     /* ignore the first (VER) byte */
539     if(socksreq[1] != 0) { /* status */
540       failf(data, "User was rejected by the SOCKS5 server (%d %d).",
541             socksreq[0], socksreq[1]);
542       return CURLE_COULDNT_CONNECT;
543     }
544
545     /* Everything is good so far, user was authenticated! */
546   }
547   else {
548     /* error */
549 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
550     if(socksreq[1] == 255) {
551 #else
552     if(socksreq[1] == 1) {
553       failf(data,
554             "SOCKS5 GSSAPI per-message authentication is not supported.");
555       return CURLE_COULDNT_CONNECT;
556     }
557     else if(socksreq[1] == 255) {
558 #endif
559       if(!proxy_name || !*proxy_name) {
560         failf(data,
561               "No authentication method was acceptable. (It is quite likely"
562               " that the SOCKS5 server wanted a username/password, since none"
563               " was supplied to the server on this connection.)");
564       }
565       else {
566         failf(data, "No authentication method was acceptable.");
567       }
568       return CURLE_COULDNT_CONNECT;
569     }
570     else {
571       failf(data,
572             "Undocumented SOCKS5 mode attempted to be used by server.");
573       return CURLE_COULDNT_CONNECT;
574     }
575   }
576
577   /* Authentication is complete, now specify destination to the proxy */
578   socksreq[0] = 5; /* version (SOCKS5) */
579   socksreq[1] = 1; /* connect */
580   socksreq[2] = 0; /* must be zero */
581
582   if(!socks5_resolve_local) {
583     packetsize = (ssize_t)(5 + hostname_len + 2);
584
585     socksreq[3] = 3; /* ATYP: domain name = 3 */
586     socksreq[4] = (char) hostname_len; /* address length */
587     memcpy(&socksreq[5], hostname, hostname_len); /* address bytes w/o NULL */
588
589     *((unsigned short*)&socksreq[hostname_len+5]) =
590       htons((unsigned short)remote_port);
591   }
592   else {
593     struct Curl_dns_entry *dns;
594     Curl_addrinfo *hp=NULL;
595     int rc = Curl_resolv(conn, hostname, remote_port, &dns);
596
597     packetsize = 10;
598
599     socksreq[3] = 1; /* IPv4 = 1 */
600
601     if(rc == CURLRESOLV_ERROR)
602       return CURLE_COULDNT_RESOLVE_HOST;
603
604     if(rc == CURLRESOLV_PENDING) {
605       /* this requires that we're in "wait for resolve" state */
606       code = Curl_wait_for_resolv(conn, &dns);
607       if(code != CURLE_OK)
608         return code;
609     }
610
611     /*
612      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
613      * returns a Curl_addrinfo pointer that may not always look the same.
614      */
615     if(dns)
616       hp=dns->addr;
617     if(hp) {
618       char buf[64];
619       unsigned short ip[4];
620       Curl_printable_address(hp, buf, sizeof(buf));
621
622       if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
623                       &ip[0], &ip[1], &ip[2], &ip[3])) {
624         socksreq[4] = (unsigned char)ip[0];
625         socksreq[5] = (unsigned char)ip[1];
626         socksreq[6] = (unsigned char)ip[2];
627         socksreq[7] = (unsigned char)ip[3];
628       }
629       else
630         hp = NULL; /* fail! */
631
632       Curl_resolv_unlock(data, dns); /* not used anymore from now on */
633     }
634     if(!hp) {
635       failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
636             hostname);
637       return CURLE_COULDNT_RESOLVE_HOST;
638     }
639
640     *((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port);
641   }
642
643 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
644   if(conn->socks5_gssapi_enctype) {
645     failf(data, "SOCKS5 gssapi protection not yet implemented.");
646   } else
647 #endif
648   code = Curl_write_plain(conn, sock, (char *)socksreq, packetsize, &written);
649   if((code != CURLE_OK) || (written != packetsize)) {
650     failf(data, "Failed to send SOCKS5 connect request.");
651     return CURLE_COULDNT_CONNECT;
652   }
653
654   packetsize = 10; /* minimum packet size is 10 */
655
656 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
657   if(conn->socks5_gssapi_enctype) {
658     failf(data, "SOCKS5 gssapi protection not yet implemented.");
659   } else
660 #endif
661     result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
662                            &actualread, timeout);
663   if((result != CURLE_OK) || (actualread != packetsize)) {
664     failf(data, "Failed to receive SOCKS5 connect request ack.");
665     return CURLE_COULDNT_CONNECT;
666   }
667
668   if(socksreq[0] != 5) { /* version */
669     failf(data,
670           "SOCKS5 reply has wrong version, version should be 5.");
671     return CURLE_COULDNT_CONNECT;
672   }
673   if(socksreq[1] != 0) { /* Anything besides 0 is an error */
674       failf(data,
675             "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
676             (unsigned char)socksreq[4], (unsigned char)socksreq[5],
677             (unsigned char)socksreq[6], (unsigned char)socksreq[7],
678             (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
679             socksreq[1]);
680       return CURLE_COULDNT_CONNECT;
681   }
682
683   /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
684      1928, so the reply packet should be read until the end to avoid errors at
685      subsequent protocol level.
686
687     +----+-----+-------+------+----------+----------+
688     |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
689     +----+-----+-------+------+----------+----------+
690     | 1  |  1  | X'00' |  1   | Variable |    2     |
691     +----+-----+-------+------+----------+----------+
692
693      ATYP:
694      o  IP v4 address: X'01', BND.ADDR = 4 byte
695      o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
696      o  IP v6 address: X'04', BND.ADDR = 16 byte
697      */
698
699   /* Calculate real packet size */
700   if(socksreq[3] == 3) {
701     /* domain name */
702     int addrlen = (int) socksreq[4];
703     packetsize = 5 + addrlen + 2;
704   }
705   else if(socksreq[3] == 4) {
706     /* IPv6 */
707     packetsize = 4 + 16 + 2;
708   }
709
710   /* At this point we already read first 10 bytes */
711 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
712   if(!conn->socks5_gssapi_enctype) {
713     /* decrypt_gssapi_blockread already read the whole packet */
714 #endif
715     if(packetsize > 10) {
716       packetsize -= 10;
717       result = Curl_blockread_all(conn, sock, (char *)&socksreq[10],
718                                   packetsize, &actualread, timeout);
719       if((result != CURLE_OK) || (actualread != packetsize)) {
720         failf(data, "Failed to receive SOCKS5 connect request ack.");
721         return CURLE_COULDNT_CONNECT;
722       }
723     }
724 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
725   }
726 #endif
727
728   curlx_nonblock(sock, TRUE);
729   return CURLE_OK; /* Proxy was successful! */
730 }
731
732 #endif /* CURL_DISABLE_PROXY */
733