diff --git a/lib/connect.c b/lib/connect.c index 3d1aec1f70..b8d200b149 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -374,7 +374,27 @@ connect_sub_chain: /* sub-chain connected, do we need to add more? */ #ifndef CURL_DISABLE_PROXY if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { - result = Curl_cf_socks_proxy_insert_after(cf, data); + /* for the secondary socket (FTP), use the "connect to host" + * but ignore the "connect to port" (use the secondary port) + */ + const char *hostname = + cf->conn->bits.httpproxy ? + cf->conn->http_proxy.host.name : + cf->conn->bits.conn_to_host ? + cf->conn->conn_to_host.name : + cf->sockindex == SECONDARYSOCKET ? + cf->conn->secondaryhostname : cf->conn->host.name; + uint16_t port = + cf->conn->bits.httpproxy ? cf->conn->http_proxy.port : + cf->sockindex == SECONDARYSOCKET ? cf->conn->secondary_port : + cf->conn->bits.conn_to_port ? cf->conn->conn_to_port : + cf->conn->remote_port; + const char *user = cf->conn->socks_proxy.user; + const char *passwd = cf->conn->socks_proxy.passwd; + + result = Curl_cf_socks_proxy_insert_after( + cf, data, hostname, port, cf->conn->ip_version, + cf->conn->socks_proxy.proxytype, user, passwd); if(result) return result; ctx->state = CF_SETUP_CNNCT_SOCKS; diff --git a/lib/socks.c b/lib/socks.c index 57e5a50f7a..b3da5be0a3 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -95,19 +95,21 @@ static const char * const cf_socks_statename[] = { #define SOCKS_CHUNKS 1 -struct socks_state { +struct socks_ctx { enum socks_state_t state; struct bufq iobuf; - const char *hostname; uint16_t remote_port; - const char *proxy_user; - const char *proxy_password; + const char *user; + const char *passwd; CURLproxycode presult; uint32_t resolv_id; + uint8_t ip_version; + uint8_t proxy_type; unsigned char version; BIT(resolve_local); BIT(start_resolving); BIT(socks4a); + char hostname[1]; }; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -129,13 +131,15 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf, *pnread = 0; for(;;) { timediff_t timeout_ms = Curl_timeleft_ms(data); + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + if(timeout_ms < 0) { /* we already got the timeout */ return CURLE_OPERATION_TIMEDOUT; } if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) + if(SOCKET_READABLE(sock, timeout_ms) <= 0) return CURLE_OPERATION_TIMEDOUT; result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); if(result == CURLE_AGAIN) @@ -164,7 +168,7 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf, #endif /* always use this function to change state, to make debugging easier */ -static void socksstate(struct socks_state *sx, +static void socksstate(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, enum socks_state_t state @@ -191,7 +195,7 @@ static void socksstate(struct socks_state *sx, #endif } -static CURLproxycode socks_failed(struct socks_state *sx, +static CURLproxycode socks_failed(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, CURLproxycode presult) @@ -201,7 +205,7 @@ static CURLproxycode socks_failed(struct socks_state *sx, return presult; } -static CURLproxycode socks_flush(struct socks_state *sx, +static CURLproxycode socks_flush(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -225,7 +229,7 @@ static CURLproxycode socks_flush(struct socks_state *sx, return CURLPX_OK; } -static CURLproxycode socks_recv(struct socks_state *sx, +static CURLproxycode socks_recv(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, size_t min_bytes, @@ -259,7 +263,7 @@ static CURLproxycode socks_recv(struct socks_state *sx, return CURLPX_OK; } -static CURLproxycode socks4_req_add_hd(struct socks_state *sx, +static CURLproxycode socks4_req_add_hd(struct socks_ctx *sx, struct Curl_easy *data) { unsigned char buf[4]; @@ -278,14 +282,14 @@ static CURLproxycode socks4_req_add_hd(struct socks_state *sx, return CURLPX_OK; } -static CURLproxycode socks4_req_add_user(struct socks_state *sx, +static CURLproxycode socks4_req_add_user(struct socks_ctx *sx, struct Curl_easy *data) { CURLcode result; size_t nwritten; - if(sx->proxy_user) { - size_t plen = strlen(sx->proxy_user); + if(sx->user) { + size_t plen = strlen(sx->user); if(plen > 255) { /* there is no real size limit to this field in the protocol, but SOCKS5 limits the proxy user field to 255 bytes and it seems likely @@ -294,7 +298,7 @@ static CURLproxycode socks4_req_add_user(struct socks_state *sx, return CURLPX_LONG_USER; } /* add proxy name WITH trailing zero */ - result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, plen + 1, + result = Curl_bufq_cwrite(&sx->iobuf, sx->user, plen + 1, &nwritten); if(result || (nwritten != (plen + 1))) return CURLPX_SEND_REQUEST; @@ -309,7 +313,7 @@ static CURLproxycode socks4_req_add_user(struct socks_state *sx, return CURLPX_OK; } -static CURLproxycode socks4_resolving(struct socks_state *sx, +static CURLproxycode socks4_resolving(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -323,9 +327,8 @@ static CURLproxycode socks4_resolving(struct socks_state *sx, if(sx->start_resolving) { /* need to resolve hostname to add destination address */ sx->start_resolving = FALSE; - DEBUGASSERT(sx->hostname && *sx->hostname); result = Curl_cf_dns_insert_after( - cf, data, Curl_resolv_dns_queries(data, cf->conn->ip_version), + cf, data, Curl_resolv_dns_queries(data, sx->ip_version), sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); if(result) { failf(data, "unable to create DNS filter for socks"); @@ -370,7 +373,7 @@ static CURLproxycode socks4_resolving(struct socks_state *sx, return CURLPX_OK; } -static CURLproxycode socks4_check_resp(struct socks_state *sx, +static CURLproxycode socks4_check_resp(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -459,7 +462,7 @@ static CURLproxycode socks4_check_resp(struct socks_state *sx, * Nonsupport "Identification Protocol (RFC1413)" */ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { size_t nwritten; @@ -477,16 +480,14 @@ process_state: case SOCKS4_ST_START: Curl_bufq_reset(&sx->iobuf); sx->start_resolving = FALSE; - sx->socks4a = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A); + sx->socks4a = (sx->proxy_type == CURLPROXY_SOCKS4A); sx->resolve_local = !sx->socks4a; sx->presult = CURLPX_OK; /* SOCKS4 can only do IPv4, insist! */ - cf->conn->ip_version = CURL_IPRESOLVE_V4; - CURL_TRC_CF(data, cf, "SOCKS4%s communication to%s %s:%u", - sx->socks4a ? "a" : "", - cf->conn->bits.httpproxy ? " HTTP proxy" : "", - sx->hostname, sx->remote_port); + sx->ip_version = CURL_IPRESOLVE_V4; + CURL_TRC_CF(data, cf, "SOCKS4%s connecting to %s:%u", + sx->socks4a ? "a" : "", sx->hostname, sx->remote_port); /* * Compose socks4 request @@ -579,7 +580,7 @@ process_state: } static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { const unsigned char auth = data->set.socks5auth; @@ -601,7 +602,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, "CURLOPT_SOCKS5_AUTH: %u", auth); if(!(auth & CURLAUTH_BASIC)) /* disable username/password auth */ - sx->proxy_user = NULL; + sx->user = NULL; req[0] = 5; /* version */ nauths = 1; @@ -612,7 +613,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, req[1 + nauths] = 1; /* GSS-API */ } #endif - if(sx->proxy_user) { + if(sx->user) { ++nauths; req[1 + nauths] = 2; /* username/password */ } @@ -625,7 +626,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, return CURLPX_OK; } -static CURLproxycode socks5_check_resp0(struct socks_state *sx, +static CURLproxycode socks5_check_resp0(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -677,7 +678,7 @@ static CURLproxycode socks5_check_resp0(struct socks_state *sx, } static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { /* Needs username and password */ @@ -685,9 +686,9 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, unsigned char buf[2]; CURLcode result; - if(sx->proxy_user && sx->proxy_password) { - ulen = strlen(sx->proxy_user); - plen = strlen(sx->proxy_password); + if(sx->user && sx->passwd) { + ulen = strlen(sx->user); + plen = strlen(sx->passwd); /* the lengths must fit in a single byte */ if(ulen > 255) { failf(data, "Excessive username length for proxy auth"); @@ -712,7 +713,7 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; if(ulen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, ulen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->user, ulen, &nwritten); if(result || (nwritten != ulen)) return CURLPX_SEND_REQUEST; } @@ -721,7 +722,7 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 1)) return CURLPX_SEND_REQUEST; if(plen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_password, plen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->passwd, plen, &nwritten); if(result || (nwritten != plen)) return CURLPX_SEND_REQUEST; } @@ -729,7 +730,7 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, return CURLPX_OK; } -static CURLproxycode socks5_check_auth_resp(struct socks_state *sx, +static CURLproxycode socks5_check_auth_resp(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -754,7 +755,7 @@ static CURLproxycode socks5_check_auth_resp(struct socks_state *sx, return CURLPX_OK; } -static CURLproxycode socks5_req1_init(struct socks_state *sx, +static CURLproxycode socks5_req1_init(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -823,7 +824,7 @@ static CURLproxycode socks5_req1_init(struct socks_state *sx, return CURLPX_OK; } -static CURLproxycode socks5_resolving(struct socks_state *sx, +static CURLproxycode socks5_resolving(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -842,9 +843,8 @@ static CURLproxycode socks5_resolving(struct socks_state *sx, if(sx->start_resolving) { /* need to resolve hostname to add destination address */ sx->start_resolving = FALSE; - DEBUGASSERT(sx->hostname && *sx->hostname); result = Curl_cf_dns_insert_after( - cf, data, Curl_resolv_dns_queries(data, cf->conn->ip_version), + cf, data, Curl_resolv_dns_queries(data, sx->ip_version), sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); if(result) { failf(data, "unable to create DNS filter for socks"); @@ -928,7 +928,7 @@ out: return presult; } -static CURLproxycode socks5_recv_resp1(struct socks_state *sx, +static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -1027,7 +1027,7 @@ static CURLproxycode socks5_recv_resp1(struct socks_state *sx, * destination server. */ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { CURLproxycode presult; @@ -1037,14 +1037,13 @@ process_state: switch(sx->state) { case SOCKS_ST_INIT: sx->version = 5; - sx->resolve_local = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS5); + sx->resolve_local = (sx->proxy_type == CURLPROXY_SOCKS5); sxstate(sx, cf, data, SOCKS5_ST_START); FALLTHROUGH(); case SOCKS5_ST_START: - if(cf->conn->bits.httpproxy) - CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %u", - sx->hostname, sx->remote_port); + CURL_TRC_CF(data, cf, "SOCKS5: connecting to %s:%u", + sx->hostname, sx->remote_port); presult = socks5_req0_init(cf, sx, data); if(presult) return socks_failed(sx, cf, data, presult); @@ -1179,13 +1178,11 @@ process_state: } } -static void socks_proxy_cf_free(struct Curl_cfilter *cf) +static void socks_proxy_ctx_free(struct socks_ctx *ctx) { - struct socks_state *sxstate = cf->ctx; - if(sxstate) { - Curl_bufq_free(&sxstate->iobuf); - curlx_free(sxstate); - cf->ctx = NULL; + if(ctx) { + Curl_bufq_free(&ctx->iobuf); + curlx_free(ctx); } } @@ -1200,11 +1197,9 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { - CURLcode result; - struct connectdata *conn = cf->conn; - int sockindex = cf->sockindex; - struct socks_state *sx = cf->ctx; + struct socks_ctx *ctx = cf->ctx; CURLproxycode pxresult = CURLPX_OK; + CURLcode result; if(cf->connected) { *done = TRUE; @@ -1215,47 +1210,19 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, if(result || !*done) return result; - if(!sx) { - cf->ctx = sx = curlx_calloc(1, sizeof(*sx)); - if(!sx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - /* for the secondary socket (FTP), use the "connect to host" - * but ignore the "connect to port" (use the secondary port) - */ - 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 ? conn->http_proxy.port : - sockindex == SECONDARYSOCKET ? conn->secondary_port : - conn->bits.conn_to_port ? conn->conn_to_port : - (uint16_t)conn->remote_port; - sx->proxy_user = conn->socks_proxy.user; - sx->proxy_password = conn->socks_proxy.passwd; - Curl_bufq_init2(&sx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, - BUFQ_OPT_SOFT_LIMIT); - } - - switch(conn->socks_proxy.proxytype) { + switch(ctx->proxy_type) { case CURLPROXY_SOCKS5: case CURLPROXY_SOCKS5_HOSTNAME: - pxresult = socks5_connect(cf, sx, data); + pxresult = socks5_connect(cf, ctx, data); break; case CURLPROXY_SOCKS4: case CURLPROXY_SOCKS4A: - pxresult = socks4_connect(cf, sx, data); + pxresult = socks4_connect(cf, ctx, data); break; default: - failf(data, "unknown proxytype option given"); + DEBUGASSERT(0); /* should not come here, checked it at creation time */ result = CURLE_COULDNT_CONNECT; goto out; } @@ -1265,7 +1232,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, data->info.pxcode = pxresult; goto out; } - else if(sx->state != SOCKS_ST_SUCCESS) + else if(ctx->state != SOCKS_ST_SUCCESS) goto out; #ifdef CURLVERBOSE @@ -1275,20 +1242,23 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) infof(data, "Opened %sSOCKS connection from %s port %d to %s port %d " "(via %s port %u)", - (sockindex == SECONDARYSOCKET) ? "2nd " : "", + (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", ipquad.local_ip, ipquad.local_port, - sx->hostname, sx->remote_port, + ctx->hostname, ctx->remote_port, ipquad.remote_ip, ipquad.remote_port); else infof(data, "Opened %sSOCKS connection", - (sockindex == SECONDARYSOCKET) ? "2nd " : ""); + (cf->sockindex == SECONDARYSOCKET) ? "2nd " : ""); } #endif - socks_proxy_cf_free(cf); cf->connected = TRUE; out: *done = (bool)cf->connected; + if(*done || result) { + ctx->user = NULL; + ctx->passwd = NULL; + } return result; } @@ -1296,7 +1266,7 @@ static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) { - struct socks_state *sx = cf->ctx; + struct socks_ctx *sx = cf->ctx; CURLcode result = CURLE_OK; if(!cf->connected && sx) { @@ -1323,24 +1293,24 @@ static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf, 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->do_close(cf->next, data); + if(cf->next) + cf->next->cft->do_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); + socks_proxy_ctx_free(cf->ctx); + cf->ctx = NULL; } static CURLcode socks_cf_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2) { - struct socks_state *sx = cf->ctx; + struct socks_ctx *sx = cf->ctx; switch(query) { case CF_QUERY_HOST_PORT: @@ -1383,15 +1353,53 @@ struct Curl_cftype Curl_cft_socks_proxy = { }; CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + const char *hostname, + uint16_t port, + uint8_t ip_version, + uint8_t proxy_type, + const char *user, + const char *passwd) { struct Curl_cfilter *cf; + struct socks_ctx *ctx; + size_t hostlen = hostname ? strlen(hostname) : 0; CURLcode result; - (void)data; - result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL); + if(!hostlen) + return CURLE_FAILED_INIT; + + switch(proxy_type) { + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + break; /* all supported */ + default: + failf(data, "unknown proxytype %d option given", proxy_type); + return CURLE_COULDNT_CONNECT; + } + + /* NUL byte already part of struct size */ + ctx = curlx_calloc(1, sizeof(*ctx) + hostlen); + if(!ctx) { + return CURLE_OUT_OF_MEMORY; + } + + memcpy(ctx->hostname, hostname, hostlen); + ctx->remote_port = port; + ctx->ip_version = ip_version; + ctx->proxy_type = proxy_type; + ctx->user = user; + ctx->passwd = passwd; + Curl_bufq_init2(&ctx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); + + result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, ctx); if(!result) Curl_conn_cf_insert_after(cf_at, cf); + else + socks_proxy_ctx_free(ctx); return result; } diff --git a/lib/socks.h b/lib/socks.h index 520fb75ddb..ea368326d2 100644 --- a/lib/socks.h +++ b/lib/socks.h @@ -46,8 +46,19 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, struct Curl_easy *data); #endif +/* Insert a SOCKS filter after `cf_at` for connecting to `hostname` + * and `port` with optional credentials. + * Credentials are NOT duplicated and are + * expected to exist during connect phase. + */ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + const char *hostname, + uint16_t port, + uint8_t ip_version, + uint8_t proxy_type, + const char *user, + const char *passwd); extern struct Curl_cftype Curl_cft_socks_proxy;