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