diff options
author | Brad King <brad.king@kitware.com> | 2023-01-27 15:58:44 -0500 |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2023-01-27 15:58:44 -0500 |
commit | f9f5957884c279af81766f3f339bdd0e768e814f (patch) | |
tree | 310f21f037896ac658f8d59e81fa671ee9dee1ef /Utilities/cmcurl/lib/socks.c | |
parent | 39dcf9469d3dae1c319dbe9d1fbd86bef91b73e0 (diff) | |
parent | dac458ddbf2b48168779821654a7e69cbd828c14 (diff) | |
download | cmake-f9f5957884c279af81766f3f339bdd0e768e814f.tar.gz |
Merge branch 'upstream-curl' into update-curl
* upstream-curl:
curl 2022-12-21 (c12fb3dd)
Diffstat (limited to 'Utilities/cmcurl/lib/socks.c')
-rw-r--r-- | Utilities/cmcurl/lib/socks.c | 457 |
1 files changed, 330 insertions, 127 deletions
diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c index 52c29880a6..d491e089be 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c @@ -36,17 +36,52 @@ #include "urldata.h" #include "sendf.h" #include "select.h" +#include "cfilters.h" #include "connect.h" #include "timeval.h" #include "socks.h" #include "multiif.h" /* for getsock macros */ #include "inet_pton.h" +#include "url.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" +/* for the (SOCKS) connect state machine */ +enum connect_t { + CONNECT_INIT, + CONNECT_SOCKS_INIT, /* 1 */ + CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */ + CONNECT_SOCKS_READ_INIT, /* 3 set up read */ + CONNECT_SOCKS_READ, /* 4 read server response */ + CONNECT_GSSAPI_INIT, /* 5 */ + CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */ + CONNECT_AUTH_SEND, /* 7 send auth */ + CONNECT_AUTH_READ, /* 8 read auth response */ + CONNECT_REQ_INIT, /* 9 init SOCKS "request" */ + CONNECT_RESOLVING, /* 10 */ + CONNECT_RESOLVED, /* 11 */ + CONNECT_RESOLVE_REMOTE, /* 12 */ + CONNECT_REQ_SEND, /* 13 */ + CONNECT_REQ_SENDING, /* 14 */ + CONNECT_REQ_READ, /* 15 */ + CONNECT_REQ_READ_MORE, /* 16 */ + CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */ +}; + +struct socks_state { + enum connect_t state; + ssize_t outstanding; /* send this many bytes more */ + unsigned char *outp; /* send from this pointer */ + + const char *hostname; + int remote_port; + const char *proxy_user; + const char *proxy_password; +}; + #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* * Helper read-from-socket functions. Does the same as Curl_read() but it @@ -77,7 +112,7 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */ result = ~CURLE_OK; break; } - result = Curl_read_plain(sockfd, buf, buffersize, &nread); + result = Curl_read_plain(data, sockfd, buf, buffersize, &nread); if(CURLE_AGAIN == result) continue; if(result) @@ -104,21 +139,20 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */ #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) #define DEBUG_AND_VERBOSE -#define sxstate(x,y) socksstate(x,y, __LINE__) +#define sxstate(x,d,y) socksstate(x,d,y, __LINE__) #else -#define sxstate(x,y) socksstate(x,y) +#define sxstate(x,d,y) socksstate(x,d,y) #endif /* always use this function to change state, to make debugging easier */ -static void socksstate(struct Curl_easy *data, +static void socksstate(struct socks_state *sx, struct Curl_easy *data, enum connect_t state #ifdef DEBUG_AND_VERBOSE , int lineno #endif ) { - struct connectdata *conn = data->conn; - enum connect_t oldstate = conn->cnnct.state; + enum connect_t oldstate = sx->state; #ifdef DEBUG_AND_VERBOSE /* synced with the state list in urldata.h */ static const char * const statename[] = { @@ -143,40 +177,21 @@ static void socksstate(struct Curl_easy *data, }; #endif + (void)data; if(oldstate == state) /* don't bother when the new state is the same as the old state */ return; - conn->cnnct.state = state; + sx->state = state; #ifdef DEBUG_AND_VERBOSE infof(data, - "SXSTATE: %s => %s conn %p; line %d", - statename[oldstate], statename[conn->cnnct.state], conn, + "SXSTATE: %s => %s; line %d", + statename[oldstate], statename[sx->state], lineno); #endif } -int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock, - int sockindex) -{ - int rc = 0; - sock[0] = conn->sock[sockindex]; - switch(conn->cnnct.state) { - case CONNECT_RESOLVING: - case CONNECT_SOCKS_READ: - case CONNECT_AUTH_READ: - case CONNECT_REQ_READ: - case CONNECT_REQ_READ_MORE: - rc = GETSOCK_READSOCK(0); - break; - default: - rc = GETSOCK_WRITESOCK(0); - break; - } - return rc; -} - /* * This function logs in to a SOCKS4 proxy and sends the specifics to the final * destination server. @@ -188,20 +203,16 @@ int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock, * Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" * Nonsupport "Identification Protocol (RFC1413)" */ -CURLproxycode Curl_SOCKS4(const char *proxy_user, - const char *hostname, - int remote_port, - int sockindex, - struct Curl_easy *data, - bool *done) +static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data) { - struct connectdata *conn = data->conn; + struct connectdata *conn = cf->conn; const bool protocol4a = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE; unsigned char *socksreq = (unsigned char *)data->state.buffer; CURLcode result; - curl_socket_t sockfd = conn->sock[sockindex]; - struct connstate *sx = &conn->cnnct; + curl_socket_t sockfd = conn->sock[cf->sockindex]; struct Curl_dns_entry *dns = NULL; ssize_t actualread; ssize_t written; @@ -209,18 +220,16 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, /* make sure that the buffer is at least 600 bytes */ DEBUGASSERT(READBUFFER_MIN >= 600); - if(!SOCKS_STATE(sx->state) && !*done) - sxstate(data, CONNECT_SOCKS_INIT); - switch(sx->state) { case CONNECT_SOCKS_INIT: /* SOCKS4 can only do IPv4, insist! */ conn->ip_version = CURL_IPRESOLVE_V4; if(conn->bits.httpproxy) infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d", - protocol4a ? "a" : "", hostname, remote_port); + protocol4a ? "a" : "", sx->hostname, sx->remote_port); - infof(data, "SOCKS4 communication to %s:%d", hostname, remote_port); + infof(data, "SOCKS4 communication to %s:%d", + sx->hostname, sx->remote_port); /* * Compose socks4 request @@ -235,40 +244,40 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, socksreq[0] = 4; /* version (SOCKS4) */ socksreq[1] = 1; /* connect */ - socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */ - socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */ + socksreq[2] = (unsigned char)((sx->remote_port >> 8) & 0xff); /* MSB */ + socksreq[3] = (unsigned char)(sx->remote_port & 0xff); /* LSB */ /* DNS resolve only for SOCKS4, not SOCKS4a */ if(!protocol4a) { enum resolve_t rc = - Curl_resolv(data, hostname, remote_port, FALSE, &dns); + Curl_resolv(data, sx->hostname, sx->remote_port, FALSE, &dns); if(rc == CURLRESOLV_ERROR) return CURLPX_RESOLVE_HOST; else if(rc == CURLRESOLV_PENDING) { - sxstate(data, CONNECT_RESOLVING); - infof(data, "SOCKS4 non-blocking resolve of %s", hostname); + sxstate(sx, data, CONNECT_RESOLVING); + infof(data, "SOCKS4 non-blocking resolve of %s", sx->hostname); return CURLPX_OK; } - sxstate(data, CONNECT_RESOLVED); + sxstate(sx, data, CONNECT_RESOLVED); goto CONNECT_RESOLVED; } /* socks4a doesn't resolve anything locally */ - sxstate(data, CONNECT_REQ_INIT); + sxstate(sx, data, CONNECT_REQ_INIT); goto CONNECT_REQ_INIT; case CONNECT_RESOLVING: /* check if we have the name resolved by now */ - dns = Curl_fetch_addr(data, hostname, (int)conn->port); + dns = Curl_fetch_addr(data, sx->hostname, (int)conn->port); if(dns) { #ifdef CURLRES_ASYNCH data->state.async.dns = dns; data->state.async.done = TRUE; #endif - infof(data, "Hostname '%s' was found", hostname); - sxstate(data, CONNECT_RESOLVED); + infof(data, "Hostname '%s' was found", sx->hostname); + sxstate(sx, data, CONNECT_RESOLVED); } else { result = Curl_resolv_check(data, &dns); @@ -309,11 +318,11 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } else - failf(data, "SOCKS4 connection to %s not supported", hostname); + failf(data, "SOCKS4 connection to %s not supported", sx->hostname); } else failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", - hostname); + sx->hostname); if(!hp) return CURLPX_RESOLVE_HOST; @@ -325,14 +334,14 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, * This is currently not supporting "Identification Protocol (RFC1413)". */ socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ - if(proxy_user) { - size_t plen = strlen(proxy_user); + if(sx->proxy_user) { + size_t plen = strlen(sx->proxy_user); if(plen >= (size_t)data->set.buffer_size - 8) { failf(data, "Too long SOCKS proxy user name, can't use"); return CURLPX_LONG_USER; } /* copy the proxy name WITH trailing zero */ - memcpy(socksreq + 8, proxy_user, plen + 1); + memcpy(socksreq + 8, sx->proxy_user, plen + 1); } /* @@ -350,9 +359,9 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, socksreq[6] = 0; socksreq[7] = 1; /* append hostname */ - hostnamelen = strlen(hostname) + 1; /* length including NUL */ + hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */ if(hostnamelen <= 255) - strcpy((char *)socksreq + packetsize, hostname); + strcpy((char *)socksreq + packetsize, sx->hostname); else { failf(data, "SOCKS4: too long host name"); return CURLPX_LONG_HOSTNAME; @@ -361,7 +370,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, } sx->outp = socksreq; sx->outstanding = packetsize; - sxstate(data, CONNECT_REQ_SENDING); + sxstate(sx, data, CONNECT_REQ_SENDING); } /* FALLTHROUGH */ case CONNECT_REQ_SENDING: @@ -382,12 +391,12 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, /* done sending! */ sx->outstanding = 8; /* receive data size */ sx->outp = socksreq; - sxstate(data, CONNECT_SOCKS_READ); + sxstate(sx, data, CONNECT_SOCKS_READ); /* FALLTHROUGH */ case CONNECT_SOCKS_READ: /* Receive response */ - result = Curl_read_plain(sockfd, (char *)sx->outp, + result = Curl_read_plain(data, sockfd, (char *)sx->outp, sx->outstanding, &actualread); if(result && (CURLE_AGAIN != result)) { failf(data, "SOCKS4: Failed receiving connect request ack: %s", @@ -405,7 +414,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, sx->outp += actualread; return CURLPX_OK; } - sxstate(data, CONNECT_DONE); + sxstate(sx, data, CONNECT_DONE); break; default: /* lots of unused states in SOCKS4 */ break; @@ -478,7 +487,6 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, return CURLPX_UNKNOWN_FAIL; } - *done = TRUE; return CURLPX_OK; /* Proxy was successful! */ } @@ -486,13 +494,9 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user, * This function logs in to a SOCKS5 proxy and sends the specifics to the final * destination server. */ -CURLproxycode Curl_SOCKS5(const char *proxy_user, - const char *proxy_password, - const char *hostname, - int remote_port, - int sockindex, - struct Curl_easy *data, - bool *done) +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 @@ -510,31 +514,28 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, o REP Reply field: o X'00' succeeded */ - struct connectdata *conn = data->conn; + struct connectdata *conn = cf->conn; unsigned char *socksreq = (unsigned char *)data->state.buffer; char dest[256] = "unknown"; /* printable hostname:port */ int idx; ssize_t actualread; ssize_t written; CURLcode result; - curl_socket_t sockfd = conn->sock[sockindex]; + curl_socket_t sockfd = conn->sock[cf->sockindex]; bool socks5_resolve_local = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; - const size_t hostname_len = strlen(hostname); + const size_t hostname_len = strlen(sx->hostname); ssize_t len = 0; - const unsigned long auth = data->set.socks5auth; + const unsigned char auth = data->set.socks5auth; bool allow_gssapi = FALSE; - struct connstate *sx = &conn->cnnct; struct Curl_dns_entry *dns = NULL; - if(!SOCKS_STATE(sx->state) && !*done) - sxstate(data, CONNECT_SOCKS_INIT); - + 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", - hostname, remote_port); + sx->hostname, sx->remote_port); /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ if(!socks5_resolve_local && hostname_len > 255) { @@ -545,11 +546,11 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) infof(data, - "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu", + "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %u", auth); if(!(auth & CURLAUTH_BASIC)) /* disable username/password auth */ - proxy_user = NULL; + sx->proxy_user = NULL; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) if(auth & CURLAUTH_GSSAPI) allow_gssapi = TRUE; @@ -561,23 +562,23 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, socksreq[idx++] = 0; /* no authentication */ if(allow_gssapi) socksreq[idx++] = 1; /* GSS-API */ - if(proxy_user) + if(sx->proxy_user) socksreq[idx++] = 2; /* username/password */ /* write the number of authentication methods */ socksreq[1] = (unsigned char) (idx - 2); - result = Curl_write_plain(data, sockfd, (char *)socksreq, idx, &written); + result = Curl_write_plain(data, sockfd, socksreq, idx, &written); if(result && (CURLE_AGAIN != result)) { failf(data, "Unable to send initial SOCKS5 request."); return CURLPX_SEND_CONNECT; } if(written != idx) { - sxstate(data, CONNECT_SOCKS_SEND); + sxstate(sx, data, CONNECT_SOCKS_SEND); sx->outstanding = idx - written; sx->outp = &socksreq[written]; return CURLPX_OK; } - sxstate(data, CONNECT_SOCKS_READ); + sxstate(sx, data, CONNECT_SOCKS_READ); goto CONNECT_SOCKS_READ_INIT; case CONNECT_SOCKS_SEND: result = Curl_write_plain(data, sockfd, (char *)sx->outp, @@ -599,7 +600,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, sx->outp = socksreq; /* store it here */ /* FALLTHROUGH */ case CONNECT_SOCKS_READ: - result = Curl_read_plain(sockfd, (char *)sx->outp, + result = Curl_read_plain(data, sockfd, (char *)sx->outp, sx->outstanding, &actualread); if(result && (CURLE_AGAIN != result)) { failf(data, "Unable to receive initial SOCKS5 response."); @@ -622,18 +623,18 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, } else if(socksreq[1] == 0) { /* DONE! No authentication needed. Send request. */ - sxstate(data, CONNECT_REQ_INIT); + sxstate(sx, data, CONNECT_REQ_INIT); goto CONNECT_REQ_INIT; } else if(socksreq[1] == 2) { /* regular name + password authentication */ - sxstate(data, CONNECT_AUTH_INIT); + 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(data, CONNECT_GSSAPI_INIT); - result = Curl_SOCKS5_gssapi_negotiate(sockindex, data); + sxstate(sx, data, CONNECT_GSSAPI_INIT); + result = Curl_SOCKS5_gssapi_negotiate(cf->sockindex, data); if(result) { failf(data, "Unable to negotiate SOCKS5 GSS-API context."); return CURLPX_GSSAPI; @@ -668,9 +669,9 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, case CONNECT_AUTH_INIT: { /* Needs user name and password */ size_t proxy_user_len, proxy_password_len; - if(proxy_user && proxy_password) { - proxy_user_len = strlen(proxy_user); - proxy_password_len = strlen(proxy_password); + 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; @@ -687,32 +688,32 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, len = 0; socksreq[len++] = 1; /* username/pw subnegotiation version */ socksreq[len++] = (unsigned char) proxy_user_len; - if(proxy_user && proxy_user_len) { + if(sx->proxy_user && proxy_user_len) { /* the length must fit in a single byte */ - if(proxy_user_len >= 255) { + if(proxy_user_len > 255) { failf(data, "Excessive user name length for proxy auth"); return CURLPX_LONG_USER; } - memcpy(socksreq + len, proxy_user, proxy_user_len); + memcpy(socksreq + len, sx->proxy_user, proxy_user_len); } len += proxy_user_len; socksreq[len++] = (unsigned char) proxy_password_len; - if(proxy_password && 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, proxy_password, proxy_password_len); + memcpy(socksreq + len, sx->proxy_password, proxy_password_len); } len += proxy_password_len; - sxstate(data, CONNECT_AUTH_SEND); + sxstate(sx, data, CONNECT_AUTH_SEND); sx->outstanding = len; sx->outp = socksreq; } /* FALLTHROUGH */ case CONNECT_AUTH_SEND: - result = Curl_write_plain(data, sockfd, (char *)sx->outp, + result = Curl_write_plain(data, sockfd, sx->outp, sx->outstanding, &written); if(result && (CURLE_AGAIN != result)) { failf(data, "Failed to send SOCKS5 sub-negotiation request."); @@ -726,10 +727,10 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, } sx->outp = socksreq; sx->outstanding = 2; - sxstate(data, CONNECT_AUTH_READ); + sxstate(sx, data, CONNECT_AUTH_READ); /* FALLTHROUGH */ case CONNECT_AUTH_READ: - result = Curl_read_plain(sockfd, (char *)sx->outp, + result = Curl_read_plain(data, sockfd, (char *)sx->outp, sx->outstanding, &actualread); if(result && (CURLE_AGAIN != result)) { failf(data, "Unable to receive SOCKS5 sub-negotiation response."); @@ -754,36 +755,36 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, } /* Everything is good so far, user was authenticated! */ - sxstate(data, CONNECT_REQ_INIT); + 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, hostname, remote_port, + enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port, FALSE, &dns); if(rc == CURLRESOLV_ERROR) return CURLPX_RESOLVE_HOST; if(rc == CURLRESOLV_PENDING) { - sxstate(data, CONNECT_RESOLVING); + sxstate(sx, data, CONNECT_RESOLVING); return CURLPX_OK; } - sxstate(data, CONNECT_RESOLVED); + 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, hostname, remote_port); + 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", hostname); + infof(data, "SOCKS5: hostname '%s' found", sx->hostname); } if(!dns) { @@ -803,13 +804,13 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, hp = dns->addr; if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", - hostname); + sx->hostname); return CURLPX_RESOLVE_HOST; } Curl_printable_address(hp, dest, sizeof(dest)); destlen = strlen(dest); - msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port); + msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", sx->remote_port); len = 0; socksreq[len++] = 5; /* version (SOCKS5) */ @@ -866,7 +867,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip) { char ip6[16]; - if(1 != Curl_inet_pton(AF_INET6, hostname, ip6)) + if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6)) return CURLPX_BAD_ADDRESS_TYPE; socksreq[len++] = 4; memcpy(&socksreq[len], ip6, sizeof(ip6)); @@ -874,7 +875,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, } else #endif - if(1 == Curl_inet_pton(AF_INET, hostname, ip4)) { + if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) { socksreq[len++] = 1; memcpy(&socksreq[len], ip4, sizeof(ip4)); len += sizeof(ip4); @@ -882,20 +883,20 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, else { socksreq[len++] = 3; socksreq[len++] = (char) hostname_len; /* one byte address length */ - memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */ + memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */ len += hostname_len; } infof(data, "SOCKS5 connect to %s:%d (remotely resolved)", - hostname, remote_port); + sx->hostname, sx->remote_port); } /* FALLTHROUGH */ CONNECT_REQ_SEND: case CONNECT_REQ_SEND: /* PORT MSB */ - socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); + socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff); /* PORT LSB */ - socksreq[len++] = (unsigned char)(remote_port & 0xff); + socksreq[len++] = (unsigned char)(sx->remote_port & 0xff); #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) if(conn->socks5_gssapi_enctype) { @@ -905,7 +906,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, #endif sx->outp = socksreq; sx->outstanding = len; - sxstate(data, CONNECT_REQ_SENDING); + sxstate(sx, data, CONNECT_REQ_SENDING); /* FALLTHROUGH */ case CONNECT_REQ_SENDING: result = Curl_write_plain(data, sockfd, (char *)sx->outp, @@ -928,10 +929,10 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, #endif sx->outstanding = 10; /* minimum packet size is 10 */ sx->outp = socksreq; - sxstate(data, CONNECT_REQ_READ); + sxstate(sx, data, CONNECT_REQ_READ); /* FALLTHROUGH */ case CONNECT_REQ_READ: - result = Curl_read_plain(sockfd, (char *)sx->outp, + result = Curl_read_plain(data, sockfd, (char *)sx->outp, sx->outstanding, &actualread); if(result && (CURLE_AGAIN != result)) { failf(data, "Failed to receive SOCKS5 connect request ack."); @@ -958,7 +959,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; int code = socksreq[1]; failf(data, "Can't complete SOCKS5 connection to %s. (%d)", - hostname, (unsigned char)socksreq[1]); + sx->hostname, (unsigned char)socksreq[1]); if(code < 9) { /* RFC 1928 section 6 lists: */ static const CURLproxycode lookup[] = { @@ -1019,10 +1020,10 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, if(len > 10) { sx->outstanding = len - 10; /* get the rest */ sx->outp = &socksreq[10]; - sxstate(data, CONNECT_REQ_READ_MORE); + sxstate(sx, data, CONNECT_REQ_READ_MORE); } else { - sxstate(data, CONNECT_DONE); + sxstate(sx, data, CONNECT_DONE); break; } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -1030,7 +1031,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, #endif /* FALLTHROUGH */ case CONNECT_REQ_READ_MORE: - result = Curl_read_plain(sockfd, (char *)sx->outp, + result = Curl_read_plain(data, sockfd, (char *)sx->outp, sx->outstanding, &actualread); if(result && (CURLE_AGAIN != result)) { failf(data, "Failed to receive SOCKS5 connect request ack."); @@ -1047,12 +1048,214 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, sx->outp += actualread; return CURLPX_OK; } - sxstate(data, CONNECT_DONE); + sxstate(sx, data, CONNECT_DONE); } infof(data, "SOCKS5 request granted."); - *done = TRUE; return CURLPX_OK; /* Proxy was successful! */ } +static CURLcode connect_SOCKS(struct Curl_cfilter *cf, + struct socks_state *sxstate, + struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + CURLproxycode pxresult = CURLPX_OK; + struct connectdata *conn = cf->conn; + + switch(conn->socks_proxy.proxytype) { + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + pxresult = do_SOCKS5(cf, sxstate, data); + break; + + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + pxresult = do_SOCKS4(cf, sxstate, data); + break; + + default: + failf(data, "unknown proxytype option given"); + result = CURLE_COULDNT_CONNECT; + } /* switch proxytype */ + if(pxresult) { + result = CURLE_PROXY; + data->info.pxcode = pxresult; + } + + return result; +} + +static void socks_proxy_cf_free(struct Curl_cfilter *cf) +{ + struct socks_state *sxstate = cf->ctx; + if(sxstate) { + free(sxstate); + cf->ctx = NULL; + } +} + +/* After a TCP connection to the proxy has been verified, this function does + the next magic steps. If 'done' isn't set TRUE, it is not done yet and + must be called again. + + Note: this function's sub-functions call failf() + +*/ +static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + CURLcode result; + struct connectdata *conn = cf->conn; + int sockindex = cf->sockindex; + struct socks_state *sx = cf->ctx; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + result = cf->next->cft->connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + + if(!sx) { + sx = calloc(sizeof(*sx), 1); + if(!sx) + return CURLE_OUT_OF_MEMORY; + cf->ctx = sx; + } + + if(sx->state == CONNECT_INIT) { + /* for the secondary socket (FTP), use the "connect to host" + * but ignore the "connect to port" (use the secondary port) + */ + sxstate(sx, data, CONNECT_SOCKS_INIT); + sx->hostname = + conn->bits.httpproxy ? + conn->http_proxy.host.name : + conn->bits.conn_to_host ? + conn->conn_to_host.name : + sockindex == SECONDARYSOCKET ? + conn->secondaryhostname : conn->host.name; + sx->remote_port = + conn->bits.httpproxy ? (int)conn->http_proxy.port : + sockindex == SECONDARYSOCKET ? conn->secondary_port : + conn->bits.conn_to_port ? conn->conn_to_port : + conn->remote_port; + sx->proxy_user = conn->socks_proxy.user; + sx->proxy_password = conn->socks_proxy.passwd; + } + + result = connect_SOCKS(cf, sx, data); + if(!result && sx->state == CONNECT_DONE) { + cf->connected = TRUE; + Curl_updateconninfo(data, conn, conn->sock[cf->sockindex]); + Curl_verboseconnect(data, conn); + socks_proxy_cf_free(cf); + } + + *done = cf->connected; + return result; +} + +static int socks_cf_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) +{ + struct socks_state *sx = cf->ctx; + int fds; + + fds = cf->next->cft->get_select_socks(cf->next, data, socks); + if(!fds && cf->next->connected && !cf->connected && sx) { + /* If we are not connected, the filter below is and has nothing + * to wait on, we determine what to wait for. */ + socks[0] = cf->conn->sock[cf->sockindex]; + switch(sx->state) { + case CONNECT_RESOLVING: + case CONNECT_SOCKS_READ: + case CONNECT_AUTH_READ: + case CONNECT_REQ_READ: + case CONNECT_REQ_READ_MORE: + fds = GETSOCK_READSOCK(0); + break; + default: + fds = GETSOCK_WRITESOCK(0); + break; + } + } + return fds; +} + +static void socks_proxy_cf_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + + DEBUGASSERT(cf->next); + cf->connected = FALSE; + socks_proxy_cf_free(cf); + cf->next->cft->close(cf->next, data); +} + +static void socks_proxy_cf_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + (void)data; + socks_proxy_cf_free(cf); +} + +static void socks_proxy_cf_detach_data(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + (void)data; + socks_proxy_cf_free(cf); +} + +static void socks_cf_get_host(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char **phost, + const char **pdisplay_host, + int *pport) +{ + (void)data; + if(!cf->connected) { + *phost = cf->conn->socks_proxy.host.name; + *pdisplay_host = cf->conn->http_proxy.host.dispname; + *pport = (int)cf->conn->socks_proxy.port; + } + else { + cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); + } +} + +static const struct Curl_cftype cft_socks_proxy = { + "SOCKS-PROXYY", + CF_TYPE_IP_CONNECT, + socks_proxy_cf_destroy, + Curl_cf_def_setup, + socks_proxy_cf_connect, + socks_proxy_cf_close, + socks_cf_get_host, + socks_cf_get_select_socks, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_attach_data, + socks_proxy_cf_detach_data, +}; + +CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = Curl_cf_create(&cf, &cft_socks_proxy, NULL); + if(!result) + Curl_conn_cf_add(data, conn, sockindex, cf); + return result; +} + #endif /* CURL_DISABLE_PROXY */ |