SOCKS5 support added (contributed by a still unnamed person). Not properly
working for "IPv6 enabled" libcurls yet, but should be pretty easy for someone to adjust.
This commit is contained in:
Родитель
ac285b453e
Коммит
8aa3f14303
|
@ -202,6 +202,12 @@ typedef enum {
|
||||||
CURL_LAST /* never use! */
|
CURL_LAST /* never use! */
|
||||||
} CURLcode;
|
} 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
|
/* 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
|
is no longer used by libcurl but is instead #defined here only to not
|
||||||
make programs break */
|
make programs break */
|
||||||
|
@ -576,6 +582,10 @@ typedef enum {
|
||||||
/* Explicitly allow insecure SSL connects */
|
/* Explicitly allow insecure SSL connects */
|
||||||
CINIT(SSL_INSECURE, LONG, 101),
|
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 */
|
CURLOPT_LASTENTRY /* the last unused */
|
||||||
} CURLoption;
|
} CURLoption;
|
||||||
|
|
||||||
|
|
|
@ -420,7 +420,7 @@ CURLcode Curl_http_connect(struct connectdata *conn)
|
||||||
* has occured, can we start talking SSL
|
* 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)) {
|
((conn->protocol & PROT_HTTPS) || data->set.tunnel_thru_httpproxy)) {
|
||||||
|
|
||||||
/* either HTTPS over proxy, OR explicitly asked for a tunnel */
|
/* either HTTPS over proxy, OR explicitly asked for a tunnel */
|
||||||
|
|
207
lib/url.c
207
lib/url.c
|
@ -282,6 +282,8 @@ CURLcode Curl_open(struct SessionHandle **curl)
|
||||||
|
|
||||||
data->set.proxyport = 1080;
|
data->set.proxyport = 1080;
|
||||||
|
|
||||||
|
data->set.proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
|
||||||
|
|
||||||
/* create an array with connection data struct pointers */
|
/* create an array with connection data struct pointers */
|
||||||
data->state.numconnects = 5; /* hard-coded right now */
|
data->state.numconnects = 5; /* hard-coded right now */
|
||||||
data->state.connects = (struct connectdata **)
|
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;
|
data->set.ssl.allow_insecure = va_arg(param, long)?TRUE:FALSE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CURLOPT_PROXYTYPE:
|
||||||
|
/*
|
||||||
|
* Set proxy type. HTTP/SOCKS4/SOCKS5
|
||||||
|
*/
|
||||||
|
data->set.proxytype = va_arg(param, long);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* unknown tag and its companion, just ignore: */
|
/* unknown tag and its companion, just ignore: */
|
||||||
return CURLE_FAILED_INIT; /* correct this */
|
return CURLE_FAILED_INIT; /* correct this */
|
||||||
|
@ -1327,6 +1336,189 @@ ConnectionStore(struct SessionHandle *data,
|
||||||
return i;
|
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,
|
static CURLcode ConnectPlease(struct connectdata *conn,
|
||||||
Curl_addrinfo *hostaddr,
|
Curl_addrinfo *hostaddr,
|
||||||
bool *connected)
|
bool *connected)
|
||||||
|
@ -1356,6 +1548,21 @@ static CURLcode ConnectPlease(struct connectdata *conn,
|
||||||
conn->serv_addr.sin_family = hostaddr->h_addrtype;
|
conn->serv_addr.sin_family = hostaddr->h_addrtype;
|
||||||
conn->serv_addr.sin_port = htons((unsigned short)conn->port);
|
conn->serv_addr.sin_port = htons((unsigned short)conn->port);
|
||||||
#endif
|
#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;
|
return result;
|
||||||
|
|
|
@ -645,6 +645,8 @@ struct UserDefined {
|
||||||
char *krb4_level; /* what security level */
|
char *krb4_level; /* what security level */
|
||||||
struct ssl_config_data ssl; /* user defined SSL stuff */
|
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 */
|
int dns_cache_timeout; /* DNS cache timeout */
|
||||||
long buffer_size; /* size of receive buffer to use */
|
long buffer_size; /* size of receive buffer to use */
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче