From 2e42b0a252416803a90ea232dc94a0a21d5a97e5 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 4 Jan 2008 23:01:00 +0000 Subject: [PATCH] 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. --- CHANGES | 6 ++ RELEASE-NOTES | 8 ++- docs/curl.1 | 15 ++++- docs/libcurl/curl_easy_setopt.3 | 10 ++- include/curl/curl.h | 5 ++ lib/socks.c | 110 ++++++++++++++++++++++++-------- lib/url.c | 8 +++ lib/urldata.h | 6 +- src/main.c | 30 +++++++-- 9 files changed, 158 insertions(+), 40 deletions(-) diff --git a/CHANGES b/CHANGES index b3f8bdd54..ae47a1e62 100644 --- 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 diff --git a/RELEASE-NOTES b/RELEASE-NOTES index e17dcb94a..bfa1fe291 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -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) diff --git a/docs/curl.1 b/docs/curl.1 index b94a1dc54..731c945db 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -1084,8 +1084,19 @@ mutually exclusive. If this option is used several times, the last one will be used. .IP "--socks5 " -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 " +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. diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index cc2af682e..299ac3ebb 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -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 diff --git a/include/curl/curl.h b/include/curl/curl.h index 396a0e0f2..c1288a7e9 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1172,6 +1172,11 @@ typedef enum { /* set transfer mode (;type=) 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; diff --git a/lib/socks.c b/lib/socks.c index 5146b0dc4..90ec1f215 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -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 */ - code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); - if((code != CURLE_OK) || (written != packetsize)) { - failf(data, "Failed to send SOCKS5 connect request."); + 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; + } + + 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); diff --git a/lib/url.c b/lib/url.c index 550cb2113..774e25ff2 100644 --- 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 */ diff --git a/lib/urldata.h b/lib/urldata.h index 9febb8415..2f061e035 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2008, Daniel Stenberg, , 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=) 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 */ }; diff --git a/src/main.c b/src/main.c index 77298913f..78f37c79a 100644 --- a/src/main.c +++ b/src/main.c @@ -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 Use SOCKS4 proxy on given host + port", " --socks4a Use SOCKS4a proxy on given host + port", - " --socks5 Use SOCKS5 proxy on given host + port", + " --socks5 Use SOCKS5 proxy and let the proxy resolve names", + " --socks5ip Use SOCKS5 proxy on given host + port", " --stderr Where to redirect stderr. - means stdout", " -t/--telnet-option Set telnet option", " --trace 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 */