diff --git a/include/curl/curl.h b/include/curl/curl.h index 46a52b1c5..00a7fa88a 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -202,6 +202,12 @@ typedef enum { CURL_LAST /* never use! */ } CURLcode; +typedef enum { + CURLPROXY_HTTP = 0, + CURLPROXY_SOCKS4 = 4, + CURLPROXY_SOCKS5 = 5 +} curl_proxytype; + /* this was the error code 50 in 7.7.3 and a few earlier versions, this is no longer used by libcurl but is instead #defined here only to not make programs break */ @@ -576,6 +582,10 @@ typedef enum { /* Explicitly allow insecure SSL connects */ CINIT(SSL_INSECURE, LONG, 101), + /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), + CURLPROXY_SOCKS4 and CURLPROXY_SOCKS5. */ + CINIT(PROXYTYPE, LONG, 102), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/http.c b/lib/http.c index 66fa234ec..08a5536dc 100644 --- a/lib/http.c +++ b/lib/http.c @@ -420,7 +420,7 @@ CURLcode Curl_http_connect(struct connectdata *conn) * has occured, can we start talking SSL */ - if(data->change.proxy && + if(data->change.proxy && (data->set.proxytype == CURLPROXY_HTTP) && ((conn->protocol & PROT_HTTPS) || data->set.tunnel_thru_httpproxy)) { /* either HTTPS over proxy, OR explicitly asked for a tunnel */ diff --git a/lib/url.c b/lib/url.c index 43a7e400b..c9aa3042a 100644 --- a/lib/url.c +++ b/lib/url.c @@ -282,6 +282,8 @@ CURLcode Curl_open(struct SessionHandle **curl) data->set.proxyport = 1080; + data->set.proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ + /* create an array with connection data struct pointers */ data->state.numconnects = 5; /* hard-coded right now */ data->state.connects = (struct connectdata **) @@ -1053,6 +1055,13 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) data->set.ssl.allow_insecure = va_arg(param, long)?TRUE:FALSE; break; + case CURLOPT_PROXYTYPE: + /* + * Set proxy type. HTTP/SOCKS4/SOCKS5 + */ + data->set.proxytype = va_arg(param, long); + break; + default: /* unknown tag and its companion, just ignore: */ return CURLE_FAILED_INIT; /* correct this */ @@ -1327,6 +1336,189 @@ ConnectionStore(struct SessionHandle *data, return i; } +/* + * This function logs in to a SOCKS5 proxy and sends the specifies the final + * desitination server. + */ +static int handleSock5Proxy( + const char *proxy_name, + const char *proxy_password, + struct connectdata *conn, + int sock) +{ + unsigned char socksreq[600]; /* room for large user/pw (255 max each) */ + int actualread; + int written; + CURLcode result; + + Curl_nonblock(sock, FALSE); + + socksreq[0] = 5; /* version */ + socksreq[1] = (char)(proxy_name[0] ? 2 : 1); /* number of methods (below) */ + socksreq[2] = 0; /* no authentication */ + socksreq[3] = 2; /* username/password */ + + result = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]), + &written); + if ((result != CURLE_OK) || (written != (2 + (int)socksreq[1]))) { + failf(conn->data, "Unable to send initial SOCKS5 request."); + return 1; + } + + result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread); + if ((result != CURLE_OK) || (actualread != 2)) { + failf(conn->data, "Unable to receive initial SOCKS5 response."); + return 1; + } + + if (socksreq[0] != 5) { + failf(conn->data, "Received invalid version in initial SOCKS5 response."); + return 1; + } + if (socksreq[1] == 0) { + /* Nothing to do, no authentication needed */ + ; + } + else if (socksreq[1] == 2) { + /* Needs user name and password */ + int userlen, pwlen, len; + + userlen = strlen(proxy_name); + pwlen = strlen(proxy_password); + + /* username/password request looks like + * +----+------+----------+------+----------+ + * |VER | ULEN | UNAME | PLEN | PASSWD | + * +----+------+----------+------+----------+ + * | 1 | 1 | 1 to 255 | 1 | 1 to 255 | + * +----+------+----------+------+----------+ + */ + len = 0; + socksreq[len++] = 1; /* username/pw subnegotiation version */ + socksreq[len++] = (char) userlen; + memcpy(socksreq + len, proxy_name, (int) userlen); + len += userlen; + socksreq[len++] = (char) pwlen; + memcpy(socksreq + len, proxy_password, (int) pwlen); + len += pwlen; + + result = Curl_write(conn, sock, (char *)socksreq, len, &written); + if ((result != CURLE_OK) || (len != written)) { + failf(conn->data, "Failed to send SOCKS5 sub-negotiation request."); + return 1; + } + + result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread); + if ((result != CURLE_OK) || (actualread != 2)) { + failf(conn->data, "Unable to receive SOCKS5 sub-negotiation response."); + return 1; + } + + if ((socksreq[0] != 5) || /* version */ + (socksreq[1] != 0)) { /* status */ + failf(conn->data, "User was rejected by the SOCKS5 server (%d %d).", + socksreq[0], socksreq[1]); + return 1; + } + + /* Everything is good so far, user was authenticated! */ + } + else { + /* error */ + if (socksreq[1] == 1) { + failf(conn->data, + "SOCKS5 GSSAPI per-message authentication is not supported."); + return 1; + } + else if (socksreq[1] == 255) { + if (proxy_name[0] == 0) { + failf(conn->data, + "No authentication method was acceptable. (It is quite likely" + " that the SOCKS5 server wanted a username/password, since none" + " was supplied to the server on this connection.)"); + } + else { + failf(conn->data, "No authentication method was acceptable."); + } + return 1; + } + else { + failf(conn->data, + "Undocumented SOCKS5 mode attempted to be used by server."); + return 1; + } + } + + /* Authentication is complete, now specify destination to the proxy */ + socksreq[0] = 5; /* version (SOCKS5) */ + socksreq[1] = 1; /* connect */ + socksreq[2] = 0; /* must be zero */ + socksreq[3] = 1; /* IPv4 = 1 */ + + { + Curl_addrinfo *hp; + hp = Curl_resolv(conn->data, conn->hostname, conn->remote_port); + /* + * We cannot use 'hostent' as a struct that Curl_resolv() returns. It + * returns a Curl_addrinfo pointer that may not always look the same. + */ +#ifndef ENABLE_IPV6 + if (hp && hp->h_addr_list[0]) { + socksreq[4] = ((char*)hp->h_addr_list[0])[0]; + socksreq[5] = ((char*)hp->h_addr_list[0])[1]; + socksreq[6] = ((char*)hp->h_addr_list[0])[2]; + socksreq[7] = ((char*)hp->h_addr_list[0])[3]; + } + else { + failf(conn->data, "Failed to resolve \"%s\" for SOCKS5 connect.", + conn->hostname); + return 1; + } +#else + failf(conn->data, + "%s:%d has an internal error an needs to be fixed to work", + __FILE__, __LINE__); + return 1; +#endif + } + + *((unsigned short*)&socksreq[8]) = htons(conn->remote_port); + + { + const int packetsize = 10; + + result = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); + if ((result != CURLE_OK) || (written != packetsize)) { + failf(conn->data, "Failed to send SOCKS5 connect request."); + return 1; + } + + result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread); + if ((result != CURLE_OK) || (actualread != packetsize)) { + failf(conn->data, "Failed to receive SOCKS5 connect request ack."); + return 1; + } + + if (socksreq[0] != 5) { /* version */ + failf(conn->data, + "SOCKS5 reply has wrong version, version should be 5."); + return 1; + } + if (socksreq[1] != 0) { /* Anything besides 0 is an error */ + failf(conn->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 1; + } + } + + Curl_nonblock(sock, TRUE); + return 0; /* Proxy was successful! */ +} + static CURLcode ConnectPlease(struct connectdata *conn, Curl_addrinfo *hostaddr, bool *connected) @@ -1356,6 +1548,21 @@ static CURLcode ConnectPlease(struct connectdata *conn, conn->serv_addr.sin_family = hostaddr->h_addrtype; conn->serv_addr.sin_port = htons((unsigned short)conn->port); #endif + + if (conn->data->set.proxytype == CURLPROXY_SOCKS5) { + return handleSock5Proxy(conn->data->state.proxyuser, + conn->data->state.proxypasswd, + conn, + conn->firstsocket) ? + CURLE_COULDNT_CONNECT : CURLE_OK; + } + else if (conn->data->set.proxytype == CURLPROXY_HTTP) { + /* do nothing here. handled later. */ + } + else { + failf(conn->data, "unknown proxytype option given"); + return CURLE_COULDNT_CONNECT; + } } return result; diff --git a/lib/urldata.h b/lib/urldata.h index 6acb8d816..5a93150b2 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -645,6 +645,8 @@ struct UserDefined { char *krb4_level; /* what security level */ struct ssl_config_data ssl; /* user defined SSL stuff */ + curl_proxytype proxytype; /* what kind of proxy that is in use */ + int dns_cache_timeout; /* DNS cache timeout */ long buffer_size; /* size of receive buffer to use */ diff --git a/src/main.c b/src/main.c index 24eb8fb92..e12bdc4b8 100644 --- a/src/main.c +++ b/src/main.c @@ -2698,7 +2698,7 @@ operate(struct Configurable *config, int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer); curl_easy_setopt(curl, CURLOPT_TIMEOUT, config->timeout); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config->postfields); - + /* new in libcurl 7.2: */ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config->postfieldsize);