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#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 */
59int 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                         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*/
123CURLcode 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(data, 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  socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
164  socksreq[3] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
165
166  /* DNS resolve only for SOCKS4, not SOCKS4a */
167  if(!protocol4a) {
168    struct Curl_dns_entry *dns;
169    Curl_addrinfo *hp=NULL;
170    int rc;
171
172    rc = Curl_resolv(conn, hostname, remote_port, &dns);
173
174    if(rc == CURLRESOLV_ERROR)
175      return CURLE_COULDNT_RESOLVE_PROXY;
176
177    if(rc == CURLRESOLV_PENDING)
178      /* ignores the return code, but 'dns' remains NULL on failure */
179      (void)Curl_resolver_wait_resolv(conn, &dns);
180
181    /*
182     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
183     * returns a Curl_addrinfo pointer that may not always look the same.
184     */
185    if(dns)
186      hp=dns->addr;
187    if(hp) {
188      char buf[64];
189      unsigned short ip[4];
190      Curl_printable_address(hp, buf, sizeof(buf));
191
192      if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
193                      &ip[0], &ip[1], &ip[2], &ip[3])) {
194        /* Set DSTIP */
195        socksreq[4] = (unsigned char)ip[0];
196        socksreq[5] = (unsigned char)ip[1];
197        socksreq[6] = (unsigned char)ip[2];
198        socksreq[7] = (unsigned char)ip[3];
199      }
200      else
201        hp = NULL; /* fail! */
202
203      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
204
205    }
206    if(!hp) {
207      failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
208            hostname);
209      return CURLE_COULDNT_RESOLVE_HOST;
210    }
211  }
212
213  /*
214   * This is currently not supporting "Identification Protocol (RFC1413)".
215   */
216  socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
217  if(proxy_name)
218    strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
219
220  /*
221   * Make connection
222   */
223  {
224    ssize_t actualread;
225    ssize_t written;
226    ssize_t hostnamelen = 0;
227    int packetsize = 9 +
228      (int)strlen((char*)socksreq + 8); /* size including NUL */
229
230    /* If SOCKS4a, set special invalid IP address 0.0.0.x */
231    if(protocol4a) {
232      socksreq[4] = 0;
233      socksreq[5] = 0;
234      socksreq[6] = 0;
235      socksreq[7] = 1;
236      /* If still enough room in buffer, also append hostname */
237      hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
238      if(packetsize + hostnamelen <= SOCKS4REQLEN)
239        strcpy((char*)socksreq + packetsize, hostname);
240      else
241        hostnamelen = 0; /* Flag: hostname did not fit in buffer */
242    }
243
244    /* Send request */
245    code = Curl_write_plain(conn, sock, (char *)socksreq,
246                            packetsize + hostnamelen,
247                            &written);
248    if((code != CURLE_OK) || (written != packetsize + hostnamelen)) {
249      failf(data, "Failed to send SOCKS4 connect request.");
250      return CURLE_COULDNT_CONNECT;
251    }
252    if(protocol4a && hostnamelen == 0) {
253      /* SOCKS4a with very long hostname - send that name separately */
254      hostnamelen = (ssize_t)strlen(hostname) + 1;
255      code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen,
256                              &written);
257      if((code != CURLE_OK) || (written != hostnamelen)) {
258        failf(data, "Failed to send SOCKS4 connect request.");
259        return CURLE_COULDNT_CONNECT;
260      }
261    }
262
263    packetsize = 8; /* receive data size */
264
265    /* Receive response */
266    result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
267                           &actualread, timeout);
268    if((result != CURLE_OK) || (actualread != packetsize)) {
269      failf(data, "Failed to receive SOCKS4 connect request ack.");
270      return CURLE_COULDNT_CONNECT;
271    }
272
273    /*
274     * Response format
275     *
276     *     +----+----+----+----+----+----+----+----+
277     *     | VN | CD | DSTPORT |      DSTIP        |
278     *     +----+----+----+----+----+----+----+----+
279     * # of bytes:  1    1      2              4
280     *
281     * VN is the version of the reply code and should be 0. CD is the result
282     * code with one of the following values:
283     *
284     * 90: request granted
285     * 91: request rejected or failed
286     * 92: request rejected because SOCKS server cannot connect to
287     *     identd on the client
288     * 93: request rejected because the client program and identd
289     *     report different user-ids
290     */
291
292    /* wrong version ? */
293    if(socksreq[0] != 0) {
294      failf(data,
295            "SOCKS4 reply has wrong version, version should be 4.");
296      return CURLE_COULDNT_CONNECT;
297    }
298
299    /* Result */
300    switch(socksreq[1]) {
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            ((socksreq[8] << 8) | socksreq[9]),
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            ((socksreq[8] << 8) | socksreq[9]),
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            ((socksreq[8] << 8) | socksreq[9]),
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            ((socksreq[8] << 8) | socksreq[9]),
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 */
357CURLcode 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)(conn->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    infof(conn->data,"SOCKS5: server resolving disabled for hostnames of "
396          "length > 255 [actual len=%zu]\n", hostname_len);
397    socks5_resolve_local = TRUE;
398  }
399
400  /* get timeout */
401  timeout = Curl_timeleft(data, NULL, TRUE);
402
403  if(timeout < 0) {
404    /* time-out, bail out, go home */
405    failf(data, "Connection time-out");
406    return CURLE_OPERATION_TIMEDOUT;
407  }
408
409  curlx_nonblock(sock, TRUE);
410
411  /* wait until socket gets connected */
412  result = Curl_socket_ready(CURL_SOCKET_BAD, sock, timeout);
413
414  if(-1 == result) {
415    failf(conn->data, "SOCKS5: no connection here");
416    return CURLE_COULDNT_CONNECT;
417  }
418  else if(0 == result) {
419    failf(conn->data, "SOCKS5: connection timeout");
420    return CURLE_OPERATION_TIMEDOUT;
421  }
422
423  if(result & CURL_CSELECT_ERR) {
424    failf(conn->data, "SOCKS5: error occurred during connection");
425    return CURLE_COULDNT_CONNECT;
426  }
427
428  socksreq[0] = 5; /* version */
429#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
430  socksreq[1] = (char)(proxy_name ? 3 : 2); /* number of methods (below) */
431  socksreq[2] = 0; /* no authentication */
432  socksreq[3] = 1; /* gssapi */
433  socksreq[4] = 2; /* username/password */
434#else
435  socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
436  socksreq[2] = 0; /* no authentication */
437  socksreq[3] = 2; /* username/password */
438#endif
439
440  curlx_nonblock(sock, FALSE);
441
442  code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
443                          &written);
444  if((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
445    failf(data, "Unable to send initial SOCKS5 request.");
446    return CURLE_COULDNT_CONNECT;
447  }
448
449  curlx_nonblock(sock, TRUE);
450
451  result = Curl_socket_ready(sock, CURL_SOCKET_BAD, timeout);
452
453  if(-1 == result) {
454    failf(conn->data, "SOCKS5 nothing to read");
455    return CURLE_COULDNT_CONNECT;
456  }
457  else if(0 == result) {
458    failf(conn->data, "SOCKS5 read timeout");
459    return CURLE_OPERATION_TIMEDOUT;
460  }
461
462  if(result & CURL_CSELECT_ERR) {
463    failf(conn->data, "SOCKS5 read error occurred");
464    return CURLE_RECV_ERROR;
465  }
466
467  curlx_nonblock(sock, FALSE);
468
469  result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
470                            timeout);
471  if((result != CURLE_OK) || (actualread != 2)) {
472    failf(data, "Unable to receive initial SOCKS5 response.");
473    return CURLE_COULDNT_CONNECT;
474  }
475
476  if(socksreq[0] != 5) {
477    failf(data, "Received invalid version in initial SOCKS5 response.");
478    return CURLE_COULDNT_CONNECT;
479  }
480  if(socksreq[1] == 0) {
481    /* Nothing to do, no authentication needed */
482    ;
483  }
484#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
485  else if(socksreq[1] == 1) {
486    code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
487    if(code != CURLE_OK) {
488      failf(data, "Unable to negotiate SOCKS5 gssapi context.");
489      return CURLE_COULDNT_CONNECT;
490    }
491  }
492#endif
493  else if(socksreq[1] == 2) {
494    /* Needs user name and password */
495    size_t userlen, pwlen;
496    int len;
497    if(proxy_name && proxy_password) {
498      userlen = strlen(proxy_name);
499      pwlen = strlen(proxy_password);
500    }
501    else {
502      userlen = 0;
503      pwlen = 0;
504    }
505
506    /*   username/password request looks like
507     * +----+------+----------+------+----------+
508     * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
509     * +----+------+----------+------+----------+
510     * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
511     * +----+------+----------+------+----------+
512     */
513    len = 0;
514    socksreq[len++] = 1;    /* username/pw subnegotiation version */
515    socksreq[len++] = (unsigned char) userlen;
516    if(proxy_name && userlen)
517      memcpy(socksreq + len, proxy_name, userlen);
518    len += (int)userlen;
519    socksreq[len++] = (unsigned char) pwlen;
520    if(proxy_password && pwlen)
521      memcpy(socksreq + len, proxy_password, pwlen);
522    len += (int)pwlen;
523
524    code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
525    if((code != CURLE_OK) || (len != written)) {
526      failf(data, "Failed to send SOCKS5 sub-negotiation request.");
527      return CURLE_COULDNT_CONNECT;
528    }
529
530    result=Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
531                         timeout);
532    if((result != CURLE_OK) || (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  socksreq[0] = 5; /* version (SOCKS5) */
578  socksreq[1] = 1; /* connect */
579  socksreq[2] = 0; /* must be zero */
580
581  if(!socks5_resolve_local) {
582    packetsize = (ssize_t)(5 + hostname_len + 2);
583
584    socksreq[3] = 3; /* ATYP: domain name = 3 */
585    socksreq[4] = (char) hostname_len; /* address length */
586    memcpy(&socksreq[5], hostname, hostname_len); /* address bytes w/o NULL */
587
588    /* PORT MSB */
589    socksreq[hostname_len+5] = (unsigned char)((remote_port >> 8) & 0xff);
590    /* PORT LSB */
591    socksreq[hostname_len+6] = (unsigned char)(remote_port & 0xff);
592  }
593  else {
594    struct Curl_dns_entry *dns;
595    Curl_addrinfo *hp=NULL;
596    int rc = Curl_resolv(conn, hostname, remote_port, &dns);
597
598    packetsize = 10;
599
600    socksreq[3] = 1; /* IPv4 = 1 */
601
602    if(rc == CURLRESOLV_ERROR)
603      return CURLE_COULDNT_RESOLVE_HOST;
604
605    if(rc == CURLRESOLV_PENDING) {
606      /* this requires that we're in "wait for resolve" state */
607      code = Curl_resolver_wait_resolv(conn, &dns);
608      if(code != CURLE_OK)
609        return code;
610    }
611
612    /*
613     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
614     * returns a Curl_addrinfo pointer that may not always look the same.
615     */
616    if(dns)
617      hp=dns->addr;
618    if(hp) {
619      char buf[64];
620      unsigned short ip[4];
621      Curl_printable_address(hp, buf, sizeof(buf));
622
623      if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
624                      &ip[0], &ip[1], &ip[2], &ip[3])) {
625        socksreq[4] = (unsigned char)ip[0];
626        socksreq[5] = (unsigned char)ip[1];
627        socksreq[6] = (unsigned char)ip[2];
628        socksreq[7] = (unsigned char)ip[3];
629      }
630      else
631        hp = NULL; /* fail! */
632
633      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
634    }
635    if(!hp) {
636      failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
637            hostname);
638      return CURLE_COULDNT_RESOLVE_HOST;
639    }
640
641    socksreq[8] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
642    socksreq[9] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
643  }
644
645#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
646  if(conn->socks5_gssapi_enctype) {
647    failf(data, "SOCKS5 gssapi protection not yet implemented.");
648  }
649  else
650#endif
651    code = Curl_write_plain(conn, sock, (char *)socksreq, packetsize,
652                            &written);
653  if((code != CURLE_OK) || (written != packetsize)) {
654    failf(data, "Failed to send SOCKS5 connect request.");
655    return CURLE_COULDNT_CONNECT;
656  }
657
658  packetsize = 10; /* minimum packet size is 10 */
659
660#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
661  if(conn->socks5_gssapi_enctype) {
662    failf(data, "SOCKS5 gssapi protection not yet implemented.");
663  }
664  else
665#endif
666    result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
667                           &actualread, timeout);
668  if((result != CURLE_OK) || (actualread != packetsize)) {
669    failf(data, "Failed to receive SOCKS5 connect request ack.");
670    return CURLE_COULDNT_CONNECT;
671  }
672
673  if(socksreq[0] != 5) { /* version */
674    failf(data,
675          "SOCKS5 reply has wrong version, version should be 5.");
676    return CURLE_COULDNT_CONNECT;
677  }
678  if(socksreq[1] != 0) { /* Anything besides 0 is an error */
679      failf(data,
680            "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
681            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
682            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
683            ((socksreq[8] << 8) | socksreq[9]),
684            socksreq[1]);
685      return CURLE_COULDNT_CONNECT;
686  }
687
688  /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
689     1928, so the reply packet should be read until the end to avoid errors at
690     subsequent protocol level.
691
692    +----+-----+-------+------+----------+----------+
693    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
694    +----+-----+-------+------+----------+----------+
695    | 1  |  1  | X'00' |  1   | Variable |    2     |
696    +----+-----+-------+------+----------+----------+
697
698     ATYP:
699     o  IP v4 address: X'01', BND.ADDR = 4 byte
700     o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
701     o  IP v6 address: X'04', BND.ADDR = 16 byte
702     */
703
704  /* Calculate real packet size */
705  if(socksreq[3] == 3) {
706    /* domain name */
707    int addrlen = (int) socksreq[4];
708    packetsize = 5 + addrlen + 2;
709  }
710  else if(socksreq[3] == 4) {
711    /* IPv6 */
712    packetsize = 4 + 16 + 2;
713  }
714
715  /* At this point we already read first 10 bytes */
716#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
717  if(!conn->socks5_gssapi_enctype) {
718    /* decrypt_gssapi_blockread already read the whole packet */
719#endif
720    if(packetsize > 10) {
721      packetsize -= 10;
722      result = Curl_blockread_all(conn, sock, (char *)&socksreq[10],
723                                  packetsize, &actualread, timeout);
724      if((result != CURLE_OK) || (actualread != packetsize)) {
725        failf(data, "Failed to receive SOCKS5 connect request ack.");
726        return CURLE_COULDNT_CONNECT;
727      }
728    }
729#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
730  }
731#endif
732
733  curlx_nonblock(sock, TRUE);
734  return CURLE_OK; /* Proxy was successful! */
735}
736
737#endif /* CURL_DISABLE_PROXY */
738
739