2026-09-04 16:45:38 +02:00
|
|
|
/* Provenance: curl/curl do_SOCKS5 (fixed).
|
|
|
|
|
* repo: curl/curl
|
|
|
|
|
* commit: fb4415d8aee6c1045be932a34fe6107c2f5ed147
|
|
|
|
|
* parent: 09e25b9d94f4106eac8ca3a43b221bdc66f405e4
|
|
|
|
|
* commit_url: https://github.com/curl/curl/commit/fb4415d8aee6c1045be932a34fe6107c2f5ed147
|
|
|
|
|
* cve: CVE-2023-38545
|
|
|
|
|
* license: curl
|
|
|
|
|
* function: do_SOCKS5
|
|
|
|
|
* relpath: lib/socks.c
|
2026-09-05 09:27:03 +02:00
|
|
|
* provenance: human
|
|
|
|
|
* mechanism: unchecked_length_copy
|
2026-09-04 16:45:38 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
|
|
|
|
|
struct socks_state *sx,
|
|
|
|
|
struct Curl_easy *data)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
According to the RFC1928, section "6. Replies". This is what a SOCK5
|
|
|
|
|
replies:
|
|
|
|
|
|
|
|
|
|
+----+-----+-------+------+----------+----------+
|
|
|
|
|
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
|
|
|
|
|
+----+-----+-------+------+----------+----------+
|
|
|
|
|
| 1 | 1 | X'00' | 1 | Variable | 2 |
|
|
|
|
|
+----+-----+-------+------+----------+----------+
|
|
|
|
|
|
|
|
|
|
Where:
|
|
|
|
|
|
|
|
|
|
o VER protocol version: X'05'
|
|
|
|
|
o REP Reply field:
|
|
|
|
|
o X'00' succeeded
|
|
|
|
|
*/
|
|
|
|
|
struct connectdata *conn = cf->conn;
|
|
|
|
|
unsigned char *socksreq = (unsigned char *)data->state.buffer;
|
|
|
|
|
int idx;
|
|
|
|
|
CURLcode result;
|
|
|
|
|
CURLproxycode presult;
|
|
|
|
|
bool socks5_resolve_local =
|
|
|
|
|
(conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
|
|
|
|
|
const size_t hostname_len = strlen(sx->hostname);
|
|
|
|
|
ssize_t len = 0;
|
|
|
|
|
const unsigned char auth = data->set.socks5auth;
|
|
|
|
|
bool allow_gssapi = FALSE;
|
|
|
|
|
struct Curl_dns_entry *dns = NULL;
|
|
|
|
|
|
|
|
|
|
DEBUGASSERT(auth & (CURLAUTH_BASIC | CURLAUTH_GSSAPI));
|
|
|
|
|
switch(sx->state) {
|
|
|
|
|
case CONNECT_SOCKS_INIT:
|
|
|
|
|
if(conn->bits.httpproxy)
|
|
|
|
|
infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
|
|
|
|
|
sx->hostname, sx->remote_port);
|
|
|
|
|
|
|
|
|
|
/* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
|
|
|
|
|
if(!socks5_resolve_local && hostname_len > 255) {
|
|
|
|
|
failf(data, "SOCKS5: the destination hostname is too long to be "
|
|
|
|
|
"resolved remotely by the proxy.");
|
|
|
|
|
return CURLPX_LONG_HOSTNAME;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
|
|
|
|
|
infof(data,
|
|
|
|
|
"warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %u",
|
|
|
|
|
auth);
|
|
|
|
|
if(!(auth & CURLAUTH_BASIC))
|
|
|
|
|
/* disable username/password auth */
|
|
|
|
|
sx->proxy_user = NULL;
|
|
|
|
|
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
|
|
|
|
|
if(auth & CURLAUTH_GSSAPI)
|
|
|
|
|
allow_gssapi = TRUE;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
idx = 0;
|
|
|
|
|
socksreq[idx++] = 5; /* version */
|
|
|
|
|
idx++; /* number of authentication methods */
|
|
|
|
|
socksreq[idx++] = 0; /* no authentication */
|
|
|
|
|
if(allow_gssapi)
|
|
|
|
|
socksreq[idx++] = 1; /* GSS-API */
|
|
|
|
|
if(sx->proxy_user)
|
|
|
|
|
socksreq[idx++] = 2; /* username/password */
|
|
|
|
|
/* write the number of authentication methods */
|
|
|
|
|
socksreq[1] = (unsigned char) (idx - 2);
|
|
|
|
|
|
|
|
|
|
sx->outp = socksreq;
|
|
|
|
|
sx->outstanding = idx;
|
|
|
|
|
presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
|
|
|
|
|
"initial SOCKS5 request");
|
|
|
|
|
if(CURLPX_OK != presult)
|
|
|
|
|
return presult;
|
|
|
|
|
else if(sx->outstanding) {
|
|
|
|
|
/* remain in sending state */
|
|
|
|
|
return CURLPX_OK;
|
|
|
|
|
}
|
|
|
|
|
sxstate(sx, data, CONNECT_SOCKS_READ);
|
|
|
|
|
goto CONNECT_SOCKS_READ_INIT;
|
|
|
|
|
case CONNECT_SOCKS_SEND:
|
|
|
|
|
presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
|
|
|
|
|
"initial SOCKS5 request");
|
|
|
|
|
if(CURLPX_OK != presult)
|
|
|
|
|
return presult;
|
|
|
|
|
else if(sx->outstanding) {
|
|
|
|
|
/* remain in sending state */
|
|
|
|
|
return CURLPX_OK;
|
|
|
|
|
}
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
CONNECT_SOCKS_READ_INIT:
|
|
|
|
|
case CONNECT_SOCKS_READ_INIT:
|
|
|
|
|
sx->outstanding = 2; /* expect two bytes */
|
|
|
|
|
sx->outp = socksreq; /* store it here */
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
case CONNECT_SOCKS_READ:
|
|
|
|
|
presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
|
|
|
|
|
"initial SOCKS5 response");
|
|
|
|
|
if(CURLPX_OK != presult)
|
|
|
|
|
return presult;
|
|
|
|
|
else if(sx->outstanding) {
|
|
|
|
|
/* remain in reading state */
|
|
|
|
|
return CURLPX_OK;
|
|
|
|
|
}
|
|
|
|
|
else if(socksreq[0] != 5) {
|
|
|
|
|
failf(data, "Received invalid version in initial SOCKS5 response.");
|
|
|
|
|
return CURLPX_BAD_VERSION;
|
|
|
|
|
}
|
|
|
|
|
else if(socksreq[1] == 0) {
|
|
|
|
|
/* DONE! No authentication needed. Send request. */
|
|
|
|
|
sxstate(sx, data, CONNECT_REQ_INIT);
|
|
|
|
|
goto CONNECT_REQ_INIT;
|
|
|
|
|
}
|
|
|
|
|
else if(socksreq[1] == 2) {
|
|
|
|
|
/* regular name + password authentication */
|
|
|
|
|
sxstate(sx, data, CONNECT_AUTH_INIT);
|
|
|
|
|
goto CONNECT_AUTH_INIT;
|
|
|
|
|
}
|
|
|
|
|
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
|
|
|
|
|
else if(allow_gssapi && (socksreq[1] == 1)) {
|
|
|
|
|
sxstate(sx, data, CONNECT_GSSAPI_INIT);
|
|
|
|
|
result = Curl_SOCKS5_gssapi_negotiate(cf, data);
|
|
|
|
|
if(result) {
|
|
|
|
|
failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
|
|
|
|
|
return CURLPX_GSSAPI;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
else {
|
|
|
|
|
/* error */
|
|
|
|
|
if(!allow_gssapi && (socksreq[1] == 1)) {
|
|
|
|
|
failf(data,
|
|
|
|
|
"SOCKS5 GSSAPI per-message authentication is not supported.");
|
|
|
|
|
return CURLPX_GSSAPI_PERMSG;
|
|
|
|
|
}
|
|
|
|
|
else if(socksreq[1] == 255) {
|
|
|
|
|
failf(data, "No authentication method was acceptable.");
|
|
|
|
|
return CURLPX_NO_AUTH;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
failf(data,
|
|
|
|
|
"Undocumented SOCKS5 mode attempted to be used by server.");
|
|
|
|
|
return CURLPX_UNKNOWN_MODE;
|
|
|
|
|
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
|
|
|
|
|
case CONNECT_GSSAPI_INIT:
|
|
|
|
|
/* GSSAPI stuff done non-blocking */
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
default: /* do nothing! */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
CONNECT_AUTH_INIT:
|
|
|
|
|
case CONNECT_AUTH_INIT: {
|
|
|
|
|
/* Needs user name and password */
|
|
|
|
|
size_t proxy_user_len, proxy_password_len;
|
|
|
|
|
if(sx->proxy_user && sx->proxy_password) {
|
|
|
|
|
proxy_user_len = strlen(sx->proxy_user);
|
|
|
|
|
proxy_password_len = strlen(sx->proxy_password);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
proxy_user_len = 0;
|
|
|
|
|
proxy_password_len = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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++] = (unsigned char) proxy_user_len;
|
|
|
|
|
if(sx->proxy_user && proxy_user_len) {
|
|
|
|
|
/* the length must fit in a single byte */
|
|
|
|
|
if(proxy_user_len > 255) {
|
|
|
|
|
failf(data, "Excessive user name length for proxy auth");
|
|
|
|
|
return CURLPX_LONG_USER;
|
|
|
|
|
}
|
|
|
|
|
memcpy(socksreq + len, sx->proxy_user, proxy_user_len);
|
|
|
|
|
}
|
|
|
|
|
len += proxy_user_len;
|
|
|
|
|
socksreq[len++] = (unsigned char) proxy_password_len;
|
|
|
|
|
if(sx->proxy_password && proxy_password_len) {
|
|
|
|
|
/* the length must fit in a single byte */
|
|
|
|
|
if(proxy_password_len > 255) {
|
|
|
|
|
failf(data, "Excessive password length for proxy auth");
|
|
|
|
|
return CURLPX_LONG_PASSWD;
|
|
|
|
|
}
|
|
|
|
|
memcpy(socksreq + len, sx->proxy_password, proxy_password_len);
|
|
|
|
|
}
|
|
|
|
|
len += proxy_password_len;
|
|
|
|
|
sxstate(sx, data, CONNECT_AUTH_SEND);
|
|
|
|
|
sx->outstanding = len;
|
|
|
|
|
sx->outp = socksreq;
|
|
|
|
|
}
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
case CONNECT_AUTH_SEND:
|
|
|
|
|
presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH,
|
|
|
|
|
"SOCKS5 sub-negotiation request");
|
|
|
|
|
if(CURLPX_OK != presult)
|
|
|
|
|
return presult;
|
|
|
|
|
else if(sx->outstanding) {
|
|
|
|
|
/* remain in sending state */
|
|
|
|
|
return CURLPX_OK;
|
|
|
|
|
}
|
|
|
|
|
sx->outp = socksreq;
|
|
|
|
|
sx->outstanding = 2;
|
|
|
|
|
sxstate(sx, data, CONNECT_AUTH_READ);
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
case CONNECT_AUTH_READ:
|
|
|
|
|
presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH,
|
|
|
|
|
"SOCKS5 sub-negotiation response");
|
|
|
|
|
if(CURLPX_OK != presult)
|
|
|
|
|
return presult;
|
|
|
|
|
else if(sx->outstanding) {
|
|
|
|
|
/* remain in reading state */
|
|
|
|
|
return CURLPX_OK;
|
|
|
|
|
}
|
|
|
|
|
/* ignore the first (VER) byte */
|
|
|
|
|
else if(socksreq[1]) { /* status */
|
|
|
|
|
failf(data, "User was rejected by the SOCKS5 server (%d %d).",
|
|
|
|
|
socksreq[0], socksreq[1]);
|
|
|
|
|
return CURLPX_USER_REJECTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Everything is good so far, user was authenticated! */
|
|
|
|
|
sxstate(sx, data, CONNECT_REQ_INIT);
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
CONNECT_REQ_INIT:
|
|
|
|
|
case CONNECT_REQ_INIT:
|
|
|
|
|
if(socks5_resolve_local) {
|
|
|
|
|
enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
|
|
|
|
|
TRUE, &dns);
|
|
|
|
|
|
|
|
|
|
if(rc == CURLRESOLV_ERROR)
|
|
|
|
|
return CURLPX_RESOLVE_HOST;
|
|
|
|
|
|
|
|
|
|
if(rc == CURLRESOLV_PENDING) {
|
|
|
|
|
sxstate(sx, data, CONNECT_RESOLVING);
|
|
|
|
|
return CURLPX_OK;
|
|
|
|
|
}
|
|
|
|
|
sxstate(sx, data, CONNECT_RESOLVED);
|
|
|
|
|
goto CONNECT_RESOLVED;
|
|
|
|
|
}
|
|
|
|
|
goto CONNECT_RESOLVE_REMOTE;
|
|
|
|
|
|
|
|
|
|
case CONNECT_RESOLVING:
|
|
|
|
|
/* check if we have the name resolved by now */
|
|
|
|
|
dns = Curl_fetch_addr(data, sx->hostname, sx->remote_port);
|
|
|
|
|
|
|
|
|
|
if(dns) {
|
|
|
|
|
#ifdef CURLRES_ASYNCH
|
|
|
|
|
data->state.async.dns = dns;
|
|
|
|
|
data->state.async.done = TRUE;
|
|
|
|
|
#endif
|
|
|
|
|
infof(data, "SOCKS5: hostname '%s' found", sx->hostname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!dns) {
|
|
|
|
|
result = Curl_resolv_check(data, &dns);
|
|
|
|
|
if(!dns) {
|
|
|
|
|
if(result)
|
|
|
|
|
return CURLPX_RESOLVE_HOST;
|
|
|
|
|
return CURLPX_OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
CONNECT_RESOLVED:
|
|
|
|
|
case CONNECT_RESOLVED: {
|
|
|
|
|
char dest[MAX_IPADR_LEN] = "unknown"; /* printable address */
|
|
|
|
|
struct Curl_addrinfo *hp = NULL;
|
|
|
|
|
if(dns)
|
|
|
|
|
hp = dns->addr;
|
|
|
|
|
if(!hp) {
|
|
|
|
|
failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
|
|
|
|
|
sx->hostname);
|
|
|
|
|
return CURLPX_RESOLVE_HOST;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Curl_printable_address(hp, dest, sizeof(dest));
|
|
|
|
|
|
|
|
|
|
len = 0;
|
|
|
|
|
socksreq[len++] = 5; /* version (SOCKS5) */
|
|
|
|
|
socksreq[len++] = 1; /* connect */
|
|
|
|
|
socksreq[len++] = 0; /* must be zero */
|
|
|
|
|
if(hp->ai_family == AF_INET) {
|
|
|
|
|
int i;
|
|
|
|
|
struct sockaddr_in *saddr_in;
|
|
|
|
|
socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
|
|
|
|
|
|
|
|
|
|
saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
|
|
|
|
|
for(i = 0; i < 4; i++) {
|
|
|
|
|
socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
infof(data, "SOCKS5 connect to %s:%d (locally resolved)", dest,
|
|
|
|
|
sx->remote_port);
|
|
|
|
|
}
|
|
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
|
else if(hp->ai_family == AF_INET6) {
|
|
|
|
|
int i;
|
|
|
|
|
struct sockaddr_in6 *saddr_in6;
|
|
|
|
|
socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
|
|
|
|
|
|
|
|
|
|
saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
|
|
|
|
|
for(i = 0; i < 16; i++) {
|
|
|
|
|
socksreq[len++] =
|
|
|
|
|
((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
infof(data, "SOCKS5 connect to [%s]:%d (locally resolved)", dest,
|
|
|
|
|
sx->remote_port);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
else {
|
|
|
|
|
hp = NULL; /* fail! */
|
|
|
|
|
failf(data, "SOCKS5 connection to %s not supported", dest);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Curl_resolv_unlock(data, dns); /* not used anymore from now on */
|
|
|
|
|
goto CONNECT_REQ_SEND;
|
|
|
|
|
}
|
|
|
|
|
CONNECT_RESOLVE_REMOTE:
|
|
|
|
|
case CONNECT_RESOLVE_REMOTE:
|
|
|
|
|
/* Authentication is complete, now specify destination to the proxy */
|
|
|
|
|
len = 0;
|
|
|
|
|
socksreq[len++] = 5; /* version (SOCKS5) */
|
|
|
|
|
socksreq[len++] = 1; /* connect */
|
|
|
|
|
socksreq[len++] = 0; /* must be zero */
|
|
|
|
|
|
|
|
|
|
if(!socks5_resolve_local) {
|
|
|
|
|
/* ATYP: domain name = 3,
|
|
|
|
|
IPv6 == 4,
|
|
|
|
|
IPv4 == 1 */
|
|
|
|
|
unsigned char ip4[4];
|
|
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
|
if(conn->bits.ipv6_ip) {
|
|
|
|
|
char ip6[16];
|
|
|
|
|
if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6))
|
|
|
|
|
return CURLPX_BAD_ADDRESS_TYPE;
|
|
|
|
|
socksreq[len++] = 4;
|
|
|
|
|
memcpy(&socksreq[len], ip6, sizeof(ip6));
|
|
|
|
|
len += sizeof(ip6);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
#endif
|
|
|
|
|
if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) {
|
|
|
|
|
socksreq[len++] = 1;
|
|
|
|
|
memcpy(&socksreq[len], ip4, sizeof(ip4));
|
|
|
|
|
len += sizeof(ip4);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
socksreq[len++] = 3;
|
|
|
|
|
socksreq[len++] = (unsigned char) hostname_len; /* one byte length */
|
|
|
|
|
memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */
|
|
|
|
|
len += hostname_len;
|
|
|
|
|
}
|
|
|
|
|
infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
|
|
|
|
|
sx->hostname, sx->remote_port);
|
|
|
|
|
}
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
|
|
|
|
CONNECT_REQ_SEND:
|
|
|
|
|
case CONNECT_REQ_SEND:
|
|
|
|
|
/* PORT MSB */
|
|
|
|
|
socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff);
|
|
|
|
|
/* PORT LSB */
|
|
|
|
|
socksreq[len++] = (unsigned char)(sx->remote_port & 0xff);
|
|
|
|
|
|
|
|
|
|
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
|
|
|
|
|
if(conn->socks5_gssapi_enctype) {
|
|
|
|
|
failf(data, "SOCKS5 GSS-API protection not yet implemented.");
|
|
|
|
|
return CURLPX_GSSAPI_PROTECTION;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
sx->outp = socksreq;
|
|
|
|
|
sx->outstanding = len;
|
|
|
|
|
sxstate(sx, data, CONNECT_REQ_SENDING);
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
case CONNECT_REQ_SENDING:
|
|
|
|
|
presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST,
|
|
|
|
|
"SOCKS5 connect request");
|
|
|
|
|
if(CURLPX_OK != presult)
|
|
|
|
|
return presult;
|
|
|
|
|
else if(sx->outstanding) {
|
|
|
|
|
/* remain in send state */
|
|
|
|
|
return CURLPX_OK;
|
|
|
|
|
}
|
|
|
|
|
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
|
|
|
|
|
if(conn->socks5_gssapi_enctype) {
|
|
|
|
|
failf(data, "SOCKS5 GSS-API protection not yet implemented.");
|
|
|
|
|
return CURLPX_GSSAPI_PROTECTION;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
sx->outstanding = 10; /* minimum packet size is 10 */
|
|
|
|
|
sx->outp = socksreq;
|
|
|
|
|
sxstate(sx, data, CONNECT_REQ_READ);
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
case CONNECT_REQ_READ:
|
|
|
|
|
presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK,
|
|
|
|
|
"SOCKS5 connect request ack");
|
|
|
|
|
if(CURLPX_OK != presult)
|
|
|
|
|
return presult;
|
|
|
|
|
else if(sx->outstanding) {
|
|
|
|
|
/* remain in reading state */
|
|
|
|
|
return CURLPX_OK;
|
|
|
|
|
}
|
|
|
|
|
else if(socksreq[0] != 5) { /* version */
|
|
|
|
|
failf(data,
|
|
|
|
|
"SOCKS5 reply has wrong version, version should be 5.");
|
|
|
|
|
return CURLPX_BAD_VERSION;
|
|
|
|
|
}
|
|
|
|
|
else if(socksreq[1]) { /* Anything besides 0 is an error */
|
|
|
|
|
CURLproxycode rc = CURLPX_REPLY_UNASSIGNED;
|
|
|
|
|
int code = socksreq[1];
|
|
|
|
|
failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
|
|
|
|
|
sx->hostname, (unsigned char)socksreq[1]);
|
|
|
|
|
if(code < 9) {
|
|
|
|
|
/* RFC 1928 section 6 lists: */
|
|
|
|
|
static const CURLproxycode lookup[] = {
|
|
|
|
|
CURLPX_OK,
|
|
|
|
|
CURLPX_REPLY_GENERAL_SERVER_FAILURE,
|
|
|
|
|
CURLPX_REPLY_NOT_ALLOWED,
|
|
|
|
|
CURLPX_REPLY_NETWORK_UNREACHABLE,
|
|
|
|
|
CURLPX_REPLY_HOST_UNREACHABLE,
|
|
|
|
|
CURLPX_REPLY_CONNECTION_REFUSED,
|
|
|
|
|
CURLPX_REPLY_TTL_EXPIRED,
|
|
|
|
|
CURLPX_REPLY_COMMAND_NOT_SUPPORTED,
|
|
|
|
|
CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED,
|
|
|
|
|
};
|
|
|
|
|
rc = lookup[code];
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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];
|
|
|
|
|
len = 5 + addrlen + 2;
|
|
|
|
|
}
|
|
|
|
|
else if(socksreq[3] == 4) {
|
|
|
|
|
/* IPv6 */
|
|
|
|
|
len = 4 + 16 + 2;
|
|
|
|
|
}
|
|
|
|
|
else if(socksreq[3] == 1) {
|
|
|
|
|
len = 4 + 4 + 2;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
failf(data, "SOCKS5 reply has wrong address type.");
|
|
|
|
|
return CURLPX_BAD_ADDRESS_TYPE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* At this point we already read first 10 bytes */
|
|
|
|
|
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
|
|
|
|
|
if(!conn->socks5_gssapi_enctype) {
|
|
|
|
|
/* decrypt_gssapi_blockread already read the whole packet */
|
|
|
|
|
#endif
|
|
|
|
|
if(len > 10) {
|
|
|
|
|
sx->outstanding = len - 10; /* get the rest */
|
|
|
|
|
sx->outp = &socksreq[10];
|
|
|
|
|
sxstate(sx, data, CONNECT_REQ_READ_MORE);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
sxstate(sx, data, CONNECT_DONE);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
case CONNECT_REQ_READ_MORE:
|
|
|
|
|
presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS,
|
|
|
|
|
"SOCKS5 connect request address");
|
|
|
|
|
if(CURLPX_OK != presult)
|
|
|
|
|
return presult;
|
|
|
|
|
else if(sx->outstanding) {
|
|
|
|
|
/* remain in reading state */
|
|
|
|
|
return CURLPX_OK;
|
|
|
|
|
}
|
|
|
|
|
sxstate(sx, data, CONNECT_DONE);
|
|
|
|
|
}
|
|
|
|
|
infof(data, "SOCKS5 request granted.");
|
|
|
|
|
|
|
|
|
|
return CURLPX_OK; /* Proxy was successful! */
|
|
|
|
|
}
|