Based on Maxim Perenesenko's patch, we now do SOCKS5 operations and let the
authorDaniel Stenberg <daniel@haxx.se>
Fri, 4 Jan 2008 23:01:00 +0000 (23:01 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 4 Jan 2008 23:01:00 +0000 (23:01 +0000)
proxy do the host name resolving and only if --socks5ip (or
CURLOPT_SOCKS5_RESOLVE_LOCAL) is used we resolve the host name locally and
pass on the IP address only to the proxy.

CHANGES
RELEASE-NOTES
docs/curl.1
docs/libcurl/curl_easy_setopt.3
include/curl/curl.h
lib/socks.c
lib/url.c
lib/urldata.h
src/main.c

diff --git a/CHANGES b/CHANGES
index b3f8bdd..ae47a1e 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,12 @@
 
                                   Changelog
 
+Daniel S (4 Jan 2008)
+- Based on Maxim Perenesenko's patch, we now do SOCKS5 operations and let the
+  proxy do the host name resolving and only if --socks5ip (or
+  CURLOPT_SOCKS5_RESOLVE_LOCAL) is used we resolve the host name locally and
+  pass on the IP address only to the proxy.
+
 Yang Tse (3 Jan 2008)
 - Modified test harness to allow SCP, SFTP and SOCKS4 tests to run with
   OpenSSH 2.9.9, SunSSH 1.0 or later versions. SOCKS5 tests need OpenSSH
index e17dcb9..bfa1fe2 100644 (file)
@@ -1,8 +1,8 @@
 Curl and libcurl 7.17.2
 
  Public curl releases:         103
- Command line options:         124
- curl_easy_setopt() options:   148
+ Command line options:         125
+ curl_easy_setopt() options:   149
  Public functions in libcurl:  55
  Public web site mirrors:      42
  Known libcurl bindings:       36
@@ -14,6 +14,7 @@ This release includes the following changes:
  o CURLOPT_PROXY_TRANSFER_MODE was added
  o --no-keep-alive was added
  o --socks4a added (proxy type CURLPROXY_SOCKS4A for libcurl)
+ o --socks5ip added (CURLOPT_SOCKS5_RESOLVE_LOCAL for libcurl)
 
 This release includes the following bugfixes:
 
@@ -42,6 +43,7 @@ This release includes the following bugfixes:
  o bad connection re-use check with environment variable-activated proxy use
  o --libcurl now generates a return statement as well
  o socklen_t is no longer used in the public includes
+ o SOCKS5 uses now let the proxy resolve the host names by default
 
 This release includes the following known bugs:
 
@@ -63,6 +65,6 @@ advice from friends like these:
  Robin Johnson, Michal Marek, Ates Goral, Andres Garcia, Rob Crittenden,
  Emil Romanus, Alessandro Vesely, Ray Pekowski, Spacen Jasset, Andrew Moise,
  Gilles Blanc, David Wright, Vikram Saxena, Mateusz Loskot, Gary Maxwell,
- Dmitry Kurochkin, Mohun Biswas, Richard Atterer
+ Dmitry Kurochkin, Mohun Biswas, Richard Atterer, Maxim Perenesenko
  
         Thanks! (and sorry if I forgot to mention someone)
index b94a1dc..731c945 100644 (file)
@@ -1084,8 +1084,19 @@ mutually exclusive.
 
 If this option is used several times, the last one will be used.
 .IP "--socks5 <host[:port]>"
-Use the specified SOCKS5 proxy. If the port number is not specified, it is
-assumed at port 1080. (Added in 7.11.1)
+Use the specified SOCKS5 proxy (and let the proxy resolve the host name). If
+the port number is not specified, it is assumed at port 1080. (Added in
+7.11.1)
+
+This option overrides any previous use of \fI-x/--proxy\fP, as they are
+mutually exclusive.
+
+If this option is used several times, the last one will be used. (This option
+was previously wrongly documented and used as --socks without the number
+appended.)
+.IP "--socks5ip <host[:port]>"
+Use the specified SOCKS5 proxy - but resolve the host name locally. If the
+port number is not specified, it is assumed at port 1080. (Added in 7.17.2)
 
 This option overrides any previous use of \fI-x/--proxy\fP, as they are
 mutually exclusive.
index cc2af68..299ac3e 100644 (file)
@@ -21,7 +21,7 @@
 .\" * $Id$
 .\" **************************************************************************
 .\"
-.TH curl_easy_setopt 3 "30 Aug 2007" "libcurl 7.17.0" "libcurl Manual"
+.TH curl_easy_setopt 3 "4 Jan 2008" "libcurl 7.17.2" "libcurl Manual"
 .SH NAME
 curl_easy_setopt \- set options for a curl easy handle
 .SH SYNOPSIS
@@ -433,11 +433,19 @@ Pass a long with this option to set type of the proxy. Available options for
 this are \fICURLPROXY_HTTP\fP, \fICURLPROXY_SOCKS4\fP (added in 7.15.2),
 \fICURLPROXY_SOCKS5\fP and \fICURLPROXY_SOCKS4A\fP (added in 7.17.2). The HTTP
 type is default. (Added in 7.10)
+
+See also \fIURLOPT_SOCKS5_RESOLVE_LOCAL\fP.
 .IP CURLOPT_HTTPPROXYTUNNEL
 Set the parameter to non-zero to get the library to tunnel all operations
 through a given HTTP proxy. There is a big difference between using a proxy
 and to tunnel through it. If you don't know what this means, you probably
 don't want this tunneling option.
+.IP CURLOPT_SOCKS5_RESOLVE_LOCAL
+Set the parameter to 1 to get the library to resolve the host name locally
+instead of passing it to the proxy to resolve, when using a SOCKS5 proxy.
+
+Note that libcurl before 7.17.2 always resolved the host name locally even
+when SOCKS5 was used. (Added in 7.17.2)
 .IP CURLOPT_INTERFACE
 Pass a char * as parameter. This set the interface name to use as outgoing
 network interface. The name can be an interface name, an IP address or a host
index 396a0e0..c1288a7 100644 (file)
@@ -1172,6 +1172,11 @@ typedef enum {
   /* set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy */
   CINIT(PROXY_TRANSFER_MODE, LONG, 166),
 
+  /* Set using of SOCKS5 to resolve host names locally instead of sending them
+     to the proxy to let it resolve them. Valid only if CURLOPT_PROXYTYPE ==
+     CURLPROXY_SOCKS5, otherwise ignored. */
+  CINIT(SOCKS5_RESOLVE_LOCAL, LONG, 167),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
index 5146b0d..90ec1f2 100644 (file)
@@ -390,6 +390,17 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
   curl_socket_t sock = conn->sock[sockindex];
   struct SessionHandle *data = conn->data;
   long timeout;
+  bool socks5_resolve_local = data->set.socks5_resolve_local;
+  const size_t hostname_len = strlen(hostname);
+  int packetsize = 0;
+
+  /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
+  if(!socks5_resolve_local && hostname_len > 255)
+  {
+    infof(conn->data,"SOCKS5: server resolving disabled for hostnames of "
+          "length > 255 [actual len=%d]\n", hostname_len);
+    socks5_resolve_local = TRUE;
+  }
 
   /* get timeout */
   if(data->set.timeout && data->set.connecttimeout) {
@@ -553,13 +564,26 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
   socksreq[0] = 5; /* version (SOCKS5) */
   socksreq[1] = 1; /* connect */
   socksreq[2] = 0; /* must be zero */
-  socksreq[3] = 1; /* IPv4 = 1 */
 
-  {
+  if(!socks5_resolve_local) {
+    packetsize = 5 + hostname_len + 2;
+
+    socksreq[3] = 3; /* ATYP: domain name = 3 */
+    socksreq[4] = (char) hostname_len; /* address length */
+    memcpy(&socksreq[5], hostname, hostname_len); /* address bytes w/o NULL */
+
+    *((unsigned short*)&socksreq[hostname_len+5]) =
+      htons((unsigned short)remote_port);
+  }
+  else {
     struct Curl_dns_entry *dns;
     Curl_addrinfo *hp=NULL;
     int rc = Curl_resolv(conn, hostname, remote_port, &dns);
 
+    packetsize = 10;
+
+    socksreq[3] = 1; /* IPv4 = 1 */
+
     if(rc == CURLRESOLV_ERROR)
       return CURLE_COULDNT_RESOLVE_HOST;
 
@@ -595,40 +619,76 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
             hostname);
       return CURLE_COULDNT_RESOLVE_HOST;
     }
+
+    *((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port);
   }
 
-  *((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port);
+  code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
+  if((code != CURLE_OK) || (written != packetsize)) {
+    failf(data, "Failed to send SOCKS5 connect request.");
+    return CURLE_COULDNT_CONNECT;
+  }
 
-  {
-    const int packetsize = 10;
+  packetsize = 10; /* minimum packet size is 10 */
+
+  result = blockread_all(conn, sock, (char *)socksreq, packetsize,
+                           &actualread, timeout);
+  if((result != CURLE_OK) || (actualread != packetsize)) {
+    failf(data, "Failed to receive SOCKS5 connect request ack.");
+    return CURLE_COULDNT_CONNECT;
+  }
 
-    code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
-    if((code != CURLE_OK) || (written != packetsize)) {
-      failf(data, "Failed to send SOCKS5 connect request.");
+  if(socksreq[0] != 5) { /* version */
+    failf(data,
+          "SOCKS5 reply has wrong version, version should be 5.");
+    return CURLE_COULDNT_CONNECT;
+  }
+  if(socksreq[1] != 0) { /* Anything besides 0 is an error */
+      failf(data,
+            "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
+            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
+            socksreq[1]);
       return CURLE_COULDNT_CONNECT;
-    }
+  }
 
-    result = blockread_all(conn, sock, (char *)socksreq, packetsize,
+  /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
+     1928, so the reply packet should be read until the end to avoid errors at
+     subsequent protocol level.
+
+    +----+-----+-------+------+----------+----------+
+    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
+    +----+-----+-------+------+----------+----------+
+    | 1  |  1  | X'00' |  1   | Variable |    2     |
+    +----+-----+-------+------+----------+----------+
+
+     ATYP:
+     o  IP v4 address: X'01', BND.ADDR = 4 byte
+     o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
+     o  IP v6 address: X'04', BND.ADDR = 16 byte
+     */
+
+  /* Calculate real packet size */
+  if(socksreq[3] == 3) {
+    /* domain name */
+    int addrlen = (int) socksreq[4];
+    packetsize = 5 + addrlen + 2;
+  }
+  else if(socksreq[3] == 4) {
+    /* IPv6 */
+    packetsize = 4 + 16 + 2;
+  }
+
+  /* At this point we already read first 10 bytes */
+  if(packetsize > 10) {
+    packetsize -= 10;
+    result = blockread_all(conn, sock, (char *)&socksreq[10], packetsize,
                            &actualread, timeout);
     if((result != CURLE_OK) || (actualread != packetsize)) {
       failf(data, "Failed to receive SOCKS5 connect request ack.");
       return CURLE_COULDNT_CONNECT;
     }
-
-    if(socksreq[0] != 5) { /* version */
-      failf(data,
-            "SOCKS5 reply has wrong version, version should be 5.");
-      return CURLE_COULDNT_CONNECT;
-    }
-    if(socksreq[1] != 0) { /* Anything besides 0 is an error */
-        failf(data,
-              "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
-              (unsigned char)socksreq[4], (unsigned char)socksreq[5],
-              (unsigned char)socksreq[6], (unsigned char)socksreq[7],
-              (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
-              socksreq[1]);
-        return CURLE_COULDNT_CONNECT;
-    }
   }
 
   Curl_nonblock(sock, TRUE);
index 550cb21..774e25f 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -2054,6 +2054,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     }
     break;
 
+  case CURLOPT_SOCKS5_RESOLVE_LOCAL:
+    /*
+     * Enable or disable using of SOCKS5 proxy server to resolve domain names
+     * instead of using platform API like gethostbyname_r etc
+     */
+    data->set.socks5_resolve_local = (bool)(0 != va_arg(param, long));
+    break;
+
   default:
     /* unknown tag and its companion, just ignore: */
     result = CURLE_FAILED_INIT; /* correct this */
index 9febb84..2f061e0 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -1441,7 +1441,9 @@ struct UserDefined {
   long new_directory_perms; /* Permissions to use when creating remote dirs */
   bool proxy_transfer_mode; /* set transfer mode (;type=<a|i>) when doing FTP
                                via an HTTP proxy */
-
+  bool socks5_resolve_local; /* resolve host names locally even if a SOCKS5
+                                proxy in use.  Valid only if CURLOPT_PROXYTYPE
+                                == CURLPROXY_SOCKS5, otherwise ignored. */
   char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */
 };
 
index 7729891..78f37c7 100644 (file)
@@ -480,6 +480,8 @@ struct Configurable {
   bool raw;
   bool post301;
   bool nokeepalive;
+  bool socks5_resolve_local; /* don't use SOCKS5 proxy server to resolve
+                                domain names */
   struct OutStruct *outs;
 };
 
@@ -713,7 +715,8 @@ static void help(void)
     " -S/--show-error    Show error. With -s, make curl show errors when they occur",
     "    --socks4 <host[:port]> Use SOCKS4 proxy on given host + port",
     "    --socks4a <host[:port]> Use SOCKS4a proxy on given host + port",
-    "    --socks5 <host[:port]> Use SOCKS5 proxy on given host + port",
+    "    --socks5 <host[:port]> Use SOCKS5 proxy and let the proxy resolve names",
+    "    --socks5ip <host[:port]> Use SOCKS5 proxy on given host + port",
     "    --stderr <file> Where to redirect stderr. - means stdout",
     " -t/--telnet-option <OPT=val> Set telnet option",
     "    --trace <file>  Write a debug trace to the given file",
@@ -1514,10 +1517,10 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
     {"*z", "disable-eprt", FALSE},
     {"$a", "ftp-ssl",    FALSE},
     {"$b", "ftp-pasv",   FALSE},
-    {"$c", "socks5",     TRUE},
-    {"$c", "socks",      TRUE}, /* this is how the option was documented but
-                                   we prefer the --socks5 version for explicit
-                                   version */
+    {"$c", "socks5ip",   TRUE},
+    {"$c", "socks",      TRUE}, /* this is how the option once was documented
+                                   but we prefer the --socks5 version for
+                                   explicit version */
     {"$d", "tcp-nodelay",FALSE},
     {"$e", "proxy-digest", FALSE},
     {"$f", "proxy-basic", FALSE},
@@ -1544,6 +1547,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
     {"$#", "raw",        FALSE},
     {"$0", "post301",    FALSE},
     {"$1", "no-keep-alive",    FALSE},
+    {"$2", "socks5",    TRUE},
 
     {"0", "http1.0",     FALSE},
     {"1", "tlsv1",       FALSE},
@@ -1900,9 +1904,11 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
           free(config->ftpport);
         config->ftpport = NULL;
         break;
-      case 'c': /* --socks5 specifies a socks5 proxy to use */
+      case 'c': /* --socks5ip specifies a socks5 proxy to use, but resolves
+                   the name locally and passes on the resolved address */
         GetStr(&config->socksproxy, nextarg);
         config->socksver = CURLPROXY_SOCKS5;
+        config->socks5_resolve_local = TRUE;
         break;
       case 't': /* --socks4 specifies a socks4 proxy to use */
         GetStr(&config->socksproxy, nextarg);
@@ -1912,6 +1918,12 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
         GetStr(&config->socksproxy, nextarg);
         config->socksver = CURLPROXY_SOCKS4A;
         break;
+      case '2': /* --socks5 specifies a socks5 proxy and enables name resolving
+                   with the proxy */
+        GetStr(&config->socksproxy, nextarg);
+        config->socksver = CURLPROXY_SOCKS5;
+        config->socks5_resolve_local = FALSE;
+        break;
       case 'd': /* --tcp-nodelay option */
         config->tcp_nodelay ^= TRUE;
         break;
@@ -4340,7 +4352,7 @@ operate(struct Configurable *config, int argc, argv_item_t argv[])
         my_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey);
 
         /* SSH host key md5 checking allows us to fail if we are
-         * not talking to who we think we should 
+         * not talking to who we think we should
          */
         my_setopt(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, config->hostpubmd5);
 
@@ -4497,6 +4509,10 @@ operate(struct Configurable *config, int argc, argv_item_t argv[])
         if(config->socksproxy) {
           my_setopt(curl, CURLOPT_PROXY, config->socksproxy);
           my_setopt(curl, CURLOPT_PROXYTYPE, config->socksver);
+          if(config->socksver==CURLPROXY_SOCKS5)
+            /* added in 7.17.2 */
+            my_setopt(curl, CURLOPT_SOCKS5_RESOLVE_LOCAL,
+                      config->socks5_resolve_local);
         }
 
         /* curl 7.13.0 */