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