~ubuntu-branches/ubuntu/lucid/curl/lucid-201101212007

« back to all changes in this revision

Viewing changes to lib/socks.c

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2008-02-08 11:20:41 UTC
  • mto: (3.1.1 lenny) (1.2.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 26.
  • Revision ID: james.westby@ubuntu.com-20080208112041-hed7sb5r6ghmjf8v
Tags: upstream-7.18.0
ImportĀ upstreamĀ versionĀ 7.18.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
 *                            | (__| |_| |  _ <| |___
6
6
 *                             \___|\___/|_| \_\_____|
7
7
 *
8
 
 * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
 
8
 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
9
9
 *
10
10
 * This software is licensed as described in the file COPYING, which
11
11
 * you should have received as part of this distribution. The terms
18
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
19
 * KIND, either express or implied.
20
20
 *
21
 
 * $Id: socks.c,v 1.15 2007-08-27 06:31:28 danf Exp $
 
21
 * $Id: socks.c,v 1.22 2008-01-14 19:40:10 yangtse Exp $
22
22
 ***************************************************************************/
23
23
 
24
24
#include "setup.h"
118
118
*   http://socks.permeo.com/protocol/socks4.protocol
119
119
*
120
120
* Note :
121
 
*   Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
 
121
*   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
122
122
*   Nonsupport "Identification Protocol (RFC1413)"
123
123
*/
124
124
CURLcode Curl_SOCKS4(const char *proxy_name,
125
125
                     const char *hostname,
126
126
                     int remote_port,
127
127
                     int sockindex,
128
 
                     struct connectdata *conn)
 
128
                     struct connectdata *conn,
 
129
                     bool protocol4a)
129
130
{
130
 
  unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */
 
131
#define SOCKS4REQLEN 262
 
132
  unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
 
133
                                           id */
131
134
  int result;
132
135
  CURLcode code;
133
136
  curl_socket_t sock = conn->sock[sockindex];
136
139
 
137
140
  /* get timeout */
138
141
  if(data->set.timeout && data->set.connecttimeout) {
139
 
    if (data->set.timeout < data->set.connecttimeout)
 
142
    if(data->set.timeout < data->set.connecttimeout)
140
143
      timeout = data->set.timeout;
141
144
    else
142
145
      timeout = data->set.connecttimeout;
165
168
  socksreq[1] = 1; /* connect */
166
169
  *((unsigned short*)&socksreq[2]) = htons((unsigned short)remote_port);
167
170
 
168
 
  /* DNS resolve */
169
 
  {
 
171
  /* DNS resolve only for SOCKS4, not SOCKS4a */
 
172
  if (!protocol4a) {
170
173
    struct Curl_dns_entry *dns;
171
174
    Curl_addrinfo *hp=NULL;
172
175
    int rc;
186
189
     */
187
190
    if(dns)
188
191
      hp=dns->addr;
189
 
    if (hp) {
 
192
    if(hp) {
190
193
      char buf[64];
191
194
      unsigned short ip[4];
192
195
      Curl_printable_address(hp, buf, sizeof(buf));
216
219
   * This is currently not supporting "Identification Protocol (RFC1413)".
217
220
   */
218
221
  socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
219
 
  if (proxy_name)
 
222
  if(proxy_name)
220
223
    strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
221
224
 
222
225
  /*
225
228
  {
226
229
    ssize_t actualread;
227
230
    ssize_t written;
 
231
    ssize_t hostnamelen = 0;
228
232
    int packetsize = 9 +
229
233
      (int)strlen((char*)socksreq + 8); /* size including NUL */
230
234
 
 
235
    /* If SOCKS4a, set special invalid IP address 0.0.0.x */
 
236
    if (protocol4a) {
 
237
      socksreq[4] = 0;
 
238
      socksreq[5] = 0;
 
239
      socksreq[6] = 0;
 
240
      socksreq[7] = 1;
 
241
      /* If still enough room in buffer, also append hostname */
 
242
      hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
 
243
      if (packetsize + hostnamelen <= SOCKS4REQLEN)
 
244
        strcpy((char*)socksreq + packetsize, hostname);
 
245
      else
 
246
        hostnamelen = 0; /* Flag: hostname did not fit in buffer */
 
247
    }
 
248
 
231
249
    /* Send request */
232
 
    code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
233
 
    if ((code != CURLE_OK) || (written != packetsize)) {
 
250
    code = Curl_write(conn, sock, (char *)socksreq, packetsize + hostnamelen,
 
251
                      &written);
 
252
    if((code != CURLE_OK) || (written != packetsize + hostnamelen)) {
234
253
      failf(data, "Failed to send SOCKS4 connect request.");
235
254
      return CURLE_COULDNT_CONNECT;
236
255
    }
 
256
    if (protocol4a && hostnamelen == 0) {
 
257
      /* SOCKS4a with very long hostname - send that name separately */
 
258
      hostnamelen = (ssize_t)strlen(hostname) + 1;
 
259
      code = Curl_write(conn, sock, (char *)hostname, hostnamelen, &written);
 
260
      if((code != CURLE_OK) || (written != hostnamelen)) {
 
261
        failf(data, "Failed to send SOCKS4 connect request.");
 
262
        return CURLE_COULDNT_CONNECT;
 
263
      }
 
264
    }
237
265
 
238
266
    packetsize = 8; /* receive data size */
239
267
 
240
268
    /* Receive response */
241
269
    result = blockread_all(conn, sock, (char *)socksreq, packetsize,
242
270
                           &actualread, timeout);
243
 
    if ((result != CURLE_OK) || (actualread != packetsize)) {
 
271
    if((result != CURLE_OK) || (actualread != packetsize)) {
244
272
      failf(data, "Failed to receive SOCKS4 connect request ack.");
245
273
      return CURLE_COULDNT_CONNECT;
246
274
    }
265
293
     */
266
294
 
267
295
    /* wrong version ? */
268
 
    if (socksreq[0] != 0) {
 
296
    if(socksreq[0] != 0) {
269
297
      failf(data,
270
298
            "SOCKS4 reply has wrong version, version should be 4.");
271
299
      return CURLE_COULDNT_CONNECT;
275
303
    switch(socksreq[1])
276
304
    {
277
305
    case 90:
278
 
      infof(data, "SOCKS4 request granted.\n");
 
306
      if (protocol4a)
 
307
        infof(data, "SOCKS4a request granted.\n");
 
308
      else
 
309
        infof(data, "SOCKS4 request granted.\n");
279
310
      break;
280
311
    case 91:
281
312
      failf(data,
359
390
  curl_socket_t sock = conn->sock[sockindex];
360
391
  struct SessionHandle *data = conn->data;
361
392
  long timeout;
 
393
  bool socks5_resolve_local = (bool)(data->set.proxytype == CURLPROXY_SOCKS5);
 
394
  const size_t hostname_len = strlen(hostname);
 
395
  ssize_t packetsize = 0;
 
396
 
 
397
  /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
 
398
  if(!socks5_resolve_local && hostname_len > 255)
 
399
  {
 
400
    infof(conn->data,"SOCKS5: server resolving disabled for hostnames of "
 
401
          "length > 255 [actual len=%d]\n", hostname_len);
 
402
    socks5_resolve_local = TRUE;
 
403
  }
362
404
 
363
405
  /* get timeout */
364
406
  if(data->set.timeout && data->set.connecttimeout) {
365
 
    if (data->set.timeout < data->set.connecttimeout)
 
407
    if(data->set.timeout < data->set.connecttimeout)
366
408
      timeout = data->set.timeout;
367
409
    else
368
410
      timeout = data->set.connecttimeout;
402
444
 
403
445
  code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
404
446
                      &written);
405
 
  if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
 
447
  if((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
406
448
    failf(data, "Unable to send initial SOCKS5 request.");
407
449
    return CURLE_COULDNT_CONNECT;
408
450
  }
428
470
  Curl_nonblock(sock, FALSE);
429
471
 
430
472
  result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout);
431
 
  if ((result != CURLE_OK) || (actualread != 2)) {
 
473
  if((result != CURLE_OK) || (actualread != 2)) {
432
474
    failf(data, "Unable to receive initial SOCKS5 response.");
433
475
    return CURLE_COULDNT_CONNECT;
434
476
  }
435
477
 
436
 
  if (socksreq[0] != 5) {
 
478
  if(socksreq[0] != 5) {
437
479
    failf(data, "Received invalid version in initial SOCKS5 response.");
438
480
    return CURLE_COULDNT_CONNECT;
439
481
  }
440
 
  if (socksreq[1] == 0) {
 
482
  if(socksreq[1] == 0) {
441
483
    /* Nothing to do, no authentication needed */
442
484
    ;
443
485
  }
444
 
  else if (socksreq[1] == 2) {
 
486
  else if(socksreq[1] == 2) {
445
487
    /* Needs user name and password */
446
488
    size_t userlen, pwlen;
447
489
    int len;
471
513
    len += pwlen;
472
514
 
473
515
    code = Curl_write(conn, sock, (char *)socksreq, len, &written);
474
 
    if ((code != CURLE_OK) || (len != written)) {
 
516
    if((code != CURLE_OK) || (len != written)) {
475
517
      failf(data, "Failed to send SOCKS5 sub-negotiation request.");
476
518
      return CURLE_COULDNT_CONNECT;
477
519
    }
478
520
 
479
521
    result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
480
522
                         timeout);
481
 
    if ((result != CURLE_OK) || (actualread != 2)) {
 
523
    if((result != CURLE_OK) || (actualread != 2)) {
482
524
      failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
483
525
      return CURLE_COULDNT_CONNECT;
484
526
    }
485
527
 
486
528
    /* ignore the first (VER) byte */
487
 
    if (socksreq[1] != 0) { /* status */
 
529
    if(socksreq[1] != 0) { /* status */
488
530
      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
489
531
            socksreq[0], socksreq[1]);
490
532
      return CURLE_COULDNT_CONNECT;
494
536
  }
495
537
  else {
496
538
    /* error */
497
 
    if (socksreq[1] == 1) {
 
539
    if(socksreq[1] == 1) {
498
540
      failf(data,
499
541
            "SOCKS5 GSSAPI per-message authentication is not supported.");
500
542
      return CURLE_COULDNT_CONNECT;
501
543
    }
502
 
    else if (socksreq[1] == 255) {
503
 
      if (!proxy_name || !*proxy_name) {
 
544
    else if(socksreq[1] == 255) {
 
545
      if(!proxy_name || !*proxy_name) {
504
546
        failf(data,
505
547
              "No authentication method was acceptable. (It is quite likely"
506
548
              " that the SOCKS5 server wanted a username/password, since none"
522
564
  socksreq[0] = 5; /* version (SOCKS5) */
523
565
  socksreq[1] = 1; /* connect */
524
566
  socksreq[2] = 0; /* must be zero */
525
 
  socksreq[3] = 1; /* IPv4 = 1 */
526
 
 
527
 
  {
 
567
 
 
568
  if(!socks5_resolve_local) {
 
569
    packetsize = (ssize_t)(5 + hostname_len + 2);
 
570
 
 
571
    socksreq[3] = 3; /* ATYP: domain name = 3 */
 
572
    socksreq[4] = (char) hostname_len; /* address length */
 
573
    memcpy(&socksreq[5], hostname, hostname_len); /* address bytes w/o NULL */
 
574
 
 
575
    *((unsigned short*)&socksreq[hostname_len+5]) =
 
576
      htons((unsigned short)remote_port);
 
577
  }
 
578
  else {
528
579
    struct Curl_dns_entry *dns;
529
580
    Curl_addrinfo *hp=NULL;
530
581
    int rc = Curl_resolv(conn, hostname, remote_port, &dns);
531
582
 
 
583
    packetsize = 10;
 
584
 
 
585
    socksreq[3] = 1; /* IPv4 = 1 */
 
586
 
532
587
    if(rc == CURLRESOLV_ERROR)
533
588
      return CURLE_COULDNT_RESOLVE_HOST;
534
589
 
542
597
     */
543
598
    if(dns)
544
599
      hp=dns->addr;
545
 
    if (hp) {
 
600
    if(hp) {
546
601
      char buf[64];
547
602
      unsigned short ip[4];
548
603
      Curl_printable_address(hp, buf, sizeof(buf));
564
619
            hostname);
565
620
      return CURLE_COULDNT_RESOLVE_HOST;
566
621
    }
567
 
  }
568
 
 
569
 
  *((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port);
570
 
 
571
 
  {
572
 
    const int packetsize = 10;
573
 
 
574
 
    code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
575
 
    if ((code != CURLE_OK) || (written != packetsize)) {
576
 
      failf(data, "Failed to send SOCKS5 connect request.");
 
622
 
 
623
    *((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port);
 
624
  }
 
625
 
 
626
  code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
 
627
  if((code != CURLE_OK) || (written != packetsize)) {
 
628
    failf(data, "Failed to send SOCKS5 connect request.");
 
629
    return CURLE_COULDNT_CONNECT;
 
630
  }
 
631
 
 
632
  packetsize = 10; /* minimum packet size is 10 */
 
633
 
 
634
  result = blockread_all(conn, sock, (char *)socksreq, packetsize,
 
635
                           &actualread, timeout);
 
636
  if((result != CURLE_OK) || (actualread != packetsize)) {
 
637
    failf(data, "Failed to receive SOCKS5 connect request ack.");
 
638
    return CURLE_COULDNT_CONNECT;
 
639
  }
 
640
 
 
641
  if(socksreq[0] != 5) { /* version */
 
642
    failf(data,
 
643
          "SOCKS5 reply has wrong version, version should be 5.");
 
644
    return CURLE_COULDNT_CONNECT;
 
645
  }
 
646
  if(socksreq[1] != 0) { /* Anything besides 0 is an error */
 
647
      failf(data,
 
648
            "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
 
649
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
 
650
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
 
651
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
 
652
            socksreq[1]);
577
653
      return CURLE_COULDNT_CONNECT;
578
 
    }
579
 
 
580
 
    result = blockread_all(conn, sock, (char *)socksreq, packetsize,
 
654
  }
 
655
 
 
656
  /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
 
657
     1928, so the reply packet should be read until the end to avoid errors at
 
658
     subsequent protocol level.
 
659
 
 
660
    +----+-----+-------+------+----------+----------+
 
661
    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
 
662
    +----+-----+-------+------+----------+----------+
 
663
    | 1  |  1  | X'00' |  1   | Variable |    2     |
 
664
    +----+-----+-------+------+----------+----------+
 
665
 
 
666
     ATYP:
 
667
     o  IP v4 address: X'01', BND.ADDR = 4 byte
 
668
     o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
 
669
     o  IP v6 address: X'04', BND.ADDR = 16 byte
 
670
     */
 
671
 
 
672
  /* Calculate real packet size */
 
673
  if(socksreq[3] == 3) {
 
674
    /* domain name */
 
675
    int addrlen = (int) socksreq[4];
 
676
    packetsize = 5 + addrlen + 2;
 
677
  }
 
678
  else if(socksreq[3] == 4) {
 
679
    /* IPv6 */
 
680
    packetsize = 4 + 16 + 2;
 
681
  }
 
682
 
 
683
  /* At this point we already read first 10 bytes */
 
684
  if(packetsize > 10) {
 
685
    packetsize -= 10;
 
686
    result = blockread_all(conn, sock, (char *)&socksreq[10], packetsize,
581
687
                           &actualread, timeout);
582
 
    if ((result != CURLE_OK) || (actualread != packetsize)) {
 
688
    if((result != CURLE_OK) || (actualread != packetsize)) {
583
689
      failf(data, "Failed to receive SOCKS5 connect request ack.");
584
690
      return CURLE_COULDNT_CONNECT;
585
691
    }
586
 
 
587
 
    if (socksreq[0] != 5) { /* version */
588
 
      failf(data,
589
 
            "SOCKS5 reply has wrong version, version should be 5.");
590
 
      return CURLE_COULDNT_CONNECT;
591
 
    }
592
 
    if (socksreq[1] != 0) { /* Anything besides 0 is an error */
593
 
        failf(data,
594
 
              "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
595
 
              (unsigned char)socksreq[4], (unsigned char)socksreq[5],
596
 
              (unsigned char)socksreq[6], (unsigned char)socksreq[7],
597
 
              (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
598
 
              socksreq[1]);
599
 
        return CURLE_COULDNT_CONNECT;
600
 
    }
601
692
  }
602
693
 
603
694
  Curl_nonblock(sock, TRUE);