diff --git a/CHANGES b/CHANGES index 832470d9b9..742d01f9bf 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,13 @@ Changelog +Daniel (21 February 2006) +- Peter Su added support for SOCKS4 proxies. Enable this by setting the proxy + type to the already provided type CURLPROXY_SOCKS4. + + I added a --socks4 option that works like the current --socks5 option but + instead use the socks4 protocol. + Daniel (20 February 2006) - Shmulik Regev fixed an issue with multi-pass authentication and compressed content when libcurl didn't honor the internal ignorebody flag. diff --git a/RELEASE-NOTES b/RELEASE-NOTES index e72a56b247..943b6b5eda 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -2,7 +2,7 @@ Curl and libcurl 7.15.2 Public curl release number: 92 Releases counted from the very beginning: 119 - Available command line options: 111 + Available command line options: 112 Available curl_easy_setopt() options: 129 Number of public functions in libcurl: 46 Amount of public web site mirrors: 31 @@ -11,6 +11,7 @@ Curl and libcurl 7.15.2 This release includes the following changes: + o Support for SOCKS4 proxies (added --socks4) o CURLOPT_CONNECT_ONLY and CURLINFO_LASTSOCKET added o CURLOPT_LOCALPORT and CURLOPT_LOCALPORTRANGE (--local-port) added o Dropped support for the LPRT ftp command @@ -65,6 +66,6 @@ advice from friends like these: Dov Murik, Jean Jacques Drouin, Andres Garcia, Yang Tse, Gisle Vanem, Dan Fandrich, Alexander Lazic, Michael Jahn, Andrew Benham, Bryan Henderson, David Shaw, Jon Turner, Duane Cathey, Michal Marek, Philippe Vaucher, Kent - Boortz, Karl Moerder, Shmulik Regev, Ulf Härnhammar, Shmulik Regev + Boortz, Karl Moerder, Shmulik Regev, Ulf Härnhammar, Peter Su Thanks! (and sorry if I forgot to mention someone) diff --git a/docs/curl.1 b/docs/curl.1 index 52949c34ff..d84d42cc61 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -21,7 +21,7 @@ .\" * $Id$ .\" ************************************************************************** .\" -.TH curl 1 "24 Nov 2005" "Curl 7.15.1" "Curl Manual" +.TH curl 1 "21 Feb 2006" "Curl 7.15.2" "Curl Manual" .SH NAME curl \- transfer a URL .SH SYNOPSIS @@ -859,14 +859,24 @@ If this option is used twice, the second will again disable silent mode. When used with -s it makes curl show error message if it fails. If this option is used twice, the second will again disable show error. -.IP "--socks " +.IP "--socks4 " +Use the specified SOCKS4 proxy. If the port number is not specified, it is +assumed at port 1080. (Added in 7.15.2) + +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. +.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) 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. +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 "--stderr " Redirect all writes to stderr to the specified file instead. If the file name is a plain '-', it is instead written to stdout. This option has no point when diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 8a152947ca..5db673eb2e 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 "28 Jan 2006" "libcurl 7.15.2" "libcurl Manual" +.TH curl_easy_setopt 3 "21 Feb 2006" "libcurl 7.15.2" "libcurl Manual" .SH NAME curl_easy_setopt \- set options for a curl easy handle .SH SYNOPSIS @@ -324,8 +324,8 @@ Pass a long with this option to set the proxy port to connect to unless it is specified in the proxy string \fICURLOPT_PROXY\fP. .IP CURLOPT_PROXYTYPE Pass a long with this option to set type of the proxy. Available options for -this are \fICURLPROXY_HTTP\fP and \fICURLPROXY_SOCKS5\fP, with the HTTP one -being default. (Added in 7.10) +this are \fICURLPROXY_HTTP\fP, \fICURLPROXY_SOCKS4\fP (added in 7.15.2) +\fICURLPROXY_SOCKS5\fP. The HTTP type is default. (Added in 7.10) .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 diff --git a/lib/url.c b/lib/url.c index fe0dc3c257..c7aad213a8 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1809,6 +1809,191 @@ ConnectionStore(struct SessionHandle *data, return i; } +/* +* This function logs in to a SOCKS4 proxy and sends the specifies the final +* desitination server. +* +* Reference : +* http://socks.permeo.com/protocol/socks4.protocol +* +* Note : +* Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" +* Nonsupport "Identification Protocol (RFC1413)" +*/ +static int handleSock4Proxy(struct connectdata *conn) +{ + unsigned char socksreq[600]; /* room for large user/pw (255 max each) */ + int result; + CURLcode code; + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + struct SessionHandle *data = conn->data; + + Curl_nonblock(sock, FALSE); + + /* + * Compose socks4 request + * + * Request format + * + * +----+----+----+----+----+----+----+----+----+----+....+----+ + * | VN | CD | DSTPORT | DSTIP | USERID |NULL| + * +----+----+----+----+----+----+----+----+----+----+....+----+ + * # of bytes: 1 1 2 4 variable 1 + */ + + socksreq[0] = 4; /* version (SOCKS4) */ + socksreq[1] = 1; /* connect */ + *((unsigned short*)&socksreq[2]) = htons(conn->remote_port); + + /* DNS resolve */ + { + struct Curl_dns_entry *dns; + Curl_addrinfo *hp=NULL; + int rc; + + rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns); + + if(rc == CURLRESOLV_ERROR) + return 1; + + if(rc == CURLRESOLV_PENDING) + /* this requires that we're in "wait for resolve" state */ + rc = Curl_wait_for_resolv(conn, &dns); + + /* + * We cannot use 'hostent' as a struct that Curl_resolv() returns. It + * returns a Curl_addrinfo pointer that may not always look the same. + */ + if(dns) + hp=dns->addr; + if (hp) { + char buf[64]; + unsigned short ip[4]; + Curl_printable_address(hp, buf, sizeof(buf)); + + if(4 == sscanf( buf, "%hu.%hu.%hu.%hu", + &ip[0], &ip[1], &ip[2], &ip[3])) { + /* Set DSTIP */ + socksreq[4] = (unsigned char)ip[0]; + socksreq[5] = (unsigned char)ip[1]; + socksreq[6] = (unsigned char)ip[2]; + socksreq[7] = (unsigned char)ip[3]; + } + else + hp = NULL; /* fail! */ + + Curl_resolv_unlock(conn->data, dns); /* not used anymore from now on */ + + } + if(!hp) { + failf(conn->data, "Failed to resolve \"%s\" for SOCKS4 connect.\n", + conn->host.name); + return 1; + } + } + + /* + * Make connection + */ + { + ssize_t actualread; + ssize_t written; + int packetsize = 9; /* request data size (include NULL) */ + + /* Send request */ + code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); + if ((code != CURLE_OK) || (written != packetsize)) { + failf(conn->data, "Failed to send SOCKS4 connect request.\n"); + return 1; + } + + packetsize = 8; /* receive data size */ + + /* Receive response */ + result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread); + if ((result != CURLE_OK) || (actualread != packetsize)) { + failf(conn->data, "Failed to receive SOCKS4 connect request ack.\n"); + return 1; + } + + /* + * Response format + * + * +----+----+----+----+----+----+----+----+ + * | VN | CD | DSTPORT | DSTIP | + * +----+----+----+----+----+----+----+----+ + * # of bytes: 1 1 2 4 + * + * VN is the version of the reply code and should be 0. CD is the result + * code with one of the following values: + * + * 90: request granted + * 91: request rejected or failed + * 92: request rejected becasue SOCKS server cannot connect to + * identd on the client + * 93: request rejected because the client program and identd + * report different user-ids + */ + + /* wrong version ? */ + if (socksreq[0] != 0) { + failf(conn->data, + "SOCKS4 reply has wrong version, version should be 4.\n"); + return 1; + } + + /* Result */ + switch(socksreq[1]) + { + case 90: + infof(data, "SOCKS4 request granted.\n"); + break; + case 91: + failf(conn->data, + "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", request rejected or failed.\n", + (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 1; + case 92: + failf(conn->data, + "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", request rejected becasue SOCKS server cannot connect to " + "identd on the client.\n", + (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 1; + case 93: + failf(conn->data, + "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", request rejected because the client program and identd " + "report different user-ids.\n", + (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 1; + default : + failf(conn->data, + "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + ", Unknown.\n", + (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 1; + } + } + + Curl_nonblock(sock, TRUE); + + return 0; /* Proxy was successful! */ +} + /* * This function logs in to a SOCKS5 proxy and sends the specifies the final * desitination server. @@ -2052,16 +2237,18 @@ static CURLcode ConnectPlease(struct connectdata *conn, Curl_store_ip_addr(conn); - if (conn->data->set.proxytype == CURLPROXY_SOCKS5) { + switch(conn->data->set.proxytype) { + case CURLPROXY_SOCKS5: return handleSock5Proxy(conn->proxyuser, conn->proxypasswd, conn) ? CURLE_COULDNT_CONNECT : CURLE_OK; - } - else if (conn->data->set.proxytype == CURLPROXY_HTTP) { + case CURLPROXY_HTTP: /* do nothing here. handled later. */ - } - else { + break; + case CURLPROXY_SOCKS4: + return handleSock4Proxy(conn) ? CURLE_COULDNT_CONNECT : CURLE_OK; + default: failf(conn->data, "unknown proxytype option given"); return CURLE_COULDNT_CONNECT; } diff --git a/src/main.c b/src/main.c index c6049242c1..89289db8bb 100644 --- a/src/main.c +++ b/src/main.c @@ -343,7 +343,10 @@ struct Configurable { struct timeval lastrecvtime; size_t lastrecvsize; bool ftp_ssl; - char *socks5proxy; + + char *socksproxy; /* set to server string */ + int socksver; /* set to CURLPROXY_SOCKS* define */ + bool tcp_nodelay; long req_retry; /* number of retries */ long retry_delay; /* delay between retries (in seconds) */ @@ -557,7 +560,8 @@ static void help(void) " --retry-max-time Retry only within this period", " -s/--silent Silent mode. Don't output anything", " -S/--show-error Show error. With -s, make curl show errors when they occur", - " --socks Use SOCKS5 proxy on given host + port", + " --socks4 Use SOCKS4 proxy on given host + port", + " --socks5 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", @@ -1314,6 +1318,9 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"$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 */ {"$d", "tcp-nodelay",FALSE}, {"$e", "proxy-digest", FALSE}, {"$f", "proxy-basic", FALSE}, @@ -1330,6 +1337,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"$q", "ftp-skip-pasv-ip", FALSE}, {"$r", "ftp-method", TRUE}, {"$s", "local-port", TRUE}, + {"$t", "socks4", TRUE}, {"0", "http1.0", FALSE}, {"1", "tlsv1", FALSE}, @@ -1673,8 +1681,13 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ free(config->ftpport); config->ftpport = NULL; break; - case 'c': /* --socks specifies a socks5 proxy to use */ - GetStr(&config->socks5proxy, nextarg); + case 'c': /* --socks5 specifies a socks5 proxy to use */ + GetStr(&config->socksproxy, nextarg); + config->socksver = CURLPROXY_SOCKS5; + break; + case 't': /* --socks4 specifies a socks5 proxy to use */ + GetStr(&config->socksproxy, nextarg); + config->socksver = CURLPROXY_SOCKS4; break; case 'd': /* --tcp-nodelay option */ config->tcp_nodelay ^= TRUE; @@ -3972,10 +3985,10 @@ operate(struct Configurable *config, int argc, char *argv[]) if(config->ftp_ssl) curl_easy_setopt(curl, CURLOPT_FTP_SSL, CURLFTPSSL_TRY); - /* new in curl 7.11.1 */ - if(config->socks5proxy) { - curl_easy_setopt(curl, CURLOPT_PROXY, config->socks5proxy); - curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); + /* new in curl 7.11.1, modified in 7.15.2 */ + if(config->socksproxy) { + curl_easy_setopt(curl, CURLOPT_PROXY, config->socksproxy); + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, config->socksver); } /* curl 7.13.0 */