diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index ef4bbcbcc6..106c7333f5 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -188,25 +188,6 @@ out: return result; } -/* - * async_ares_cleanup() cleans up async resolver data. - */ -static void async_ares_cleanup(struct Curl_resolv_async *async) -{ - struct async_ares_ctx *ares = &async->ares; - if(ares->res_A) { - Curl_freeaddrinfo(ares->res_A); - ares->res_A = NULL; - } - if(ares->res_AAAA) { - Curl_freeaddrinfo(ares->res_AAAA); - ares->res_AAAA = NULL; - } -#ifdef USE_HTTPSRR - Curl_httpsrr_cleanup(&ares->hinfo); -#endif -} - void Curl_async_ares_shutdown(struct Curl_easy *data, struct Curl_resolv_async *async) { @@ -227,7 +208,19 @@ void Curl_async_ares_destroy(struct Curl_easy *data, ares_destroy(ares->channel); ares->channel = NULL; } - async_ares_cleanup(async); + if(ares->res_A) { + Curl_freeaddrinfo(ares->res_A); + ares->res_A = NULL; + } + if(ares->res_AAAA) { + Curl_freeaddrinfo(ares->res_AAAA); + ares->res_AAAA = NULL; + } +#ifdef USE_HTTPSRR + curlx_safefree(ares->https_name); + Curl_httpsrr_destroy(ares->hinfo); + ares->hinfo = NULL; +#endif } CURLcode Curl_async_pollset(struct Curl_easy *data, @@ -259,6 +252,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, struct Curl_dns_entry **pdns) { struct async_ares_ctx *ares = &async->ares; + struct Curl_dns_entry *dns = NULL; CURLcode result = CURLE_OK; DEBUGASSERT(pdns); @@ -279,34 +273,31 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, /* all c-ares operations done, what is the result to report? */ result = ares->result; if(ares->ares_status == ARES_SUCCESS && !result) { - struct Curl_dns_entry *dns = - Curl_dnscache_mk_entry2(data, async->dns_queries, - &ares->res_AAAA, &ares->res_A, - async->hostname, async->port); - if(!dns) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } -#ifdef HTTPSRR_WORKS - if(async->dns_queries & CURL_DNSQ_HTTPS) { - if(ares->hinfo.complete) { - struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo); - if(!lhrr) - result = CURLE_OUT_OF_MEMORY; - else - Curl_dns_entry_set_https_rr(dns, lhrr); + + if(CURL_DNSQ_IS_ADDR(async->dns_queries)) { + dns = Curl_dnsc_mk_addr2(data, async->dns_queries, + &ares->res_AAAA, &ares->res_A, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + +#ifdef HTTPSRR_WORKS + if(!dns && (async->dns_queries & CURL_DNSQ_HTTPS)) { + dns = Curl_dnsc_mk_https(data, &ares->hinfo, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - else - Curl_dns_entry_set_https_rr(dns, NULL); } #endif - if(!result) { - *pdns = dns; - } } /* if we have not found anything, report the proper * CURLE_COULDNT_RESOLVE_* code */ - if(!result && !*pdns) { + if(!result && !dns) { const char *msg = NULL; /* only an authoritative "does not exist" answer from every query may be cached as a negative entry, not transient failures like @@ -319,13 +310,16 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, result = Curl_async_failed(data, async, msg); } - CURL_TRC_DNS(data, "ares: is_resolved() result=%d, dns=%sfound", - (int)result, *pdns ? "" : "not "); - async_ares_cleanup(async); + CURL_TRC_DNS(data, "[%s] ares_take_result, result=%d, ares_result=%d, " + "ares_status=%d, dns=%sfound", + Curl_resolv_query_str(async->dns_queries), + (int)result, (int)ares->result, ares->ares_status, + dns ? "" : "not "); out: if(result != CURLE_AGAIN) ares->result = result; + *pdns = result ? NULL : dns; return result; } @@ -400,7 +394,7 @@ const struct Curl_https_rrinfo *Curl_async_get_https( struct Curl_resolv_async *async) { if(Curl_async_knows_https(data, async)) - return &async->ares.hinfo; + return async->ares.hinfo; return NULL; } @@ -626,6 +620,9 @@ static void async_ares_rr_done(void *user_data, ares_status_t status, async->dns_responses |= CURL_DNSQ_HTTPS; async->queries_ongoing--; async->done = !async->queries_ongoing; + ares->ares_status = status; + if((status != ARES_ENOTFOUND) && (status != ARES_ENODATA)) + ares->transient_err = TRUE; if((ARES_SUCCESS != status) || !dnsrec) return; ares->result = Curl_httpsrr_from_ares(dnsrec, &ares->hinfo); @@ -677,7 +674,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, struct ares_addrinfo_hints hints; memset(&hints, 0, sizeof(hints)); - CURL_TRC_DNS(data, "ares: query AAAA records for %s", async->hostname); + CURL_TRC_DNS(data, "[AAAA] ares: query records for %s", async->hostname); hints.ai_family = PF_INET6; hints.ai_socktype = socktype; hints.ai_flags = ARES_AI_NUMERICSERV; @@ -691,7 +688,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, struct ares_addrinfo_hints hints; memset(&hints, 0, sizeof(hints)); - CURL_TRC_DNS(data, "ares: query A records for %s", async->hostname); + CURL_TRC_DNS(data, "[A] ares: query records for %s", async->hostname); hints.ai_family = PF_INET; hints.ai_socktype = socktype; hints.ai_flags = ARES_AI_NUMERICSERV; @@ -701,20 +698,19 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, } #ifdef USE_HTTPSRR - memset(&ares->hinfo, 0, sizeof(ares->hinfo)); if(async->dns_queries & CURL_DNSQ_HTTPS) { - char *rrname = NULL; + ares->https_name = NULL; if(async->port != 443) { - rrname = curl_maprintf("_%d._https.%s", async->port, async->hostname); - if(!rrname) + ares->https_name = curl_maprintf("_%d._https.%s", + async->port, async->hostname); + if(!ares->https_name) return CURLE_OUT_OF_MEMORY; } - CURL_TRC_DNS(data, "ares: query HTTPS records for %s", - rrname ? rrname : async->hostname); - ares->hinfo.rrname = rrname; + CURL_TRC_DNS(data, "[HTTPS] ares: query records for %s", + ares->https_name ? ares->https_name : async->hostname); async->queries_ongoing++; ares_query_dnsrec(ares->channel, - rrname ? rrname : async->hostname, + ares->https_name ? ares->https_name : async->hostname, ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, async_ares_rr_done, async, NULL); } diff --git a/lib/asyn-base.c b/lib/asyn-base.c index 62cb0effe4..b140b0f5b9 100644 --- a/lib/asyn-base.c +++ b/lib/asyn-base.c @@ -256,9 +256,10 @@ CURLcode Curl_async_failed(struct Curl_easy *data, } #endif - failf(data, "Could not resolve %s: %s%s%s%s", - host_or_proxy, async->hostname, - detail ? " (" : "", detail ? detail : "", detail ? ")" : ""); + if(async->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + failf(data, "Could not resolve %s: %s%s%s%s", + host_or_proxy, async->hostname, + detail ? " (" : "", detail ? detail : "", detail ? ")" : ""); return result; } diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 29e9b138d3..a3166f52f8 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -242,8 +242,7 @@ static CURLcode async_rr_start(struct Curl_easy *data, } #endif - memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo)); - thrdd->rr.hinfo.rrname = rrname; + thrdd->rr.https_name = rrname; async->queries_ongoing++; ares_query_dnsrec(thrdd->rr.channel, rrname ? rrname : async->hostname, ARES_CLASS_IN, @@ -291,7 +290,8 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data, ares_destroy(async->thrdd.rr.channel); async->thrdd.rr.channel = NULL; } - Curl_httpsrr_cleanup(&async->thrdd.rr.hinfo); + curlx_safefree(async->thrdd.rr.https_name); + Curl_httpsrr_destroy(async->thrdd.rr.hinfo); #endif async_thrdd_item_destroy(async->thrdd.res_A); async->thrdd.res_A = NULL; @@ -668,6 +668,9 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, #endif out: + if(!async->queries_ongoing) + async->done = TRUE; + if(result) CURL_TRC_DNS(data, "error queueing query %s:%d -> %d", async->hostname, async->port, (int)result); @@ -769,7 +772,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, if((thrdd->res_A && thrdd->res_A->res) || (thrdd->res_AAAA && thrdd->res_AAAA->res)) { - dns = Curl_dnscache_mk_entry2( + dns = Curl_dnsc_mk_addr2( data, async->dns_queries, thrdd->res_A ? &thrdd->res_A->res : NULL, thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL, @@ -778,22 +781,19 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; goto out; } + } #ifdef USE_HTTPSRR_ARES - if(thrdd->rr.channel) { - struct Curl_https_rrinfo *lhrr = NULL; - if(thrdd->rr.hinfo.complete) { - lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo); - if(!lhrr) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - Curl_httpsrr_trace(data, lhrr); - Curl_dns_entry_set_https_rr(dns, lhrr); + if(!dns && thrdd->rr.channel) { + Curl_httpsrr_trace(data, thrdd->rr.hinfo); + dns = Curl_dnsc_mk_https(data, &thrdd->rr.hinfo, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; } -#endif } +#endif if(dns) { *pdns = dns; @@ -864,7 +864,7 @@ const struct Curl_https_rrinfo *Curl_async_get_https( { #ifdef USE_HTTPSRR_ARES if(Curl_async_knows_https(data, async)) - return &async->thrdd.rr.hinfo; + return async->thrdd.rr.hinfo; #else (void)data; (void)async; diff --git a/lib/asyn.h b/lib/asyn.h index e4bc05e0dd..76996f888a 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -120,7 +120,8 @@ struct async_ares_ctx { CURLcode result; /* CURLE_OK or error handling response */ struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ #ifdef USE_HTTPSRR - struct Curl_https_rrinfo hinfo; + char *https_name; + struct Curl_https_rrinfo *hinfo; #endif BIT(transient_err); /* an A/AAAA query failed without the resolver answering that the name does not exist */ @@ -144,7 +145,8 @@ struct async_thrdd_ctx { #if defined(USE_HTTPSRR) && defined(USE_ARES) struct { ares_channel channel; - struct Curl_https_rrinfo hinfo; + char *https_name; + struct Curl_https_rrinfo *hinfo; } rr; #endif }; diff --git a/lib/cf-dns.c b/lib/cf-dns.c index 6388d65a9e..4152a00c17 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -30,6 +30,7 @@ #include "dnscache.h" #include "httpsrr.h" #include "curl_trc.h" +#include "multiif.h" #include "progress.h" #include "url.h" #include "cf-dns.h" @@ -44,7 +45,6 @@ struct cf_dns_ctx { uint8_t transport; BIT(started); BIT(announced); - BIT(complete_resolve); BIT(for_proxy); }; @@ -52,8 +52,7 @@ static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, struct Curl_peer *peer, uint8_t dns_queries, uint8_t transport, - bool for_proxy, - bool complete_resolve) + bool for_proxy) { struct cf_dns_ctx *ctx; @@ -65,10 +64,10 @@ static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, ctx->dns_queries = dns_queries; ctx->transport = transport; ctx->for_proxy = for_proxy; - ctx->complete_resolve = complete_resolve; - CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x", - peer->hostname, peer->port, ctx->transport, ctx->dns_queries); + CURL_TRC_DNS(data, "[%s] created DNS filter for %s:%u, transport=%x", + Curl_resolv_query_str(ctx->dns_queries), + peer->hostname, peer->port, ctx->transport); return ctx; } @@ -131,22 +130,28 @@ static void cf_dns_report(struct Curl_cfilter *cf, } else { curlx_dyn_init(&tmp, 1024); - infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port); + if(CURL_DNSQ_IS_ADDR(ctx->dns_queries)) { + infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port); #ifdef CURLRES_IPV6 - cf_dns_report_addr(data, &tmp, "IPv6: ", AF_INET6, dns->addr); + cf_dns_report_addr(data, &tmp, "IPv6: ", AF_INET6, dns->addr); #endif - cf_dns_report_addr(data, &tmp, "IPv4: ", AF_INET, dns->addr); + cf_dns_report_addr(data, &tmp, "IPv4: ", AF_INET, dns->addr); + } #ifdef USE_HTTPSRR - if(!dns->hinfo) - infof(data, "HTTPS-RR: -"); - else if(!Curl_httpsrr_applicable(data, dns->hinfo)) - infof(data, "HTTPS-RR: not applicable"); - else { - CURLcode result = Curl_httpsrr_print(&tmp, dns->hinfo); - if(!result) - infof(data, "HTTPS-RR: %s", curlx_dyn_ptr(&tmp)); - else - infof(data, "Error printing HTTPS-RR information"); + else if(ctx->dns_queries & CURL_DNSQ_HTTPS) { + if(!dns->hinfo) + infof(data, "HTTPS-RR %s:%u: -", dns->hostname, dns->port); + else if(!Curl_httpsrr_applicable(data, dns->hinfo)) + infof(data, "HTTPS-RR %s:%u: not applicable", + dns->hostname, dns->port); + else { + CURLcode result = Curl_httpsrr_print(&tmp, dns->hinfo); + if(!result) + infof(data, "HTTPS-RR %s:%u: %s", + dns->hostname, dns->port, curlx_dyn_ptr(&tmp)); + else + infof(data, "Error printing HTTPS-RR information"); + } } #endif curlx_dyn_free(&tmp); @@ -165,25 +170,40 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, { struct cf_dns_ctx *ctx = cf->ctx; timediff_t timeout_ms = Curl_timeleft_ms(data); - CURLcode result; + CURLcode result = CURLE_OK; *pdns = NULL; - CURL_TRC_CF(data, cf, "cf_dns_start %s %s:%u", + CURL_TRC_CF(data, cf, "[%s] cf_dns_start %s %s:%u", + Curl_resolv_query_str(ctx->dns_queries), ctx->peer->unix_socket ? "unix-domain-socket" : "host", ctx->peer->hostname, ctx->peer->port); if(ctx->peer->unix_socket) - ctx->dns_queries = 0; - else if(Curl_is_ipv4addr(ctx->peer->hostname)) - ctx->dns_queries |= CURL_DNSQ_A; + ctx->dns_queries = CURL_DNSQ_A; /* treat it like an A resolve */ + + if(CURL_DNSQ_IS_ADDR(ctx->dns_queries)) { + if(Curl_is_ipv4addr(ctx->peer->hostname)) + ctx->dns_queries |= CURL_DNSQ_A; #ifdef USE_IPV6 - else if(ctx->peer->ipv6) - ctx->dns_queries |= CURL_DNSQ_AAAA; + else if(ctx->peer->ipv6) + ctx->dns_queries |= CURL_DNSQ_AAAA; #endif - result = Curl_resolv(data, ctx->peer, ctx->dns_queries, ctx->transport, - (bool)ctx->for_proxy, timeout_ms, - &ctx->resolv_id, pdns); + result = Curl_resolv(data, ctx->peer, ctx->dns_queries, ctx->transport, + (bool)ctx->for_proxy, timeout_ms, + &ctx->resolv_id, pdns); + } +#ifdef USE_HTTPSRR + else if(ctx->dns_queries == CURL_DNSQ_HTTPS) { + result = Curl_resolv_https(data, ctx->peer, (bool)ctx->for_proxy, + timeout_ms, &ctx->resolv_id, pdns); + } +#endif + else { + failf(data, "unsupported DNS queries %x", ctx->dns_queries); + return CURLE_FAILED_INIT; + } + DEBUGASSERT(!result || !*pdns); if(!result) { /* resolved right away, either sync or from dnscache */ DEBUGASSERT(*pdns); @@ -201,7 +221,8 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, } else { DEBUGASSERT(result); - failf(data, "Could not resolve: %s", ctx->peer->hostname); + if(ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + failf(data, "Could not resolve: %s", ctx->peer->hostname); return result; } } @@ -222,8 +243,6 @@ static bool cf_dns_ready_to_connect(struct Curl_cfilter *cf, /* We want AAAA answer as we prefer IPv6. If a sub-filter desires * HTTPS-RR, we check for that query as well. */ uint8_t wanted_answers = CURL_DNSQ_AAAA; - if(Curl_conn_cf_wants_httpsrr(cf, data)) - wanted_answers |= CURL_DNSQ_HTTPS; /* Note: if a query was never started, it is considered to have * an answer (e.g. a negative one). */ @@ -246,6 +265,7 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, bool *done) { struct cf_dns_ctx *ctx = cf->ctx; + bool ip_query = (ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)); if(cf->connected) { *done = TRUE; @@ -263,7 +283,8 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, Curl_resolv_take_result(data, ctx->resolv_id, &ctx->dns); } - if(ctx->resolv_result) { + if(ctx->resolv_result && ip_query) { + /* failing A|AAAA resolves is a hard failure. */ CURL_TRC_CF(data, cf, "error resolving: %d", (int)ctx->resolv_result); return ctx->resolv_result; } @@ -289,13 +310,9 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, DEBUGASSERT(sub_done); } - /* sub filter chain is connected */ + /* sub filter chain is connected, this means the filter has done + * its work and is connected as well, if it has results or not. */ CURL_TRC_CF(data, cf, "connected filter chain below"); - if(ctx->complete_resolve && !ctx->dns && !ctx->resolv_result) { - /* This filter only connects when it has resolved everything. */ - CURL_TRC_CF(data, cf, "delay connect until resolve complete"); - return CURLE_OK; - } *done = TRUE; cf->connected = TRUE; Curl_resolv_destroy(data, ctx->resolv_id); @@ -315,8 +332,24 @@ static CURLcode cf_dns_adjust_pollset(struct Curl_cfilter *cf, struct easy_pollset *ps) { #ifdef USE_CURL_ASYNC - if(!cf->connected) + if(!cf->connected) { + /* The pollset only works when we have started the resolving. + * Otherwise we might wait on the WAKEUP socketpair forever. + * Since DNS filters can be added in the middle of a connect + * attempt, they are not always started that way. */ + struct cf_dns_ctx *ctx = cf->ctx; + if(!ctx->started) { + CURL_TRC_CF(data, cf, "adjust_pollset, starting %s:%u queries=%s", + ctx->peer->hostname, ctx->peer->port, + Curl_resolv_query_str(ctx->dns_queries)); + ctx->started = TRUE; + ctx->resolv_result = cf_dns_start(cf, data, &ctx->dns); + if(ctx->resolv_result || ctx->dns) { + Curl_multi_mark_dirty(data); + } + } return Curl_resolv_pollset(data, ps); + } #else (void)cf; (void)data; @@ -351,7 +384,7 @@ static CURLcode cf_dns_cntrl(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_dns = { "DNS", - CF_TYPE_SETUP, + CF_TYPE_SETUP | CF_TYPE_DNS, CURL_LOG_LVL_NONE, cf_dns_destroy, cf_dns_connect, @@ -371,8 +404,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, struct Curl_peer *peer, uint8_t dns_queries, uint8_t transport, - bool for_proxy, - bool complete_resolve) + bool for_proxy) { struct Curl_cfilter *cf = NULL; struct cf_dns_ctx *ctx; @@ -380,7 +412,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, (void)data; ctx = cf_dns_ctx_create(data, peer, dns_queries, transport, - for_proxy, complete_resolve); + for_proxy); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -397,12 +429,12 @@ out: /* Adds a "resolv" filter at the top of the connection's filter chain. * The filter will resolve the peer on the first connect attempt. */ -CURLcode Curl_cf_dns_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - struct Curl_peer *peer, - uint8_t dns_queries, - uint8_t transport) +static CURLcode cf_dns_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer, + uint8_t dns_queries, + uint8_t transport) { struct Curl_cfilter *cf = NULL; bool for_proxy = FALSE; @@ -415,8 +447,7 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, (peer == conn->http_proxy.peer); #endif - result = cf_dns_create(&cf, data, peer, dns_queries, transport, - for_proxy, FALSE); + result = cf_dns_create(&cf, data, peer, dns_queries, transport, for_proxy); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -430,18 +461,16 @@ out: * See socks.c on how this is used to make a non-blocking DNS * resolve during connect. */ -CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - uint8_t dns_queries, - struct Curl_peer *peer, - uint8_t transport, - bool complete_resolve) +static CURLcode cf_dns_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *peer, + uint8_t dns_queries, + uint8_t transport) { struct Curl_cfilter *cf; CURLcode result; - result = cf_dns_create(&cf, data, peer, dns_queries, transport, - FALSE, complete_resolve); + result = cf_dns_create(&cf, data, peer, dns_queries, transport, FALSE); if(result) return result; @@ -449,38 +478,93 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, return CURLE_OK; } -/* Return the resolv result from the first "resolv" filter, starting - * the given filter `cf` downwards. - */ -static CURLcode cf_dns_result(struct Curl_cfilter *cf, - struct Curl_peer *peer) +static CURLcode cf_dns_add_resolve(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer, + uint8_t dns_queries, + uint8_t transport) { - for(; cf; cf = cf->next) { + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + struct Curl_cfilter *cf_dns = NULL; + bool is_addr = CURL_DNSQ_IS_ADDR(dns_queries); + + if((dns_queries & CURL_DNSQ_HTTPS) && + Curl_is_ipaddr(peer->hostname)) { + dns_queries = (uint8_t)(dns_queries & ~CURL_DNSQ_HTTPS); + } + + for(; cf && dns_queries; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; + cf_dns = cf; if(Curl_peer_same_destination(ctx->peer, peer)) { - if(ctx->dns || ctx->resolv_result) - return ctx->resolv_result; - return CURLE_AGAIN; + /* subtract queries already being scheduled/ongoing */ + dns_queries = (uint8_t)(~ctx->dns_queries & dns_queries); + if(!dns_queries) { /* already there */ + return CURLE_OK; + } + else if(is_addr && !ctx->started && + CURL_DNSQ_IS_ADDR(ctx->dns_queries)) { + ctx->dns_queries |= dns_queries; + CURL_TRC_DNS(data, "[%s] added queries=%s for %s:%u", + Curl_resolv_query_str(ctx->dns_queries), + Curl_resolv_query_str(dns_queries), + peer->hostname, peer->port); + return CURLE_OK; + } } - return CURLE_OK; /* ok, but no results */ } } - return CURLE_FAILED_INIT; + + if(dns_queries) { + /* No existing filter is handling these, add a new DNS filter + * (after the last one if there was one already. FCFS. */ + if(cf_dns) + return cf_dns_insert_after(cf_dns, data, peer, dns_queries, transport); + else + return cf_dns_add(data, conn, sockindex, peer, dns_queries, transport); + } + return CURLE_OK; } -/* Return the result of the DNS resolution for peer. Searches for a "resolv" - * filter from the top of the filter chain down. Returns +CURLcode Curl_conn_dns_add_addr_resolve(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer, + uint8_t dns_queries, + uint8_t transport) +{ + /* should only have address query bits */ + DEBUGASSERT(!(dns_queries & ~CURL_DNSQ_ADDR)); + return cf_dns_add_resolve(data, conn, sockindex, peer, + (dns_queries & CURL_DNSQ_ADDR), transport); +} + +/* Return the result of the DNS address resolution for peer. + * Searches for a DNS filter from the top of the filter chain down. Returns * - CURLE_AGAIN when not done yet * - CURLE_OK when DNS was successfully resolved * - CURLR_FAILED_INIT when no resolv filter was found * - error returned by the DNS resolv */ -CURLcode Curl_conn_dns_result(struct connectdata *conn, - int sockindex, - struct Curl_peer *peer) +CURLcode Curl_conn_dns_addr_result(struct connectdata *conn, + int sockindex, + struct Curl_peer *peer) { - return cf_dns_result(conn->cfilter[sockindex], peer); + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(Curl_peer_same_destination(ctx->peer, peer) && + (ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA))) { + if(ctx->dns || ctx->resolv_result) + return ctx->resolv_result; + return CURLE_AGAIN; + } + } + } + return CURLE_FAILED_INIT; /* no one is resolving */ } static const struct Curl_addrinfo *cf_dns_get_nth_ai( @@ -508,20 +592,25 @@ static const struct Curl_addrinfo *cf_dns_get_nth_ai( } /* Return the addrinfo at `index` for the given `family` from the - * first "resolve" filter underneath `cf`. If the DNS resolving is + * first "resolve" filter at the connection. If the DNS resolving is * not done yet or if no address for the family exists, returns NULL. */ -const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct Curl_peer *peer, - int ai_family, - unsigned int index) +const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, + struct Curl_peer *peer, + int sockindex, + int ai_family, + unsigned int index) { - (void)data; + struct connectdata *conn = data->conn; + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + for(; cf; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; - if(Curl_peer_same_destination(ctx->peer, peer)) { + if(Curl_peer_same_destination(ctx->peer, peer) && + (ctx->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA))) { + CURL_TRC_CF(data, cf, "get %uth result for %s:%u, family=%d, dns=%d", + index, peer->hostname, peer->port, ai_family, !!ctx->dns); if(ctx->resolv_result) return NULL; else if(ctx->dns) @@ -534,22 +623,16 @@ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, return NULL; } -/* Return the addrinfo at `index` for the given `family` from the - * first "resolve" filter at the connection. If the DNS resolving is - * not done yet or if no address for the family exists, returns NULL. - */ -const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, - struct Curl_peer *peer, - int sockindex, - int ai_family, - unsigned int index) +#ifdef USE_HTTPSRR +CURLcode Curl_conn_dns_add_https_resolve(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer) { - struct connectdata *conn = data->conn; - return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, peer, - ai_family, index); + return cf_dns_add_resolve(data, conn, sockindex, peer, + CURL_DNSQ_HTTPS, conn->transport_wanted); } -#ifdef USE_HTTPSRR /* Return the HTTPS-RR info from the first "resolve" filter at the * connection. If the DNS resolving is not done yet or if there * is no HTTPS-RR info, returns NULL. @@ -563,7 +646,8 @@ Curl_conn_dns_get_https(struct Curl_easy *data, for(; cf; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; - if(Curl_peer_same_destination(ctx->peer, peer)) { + if(Curl_peer_same_destination(ctx->peer, peer) && + (ctx->dns_queries & CURL_DNSQ_HTTPS)) { if(ctx->dns) return ctx->dns->hinfo; else @@ -582,7 +666,8 @@ bool Curl_conn_dns_resolved_https(struct Curl_easy *data, for(; cf; cf = cf->next) { if(cf->cft == &Curl_cft_dns) { struct cf_dns_ctx *ctx = cf->ctx; - if(Curl_peer_same_destination(ctx->peer, peer)) { + if(Curl_peer_same_destination(ctx->peer, peer) && + (ctx->dns_queries & CURL_DNSQ_HTTPS)) { if(ctx->dns) return TRUE; else diff --git a/lib/cf-dns.h b/lib/cf-dns.h index 891b1efea6..ef90d711e2 100644 --- a/lib/cf-dns.h +++ b/lib/cf-dns.h @@ -31,23 +31,16 @@ struct Curl_dns_entry; struct Curl_addrinfo; struct Curl_peer; -CURLcode Curl_cf_dns_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - struct Curl_peer *peer, - uint8_t dns_queries, - uint8_t transport); +CURLcode Curl_conn_dns_add_addr_resolve(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer, + uint8_t dns_queries, + uint8_t transport); -CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - uint8_t dns_queries, - struct Curl_peer *peer, - uint8_t transport, - bool complete_resolve); - -CURLcode Curl_conn_dns_result(struct connectdata *conn, - int sockindex, - struct Curl_peer *peer); +CURLcode Curl_conn_dns_addr_result(struct connectdata *conn, + int sockindex, + struct Curl_peer *peer); const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, struct Curl_peer *peer, @@ -55,13 +48,12 @@ const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, int ai_family, unsigned int index); -const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct Curl_peer *peer, - int ai_family, - unsigned int index); - #ifdef USE_HTTPSRR +CURLcode Curl_conn_dns_add_https_resolve(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + struct Curl_peer *peer); + const struct Curl_https_rrinfo * Curl_conn_dns_get_https(struct Curl_easy *data, int sockindex, diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index 1a2e966ef6..c39f59f2ee 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -110,6 +110,7 @@ static CURLcode cf_hc_baller_cntrl(struct cf_hc_baller *b, struct cf_hc_ctx { cf_hc_state state; + struct Curl_peer *destination; /* who we ultimately want to talk to */ struct curltime started; /* when connect started */ CURLcode result; /* overall result */ CURLcode check_h3_result; @@ -123,21 +124,14 @@ struct cf_hc_ctx { BIT(ballers_complete); }; -static void cf_hc_ctx_close(struct Curl_easy *data, - struct cf_hc_ctx *ctx) +static void cf_hc_ctx_destroy(struct Curl_easy *data, + struct cf_hc_ctx *ctx) { if(ctx) { size_t i; for(i = 0; i < ctx->baller_count; ++i) cf_hc_baller_discard(&ctx->ballers[i], data); - } -} - -static void cf_hc_ctx_destroy(struct Curl_easy *data, - struct cf_hc_ctx *ctx) -{ - if(ctx) { - cf_hc_ctx_close(data, ctx); + Curl_peer_unlink(&ctx->destination); curlx_free(ctx); } } @@ -222,7 +216,6 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, ctx->state = CF_HC_SUCCESS; cf->connected = TRUE; - cf_hc_ctx_close(data, ctx); /* ballers may have failf()'d, the winner resets it, so our * errorbuf is clean again. */ Curl_reset_fail(data); @@ -296,6 +289,7 @@ static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, enum alpnid not_this_one) { #ifdef USE_HTTPSRR + struct cf_hc_ctx *ctx = cf->ctx; /* Is there an HTTPSRR use its ALPNs here. * We are here after having selected a connection to a host+port and * can no longer change that. Any HTTPSRR advice for other hosts and ports @@ -304,9 +298,9 @@ static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, size_t i; /* Do we have HTTPS-RR information? */ - rr = Curl_conn_dns_get_https( - data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex)); + rr = Curl_conn_dns_get_https(data, cf->sockindex, ctx->destination); + CURL_TRC_CF(data, cf, "HTTPS-RR %savailable", rr ? "" : "not "); /* We do not support `rr->no_def_alpn`. */ if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) { for(i = 0; i < CURL_ARRAYSIZE(rr->alpns); ++i) { @@ -495,7 +489,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, if(!ctx->httpsrr_resolved) { ctx->httpsrr_resolved = Curl_conn_dns_resolved_https( - data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex)); + data, cf->sockindex, ctx->destination); #ifdef DEBUGBUILD if(!ctx->httpsrr_resolved && getenv("CURL_DBG_AWAIT_HTTPSRR")) { CURL_TRC_CF(data, cf, "awaiting HTTPS-RR"); @@ -744,7 +738,7 @@ static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_cftype Curl_cft_http_connect = { "HTTPS-CONNECT", - CF_TYPE_SETUP | CF_TYPE_HTTPSRR, + CF_TYPE_SETUP, CURL_LOG_LVL_NONE, cf_hc_destroy, cf_hc_connect, @@ -761,6 +755,7 @@ struct Curl_cftype Curl_cft_http_connect = { static CURLcode cf_hc_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *destination, uint8_t def_transport) { struct Curl_cfilter *cf = NULL; @@ -772,6 +767,7 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } + Curl_peer_link(&ctx->destination, destination); ctx->def_transport = def_transport; ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2; @@ -788,6 +784,7 @@ out: } static CURLcode cf_hc_add(struct Curl_easy *data, + struct Curl_peer *destination, struct connectdata *conn, int sockindex, uint8_t def_transport) @@ -796,15 +793,21 @@ static CURLcode cf_hc_add(struct Curl_easy *data, CURLcode result = CURLE_OK; DEBUGASSERT(data); - result = cf_hc_create(&cf, data, def_transport); + result = cf_hc_create(&cf, data, destination, def_transport); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); + +#ifdef USE_HTTPSRR + result = Curl_conn_dns_add_https_resolve(data, cf->conn, cf->sockindex, + destination); +#endif out: return result; } CURLcode Curl_cf_https_setup(struct Curl_easy *data, + struct Curl_peer *destination, struct connectdata *conn, int sockindex) { @@ -821,7 +824,8 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, !conn->bits.tls_enable_alpn) goto out; - result = cf_hc_add(data, conn, sockindex, conn->transport_wanted); + result = cf_hc_add(data, destination, conn, sockindex, + conn->transport_wanted); out: return result; diff --git a/lib/cf-https-connect.h b/lib/cf-https-connect.h index 3160c0382a..caa2b17e7f 100644 --- a/lib/cf-https-connect.h +++ b/lib/cf-https-connect.h @@ -31,10 +31,12 @@ struct Curl_cfilter; struct Curl_easy; struct connectdata; struct Curl_cftype; +struct Curl_peer; extern struct Curl_cftype Curl_cft_http_connect; CURLcode Curl_cf_https_setup(struct Curl_easy *data, + struct Curl_peer *destination, struct connectdata *conn, int sockindex); diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 91ff029f6f..794d5e4cb9 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -857,7 +857,8 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, *done = FALSE; if(!ctx->dns_resolved) { - result = Curl_conn_dns_result(cf->conn, cf->sockindex, ctx->ballers.peer); + result = Curl_conn_dns_addr_result(cf->conn, cf->sockindex, + ctx->ballers.peer); if(!result) ctx->dns_resolved = TRUE; else if(result == CURLE_AGAIN) { @@ -888,10 +889,6 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, cf->connected = TRUE; cf->next = ctx->ballers.winner->cf; ctx->ballers.winner->cf = NULL; - cf_ip_happy_ctx_clear(ctx, data); - Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); - /* whatever errors were reported by ballers, clear our errorbuf */ - Curl_reset_fail(data); if(cf->conn->scheme->protocol & PROTO_FAMILY_SSH) Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */ @@ -900,13 +897,16 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, struct ip_quadruple ipquad; bool is_ipv6; if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) { - const char *host; - Curl_conn_get_current_host(data, cf->sockindex, &host, NULL); CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u", - host, ipquad.remote_ip, ipquad.remote_port); + ctx->ballers.peer->hostname, + ipquad.remote_ip, ipquad.remote_port); } } #endif + cf_ip_happy_ctx_clear(ctx, data); + Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); + /* whatever errors were reported by ballers, clear our errorbuf */ + Curl_reset_fail(data); data->info.numconnects++; /* to track the # of connections made */ } break; diff --git a/lib/cf-setup.c b/lib/cf-setup.c index b073989a4e..7699ef5dfe 100644 --- a/lib/cf-setup.c +++ b/lib/cf-setup.c @@ -251,7 +251,7 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, (int)result); return result; } - result = Curl_cf_quic_insert_after(cf, origin, peer); + result = Curl_cf_quic_insert_after(cf, data, origin, peer); if(result) { CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result); return result; diff --git a/lib/cfilters.c b/lib/cfilters.c index c5ee51a1b4..238698d332 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -644,17 +644,6 @@ int Curl_protocol_for_transport(uint8_t transport) } } -bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft->flags & CF_TYPE_HTTPSRR) - return TRUE; - } - return FALSE; -} - const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, struct connectdata *conn) { @@ -728,17 +717,17 @@ CURLcode Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, struct easy_pollset *ps) { CURLcode result = CURLE_OK; - /* Get the lowest not-connected filter, if there are any */ - while(cf && !cf->connected && cf->next && !cf->next->connected) - cf = cf->next; - /* Skip all filters that have already shut down */ - while(cf && cf->shutdown) - cf = cf->next; - /* From there on, give all filters a chance to adjust the pollset. - * Lower filters are called later, so they may override */ - while(cf && !result) { - result = cf->cft->adjust_pollset(cf, data, ps); - cf = cf->next; + /* Go through all filters, top to bottom, and let them manage the pollset + * - connected filters can do so + * - CF_TYPE_DNS filters can + * - unconnected filters without next or connect next can + */ + for(; cf && !result; cf = cf->next) { + if(cf->shutdown) + continue; + if(cf->connected || (cf->cft->flags & CF_TYPE_DNS) || + !cf->next || cf->next->connected) + result = cf->cft->adjust_pollset(cf, data, ps); } return result; } diff --git a/lib/cfilters.h b/lib/cfilters.h index b1aad029e0..f55a572e2e 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -197,7 +197,7 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, * CF_TYPE_HTTP implement a version of the HTTP protocol * CF_TYPE_SETUP filter is only needed for connection setup and * can be removed once connected - * CF_TYPE_HTTPSRR filter that wants HTTPS-RR information + * CF_TYPE_DNS filter managing DNS resolve operations */ #define CF_TYPE_IP_CONNECT (1 << 0) #define CF_TYPE_SSL (1 << 1) @@ -205,7 +205,7 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, #define CF_TYPE_PROXY (1 << 3) #define CF_TYPE_HTTP (1 << 4) #define CF_TYPE_SETUP (1 << 5) -#define CF_TYPE_HTTPSRR (1 << 6) +#define CF_TYPE_DNS (1 << 6) /* A connection filter type, e.g. specific implementation. */ struct Curl_cftype { @@ -352,10 +352,6 @@ int Curl_protocol_for_transport(uint8_t transport); const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf, struct Curl_easy *data); -/* The filter (or one of its sub-filters) wants HTTPS-RR information. */ -bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, - struct Curl_easy *data); - #define CURL_CF_SSL_DEFAULT (-1) #define CURL_CF_SSL_DISABLE 0 #define CURL_CF_SSL_ENABLE 1 diff --git a/lib/connect.c b/lib/connect.c index 86e97b955a..ca81849ce7 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -237,22 +237,23 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, int sockindex, int ssl_mode) { + struct Curl_peer *first_peer = Curl_conn_get_first_peer(conn, sockindex); CURLcode result = CURLE_OK; - struct Curl_peer *peer = Curl_conn_get_first_peer(conn, sockindex); uint8_t dns_queries; DEBUGASSERT(data); DEBUGASSERT(conn->scheme); DEBUGASSERT(!conn->cfilter[sockindex]); - if(!peer) + if(!first_peer) return CURLE_FAILED_INIT; #ifndef CURL_DISABLE_HTTP if(!conn->cfilter[sockindex] && conn->scheme->protocol == CURLPROTO_HTTPS) { DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE); - result = Curl_cf_https_setup(data, conn, sockindex); + result = Curl_cf_https_setup( + data, Curl_conn_get_destination(conn, sockindex), conn, sockindex); if(result) goto out; } @@ -266,13 +267,16 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, goto out; } + /* Whatever the filter chain will be in the end, it will need the + * resolving of `first_peer`. Add that now so the resolve is started + * right away. */ dns_queries = Curl_resolv_dns_queries(data, conn->ip_version); -#ifdef USE_HTTPSRR - if(sockindex == FIRSTSOCKET) - dns_queries |= CURL_DNSQ_HTTPS; -#endif - result = Curl_cf_dns_add(data, conn, sockindex, peer, dns_queries, - conn->transport_wanted); + result = Curl_conn_dns_add_addr_resolve(data, conn, sockindex, + first_peer, dns_queries, + conn->transport_wanted); + if(result) + goto out; + DEBUGASSERT(conn->cfilter[sockindex]); out: return result; diff --git a/lib/dnscache.c b/lib/dnscache.c index 9c8341e56f..9f435cc0cd 100644 --- a/lib/dnscache.c +++ b/lib/dnscache.c @@ -64,10 +64,7 @@ static void dnscache_entry_free(struct Curl_dns_entry *dns) { Curl_freeaddrinfo(dns->addr); #ifdef USE_HTTPSRR - if(dns->hinfo) { - Curl_httpsrr_cleanup(dns->hinfo); - curlx_free(dns->hinfo); - } + Curl_httpsrr_destroy(dns->hinfo); #endif curlx_free(dns); } @@ -76,17 +73,19 @@ static void dnscache_entry_free(struct Curl_dns_entry *dns) * Create a hostcache id string for the provided host + port, to be used by * the DNS caching. Without alloc. Return length of the id string. */ -static size_t create_dnscache_id(const char *name, +static size_t create_dnscache_id(char type, const char *name, size_t nlen, /* 0 or actual name length */ - uint16_t port, char *ptr, size_t buflen) + uint16_t port, + char *buf, size_t buflen) { size_t len = nlen ? nlen : strlen(name); DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); - if(len > (buflen - 7)) - len = buflen - 7; + if(len > (buflen - 8)) + len = buflen - 8; /* store and lower case the name */ - Curl_strntolower(ptr, name, len); - return curl_msnprintf(&ptr[len], 7, ":%u", port) + len; + buf[0] = type; + Curl_strntolower(buf + 1, name, len); + return curl_msnprintf(&buf[len + 1], 7, ":%u", port) + len + 1; } struct dnscache_prune_data { @@ -219,6 +218,8 @@ static CURLcode fetch_addr(struct Curl_easy *data, struct Curl_dns_entry *dns = NULL; char entry_id[MAX_HOSTCACHE_LEN]; size_t entry_len; + char entry_type = CURL_DNSQ_IS_ADDR(dns_queries) ? + CURL_DNST_ADDR : CURL_DNST_HTTPS; CURLcode result = CURLE_OK; *pdns = NULL; @@ -226,15 +227,16 @@ static CURLcode fetch_addr(struct Curl_easy *data, return CURLE_OK; /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(hostname, 0, port, + entry_len = create_dnscache_id(entry_type, hostname, 0, port, entry_id, sizeof(entry_id)); /* See if it is already in our dns cache */ dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); /* No entry found in cache, check if we might have a wildcard entry */ - if(!dns && data->state.wildcard_resolve) { - entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id)); + if(!dns && data->state.wildcard_resolve && CURL_DNSQ_IS_ADDR(dns_queries)) { + entry_len = create_dnscache_id(CURL_DNST_ADDR, "*", 1, port, + entry_id, sizeof(entry_id)); /* See if it is already in our dns cache */ dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); @@ -255,9 +257,11 @@ static CURLcode fetch_addr(struct Curl_easy *data, } } - if(dns) { - if((dns->dns_queries & dns_queries) != dns_queries) { - /* The entry does not cover all wanted DNS queries, a miss. */ + /* We need to cache address information and HTTPS-RR separately. */ + if(dns && CURL_DNSQ_IS_ADDR(dns_queries)) { + if((uint8_t)(dns->dns_queries & dns_queries) != + (uint8_t)(dns_queries & CURL_DNSQ_ADDR)) { + /* The entry does not cover all wanted address queries, a miss. */ dns = NULL; } else if(!(dns->dns_responses & dns_queries)) { @@ -267,12 +271,12 @@ static CURLcode fetch_addr(struct Curl_easy *data, dns = NULL; result = CURLE_COULDNT_RESOLVE_HOST; } + else if(dns && !dns->addr) { /* negative entry */ + dns = NULL; + result = CURLE_COULDNT_RESOLVE_HOST; + } } - if(dns && !dns->addr) { /* negative entry */ - dns = NULL; - result = CURLE_COULDNT_RESOLVE_HOST; - } *pdns = dns; return result; } @@ -311,6 +315,9 @@ CURLcode Curl_dnscache_get(struct Curl_easy *data, } dnscache_unlock(data, dnscache); + CURL_TRC_DNS(data, "cache lookup %s:%u queries=%s -> %d %sfound", + hostname, port, Curl_resolv_query_str(dns_queries), + (int)result, dns ? "" : "not "); *pentry = dns; return result; } @@ -407,11 +414,8 @@ static bool dnscache_ai_has_family(struct Curl_addrinfo *ai, return FALSE; } -static struct Curl_dns_entry *dnscache_entry_create( +static struct Curl_dns_entry *dnsc_entry_create( struct Curl_easy *data, - uint8_t dns_queries, - struct Curl_addrinfo **paddr1, - struct Curl_addrinfo **paddr2, const char *hostname, size_t hostlen, uint16_t port, @@ -425,7 +429,6 @@ static struct Curl_dns_entry *dnscache_entry_create( goto out; dns->refcount = 1; /* the cache has the first reference */ - dns->dns_queries = dns_queries; dns->port = port; if(hostlen) memcpy(dns->hostname, hostname, hostlen); @@ -438,6 +441,29 @@ static struct Curl_dns_entry *dnscache_entry_create( dns->timestamp = *Curl_pgrs_now(data); } +out: + return dns; +} + +static struct Curl_dns_entry * +dnsc_entry_assign_addr( + struct Curl_easy *data, + struct Curl_dns_entry *dns, + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2) +{ + if(!dns) + goto out; + /* only do this when this is the only reference */ + DEBUGASSERT(dns->refcount == 1); + DEBUGASSERT(dns->type == CURL_DNST_INIT); + + dns->type = CURL_DNST_ADDR; + /* queries should only be about addresses */ + DEBUGASSERT(!(dns_queries & ~CURL_DNSQ_ADDR)); + dns->dns_queries = (dns_queries & CURL_DNSQ_ADDR); + /* Take the given address lists into the entry */ if(paddr1 && *paddr1) { dns->addr = *paddr1; @@ -488,67 +514,129 @@ out: return dns; } -struct Curl_dns_entry *Curl_dnscache_mk_entry(struct Curl_easy *data, +struct Curl_dns_entry *Curl_dnsc_mk_addr(struct Curl_easy *data, uint8_t dns_queries, struct Curl_addrinfo **paddr, const char *hostname, uint16_t port) { - return dnscache_entry_create(data, dns_queries, paddr, NULL, hostname, - hostname ? strlen(hostname) : 0, - port, FALSE); + struct Curl_dns_entry *dns = dnsc_entry_create( + data, hostname, hostname ? strlen(hostname) : 0, port, FALSE); + dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr, NULL); + return dns; } -struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, +struct Curl_dns_entry *Curl_dnsc_mk_addr2(struct Curl_easy *data, uint8_t dns_queries, struct Curl_addrinfo **paddr1, struct Curl_addrinfo **paddr2, const char *hostname, uint16_t port) { - return dnscache_entry_create(data, dns_queries, paddr1, paddr2, hostname, - hostname ? strlen(hostname) : 0, - port, FALSE); + struct Curl_dns_entry *dns = dnsc_entry_create( + data, hostname, hostname ? strlen(hostname) : 0, port, FALSE); + dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr1, paddr2); + return dns; } #ifdef USE_HTTPSRR -void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, - struct Curl_https_rrinfo *hinfo) +static struct Curl_dns_entry * +dnsc_entry_assign_https(struct Curl_dns_entry *dns, + struct Curl_https_rrinfo **phinfo) { + if(!dns) + goto out; /* only do this when this is the only reference */ DEBUGASSERT(dns->refcount == 1); - /* it should have been in the queries */ - DEBUGASSERT(dns->dns_queries & CURL_DNSQ_HTTPS); + DEBUGASSERT(dns->type == CURL_DNST_INIT); + if(dns->hinfo) { - Curl_httpsrr_cleanup(dns->hinfo); - curlx_free(dns->hinfo); + Curl_httpsrr_destroy(dns->hinfo); + dns->hinfo = NULL; } - dns->hinfo = hinfo; - dns->dns_responses |= CURL_DNSQ_HTTPS; + dns->type = CURL_DNST_HTTPS; + dns->dns_responses = dns->dns_queries = CURL_DNSQ_HTTPS; + if(phinfo) { + dns->hinfo = *phinfo; + *phinfo = NULL; + } +out: + if(phinfo && *phinfo) { + Curl_httpsrr_destroy(*phinfo); + *phinfo = NULL; + } + return dns; } + +struct Curl_dns_entry *Curl_dnsc_mk_https(struct Curl_easy *data, + struct Curl_https_rrinfo **phinfo, + const char *hostname, + uint16_t port) +{ + struct Curl_dns_entry *dns = dnsc_entry_create( + data, hostname, hostname ? strlen(hostname) : 0, port, FALSE); + dns = dnsc_entry_assign_https(dns, phinfo); + return dns; +} + +static struct Curl_dns_entry *dnsc_add_https(struct Curl_easy *data, + struct Curl_dnscache *dnscache, + struct Curl_https_rrinfo **phinfo, + const char *hostname, + size_t hlen, + uint16_t port, + bool permanent) +{ + char entry_id[MAX_HOSTCACHE_LEN]; + size_t entry_len; + struct Curl_dns_entry *dns, *dns2; + + dns = dnsc_entry_create(data, hostname, hostname ? strlen(hostname) : 0, + port, permanent); + dns = dnsc_entry_assign_https(dns, phinfo); + if(!dns) + return NULL; + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(CURL_DNST_HTTPS, hostname, hlen, port, + entry_id, sizeof(entry_id)); + + /* Store the resolved data in our DNS cache. */ + dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1, + (void *)dns); + if(!dns2) { + dnscache_entry_free(dns); + return NULL; + } + + dns = dns2; + dns->refcount++; /* mark entry as in-use */ + return dns; +} + #endif /* USE_HTTPSRR */ -static struct Curl_dns_entry *dnscache_add_addr(struct Curl_easy *data, - struct Curl_dnscache *dnscache, - uint8_t dns_queries, - struct Curl_addrinfo **paddr, - const char *hostname, - size_t hlen, - uint16_t port, - bool permanent) +static struct Curl_dns_entry *dnsc_add_addr(struct Curl_easy *data, + struct Curl_dnscache *dnscache, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + const char *hostname, + size_t hlen, + uint16_t port, + bool permanent) { char entry_id[MAX_HOSTCACHE_LEN]; size_t entry_len; struct Curl_dns_entry *dns; struct Curl_dns_entry *dns2; - dns = dnscache_entry_create(data, dns_queries, paddr, NULL, - hostname, hlen, port, permanent); + dns = dnsc_entry_create(data, hostname, hlen, port, permanent); + dns = dnsc_entry_assign_addr(data, dns, dns_queries, paddr, NULL); if(!dns) return NULL; /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(hostname, hlen, port, + entry_len = create_dnscache_id(CURL_DNST_ADDR, hostname, hlen, port, entry_id, sizeof(entry_id)); /* Store the resolved data in our DNS cache. */ @@ -573,8 +661,12 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data, if(!dnscache) return CURLE_FAILED_INIT; + if(!entry || (entry->type == CURL_DNST_INIT)) + return CURLE_BAD_FUNCTION_ARGUMENT; + /* Create an entry id, based upon the hostname and port */ - idlen = create_dnscache_id(entry->hostname, 0, entry->port, id, sizeof(id)); + idlen = create_dnscache_id(entry->type, entry->hostname, 0, entry->port, + id, sizeof(id)); /* Store the resolved data in our DNS cache and up ref count */ dnscache_lock(data, dnscache); @@ -584,6 +676,9 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data, } entry->refcount++; dnscache_unlock(data, dnscache); + CURL_TRC_DNS(data, "cached entry for %s:%u queries=%s", + entry->hostname, entry->port, + Curl_resolv_query_str(entry->dns_queries)); return CURLE_OK; } @@ -593,16 +688,35 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, uint16_t port) { struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns; + struct Curl_dns_entry *dns = NULL; + CURLcode result = CURLE_OK; + DEBUGASSERT(dnscache); if(!dnscache) return CURLE_FAILED_INIT; dnscache_lock(data, dnscache); - /* put this new host in the cache */ - dns = dnscache_add_addr(data, dnscache, dns_queries, NULL, - host, strlen(host), port, FALSE); + if(dns_queries & CURL_DNSQ_ADDR) { + /* put this new host in the cache */ + dns = dnsc_add_addr(data, dnscache, dns_queries, NULL, + host, strlen(host), port, FALSE); + if(!dns) + result = CURLE_OUT_OF_MEMORY; + } +#ifdef USE_HTTPSRR + else if(dns_queries == CURL_DNSQ_HTTPS) { + dns = dnsc_add_https(data, dnscache, NULL, + host, strlen(host), port, FALSE); + if(!dns) + result = CURLE_OUT_OF_MEMORY; + } +#endif + else { + /* a query we do not know, just cache nothing */ + DEBUGASSERT(0); + } + if(dns) { /* release the returned reference; the cache itself will keep the * entry alive: */ @@ -610,10 +724,9 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, dnscache_unlock(data, dnscache); CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s", host, port, Curl_resolv_query_str(dns_queries)); - return CURLE_OK; } dnscache_unlock(data, dnscache); - return CURLE_OUT_OF_MEMORY; + return result; } /* @@ -697,7 +810,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) if(!curlx_str_number(&host, &num, 0xffff)) { /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(curlx_str(&source), + entry_len = create_dnscache_id(CURL_DNST_ADDR, curlx_str(&source), curlx_strlen(&source), (uint16_t)num, entry_id, sizeof(entry_id)); dnscache_lock(data, dnscache); @@ -802,7 +915,8 @@ err: } /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source), + entry_len = create_dnscache_id(CURL_DNST_ADDR, + curlx_str(&source), curlx_strlen(&source), port, entry_id, sizeof(entry_id)); dnscache_lock(data, dnscache); @@ -828,10 +942,10 @@ err: Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); } - /* put this new host in the cache, an override for ALL dns queries */ - dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL, - &head, curlx_str(&source), - curlx_strlen(&source), port, permanent); + /* put this new host in the cache, override all address queries */ + dns = dnsc_add_addr(data, dnscache, CURL_DNSQ_ADDR, + &head, curlx_str(&source), + curlx_strlen(&source), port, permanent); if(dns) /* release the returned reference; the cache itself will keep the * entry alive: */ @@ -842,7 +956,7 @@ err: if(!dns) return CURLE_OUT_OF_MEMORY; - infof(data, "Added %.*s:%u:%s to DNS cache%s", + infof(data, "[DNS] added %.*s:%u:%s to cache%s", (int)curlx_strlen(&source), curlx_str(&source), port, addresses, permanent ? "" : " (non-permanent)"); diff --git a/lib/dnscache.h b/lib/dnscache.h index 9239977cf8..c029eb9d30 100644 --- a/lib/dnscache.h +++ b/lib/dnscache.h @@ -36,6 +36,10 @@ struct easy_pollset; struct Curl_https_rrinfo; struct Curl_multi; +#define CURL_DNST_INIT '\0' +#define CURL_DNST_ADDR 'A' +#define CURL_DNST_HTTPS 'H' + struct Curl_dns_entry { struct Curl_addrinfo *addr; #ifdef USE_HTTPSRR @@ -47,6 +51,7 @@ struct Curl_dns_entry { uint32_t refcount; /* hostname port number that resolved to addr. */ uint16_t port; + char type; /* CURL_DNST_ADDR or CURL_DNST_HTTPS */ uint8_t dns_queries; /* CURL_DNSQ_* type of queries performed for this */ uint8_t dns_responses; /* CURL_DNSQ_* type this entry has responses for */ /* hostname that resolved to addr. may be NULL (Unix domain sockets). */ @@ -62,13 +67,13 @@ struct Curl_dns_entry { * * Returns entry or NULL on OOM. */ -struct Curl_dns_entry *Curl_dnscache_mk_entry(struct Curl_easy *data, +struct Curl_dns_entry *Curl_dnsc_mk_addr(struct Curl_easy *data, uint8_t dns_queries, struct Curl_addrinfo **paddr, const char *hostname, uint16_t port); -struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, +struct Curl_dns_entry *Curl_dnsc_mk_addr2(struct Curl_easy *data, uint8_t dns_queries, struct Curl_addrinfo **paddr1, struct Curl_addrinfo **paddr2, @@ -76,8 +81,10 @@ struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, uint16_t port); #ifdef USE_HTTPSRR -void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, - struct Curl_https_rrinfo *hinfo); +struct Curl_dns_entry *Curl_dnsc_mk_https(struct Curl_easy *data, + struct Curl_https_rrinfo **phinfo, + const char *hostname, + uint16_t port); #endif /* USE_HTTPSRR */ /* unlink a dns entry, frees all resources if it was the last reference. diff --git a/lib/doh.c b/lib/doh.c index b1f138f32c..f336eb5003 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -1189,47 +1189,11 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, *hrr = lhrr; return CURLE_OK; err: - Curl_httpsrr_cleanup(lhrr); - curlx_safefree(lhrr); + Curl_httpsrr_destroy(lhrr); return result; } -#if defined(DEBUGBUILD) && defined(CURLVERBOSE) -static void doh_print_httpsrr(struct Curl_easy *data, - struct Curl_https_rrinfo *hrr) -{ - DEBUGASSERT(hrr); - infof(data, "HTTPS RR: priority %d, target: %s", hrr->priority, hrr->target); - if(hrr->alpns[0] != ALPN_none) - infof(data, "HTTPS RR: alpns %u %u %u %u", - hrr->alpns[0], hrr->alpns[1], hrr->alpns[2], hrr->alpns[3]); - else - infof(data, "HTTPS RR: no alpns"); - if(hrr->no_def_alpn) - infof(data, "HTTPS RR: no_def_alpn set"); - else - infof(data, "HTTPS RR: no_def_alpn not set"); - if(hrr->ipv4hints) { - doh_print_buf(data, "HTTPS RR: ipv4hints", - hrr->ipv4hints, hrr->ipv4hints_len); - } - else - infof(data, "HTTPS RR: no ipv4hints"); - if(hrr->echconfiglist) { - doh_print_buf(data, "HTTPS RR: ECHConfigList", - hrr->echconfiglist, hrr->echconfiglist_len); - } - else - infof(data, "HTTPS RR: no ECHConfigList"); - if(hrr->ipv6hints) { - doh_print_buf(data, "HTTPS RR: ipv6hint", - hrr->ipv6hints, hrr->ipv6hints_len); - } - else - infof(data, "HTTPS RR: no ipv6hints"); -} -# endif -#endif +#endif /* USE_HTTPSRR */ CURLcode Curl_doh_take_result(struct Curl_easy *data, struct Curl_resolv_async *async, @@ -1250,6 +1214,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; } else if(!dohp->pending) { + struct Curl_dns_entry *dns = NULL; DOHcode rc[DOH_SLOT_COUNT]; bool negative = TRUE; int slot; @@ -1277,37 +1242,48 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, } } /* next slot */ - if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) { - /* we have an address, of one kind or other */ - struct Curl_dns_entry *dns; - struct Curl_addrinfo *ai; + if(CURL_DNSQ_IS_ADDR(async->dns_queries)) { + if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) { + /* we have an address, of one kind or other */ + struct Curl_addrinfo *ai; - if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) { - CURL_TRC_DNS(data, "hostname: %s", dohp->host); - doh_show(data, &de); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) { + CURL_TRC_DNS(data, "hostname: %s", dohp->host); + doh_show(data, &de); + } + + result = doh2ai(&de, dohp->host, dohp->port, &ai); + if(result) { + /* a decoded response without any usable address, e.g. only + CNAME records, is an authoritative "no data" answer */ + if((result == CURLE_COULDNT_RESOLVE_HOST) && negative) + async->negative_answer = TRUE; + goto error; + } + + /* we got a response, create a dns entry. */ + dns = Curl_dnsc_mk_addr(data, async->dns_queries, + &ai, dohp->host, dohp->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + } /* address processing done */ + else { + /* every query failed. Only NXDOMAIN answers for all of them + make this a negative answer, eligible for caching. */ + async->negative_answer = negative; + result = async->for_proxy ? + CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; } + } - result = doh2ai(&de, dohp->host, dohp->port, &ai); - if(result) { - /* a decoded response without any usable address, e.g. only - CNAME records, is an authoritative "no data" answer */ - if((result == CURLE_COULDNT_RESOLVE_HOST) && negative) - async->negative_answer = TRUE; - goto error; - } - - /* we got a response, create a dns entry. */ - dns = Curl_dnscache_mk_entry(data, async->dns_queries, - &ai, dohp->host, dohp->port); - if(!dns) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - /* Now add and HTTPSRR information if we have */ #ifdef USE_HTTPSRR + if(!dns && (async->dns_queries & CURL_DNSQ_HTTPS)) { + /* Now add and HTTPSRR information if we have */ + struct Curl_https_rrinfo *hrr = NULL; + if(de.numhttps_rrs > 0 && result == CURLE_OK) { - struct Curl_https_rrinfo *hrr = NULL; result = doh_resp_decode_httpsrr(data, de.https_rrs->val, de.https_rrs->len, &hrr); if(result) { @@ -1316,25 +1292,20 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, goto error; } infof(data, "Some HTTPS RR to process"); -#if defined(DEBUGBUILD) && defined(CURLVERBOSE) - doh_print_httpsrr(data, hrr); -#endif - Curl_dns_entry_set_https_rr(dns, hrr); } + Curl_httpsrr_trace(data, hrr); + dns = Curl_dnsc_mk_https(data, &hrr, async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + } #endif /* USE_HTTPSRR */ - /* and add the entry to the cache */ + /* and add the entry to the cache */ + if(dns) result = Curl_dnscache_add(data, dns); - *pdns = dns; - } /* address processing done */ - else { - /* every query failed. Only NXDOMAIN answers for all of them - make this a negative answer, eligible for caching. */ - async->negative_answer = negative; - result = async->for_proxy ? - CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; - } - + *pdns = dns; } /* !dohp->pending */ else /* wait for pending DoH transactions to complete */ diff --git a/lib/hostip.c b/lib/hostip.c index 6afe63ff45..dc39f581b2 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -354,7 +354,7 @@ static bool can_resolve_dns_queries(struct Curl_easy *data, uint8_t dns_queries) { (void)data; - if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && !ipv6works(data)) + if((CURL_DNSQ_IS_ADDR(dns_queries) == CURL_DNSQ_AAAA) && !ipv6works(data)) return FALSE; return TRUE; } @@ -445,7 +445,7 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data, result = Curl_async_take_result(data, async, pdns); if(result == CURLE_AGAIN) { - CURL_TRC_DNS(data, "resolve incomplete, queries=%s, responses=%s, " + CURL_TRC_DNS(data, "[%s] resolve incomplete, responses=%s, " "ongoing=%d for %s:%d", Curl_resolv_query_str(async->dns_queries), Curl_resolv_query_str(async->dns_responses), @@ -458,11 +458,13 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data, else if(result) { /* a local failure, not a resolve answer. Keep the error as it is so it does not get treated as one. */ - CURL_TRC_DNS(data, "resolve error %d for %s:%u", + CURL_TRC_DNS(data, "[%s] resolve error %d for %s:%u", + Curl_resolv_query_str(async->dns_queries), (int)result, async->hostname, async->port); } else { - CURL_TRC_DNS(data, "resolve complete for %s:%u", + CURL_TRC_DNS(data, "[%s] resolve complete for %s:%u", + Curl_resolv_query_str(async->dns_queries), async->hostname, async->port); DEBUGASSERT(*pdns); } @@ -514,6 +516,18 @@ const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, } #ifdef USE_HTTPSRR + +CURLcode Curl_resolv_https(struct Curl_easy *data, + struct Curl_peer *peer, + bool for_proxy, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns) +{ + return Curl_resolv(data, peer, CURL_DNSQ_HTTPS, TRNSPRT_TCP, + for_proxy, timeout_ms, presolv_id, pdns); +} + const struct Curl_https_rrinfo * Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id) { @@ -555,6 +569,7 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, #endif struct Curl_addrinfo *addr = NULL; size_t hostname_len; + bool addr_queries = (dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)); CURLcode result = CURLE_OK; *pnegative = FALSE; @@ -564,33 +579,34 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, *pdns = NULL; /* Check for "known" things to resolve ourselves. */ + if(addr_queries) { #ifndef USE_RESOLVE_ON_IPS - if(Curl_is_ipaddr(hostname)) { - /* test655 verifies that the announce is done, even though there - * is no real resolving. So, keep doing this. */ - result = Curl_resolv_announce_start(data, NULL); - if(result) + if(Curl_is_ipaddr(hostname)) { + /* test655 verifies that the announce is done, even though there + * is no real resolving. So, keep doing this. */ + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + /* shortcut literal IP addresses, if we are not told to resolve them. */ + result = Curl_str2addr(hostname, port, &addr); goto out; - /* shortcut literal IP addresses, if we are not told to resolve them. */ - result = Curl_str2addr(hostname, port, &addr); - goto out; - } + } #endif - hostname_len = strlen(hostname); - if(curl_strequal(hostname, "localhost") || - curl_strequal(hostname, "localhost.") || - tailmatch(hostname, hostname_len, STRCONST(".localhost")) || - tailmatch(hostname, hostname_len, STRCONST(".localhost."))) { - result = Curl_resolv_announce_start(data, NULL); - if(result) + hostname_len = strlen(hostname); + if(curl_strequal(hostname, "localhost") || + curl_strequal(hostname, "localhost.") || + tailmatch(hostname, hostname_len, STRCONST(".localhost")) || + tailmatch(hostname, hostname_len, STRCONST(".localhost."))) { + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + addr = get_localhost(port, hostname); + if(!addr) + result = CURLE_OUT_OF_MEMORY; goto out; - addr = get_localhost(port, hostname); - if(!addr) - result = CURLE_OUT_OF_MEMORY; - goto out; + } } - #ifndef CURL_DISABLE_DOH if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) { result = Curl_resolv_announce_start(data, NULL); @@ -654,7 +670,7 @@ out: if(addr) { /* we got a response, create a dns entry, add to cache, return */ DEBUGASSERT(!*pdns); - *pdns = Curl_dnscache_mk_entry(data, dns_queries, &addr, hostname, port); + *pdns = Curl_dnsc_mk_addr(data, dns_queries, &addr, hostname, port); if(!*pdns) result = CURLE_OUT_OF_MEMORY; } @@ -717,9 +733,9 @@ static CURLcode hostip_resolv(struct Curl_easy *data, } #ifdef DEBUGBUILD - CURL_TRC_DNS(data, "hostip_resolv(%s:%u, queries=%s)", - hostname, port, Curl_resolv_query_str(dns_queries)); - if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && + CURL_TRC_DNS(data, "[%s] hostip_resolv(%s:%u)", + Curl_resolv_query_str(dns_queries), hostname, port); + if((CURL_DNSQ_IS_ADDR(dns_queries) == CURL_DNSQ_AAAA) && getenv("CURL_DBG_RESOLV_FAIL_IPV6")) { infof(data, "DEBUG fail ipv6 resolve"); result = hostip_resolv_failed(data, hostname, for_proxy); @@ -742,6 +758,8 @@ static CURLcode hostip_resolv(struct Curl_easy *data, result = hostip_resolv_start(data, dns_queries, hostname, port, transport, for_proxy, timeout_ms, allowDOH, presolv_id, pdns, &negative); + CURL_TRC_DNS(data, "[%s] hostip_resolv started -> %d", + Curl_resolv_query_str(dns_queries), (int)result); } out: @@ -750,7 +768,8 @@ out: if(IS_RESOLV_FAIL(result)) { if(cache_dns && negative) Curl_dnscache_add_negative(data, dns_queries, hostname, port); - failf(data, "Could not resolve: %s:%u", hostname, port); + if(dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + failf(data, "Could not resolve: %s:%u", hostname, port); } else { failf(data, "Error %d resolving %s:%u", (int)result, hostname, port); @@ -967,7 +986,7 @@ static CURLcode resolv_unix(struct Curl_easy *data, return result; } - *pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0); + *pdns = Curl_dnsc_mk_addr(data, 0, &addr, NULL, 0); return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY; } #endif /* USE_UNIX_SOCKETS */ @@ -1011,7 +1030,7 @@ CURLcode Curl_resolv(struct Curl_easy *data, timeout_ms = CURL_TIMEOUT_RESOLVE_MS; #ifdef USE_UNIX_SOCKETS - if(peer->unix_socket) + if((dns_queries & CURL_DNSQ_ADDR) && peer->unix_socket) return resolv_unix(data, peer->hostname, (bool)peer->abstract_uds, pdns); #else if(peer->unix_socket) @@ -1019,14 +1038,17 @@ CURLcode Curl_resolv(struct Curl_easy *data, #endif #ifdef USE_ALARM_TIMEOUT - if(timeout_ms && data->set.no_signal) { - /* Cannot use ALARM when signals are disabled */ - timeout_ms = 0; - } - if(timeout_ms && !Curl_doh_wanted(data)) { - return resolv_alarm_timeout(data, dns_queries, peer->hostname, peer->port, - transport, for_proxy, timeout_ms, presolv_id, - pdns); + if(dns_queries & CURL_DNSQ_ADDR) { + if(timeout_ms && data->set.no_signal) { + /* Cannot use ALARM when signals are disabled */ + timeout_ms = 0; + } + if(timeout_ms && !Curl_doh_wanted(data)) { + return resolv_alarm_timeout(data, dns_queries, + peer->hostname, peer->port, + transport, for_proxy, timeout_ms, + presolv_id, pdns); + } } #endif /* !USE_ALARM_TIMEOUT */ @@ -1094,7 +1116,8 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, if(async->negative_answer) Curl_dnscache_add_negative(data, async->dns_queries, async->hostname, async->port); - failf(data, "Could not resolve: %s:%u", async->hostname, async->port); + if(async->dns_queries & (CURL_DNSQ_A|CURL_DNSQ_AAAA)) + failf(data, "Could not resolve: %s:%u", async->hostname, async->port); } else if(result) { failf(data, "Error %d resolving %s:%u", diff --git a/lib/hostip.h b/lib/hostip.h index 45370ec48e..eef7991847 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -52,8 +52,8 @@ struct Curl_peer; #define CURL_DNSQ_AAAA (1U << 1) #define CURL_DNSQ_HTTPS (1U << 2) -#define CURL_DNSQ_ALL (CURL_DNSQ_A | CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS) -#define CURL_DNSQ_IP(x) (uint8_t)((x)&(CURL_DNSQ_A | CURL_DNSQ_AAAA)) +#define CURL_DNSQ_ADDR (uint8_t)(CURL_DNSQ_A | CURL_DNSQ_AAAA) +#define CURL_DNSQ_IS_ADDR(x) (uint8_t)((x)&(CURL_DNSQ_ADDR)) #ifdef CURLVERBOSE const char *Curl_resolv_query_str(uint8_t dns_queries); @@ -153,6 +153,22 @@ const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, int ai_family, unsigned int index); #ifdef USE_HTTPSRR + +/* Start DNS resolving for HTTPS records. Returns + * - CURLE_OK: `*pdns` is the resolved DNS entry (needs to be unlinked). + * `*presolv_id` is 0. + * - CURLE_AGAIN: resolve is asynchronous and not finished yet. + * `presolv_id` is the identifier for querying results later. + * - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0. + */ +CURLcode Curl_resolv_https(struct Curl_easy *data, + struct Curl_peer *peer, + bool for_proxy, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns); + + const struct Curl_https_rrinfo *Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id); bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id); diff --git a/lib/httpsrr.c b/lib/httpsrr.c index 53647b81ed..a05a5cb5c1 100644 --- a/lib/httpsrr.c +++ b/lib/httpsrr.c @@ -97,7 +97,7 @@ void Curl_httpsrr_trace(struct Curl_easy *data, struct dynbuf tmp; CURLcode result; - if(!rr || !rr->complete) { + if(!rr) { CURL_TRC_DNS(data, "[HTTPS-RR] not available"); return; } @@ -225,23 +225,15 @@ CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr, return result; } -struct Curl_https_rrinfo *Curl_httpsrr_dup_move( - struct Curl_https_rrinfo *rrinfo) +void Curl_httpsrr_destroy(struct Curl_https_rrinfo *rrinfo) { - struct Curl_https_rrinfo *dup = curlx_memdup(rrinfo, sizeof(*rrinfo)); - if(dup) - memset(rrinfo, 0, sizeof(*rrinfo)); - return dup; -} - -void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo) -{ - curlx_safefree(rrinfo->target); - curlx_safefree(rrinfo->echconfiglist); - curlx_safefree(rrinfo->ipv4hints); - curlx_safefree(rrinfo->ipv6hints); - curlx_safefree(rrinfo->rrname); - rrinfo->complete = FALSE; + if(rrinfo) { + curlx_free(rrinfo->target); + curlx_free(rrinfo->echconfiglist); + curlx_free(rrinfo->ipv4hints); + curlx_free(rrinfo->ipv6hints); + curlx_free(rrinfo); + } } bool Curl_httpsrr_applicable(struct Curl_easy *data, @@ -269,11 +261,16 @@ static CURLcode httpsrr_opt(const ares_dns_rr_t *rr, } CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, - struct Curl_https_rrinfo *hinfo) + struct Curl_https_rrinfo **phinfo) { - CURLcode result = CURLE_OK; + struct Curl_https_rrinfo *hinfo = NULL; + CURLcode result = CURLE_OUT_OF_MEMORY; size_t i; + hinfo = curlx_calloc(1, sizeof(*hinfo)); + if(!hinfo) + goto out; + for(i = 0; i < ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); i++) { const char *target; size_t opt; @@ -297,12 +294,20 @@ CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, opt++) { result = httpsrr_opt(rr, ARES_RR_HTTPS_PARAMS, opt, hinfo); if(result) - break; + goto out; } } + result = CURLE_OK; + out: - hinfo->complete = !result; - curlx_safefree(hinfo->rrname); + if(result) { + *phinfo = NULL; + Curl_httpsrr_destroy(hinfo); + } + else { + DEBUGASSERT(hinfo); + *phinfo = hinfo; + } return result; } diff --git a/lib/httpsrr.h b/lib/httpsrr.h index 2ee1beab3e..8d6eed1c93 100644 --- a/lib/httpsrr.h +++ b/lib/httpsrr.h @@ -38,7 +38,6 @@ struct Curl_easy; struct dynbuf; struct Curl_https_rrinfo { - char *rrname; /* if NULL, the same as the URL hostname */ /* * Fields from HTTPS RR. The only mandatory fields are priority and target. * See https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2 @@ -57,16 +56,12 @@ struct Curl_https_rrinfo { BIT(no_def_alpn); /* keytag = 2 */ BIT(mandatory); /* keytag = 0 */ BIT(port_set); /* port value has been assigned */ - BIT(complete); /* values have been successfully assigned */ }; CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr, uint16_t rrkey, const uint8_t *val, size_t vlen); -struct Curl_https_rrinfo *Curl_httpsrr_dup_move( - struct Curl_https_rrinfo *rrinfo); - -void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo); +void Curl_httpsrr_destroy(struct Curl_https_rrinfo *rrinfo); /* TRUE if the record is applicable to the transfer and its connection. */ bool Curl_httpsrr_applicable(struct Curl_easy *data, @@ -85,7 +80,7 @@ bool Curl_httpsrr_applicable(struct Curl_easy *data, #ifdef USE_ARES CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, - struct Curl_https_rrinfo *hinfo); + struct Curl_https_rrinfo **phinfo); #endif /* USE_ARES */ #ifdef CURLVERBOSE diff --git a/lib/multi.c b/lib/multi.c index 265a247227..9e3b092e48 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1182,10 +1182,7 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, break; case MSTATE_CONNECTING: - if(data->conn && !data->conn->bits.dns_resolved) - result = Curl_resolv_pollset(data, ps); - if(!result) - result = mstate_connecting_pollset(data, ps); + result = mstate_connecting_pollset(data, ps); break; case MSTATE_PROTOCONNECT: diff --git a/lib/socks.c b/lib/socks.c index ab3663e886..71c7d1bb56 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -105,7 +105,6 @@ struct socks_ctx { uint8_t proxy_type; unsigned char version; BIT(resolve_local); - BIT(start_resolving); BIT(socks4a); }; @@ -318,32 +317,19 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, 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; - result = Curl_cf_dns_insert_after( - cf, data, Curl_resolv_dns_queries(data, sx->ip_version), - sx->dest, TRNSPRT_TCP, TRUE); - if(result) { - failf(data, "unable to create DNS filter for socks"); - return CURLPX_UNKNOWN_FAIL; - } - } - - /* resolve the hostname by connecting the DNS filter */ - result = Curl_conn_cf_connect(cf->next, data, &dns_done); + result = Curl_conn_dns_addr_result(cf->conn, cf->sockindex, sx->dest); if(result) { - failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", - sx->dest->hostname); - return CURLPX_RESOLVE_HOST; - } - else if(!dns_done) + if(result != CURLE_AGAIN) { + failf(data, "error %d resolving SOCKS destination %s:%u", + (int)result, sx->dest->hostname, sx->dest->port); + return CURLPX_RESOLVE_HOST; + } return CURLPX_OK; + } - ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET, 0); + ai = Curl_conn_dns_get_ai(data, sx->dest, cf->sockindex, AF_INET, 0); if(ai) { struct sockaddr_in *saddr_in; char ipbuf[64]; @@ -476,9 +462,7 @@ process_state: case SOCKS4_ST_START: Curl_bufq_reset(&sx->iobuf); - sx->start_resolving = FALSE; sx->socks4a = (sx->proxy_type == CURLPROXY_SOCKS4A); - sx->resolve_local = !sx->socks4a; sx->presult = CURLPX_OK; /* SOCKS4 can only do IPv4, insist! */ @@ -526,7 +510,6 @@ process_state: sxstate(sx, cf, data, SOCKS4_ST_SEND); goto process_state; } - sx->start_resolving = TRUE; sxstate(sx, cf, data, SOCKS4_ST_RESOLVING); FALLTHROUGH(); @@ -835,37 +818,24 @@ static CURLproxycode socks5_resolving(struct socks_ctx *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; - result = Curl_cf_dns_insert_after( - cf, data, Curl_resolv_dns_queries(data, sx->ip_version), - sx->dest, TRNSPRT_TCP, TRUE); - if(result) { - failf(data, "unable to create DNS filter for socks"); - return CURLPX_UNKNOWN_FAIL; - } - } - - /* resolve the hostname by connecting the DNS filter */ - result = Curl_conn_cf_connect(cf->next, data, &dns_done); + result = Curl_conn_dns_addr_result(cf->conn, cf->sockindex, sx->dest); if(result) { - failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", - sx->dest->hostname); - return CURLPX_RESOLVE_HOST; - } - else if(!dns_done) + if(result != CURLE_AGAIN) { + failf(data, "error %d resolving SOCKS destination %s:%u", + (int)result, sx->dest->hostname, sx->dest->port); + return CURLPX_RESOLVE_HOST; + } return CURLPX_OK; + } #ifdef USE_IPV6 if(data->set.ipver != CURL_IPRESOLVE_V4) - ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET6, 0); + ai = Curl_conn_dns_get_ai(data, sx->dest, cf->sockindex, AF_INET6, 0); #endif if(!ai) - ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET, 0); + ai = Curl_conn_dns_get_ai(data, sx->dest, cf->sockindex, AF_INET, 0); if(!ai) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", @@ -1037,7 +1007,6 @@ process_state: switch(sx->state) { case SOCKS_ST_INIT: sx->version = 5; - sx->resolve_local = (sx->proxy_type == CURLPROXY_SOCKS5); sxstate(sx, cf, data, SOCKS5_ST_START); FALLTHROUGH(); @@ -1126,7 +1095,6 @@ process_state: sxstate(sx, cf, data, SOCKS5_ST_REQ1_SEND); goto process_state; } - sx->start_resolving = TRUE; sxstate(sx, cf, data, SOCKS5_ST_RESOLVING); FALLTHROUGH(); @@ -1351,6 +1319,8 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, { struct Curl_cfilter *cf; struct socks_ctx *ctx; + bool resolve_local = FALSE; + uint8_t dns_queries = Curl_resolv_dns_queries(data, ip_version); CURLcode result; if(!dest) @@ -1358,10 +1328,16 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, switch(proxy_type) { case CURLPROXY_SOCKS5: + resolve_local = TRUE; + break; case CURLPROXY_SOCKS5_HOSTNAME: + break; case CURLPROXY_SOCKS4: + resolve_local = TRUE; + dns_queries = (uint8_t)(dns_queries & ~CURL_DNSQ_AAAA); + break; case CURLPROXY_SOCKS4A: - break; /* all supported */ + break; default: failf(data, "unknown proxytype %d option given", proxy_type); return CURLE_COULDNT_CONNECT; @@ -1376,13 +1352,21 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, Curl_peer_link(&ctx->dest, dest); ctx->ip_version = ip_version; ctx->proxy_type = proxy_type; + ctx->resolve_local = resolve_local; Curl_creds_link(&ctx->creds, creds); 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) + if(!result) { Curl_conn_cf_insert_after(cf_at, cf); + if(ctx->resolve_local) { + result = Curl_conn_dns_add_addr_resolve(data, cf_at->conn, + cf_at->sockindex, + ctx->dest, dns_queries, + TRNSPRT_TCP); + } + } else socks_proxy_ctx_free(ctx); return result; diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 4cfd3fbac4..802ffb7d30 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -41,6 +41,7 @@ #include "curlx/dynbuf.h" #include "curlx/fopen.h" #include "cfilters.h" +#include "cf-dns.h" #include "vquic/cf-ngtcp2.h" #include "vquic/cf-ngtcp2-cmn.h" #include "vquic/cf-ngtcp2-proxy.h" @@ -763,19 +764,36 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, } CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, struct Curl_peer *origin, struct Curl_peer *peer) { + CURLcode result; + + (void)data; /* not used in all cases and compilers are stupid */ #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_cf_ngtcp2_insert_after(cf_at, origin, peer); + result = Curl_cf_ngtcp2_insert_after(cf_at, origin, peer); #elif defined(USE_QUICHE) - return Curl_cf_quiche_insert_after(cf_at, origin, peer); + result = Curl_cf_quiche_insert_after(cf_at, origin, peer); #else (void)cf_at; (void)origin; (void)peer; - return CURLE_NOT_BUILT_IN; + result = CURLE_NOT_BUILT_IN; #endif + +#if defined(USE_HTTPSRR) && defined(USE_ECH) + /* When using ECH, kick off the HTTPS-RR resolve */ + if(!result && (origin->scheme->family == CURLPROTO_HTTP) && + CURLECH_ENABLED(data) && + Curl_ssl_supports(data, SSLSUPP_ECH) && + (data->set.tls_ech != CURLECH_GREASE) && + !data->set.str[STRING_ECH_CONFIG]) { + result = Curl_conn_dns_add_https_resolve(data, cf_at->conn, + cf_at->sockindex, origin); + } +#endif /* USE_HTTPSRR && USE_ECH */ + return result; } CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index 5211a9b33a..8674a3001d 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -40,6 +40,7 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, int *qlogfdp); CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, struct Curl_peer *origin, struct Curl_peer *peer); diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 02dbb9f7c7..816912a135 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -46,6 +46,7 @@ #include "urldata.h" #include "cfilters.h" +#include "cf-dns.h" #include "vtls/vtls.h" /* generic SSL protos etc */ #include "vtls/vtls_int.h" @@ -1421,8 +1422,20 @@ CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, result = cf_ssl_create(&cf, data, cf_at->conn); if(!result) result = cf_ssl_peer_init(cf, origin, peer, &cf_at->conn->ssl_config); - if(!result) + if(!result) { Curl_conn_cf_insert_after(cf_at, cf); +#if defined(USE_HTTPSRR) && defined(USE_ECH) + /* When using ECH, kick off the HTTPS-RR resolve */ + if((origin->scheme->family == CURLPROTO_HTTP) && + CURLECH_ENABLED(data) && + Curl_ssl_supports(data, SSLSUPP_ECH) && + (data->set.tls_ech != CURLECH_GREASE) && + !data->set.str[STRING_ECH_CONFIG]) { + result = Curl_conn_dns_add_https_resolve(data, cf->conn, cf->sockindex, + origin); + } +#endif /* USE_HTTPSRR && USE_ECH */ + } else if(cf) Curl_conn_cf_discard_chain(&cf, data); return result; diff --git a/tests/data/test2100 b/tests/data/test2100 index 8793c57865..1bb5361e15 100644 --- a/tests/data/test2100 +++ b/tests/data/test2100 @@ -41,6 +41,7 @@ Funny-head: yesyes http +https # requires Debug so that it can use the DoH server without https @@ -55,7 +56,7 @@ IPv6 HTTP GET using DoH (with HTTPS RR) -http://foo.example.com:%HTTPPORT/%TESTNUMBER --doh-url http://%HOSTIP:%HTTPPORT/%TESTNUMBER0001 +https://foo.example.com:%HTTPSPORT/%TESTNUMBER --insecure --doh-insecure --doh-url https://%HOSTIP:%HTTPSPORT/%TESTNUMBER0001 @@ -71,43 +72,43 @@ s/com\x00\x00(\x1c|\x01)/com-00-00!/g; %if HTTPSRR POST /%TESTNUMBER0001 HTTP/1.1 -Host: %HOSTIP:%HTTPPORT -Accept: */* -Content-Type: application/dns-message -Content-Length: 33 - -%hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1 -Host: %HOSTIP:%HTTPPORT -Accept: */* -Content-Type: application/dns-message -Content-Length: 33 - -%hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1 -Host: %HOSTIP:%HTTPPORT +Host: %HOSTIP:%HTTPSPORT Accept: */* Content-Type: application/dns-message Content-Length: 47 -%hex[%00%00%01%00%00%01%00%00%00%00%00%00%06_%HTTPPORT%06_https%03foo%07example%03com%00%00A%00%01]hex%GET /%TESTNUMBER HTTP/1.1 -Host: foo.example.com:%HTTPPORT -User-Agent: curl/%VERSION -Accept: */* - -%else -POST /%TESTNUMBER0001 HTTP/1.1 -Host: %HOSTIP:%HTTPPORT +%hex[%00%00%01%00%00%01%00%00%00%00%00%00%06_%HTTPSPORT%06_https%03foo%07example%03com%00%00A%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPSPORT Accept: */* Content-Type: application/dns-message Content-Length: 33 %hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1 -Host: %HOSTIP:%HTTPPORT +Host: %HOSTIP:%HTTPSPORT Accept: */* Content-Type: application/dns-message Content-Length: 33 %hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%GET /%TESTNUMBER HTTP/1.1 -Host: foo.example.com:%HTTPPORT +Host: foo.example.com:%HTTPSPORT +User-Agent: curl/%VERSION +Accept: */* + +%else +POST /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPSPORT +Accept: */* +Content-Type: application/dns-message +Content-Length: 33 + +%hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%POST /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPSPORT +Accept: */* +Content-Type: application/dns-message +Content-Length: 33 + +%hex[%00%00%01%00%00%01%00%00%00%00%00%00%03foo%07example%03com-00-00!%00%01]hex%GET /%TESTNUMBER HTTP/1.1 +Host: foo.example.com:%HTTPSPORT User-Agent: curl/%VERSION Accept: */* diff --git a/tests/data/test716 b/tests/data/test716 index 6f692fbd09..266826c8d5 100644 --- a/tests/data/test716 +++ b/tests/data/test716 @@ -30,7 +30,7 @@ SOCKS5 proxy with too long username # it should never connect to the target server -http://hohoho.example.com:99/%TESTNUMBER -x socks5://%repeat[256 x A]%:b@%HOSTIP:%SOCKSPORT +http://localhost:99/%TESTNUMBER -x socks5://%repeat[256 x A]%:b@%HOSTIP:%SOCKSPORT diff --git a/tests/http/test_06_eyeballs.py b/tests/http/test_06_eyeballs.py index d883822d54..8e2104df19 100644 --- a/tests/http/test_06_eyeballs.py +++ b/tests/http/test_06_eyeballs.py @@ -133,7 +133,7 @@ class TestEyeballs: # no immediately failed attempts, as should be he_timers_set = [line for line in r.trace_lines if re.match(r'.*\[TIMER] \[HAPPY_EYEBALLS] set for', line)] - assert len(he_timers_set) == 2, f'found: {"".join(he_timers_set)}\n{r.dump_logs()}' + assert len(he_timers_set) >= 2, f'found: {"".join(he_timers_set)}\n{r.dump_logs()}' # download using HTTP/3 on missing server with alt-svc pointing there @pytest.mark.skipif(condition=not Env.have_h3(), reason="missing HTTP/3 support") diff --git a/tests/http/test_22_httpsrr.py b/tests/http/test_22_httpsrr.py index 51ed3bbd9d..0b95472aa7 100644 --- a/tests/http/test_22_httpsrr.py +++ b/tests/http/test_22_httpsrr.py @@ -129,3 +129,22 @@ class TestHTTPSRR: r.check_exit_code(0) r.check_stats(count=1, http_status=200, exitcode=0) assert r.stats[0]['http_version'] == '2', f'{r}' + + # download https: via https: proxytunnel + @pytest.mark.skipif(condition=not Env.curl_has_feature('HTTPS-proxy'), + reason='curl lacks HTTPS-proxy support') + @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx available") + def test_22_06_httpsrr_proxy(self, env: Env, httpd, nghttpx_fwd, dnsd): + dnsd.set_answers(addr_a=['127.0.0.1'], + https=['10 . alpn=http/1.1']) + run_env = os.environ.copy() + run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' + run_env['CURL_DBG_AWAIT_HTTPSRR'] = '1' + run_env['CURL_QUICK_EXIT'] = '1' + curl = CurlClient(env=env, run_env=run_env) + url = f'https://localhost:{env.https_port}/data.json' + xargs = curl.get_proxy_args(tunnel=True) + r = curl.http_download(urls=[url], alpn_proto='h2', with_stats=True, + extra_args=xargs) + r.check_response(count=1, http_status=200) + assert r.stats[0]['http_version'] == '1.1', f'{r}' diff --git a/tests/unit/unit1607.c b/tests/unit/unit1607.c index d4052b762f..2b55621340 100644 --- a/tests/unit/unit1607.c +++ b/tests/unit/unit1607.c @@ -51,7 +51,7 @@ static CURLcode test_unit1607(const char *arg) /* lowercase host and port to retrieve the addresses from hostcache */ const char *host; - int port; + uint16_t port; /* whether we expect a permanent or non-permanent cache entry */ bool permanent; @@ -113,6 +113,7 @@ static CURLcode test_unit1607(const char *arg) easy = curl_easy_init(); if(!easy) goto error; + curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L); /* create a multi handle and add the easy handle to it so that the hostcache is setup */ @@ -126,7 +127,8 @@ static CURLcode test_unit1607(const char *arg) Curl_loadhostpairs(easy); - entry_id = (void *)curl_maprintf("%s:%d", tests[i].host, tests[i].port); + entry_id = (void *)curl_maprintf("%c%s:%u", CURL_DNST_ADDR, + tests[i].host, tests[i].port); if(!entry_id) goto error; dns = Curl_hash_pick(&multi->dnscache.entries, diff --git a/tests/unit/unit1658.c b/tests/unit/unit1658.c index 49859c69c8..041ced7f29 100644 --- a/tests/unit/unit1658.c +++ b/tests/unit/unit1658.c @@ -544,8 +544,7 @@ static CURLcode test_unit1658(const char *arg) /* free the generated struct */ if(hrr) { - Curl_httpsrr_cleanup(hrr); - curl_free(hrr); + Curl_httpsrr_destroy(hrr); } } curl_easy_cleanup(easy);