diff --git a/CHANGES b/CHANGES index 832470d9b90d9f52e07061d8ac2ae6bb95f7b9b9..742d01f9bf307f24bb0cf23a745697b3a8f06fd4 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 e72a56b2476b2e5d267af33b9d999b857a1e977b..943b6b5edaf7386b9ee7e1d6fe13ae52d5635bfe 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 52949c34ff6c40a193aeab36ee96298f1757dc98..d84d42cc61e14cb3cb918fcfee4a305d1ad08656 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 8a152947ca2449202e1985aadc2ee6b82dc332f1..5db673eb2eb1de4f0e3188bfe3fb3aa9b90cd21f 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 fe0dc3c2576ea475393bbe0179aaea890285bdbe..c7aad213a8d5a5061b9e042b44289c26bb9e4721 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 c6049242c1f4ce31477d1cb0e3f89652e5f923e5..89289db8bbc429aed7fca0b37609bfa195b571d3 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 */