From bcd94e275076680bcad673478950ab19a7b2c9ee Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 13 Apr 2026 12:57:11 +0200 Subject: [PATCH] socks: use dns filter for resolving Use a dns connection filter to resolve hostnames where their addresses are locally resolved and forwarded to the SOCKS proxy. This makes all improvements, like in #21295 for example, also apply to socks connections. Curl_resolv() is now only called from a DNS filter. (ftp still calls Curl_resolv_blocking() in two places, one of which may be replaceable with a DNS filter as well to remove the block, tbd) Closes #21297 --- lib/cf-dns.c | 41 ++++++++++------- lib/cf-dns.h | 3 +- lib/socks.c | 122 +++++++++++++++++++-------------------------------- 3 files changed, 74 insertions(+), 92 deletions(-) diff --git a/lib/cf-dns.c b/lib/cf-dns.c index bfa7b30780..21a52a030b 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -44,6 +44,7 @@ struct cf_dns_ctx { BIT(started); BIT(announced); BIT(abstract_unix_socket); + BIT(complete_resolve); char hostname[1]; }; @@ -52,6 +53,7 @@ static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, const char *hostname, uint16_t port, uint8_t transport, bool abstract_unix_socket, + bool complete_resolve, struct Curl_dns_entry *dns) { struct cf_dns_ctx *ctx; @@ -65,11 +67,14 @@ static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, ctx->dns_queries = dns_queries; ctx->transport = transport; ctx->abstract_unix_socket = abstract_unix_socket; + ctx->complete_resolve = complete_resolve; ctx->dns = Curl_dns_entry_link(data, dns); ctx->started = !!ctx->dns; if(hlen) memcpy(ctx->hostname, hostname, hlen); + CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x", + ctx->hostname, ctx->port, ctx->transport, ctx->dns_queries); return ctx; } @@ -240,20 +245,23 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, } if(cf->next && !cf->next->connected) { - CURLcode result = Curl_conn_cf_connect(cf->next, data, done); - CURL_TRC_CF(data, cf, "connect subfilters -> %d, done=%d", result, *done); - if(result || !*done) + bool sub_done; + CURLcode result = Curl_conn_cf_connect(cf->next, data, &sub_done); + CURL_TRC_CF(data, cf, "connect subfilters -> %d, done=%d", + result, sub_done); + if(result || !sub_done) return result; + DEBUGASSERT(sub_done); } - /* sub filter chain is connected, so are we now. - * Unlink the DNS entry, it is no longer needed and if it - * came from a SHARE in `data`, we need to release it under - * that one's lock. */ - DEBUGASSERT(*done); + /* sub filter chain is connected */ + if(ctx->complete_resolve && !ctx->dns && !ctx->resolv_result) { + /* This filter only connects when it has resolved everything. */ + return CURLE_OK; + } + *done = TRUE; cf->connected = TRUE; Curl_resolv_destroy(data, ctx->resolv_id); - Curl_dns_entry_unlink(data, &ctx->dns); return CURLE_OK; } @@ -336,6 +344,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, uint16_t port, uint8_t transport, bool abstract_unix_socket, + bool complete_resolve, struct Curl_dns_entry *dns) { struct Curl_cfilter *cf = NULL; @@ -344,7 +353,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, (void)data; ctx = cf_dns_ctx_create(data, dns_queries, hostname, port, transport, - abstract_unix_socket, dns); + abstract_unix_socket, complete_resolve, dns); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -365,6 +374,7 @@ static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, struct Curl_easy *data, uint8_t dns_queries, uint8_t transport, + bool complete_resolve, struct Curl_dns_entry *dns) { struct connectdata *conn = data->conn; @@ -409,7 +419,7 @@ static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, } return cf_dns_create(pcf, data, dns_queries, hostname, port, transport, - abstract_unix_socket, dns); + abstract_unix_socket, complete_resolve, dns); } /* Adds a "resolv" filter at the top of the connection's filter chain. @@ -430,11 +440,11 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, DEBUGASSERT(data); if(sockindex == FIRSTSOCKET) - result = cf_dns_conn_create(&cf, data, dns_queries, transport, dns); + result = cf_dns_conn_create(&cf, data, dns_queries, transport, FALSE, dns); else if(dns) { result = cf_dns_create(&cf, data, dns_queries, dns->hostname, dns->port, transport, - FALSE, dns); + FALSE, FALSE, dns); } else { DEBUGASSERT(0); @@ -458,14 +468,15 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, uint8_t dns_queries, const char *hostname, uint16_t port, - uint8_t transport) + uint8_t transport, + bool complete_resolve) { struct Curl_cfilter *cf; CURLcode result; result = cf_dns_create(&cf, data, dns_queries, hostname, port, transport, - FALSE, NULL); + FALSE, complete_resolve, NULL); if(result) return result; diff --git a/lib/cf-dns.h b/lib/cf-dns.h index d9966ae295..fc97601dc3 100644 --- a/lib/cf-dns.h +++ b/lib/cf-dns.h @@ -42,7 +42,8 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, uint8_t dns_queries, const char *hostname, uint16_t port, - uint8_t transport); + uint8_t transport, + bool complete_resolve); CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex); CURLcode Curl_cf_dns_result(struct Curl_cfilter *cf); diff --git a/lib/socks.c b/lib/socks.c index 2d43b12220..8d1a4e9540 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -38,6 +38,7 @@ #include "curl_trc.h" #include "select.h" #include "cfilters.h" +#include "cf-dns.h" #include "connect.h" #include "socks.h" #include "curlx/inet_pton.h" @@ -313,71 +314,54 @@ static CURLproxycode socks4_resolving(struct socks_state *sx, struct Curl_easy *data, bool *done) { - struct Curl_dns_entry *dns = NULL; - struct Curl_addrinfo *hp = NULL; + const struct Curl_addrinfo *ai = NULL; CURLcode result; size_t nwritten; + bool dns_done; *done = FALSE; if(sx->start_resolving) { /* need to resolve hostname to add destination address */ sx->start_resolving = FALSE; DEBUGASSERT(sx->hostname && *sx->hostname); - - result = Curl_resolv(data, - Curl_resolv_dns_queries(data, cf->conn->ip_version), - sx->hostname, sx->remote_port, - Curl_conn_cf_get_transport(cf, data), - 0, &sx->resolv_id, &dns); - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "SOCKS4 non-blocking resolve of %s", sx->hostname); - return CURLPX_OK; + result = Curl_cf_dns_insert_after( + cf, data, Curl_resolv_dns_queries(data, cf->conn->ip_version), + sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + if(result) { + failf(data, "unable to create DNS filter for socks"); + return CURLPX_UNKNOWN_FAIL; } - else if(result) - return CURLPX_RESOLVE_HOST; - } - else { - /* check if we have the name resolved by now */ - result = Curl_resolv_take_result(data, sx->resolv_id, &dns); - if(!result && !dns) - return CURLPX_OK; } - if(result || !dns) { - failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", sx->hostname); - if(dns) - Curl_dns_entry_unlink(data, &dns); + /* resolve the hostname by connecting the DNS filter */ + result = Curl_conn_cf_connect(cf->next, data, &dns_done); + if(result) { + failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", + sx->hostname); return CURLPX_RESOLVE_HOST; } + else if(!dns_done) + return CURLPX_OK; - /* - * We cannot use 'hostent' as a struct that Curl_resolv() returns. It - * returns a Curl_addrinfo pointer that may not always look the same. - */ - /* scan for the first IPv4 address */ - hp = dns->addr; - while(hp && (hp->ai_family != AF_INET)) - hp = hp->ai_next; - - if(hp) { + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + if(ai) { struct sockaddr_in *saddr_in; char ipbuf[64]; - Curl_printable_address(hp, ipbuf, sizeof(ipbuf)); + Curl_printable_address(ai, ipbuf, sizeof(ipbuf)); CURL_TRC_CF(data, cf, "SOCKS4 connect to IPv4 %s (locally resolved)", ipbuf); - saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; + saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr; result = Curl_bufq_write(&sx->iobuf, (unsigned char *)&saddr_in->sin_addr.s_addr, 4, &nwritten); - Curl_dns_entry_unlink(data, &dns); /* not used anymore from now on */ if(result || (nwritten != 4)) return CURLPX_SEND_REQUEST; } else { - Curl_dns_entry_unlink(data, &dns); + /* No ipv4 address resolved */ failf(data, "SOCKS4 connection to %s not supported", sx->hostname); return CURLPX_RESOLVE_HOST; } @@ -844,8 +828,7 @@ static CURLproxycode socks5_resolving(struct socks_state *sx, struct Curl_easy *data, bool *done) { - struct Curl_dns_entry *dns = NULL; - struct Curl_addrinfo *hp = NULL; + const struct Curl_addrinfo *ai = NULL; char dest[MAX_IPADR_LEN]; /* printable address */ const unsigned char *destination = NULL; unsigned char desttype = 1, destlen = 4; @@ -853,72 +836,61 @@ static CURLproxycode socks5_resolving(struct socks_state *sx, CURLcode result; CURLproxycode presult = CURLPX_OK; size_t nwritten; + bool dns_done; *done = FALSE; if(sx->start_resolving) { /* need to resolve hostname to add destination address */ sx->start_resolving = FALSE; DEBUGASSERT(sx->hostname && *sx->hostname); - - result = Curl_resolv(data, - Curl_resolv_dns_queries(data, cf->conn->ip_version), - sx->hostname, sx->remote_port, - Curl_conn_cf_get_transport(cf, data), - 0, &sx->resolv_id, &dns); - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "SOCKS5 non-blocking resolve of %s", sx->hostname); - return CURLPX_OK; + result = Curl_cf_dns_insert_after( + cf, data, Curl_resolv_dns_queries(data, cf->conn->ip_version), + sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + if(result) { + failf(data, "unable to create DNS filter for socks"); + return CURLPX_UNKNOWN_FAIL; } - else if(result) - return CURLPX_RESOLVE_HOST; - } - else { - /* check if we have the name resolved by now */ - result = Curl_resolv_take_result(data, sx->resolv_id, &dns); - if(!result && !dns) - return CURLPX_OK; } - if(result || !dns) { + /* resolve the hostname by connecting the DNS filter */ + result = Curl_conn_cf_connect(cf->next, data, &dns_done); + if(result) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); - presult = CURLPX_RESOLVE_HOST; - goto out; + return CURLPX_RESOLVE_HOST; } + else if(!dns_done) + return CURLPX_OK; - if(dns) - hp = dns->addr; #ifdef USE_IPV6 - if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) { - int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ? - AF_INET : AF_INET6; - /* scan for the first proper address */ - while(hp && (hp->ai_family != wanted_family)) - hp = hp->ai_next; - } + if(data->set.ipver != CURL_IPRESOLVE_V4) + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET6, 0); #endif - if(!hp) { + if(!ai) + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + + if(!ai) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); presult = CURLPX_RESOLVE_HOST; goto out; } - Curl_printable_address(hp, dest, sizeof(dest)); + Curl_printable_address(ai, dest, sizeof(dest)); - if(hp->ai_family == AF_INET) { + if(ai->ai_family == AF_INET) { struct sockaddr_in *saddr_in; desttype = 1; /* ATYP: IPv4 = 1 */ destlen = 4; - saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; + saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr; destination = (const unsigned char *)&saddr_in->sin_addr.s_addr; CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (locally resolved)", dest, sx->remote_port); } #ifdef USE_IPV6 - else if(hp->ai_family == AF_INET6) { + else if(ai->ai_family == AF_INET6) { struct sockaddr_in6 *saddr_in6; desttype = 4; /* ATYP: IPv6 = 4 */ destlen = 16; - saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr; + saddr_in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr; destination = (const unsigned char *)&saddr_in6->sin6_addr.s6_addr; CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%u (locally resolved)", dest, sx->remote_port); @@ -952,8 +924,6 @@ static CURLproxycode socks5_resolving(struct socks_state *sx, } out: - if(dns) - Curl_dns_entry_unlink(data, &dns); *done = (presult == CURLPX_OK); return presult; }