lib: make resolving HTTPS DNS records reliable:
No binary (build failed)
—
(log yok)
| @@ -142,6 +142,10 @@ Tracing of SSL Session handling, e.g. caching/import/export. | |
| Tracing of SMTP operations when this protocol is enabled in your build. | |
| + | ## `threads` |
| + | |
| + | Tracing of thread queue and pools, used in threaded DNS resolving. |
| + | |
| ## `timer` | |
| Tracing of timers set for transfers. | |
| @@ -76,7 +76,13 @@ | |
| static int ares_ver = 0; | |
| static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, | |
| - | bool reset_on_null); |
| + | struct Curl_resolv_async *async); |
| + | static CURLcode async_ares_set_dns_interface(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async); |
| + | static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async); |
| + | static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async); |
| /* | |
| * Curl_async_global_init() - the generic low-level asynchronous name | |
| @@ -152,19 +158,19 @@ static CURLcode async_ares_init(struct Curl_easy *data, | |
| goto out; | |
| } | |
| - | rc = async_ares_set_dns_servers(data, FALSE); |
| + | rc = async_ares_set_dns_servers(data, async); |
| if(rc && rc != CURLE_NOT_BUILT_IN) | |
| goto out; | |
| - | rc = Curl_async_ares_set_dns_interface(data); |
| + | rc = async_ares_set_dns_interface(data, async); |
| if(rc && rc != CURLE_NOT_BUILT_IN) | |
| goto out; | |
| - | rc = Curl_async_ares_set_dns_local_ip4(data); |
| + | rc = async_ares_set_dns_local_ip4(data, async); |
| if(rc && rc != CURLE_NOT_BUILT_IN) | |
| goto out; | |
| - | rc = Curl_async_ares_set_dns_local_ip6(data); |
| + | rc = async_ares_set_dns_local_ip6(data, async); |
| if(rc && rc != CURLE_NOT_BUILT_IN) | |
| goto out; | |
| @@ -178,28 +184,6 @@ out: | |
| return rc; | |
| } | |
| - | static CURLcode async_ares_init_lazy(struct Curl_easy *data, |
| - | struct Curl_resolv_async *async) |
| - | { |
| - | struct async_ares_ctx *ares = &async->ares; |
| - | if(!ares->channel) |
| - | return async_ares_init(data, async); |
| - | return CURLE_OK; |
| - | } |
| - | |
| - | CURLcode Curl_async_get_impl(struct Curl_easy *data, |
| - | struct Curl_resolv_async *async, |
| - | void **impl) |
| - | { |
| - | struct async_ares_ctx *ares = &async->ares; |
| - | CURLcode result = CURLE_OK; |
| - | if(!ares->channel) { |
| - | result = async_ares_init(data, async); |
| - | } |
| - | *impl = ares->channel; |
| - | return result; |
| - | } |
| - | |
| /* | |
| * async_ares_cleanup() cleans up async resolver data. | |
| */ | |
| @@ -238,18 +222,21 @@ void Curl_async_ares_destroy(struct Curl_easy *data, | |
| async_ares_cleanup(async); | |
| } | |
| - | /* |
| - | * Curl_async_pollset() is called when someone from the outside world |
| - | * (using curl_multi_fdset()) wants to get our fd_set setup. |
| - | */ |
| - | |
| - | CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps) |
| + | CURLcode Curl_async_pollset(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async, |
| + | struct easy_pollset *ps) |
| { | |
| - | struct Curl_resolv_async *async = data->state.async; |
| - | struct async_ares_ctx *ares = async ? &async->ares : NULL; |
| - | if(ares && ares->channel) |
| - | return Curl_ares_pollset(data, ares->channel, ps); |
| - | return CURLE_OK; |
| + | struct async_ares_ctx *ares = &async->ares; |
| + | CURLcode result = CURLE_OK; |
| + | |
| + | if(ares->channel) { |
| + | result = Curl_ares_pollset(data, ares->channel, ps); |
| + | if(!result) { |
| + | timediff_t ms = Curl_ares_timeout_ms(data, async, ares->channel); |
| + | Curl_expire(data, ms, EXPIRE_ASYNC_NAME); |
| + | } |
| + | } |
| + | return result; |
| } | |
| /* | |
| @@ -276,25 +263,28 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, | |
| goto out; | |
| } | |
| - | if(!ares->num_pending) { |
| + | if(!async->queries_ongoing) { |
| /* 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_entry(data, &ares->temp_ai, |
| - | async->hostname, async->port, |
| - | async->ip_version); |
| + | Curl_dnscache_mk_entry(data, async->dns_queries, &ares->temp_ai, |
| + | async->hostname, async->port); |
| if(!dns) { | |
| result = CURLE_OUT_OF_MEMORY; | |
| goto out; | |
| } | |
| #ifdef HTTPSRR_WORKS | |
| - | { |
| - | struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo); |
| - | if(!lhrr) |
| - | result = CURLE_OUT_OF_MEMORY; |
| + | 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); |
| + | } |
| else | |
| - | dns->hinfo = lhrr; |
| + | Curl_dns_entry_set_https_rr(dns, NULL); |
| } | |
| #endif | |
| if(!result) { | |
| @@ -362,6 +352,27 @@ Curl_async_get_ai(struct Curl_easy *data, | |
| return NULL; | |
| } | |
| + | #ifdef USE_HTTPSRR |
| + | const struct Curl_https_rrinfo * |
| + | Curl_async_get_https(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async) |
| + | { |
| + | if(Curl_async_knows_https(data, async)) |
| + | return &async->ares.hinfo; |
| + | return NULL; |
| + | } |
| + | |
| + | bool Curl_async_knows_https(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async) |
| + | { |
| + | (void)data; |
| + | if(async->dns_queries & CURL_DNSQ_HTTPS) |
| + | return !async->queries_ongoing; |
| + | return TRUE; /* we know it will never come */ |
| + | } |
| + | |
| + | #endif /* USE_HTTPSRR */ |
| + | |
| /* | |
| * Curl_async_await() | |
| * | |
| @@ -373,17 +384,20 @@ Curl_async_get_ai(struct Curl_easy *data, | |
| * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, | |
| * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. | |
| */ | |
| - | CURLcode Curl_async_await(struct Curl_easy *data, |
| - | struct Curl_resolv_async *async, |
| + | CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, |
| struct Curl_dns_entry **pdns) | |
| { | |
| - | struct async_ares_ctx *ares = &async->ares; |
| + | struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); |
| + | struct async_ares_ctx *ares = async ? &async->ares : NULL; |
| struct curltime start = *Curl_pgrs_now(data); | |
| CURLcode result = CURLE_OK; | |
| DEBUGASSERT(pdns); | |
| *pdns = NULL; /* clear on entry */ | |
| + | if(!ares) |
| + | return CURLE_FAILED_INIT; |
| + | |
| /* Wait for the name resolve query to complete or time out. */ | |
| while(!result) { | |
| timediff_t timeout_ms; | |
| @@ -391,8 +405,8 @@ CURLcode Curl_async_await(struct Curl_easy *data, | |
| timeout_ms = Curl_timeleft_ms(data); | |
| if(!timeout_ms) { /* no applicable timeout from `data`*/ | |
| timediff_t elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &start); | |
| - | if(elapsed_ms < (CURL_TIMEOUT_RESOLVE * 1000)) |
| - | timeout_ms = (CURL_TIMEOUT_RESOLVE * 1000) - elapsed_ms; |
| + | if(elapsed_ms < CURL_TIMEOUT_RESOLVE_MS) |
| + | timeout_ms = CURL_TIMEOUT_RESOLVE_MS - elapsed_ms; |
| else | |
| timeout_ms = -1; | |
| } | |
| @@ -503,8 +517,7 @@ async_ares_node2addr(struct ares_addrinfo_node *node) | |
| static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts, | |
| struct ares_addrinfo *ares_ai) | |
| { | |
| - | struct Curl_easy *data = (struct Curl_easy *)user_data; |
| - | struct Curl_resolv_async *async = data->state.async; |
| + | struct Curl_resolv_async *async = user_data; |
| struct async_ares_ctx *ares = async ? &async->ares : NULL; | |
| (void)timeouts; | |
| @@ -516,11 +529,11 @@ static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts, | |
| ares->temp_ai = async_ares_node2addr(ares_ai->nodes); | |
| ares_freeaddrinfo(ares_ai); | |
| } | |
| - | ares->num_pending--; |
| - | CURL_TRC_DNS(data, "ares: addrinfo done, query status=%d, " |
| - | "overall status=%d, pending=%d, addr=%sfound", |
| - | status, ares->ares_status, ares->num_pending, |
| - | ares->temp_ai ? "" : "not "); |
| + | if(async->dns_queries & CURL_DNSQ_A) |
| + | async->dns_responses |= CURL_DNSQ_A; |
| + | if(async->dns_queries & CURL_DNSQ_AAAA) |
| + | async->dns_responses |= CURL_DNSQ_AAAA; |
| + | async->queries_ongoing--; |
| } | |
| #ifdef USE_HTTPSRR | |
| @@ -528,23 +541,17 @@ static void async_ares_rr_done(void *user_data, ares_status_t status, | |
| size_t timeouts, | |
| const ares_dns_record_t *dnsrec) | |
| { | |
| - | struct Curl_easy *data = user_data; |
| - | struct Curl_resolv_async *async = data->state.async; |
| + | struct Curl_resolv_async *async = user_data; |
| struct async_ares_ctx *ares = async ? &async->ares : NULL; | |
| if(!ares) | |
| return; | |
| (void)timeouts; | |
| - | --ares->num_pending; |
| - | CURL_TRC_DNS(data, "ares: httpsrr done, status=%d, pending=%d, " |
| - | "dnsres=%sfound", |
| - | status, ares->num_pending, |
| - | (dnsrec && |
| - | ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)) ? |
| - | "" : "not "); |
| + | async->dns_responses |= CURL_DNSQ_HTTPS; |
| + | async->queries_ongoing--; |
| if((ARES_SUCCESS != status) || !dnsrec) | |
| return; | |
| - | ares->result = Curl_httpsrr_from_ares(data, dnsrec, &ares->hinfo); |
| + | ares->result = Curl_httpsrr_from_ares(dnsrec, &ares->hinfo); |
| } | |
| #endif /* USE_HTTPSRR */ | |
| @@ -557,25 +564,24 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, | |
| struct Curl_resolv_async *async) | |
| { | |
| struct async_ares_ctx *ares = &async->ares; | |
| - | #ifdef USE_HTTPSRR |
| - | char *rrname = NULL; |
| - | #endif |
| - | if(async_ares_init_lazy(data, async)) |
| + | CURL_TRC_DNS(data, "ares getaddrinfo(%s:%u)", async->hostname, async->port); |
| + | if(ares->channel) { |
| + | DEBUGASSERT(0); |
| return CURLE_FAILED_INIT; | |
| - | |
| - | #ifdef USE_HTTPSRR |
| - | if(async->port != 443) { |
| - | rrname = curl_maprintf("_%d._https.%s", async->port, async->hostname); |
| - | if(!rrname) |
| - | return CURLE_OUT_OF_MEMORY; |
| } | |
| - | #endif |
| + | |
| + | if(async_ares_init(data, async)) |
| + | return CURLE_FAILED_INIT; |
| /* initial status - failed */ | |
| ares->ares_status = ARES_ENOTFOUND; | |
| ares->result = CURLE_OK; | |
| + | ares->result = Curl_resolv_announce_start(data, ares->channel); |
| + | if(ares->result) |
| + | return ares->result; |
| + | |
| #if defined(CURLVERBOSE) && ARES_VERSION >= 0x011800 /* >= v1.24.0 */ | |
| if(CURL_TRC_DNS_is_verbose(data)) { | |
| char *csv = ares_get_servers_csv(ares->channel); | |
| @@ -590,12 +596,11 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, | |
| int pf = PF_INET; | |
| memset(&hints, 0, sizeof(hints)); | |
| #ifdef CURLRES_IPV6 | |
| - | if((async->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { |
| - | /* The stack seems to be IPv6-enabled */ |
| - | if(async->ip_version == CURL_IPRESOLVE_V6) |
| - | pf = PF_INET6; |
| - | else |
| + | if(async->dns_queries & CURL_DNSQ_AAAA) { |
| + | if(async->dns_queries & CURL_DNSQ_A) |
| pf = PF_UNSPEC; | |
| + | else |
| + | pf = PF_INET6; |
| } | |
| #endif /* CURLRES_IPV6 */ | |
| CURL_TRC_DNS(data, "asyn-ares: fire off getaddrinfo for %s", | |
| @@ -610,23 +615,28 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, | |
| */ | |
| hints.ai_flags = ARES_AI_NUMERICSERV; | |
| curl_msnprintf(service, sizeof(service), "%d", async->port); | |
| - | ares->num_pending = 1; |
| + | async->queries_ongoing = 1; |
| ares_getaddrinfo(ares->channel, async->hostname, | |
| - | service, &hints, async_ares_addrinfo_cb, data); |
| + | service, &hints, async_ares_addrinfo_cb, async); |
| } | |
| #ifdef USE_HTTPSRR | |
| - | { |
| + | memset(&ares->hinfo, 0, sizeof(ares->hinfo)); |
| + | if(async->dns_queries & CURL_DNSQ_HTTPS) { |
| + | char *rrname = NULL; |
| + | if(async->port != 443) { |
| + | rrname = curl_maprintf("_%d._https.%s", async->port, async->hostname); |
| + | if(!rrname) |
| + | return CURLE_OUT_OF_MEMORY; |
| + | } |
| CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR: %s", | |
| rrname ? rrname : async->hostname); | |
| - | memset(&ares->hinfo, 0, sizeof(ares->hinfo)); |
| - | ares->hinfo.port = -1; |
| ares->hinfo.rrname = rrname; | |
| - | ares->num_pending++; /* one more */ |
| + | async->queries_ongoing++; |
| ares_query_dnsrec(ares->channel, | |
| rrname ? rrname : async->hostname, | |
| ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, | |
| - | async_ares_rr_done, data, NULL); |
| + | async_ares_rr_done, async, NULL); |
| } | |
| #endif | |
| @@ -640,9 +650,8 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, | |
| * 2. When we lazy init the ares channel and NULL means that there | |
| * are no preferences and we do not reset any existing channel. */ | |
| static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, | |
| - | bool reset_on_null) |
| + | struct Curl_resolv_async *async) |
| { | |
| - | struct Curl_resolv_async *async = data->state.async; |
| struct async_ares_ctx *ares = async ? &async->ares : NULL; | |
| CURLcode result = CURLE_NOT_BUILT_IN; | |
| const char *servers = data->set.str[STRING_DNS_SERVERS]; | |
| @@ -653,12 +662,8 @@ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, | |
| servers = getenv("CURL_DNS_SERVER"); | |
| #endif | |
| - | if(!servers) { |
| - | if(reset_on_null) { |
| - | Curl_async_destroy(data); |
| - | } |
| + | if(!servers) |
| return CURLE_OK; | |
| - | } |
| /* if channel is not there, this is a parameter check */ | |
| if(ares && ares->channel) | |
| @@ -681,14 +686,9 @@ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, | |
| return result; | |
| } | |
| - | CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data) |
| + | static CURLcode async_ares_set_dns_interface(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async) |
| { | |
| - | return async_ares_set_dns_servers(data, TRUE); |
| - | } |
| - | |
| - | CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data) |
| - | { |
| - | struct Curl_resolv_async *async = data->state.async; |
| struct async_ares_ctx *ares = async ? &async->ares : NULL; | |
| const char *interf = data->set.str[STRING_DNS_INTERFACE]; | |
| @@ -702,9 +702,9 @@ CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data) | |
| return CURLE_OK; | |
| } | |
| - | CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data) |
| + | static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async) |
| { | |
| - | struct Curl_resolv_async *async = data->state.async; |
| struct async_ares_ctx *ares = async ? &async->ares : NULL; | |
| struct in_addr a4; | |
| const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4]; | |
| @@ -726,10 +726,10 @@ CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data) | |
| return CURLE_OK; | |
| } | |
| - | CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data) |
| + | static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async) |
| { | |
| #ifdef USE_IPV6 | |
| - | struct Curl_resolv_async *async = data->state.async; |
| struct async_ares_ctx *ares = async ? &async->ares : NULL; | |
| unsigned char a6[INET6_ADDRSTRLEN]; | |
| const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6]; | |
| @@ -752,6 +752,7 @@ CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data) | |
| return CURLE_OK; | |
| #else /* no IPv6 support */ | |
| (void)data; | |
| + | (void)async; |
| return CURLE_NOT_BUILT_IN; | |
| #endif | |
| } | |
| @@ -42,9 +42,11 @@ | |
| #endif | |
| #include "urldata.h" | |
| + | #include "connect.h" |
| #include "curl_trc.h" | |
| #include "hostip.h" | |
| #include "multiif.h" | |
| + | #include "progress.h" |
| #include "select.h" | |
| #include "url.h" | |
| @@ -53,6 +55,17 @@ | |
| **********************************************************************/ | |
| #ifdef CURLRES_ASYNCH | |
| + | timediff_t Curl_async_timeleft_ms(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async) |
| + | { |
| + | if(async->timeout_ms) { |
| + | timediff_t elapsed_ms = |
| + | curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); |
| + | return async->timeout_ms - elapsed_ms; |
| + | } |
| + | return Curl_timeleft_ms(data); |
| + | } |
| + | |
| #ifdef USE_ARES | |
| #if ARES_VERSION < 0x011000 | |
| @@ -71,12 +84,8 @@ CURLcode Curl_ares_pollset(struct Curl_easy *data, | |
| ares_channel channel, | |
| struct easy_pollset *ps) | |
| { | |
| - | struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 }; |
| - | struct timeval timebuf; |
| curl_socket_t sockets[16]; /* ARES documented limit */ | |
| unsigned int bitmap, i; | |
| - | struct timeval *timeout; |
| - | timediff_t milli; |
| CURLcode result = CURLE_OK; | |
| DEBUGASSERT(channel); | |
| @@ -97,15 +106,33 @@ CURLcode Curl_ares_pollset(struct Curl_easy *data, | |
| if(result) | |
| return result; | |
| } | |
| - | |
| - | timeout = ares_timeout(channel, &maxtime, &timebuf); |
| - | if(!timeout) |
| - | timeout = &maxtime; |
| - | milli = curlx_tvtoms(timeout); |
| - | Curl_expire(data, milli, EXPIRE_ASYNC_NAME); |
| return result; | |
| } | |
| + | timediff_t Curl_ares_timeout_ms(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async, |
| + | ares_channel channel) |
| + | { |
| + | timediff_t async_timeout_ms; |
| + | |
| + | DEBUGASSERT(channel); |
| + | if(!channel) |
| + | return -1; |
| + | |
| + | async_timeout_ms = Curl_async_timeleft_ms(data, async); |
| + | if((async_timeout_ms > 0) && (async_timeout_ms < INT_MAX)) { |
| + | struct timeval timebuf; |
| + | struct timeval *timeout; |
| + | struct timeval end = { (int)async_timeout_ms / 1000, |
| + | ((int)async_timeout_ms % 1000) * 1000 }; |
| + | |
| + | timeout = ares_timeout(channel, &end, &timebuf); |
| + | if(timeout) |
| + | return curlx_tvtoms(timeout); |
| + | } |
| + | return async_timeout_ms; |
| + | } |
| + | |
| /* | |
| * Curl_ares_perform() | |
| * | |
| @@ -178,36 +205,40 @@ int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms) | |
| #include "doh.h" | |
| - | void Curl_async_shutdown(struct Curl_easy *data) |
| + | void Curl_async_shutdown(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async) |
| { | |
| - | if(data->state.async) { |
| - | CURL_TRC_DNS(data, "shutdown async"); |
| + | if(async) { |
| + | CURL_TRC_DNS(data, "[%u] shutdown async", async->id); |
| + | async->shutdown = TRUE; |
| #ifdef USE_RESOLV_ARES | |
| - | Curl_async_ares_shutdown(data, data->state.async); |
| + | Curl_async_ares_shutdown(data, async); |
| #endif | |
| #ifdef USE_RESOLV_THREADED | |
| - | Curl_async_thrdd_shutdown(data, data->state.async); |
| + | Curl_async_thrdd_shutdown(data, async); |
| #endif | |
| #ifndef CURL_DISABLE_DOH | |
| - | Curl_doh_cleanup(data, data->state.async); |
| + | Curl_doh_cleanup(data, async); |
| #endif | |
| } | |
| } | |
| - | void Curl_async_destroy(struct Curl_easy *data) |
| + | void Curl_async_destroy(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async) |
| { | |
| - | if(data->state.async) { |
| - | CURL_TRC_DNS(data, "destroy async"); |
| + | if(async) { |
| + | CURL_TRC_DNS(data, "[%u] destroy async", async->id); |
| + | async->shutdown = TRUE; |
| #ifdef USE_RESOLV_ARES | |
| - | Curl_async_ares_destroy(data, data->state.async); |
| + | Curl_async_ares_destroy(data, async); |
| #endif | |
| #ifdef USE_RESOLV_THREADED | |
| - | Curl_async_thrdd_destroy(data, data->state.async); |
| + | Curl_async_thrdd_destroy(data, async); |
| #endif | |
| #ifndef CURL_DISABLE_DOH | |
| - | Curl_doh_cleanup(data, data->state.async); |
| + | Curl_doh_cleanup(data, async); |
| #endif | |
| - | Curl_safefree(data->state.async); |
| + | Curl_safefree(async); |
| } | |
| } | |
| @@ -99,16 +99,6 @@ void Curl_async_global_cleanup(void) | |
| #endif | |
| } | |
| - | CURLcode Curl_async_get_impl(struct Curl_easy *data, |
| - | struct Curl_resolv_async *async, |
| - | void **impl) |
| - | { |
| - | (void)data; |
| - | (void)async; |
| - | *impl = NULL; |
| - | return CURLE_OK; |
| - | } |
| - | |
| #ifdef CURLVERBOSE | |
| #define CURL_ASYN_ITEM_DESC_LEN 64 | |
| #define async_item_description(x) (x)->description | |
| @@ -123,10 +113,10 @@ struct async_thrdd_item { | |
| #endif | |
| int sock_error; | |
| uint32_t mid; | |
| - | uint32_t async_id; |
| + | uint32_t resolv_id; |
| uint16_t port; | |
| - | uint8_t ip_version; |
| uint8_t transport; | |
| + | uint8_t dns_queries; |
| #ifdef DEBUGBUILD | |
| uint32_t delay_ms; | |
| uint32_t delay_fail_ms; | |
| @@ -147,13 +137,12 @@ static void async_thrdd_item_destroy(struct async_thrdd_item *item) | |
| /* Initialize context for threaded resolver */ | |
| static struct async_thrdd_item * | |
| async_thrdd_item_create(struct Curl_easy *data, | |
| + | uint32_t resolv_id, uint8_t dns_queries, |
| const char *hostname, uint16_t port, | |
| - | uint8_t ip_version, uint8_t transport, |
| - | uint32_t async_id) |
| + | uint8_t transport) |
| { | |
| size_t hostlen = strlen(hostname); | |
| struct async_thrdd_item *item; | |
| - | VERBOSE(const char *qtype); |
| item = curlx_calloc(1, sizeof(*item) + hostlen); | |
| if(!item) | |
| @@ -161,18 +150,18 @@ async_thrdd_item_create(struct Curl_easy *data, | |
| if(hostlen) /* NUL byte of name already in struct size */ | |
| memcpy(item->hostname, hostname, hostlen); | |
| + | item->mid = data->mid; |
| + | item->resolv_id = resolv_id; |
| + | item->dns_queries = dns_queries; |
| item->port = port; | |
| - | item->ip_version = ip_version; |
| item->transport = transport; | |
| - | item->mid = data->mid; |
| - | item->async_id = async_id; |
| #ifdef CURLVERBOSE | |
| - | qtype = (ip_version == CURL_IPRESOLVE_WHATEVER) ? "A+AAAA": |
| - | ((ip_version == CURL_IPRESOLVE_V6) ? "AAAA" : "A"); |
| curl_msnprintf(item->description, sizeof(item->description), | |
| - | "[%" FMT_OFF_T "/%d] %s %s:%u", |
| - | data->id, item->async_id, qtype, item->hostname, item->port); |
| + | "[%" FMT_OFF_T "/%u] %s %s:%u", |
| + | data->id, item->resolv_id, |
| + | Curl_resolv_query_str(dns_queries), |
| + | item->hostname, item->port); |
| #endif | |
| #ifdef DEBUGBUILD | |
| @@ -205,14 +194,18 @@ static void async_thrdd_rr_done(void *user_data, ares_status_t status, | |
| size_t timeouts, | |
| const ares_dns_record_t *dnsrec) | |
| { | |
| - | struct Curl_easy *data = user_data; |
| - | struct async_thrdd_ctx *thrdd = &data->state.async->thrdd; |
| + | struct Curl_resolv_async *async = user_data; |
| + | struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; |
| (void)timeouts; | |
| - | thrdd->rr.done = TRUE; |
| - | if((ARES_SUCCESS != status) || !dnsrec) |
| + | if(!thrdd) |
| return; | |
| - | thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo); |
| + | |
| + | async->dns_responses |= CURL_DNSQ_HTTPS; |
| + | async->queries_ongoing--; |
| + | async->done = !async->queries_ongoing; |
| + | if((ARES_SUCCESS == status) && dnsrec) |
| + | async->result = Curl_httpsrr_from_ares(dnsrec, &thrdd->rr.hinfo); |
| } | |
| static CURLcode async_rr_start(struct Curl_easy *data, | |
| @@ -246,13 +239,13 @@ static CURLcode async_rr_start(struct Curl_easy *data, | |
| #endif | |
| memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo)); | |
| - | thrdd->rr.hinfo.port = -1; |
| thrdd->rr.hinfo.rrname = rrname; | |
| ares_query_dnsrec(thrdd->rr.channel, | |
| rrname ? rrname : async->hostname, ARES_CLASS_IN, | |
| ARES_REC_TYPE_HTTPS, | |
| - | async_thrdd_rr_done, data, NULL); |
| - | CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s", |
| + | async_thrdd_rr_done, async, NULL); |
| + | async->queries_ongoing++; |
| + | CURL_TRC_DNS(data, "[HTTPS-RR] initiated request for %s", |
| rrname ? rrname : async->hostname); | |
| return CURLE_OK; | |
| } | |
| @@ -275,7 +268,7 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data, | |
| struct Curl_resolv_async *async) | |
| { | |
| (void)data; | |
| - | if(async->thrdd.queued && !async->thrdd.done && |
| + | if(async->queries_ongoing && !async->done && |
| data->multi && data->multi->resolv_thrdq) { | |
| /* Remove any resolve items still queued */ | |
| Curl_thrdq_clear(data->multi->resolv_thrdq, | |
| @@ -298,17 +291,19 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data, | |
| * Waits for a resolve to finish. This function should be avoided since using | |
| * this risk getting the multi interface to "hang". | |
| */ | |
| - | CURLcode Curl_async_await(struct Curl_easy *data, |
| - | struct Curl_resolv_async *async, |
| + | CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, |
| struct Curl_dns_entry **pdns) | |
| { | |
| - | struct async_thrdd_ctx *thrdd = &async->thrdd; |
| + | struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); |
| + | struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; |
| timediff_t milli, ms; | |
| - | CURL_TRC_DNS(data, "[async] await %s", async->hostname); |
| - | while(thrdd->queued && !thrdd->done) { |
| + | if(!thrdd) |
| + | return CURLE_FAILED_INIT; |
| + | |
| + | while(async->queries_ongoing && !async->done) { |
| Curl_async_thrdd_multi_process(data->multi); | |
| - | if(thrdd->done) |
| + | if(async->done) |
| break; | |
| ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); | |
| @@ -320,8 +315,7 @@ CURLcode Curl_async_await(struct Curl_easy *data, | |
| milli = 50; | |
| else | |
| milli = 200; | |
| - | CURL_TRC_DNS(data, "[async] await, waiting %" FMT_TIMEDIFF_T "ms", |
| - | milli); |
| + | CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli); |
| curlx_wait_ms(milli); | |
| } | |
| return Curl_async_take_result(data, async, pdns); | |
| @@ -350,19 +344,23 @@ static void async_thrdd_item_process(void *arg) | |
| memset(&hints, 0, sizeof(hints)); | |
| #ifdef CURLRES_IPV6 | |
| - | if(item->ip_version != CURL_IPRESOLVE_V4) { |
| - | pf = (item->ip_version == CURL_IPRESOLVE_V6) ? PF_INET6 : PF_UNSPEC; |
| + | if(item->dns_queries & CURL_DNSQ_AAAA) { |
| + | pf = (item->dns_queries & CURL_DNSQ_A) ? PF_UNSPEC : PF_INET6; |
| } | |
| #endif | |
| hints.ai_family = pf; | |
| hints.ai_socktype = Curl_socktype_for_transport(item->transport); | |
| hints.ai_protocol = Curl_protocol_for_transport(item->transport); | |
| + | #ifdef __APPLE__ |
| + | /* If we leave `ai_flags == 0` then macOS is looking for IPV4MAPPED |
| + | * when doing AAAA queries. We do not want this "help". */ |
| + | hints.ai_flags = AI_ADDRCONFIG; |
| + | #endif |
| + | |
| + | curl_msnprintf(service, sizeof(service), "%u", item->port); |
| #ifdef AI_NUMERICSERV | |
| - | /* Without service and flags, resolvers might lookup up in more |
| - | * places than we want them to, causing a delay. */ |
| hints.ai_flags |= AI_NUMERICSERV; | |
| #endif | |
| - | curl_msnprintf(service, sizeof(service), "%u", item->port); |
| rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res); | |
| if(rc) { | |
| @@ -433,7 +431,7 @@ CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi, | |
| { | |
| CURLcode result; | |
| DEBUGASSERT(!multi->resolv_thrdq); | |
| - | result = Curl_thrdq_create(&multi->resolv_thrdq, "async", 0, |
| + | result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0, |
| min_threads, max_threads, idle_time_ms, | |
| async_thrdd_item_free, | |
| async_thrdd_item_process, | |
| @@ -471,9 +469,12 @@ static void async_thrdd_report_item(struct Curl_easy *data, | |
| struct dynbuf tmp; | |
| const char *sep = ""; | |
| const struct Curl_addrinfo *ai = item->res; | |
| - | int ai_family = (item->ip_version == CURL_IPRESOLVE_V6) ? AF_INET6 : AF_INET; |
| + | int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET; |
| CURLcode result; | |
| + | if(!Curl_trc_is_verbose(data)) |
| + | return; |
| + | |
| curlx_dyn_init(&tmp, 1024); | |
| for(; ai; ai = ai->ai_next) { | |
| if(ai->ai_family == ai_family) { | |
| @@ -489,7 +490,7 @@ static void async_thrdd_report_item(struct Curl_easy *data, | |
| infof(data, "Host %s:%u resolved IPv%c: %s", | |
| item->hostname, item->port, | |
| - | (item->ip_version == CURL_IPRESOLVE_V6) ? '6' : '4', |
| + | (item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4', |
| (curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)")); | |
| out: | |
| curlx_dyn_free(&tmp); | |
| @@ -507,20 +508,20 @@ void Curl_async_thrdd_multi_process(struct Curl_multi *multi) | |
| while(!Curl_thrdq_recv(multi->resolv_thrdq, &qitem)) { | |
| /* dispatch resolve result */ | |
| struct async_thrdd_item *item = qitem; | |
| + | struct Curl_resolv_async *async = NULL; |
| data = Curl_multi_get_easy(multi, item->mid); | |
| - | /* there is a chance that the original resolve was discarded and |
| - | * either no new, or a new resolve with a different id is ongoing. */ |
| - | if(data && data->conn && data->state.async && |
| - | (data->state.async->id == item->async_id)) { |
| - | struct Curl_resolv_async *async = data->state.async; |
| + | if(data) |
| + | async = Curl_async_get(data, item->resolv_id); |
| + | if(async) { |
| struct async_thrdd_item **pdest = &async->thrdd.res_A; | |
| - | --async->thrdd.queued; |
| - | async->thrdd.done = !async->thrdd.queued; |
| + | async->dns_responses |= item->dns_queries; |
| + | --async->queries_ongoing; |
| + | async->done = !async->queries_ongoing; |
| #ifdef CURLRES_IPV6 | |
| - | if(item->ip_version == CURL_IPRESOLVE_V6) |
| + | if(item->dns_queries & CURL_DNSQ_AAAA) |
| pdest = &async->thrdd.res_AAAA; | |
| #endif | |
| if(!*pdest) { | |
| @@ -535,7 +536,7 @@ void Curl_async_thrdd_multi_process(struct Curl_multi *multi) | |
| async_thrdd_item_free(item); | |
| } | |
| #ifdef CURLVERBOSE | |
| - | Curl_thrdq_trace(multi->resolv_thrdq, multi->admin, &Curl_trc_feat_dns); |
| + | Curl_thrdq_trace(multi->resolv_thrdq, multi->admin); |
| #endif | |
| } | |
| @@ -550,25 +551,25 @@ CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi, | |
| static CURLcode async_thrdd_query(struct Curl_easy *data, | |
| struct Curl_resolv_async *async, | |
| - | uint8_t query_version) |
| + | uint8_t dns_queries) |
| { | |
| struct async_thrdd_item *item; | |
| CURLcode result; | |
| - | item = async_thrdd_item_create(data, async->hostname, async->port, |
| - | query_version, async->transport, |
| - | async->id); |
| + | item = async_thrdd_item_create(data, async->id, dns_queries, |
| + | async->hostname, async->port, |
| + | async->transport); |
| if(!item) { | |
| result = CURLE_OUT_OF_MEMORY; | |
| goto out; | |
| } | |
| - | CURL_TRC_DNS(data, "[async] queueing %s", item->description); |
| + | CURL_TRC_DNS(data, "queueing query %s", item->description); |
| result = Curl_thrdq_send(data->multi->resolv_thrdq, item, | |
| async_item_description(item), async->timeout_ms); | |
| if(result) | |
| goto out; | |
| item = NULL; | |
| - | async->thrdd.queued++; |
| + | async->queries_ongoing++; |
| out: | |
| if(item) | |
| @@ -580,89 +581,87 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, | |
| struct Curl_resolv_async *async) | |
| { | |
| CURLcode result = CURLE_FAILED_INIT; | |
| + | void *resolver = NULL; |
| - | if(async->thrdd.queued || async->thrdd.done) |
| + | if(async->queries_ongoing || async->done) |
| return CURLE_FAILED_INIT; | |
| - | switch(async->ip_version) { |
| - | #ifdef CURL_IPRESOLVE_V6 |
| - | case CURL_IPRESOLVE_V6: |
| - | result = async_thrdd_query(data, async, CURL_IPRESOLVE_V6); |
| - | break; |
| + | #ifdef USE_HTTPSRR_ARES |
| + | DEBUGASSERT(!async->thrdd.rr.channel); |
| + | if((async->dns_queries & CURL_DNSQ_HTTPS) && !async->is_ipaddr) { |
| + | result = async_rr_start(data, async); |
| + | if(result) |
| + | goto out; |
| + | resolver = async->thrdd.rr.channel; |
| + | } |
| #endif | |
| - | case CURL_IPRESOLVE_V4: |
| - | result = async_thrdd_query(data, async, CURL_IPRESOLVE_V4); |
| - | break; |
| - | default: |
| + | |
| + | result = Curl_resolv_announce_start(data, resolver); |
| + | if(result) |
| + | return result; |
| + | |
| #ifdef CURL_IPRESOLVE_V6 | |
| - | /* When resolving IP addresses, some resolvers (e.g. macOS) will |
| - | * happily "embed" an IPv4 address into the IPv6 equivalent. |
| - | * This will then confuse FTP that has been told an IPv4 for |
| - | * DATA, but suddenly sees IPv6. */ |
| - | if(Curl_ipv6works(data) && !Curl_is_ipv4addr(async->hostname)) { |
| - | result = async_thrdd_query(data, async, CURL_IPRESOLVE_V6); |
| - | if(result) |
| - | goto out; |
| - | } |
| + | /* Do not start an AAAA query for an ipv4 address when |
| + | * we will start an A query for it. */ |
| + | if((async->dns_queries & CURL_DNSQ_AAAA) && |
| + | !(async->is_ipv4addr && (async->dns_queries & CURL_DNSQ_A))) { |
| + | result = async_thrdd_query(data, async, CURL_DNSQ_AAAA); |
| + | if(result) |
| + | goto out; |
| + | } |
| #endif | |
| - | result = async_thrdd_query(data, async, CURL_IPRESOLVE_V4); |
| - | break; |
| + | if(async->dns_queries & CURL_DNSQ_A) { |
| + | result = async_thrdd_query(data, async, CURL_DNSQ_A); |
| + | if(result) |
| + | goto out; |
| } | |
| if(result) | |
| goto out; | |
| #ifdef CURLVERBOSE | |
| - | Curl_thrdq_trace(data->multi->resolv_thrdq, data, &Curl_trc_feat_dns); |
| - | #endif |
| - | |
| - | #ifdef USE_HTTPSRR_ARES |
| - | DEBUGASSERT(!async->thrdd.rr.channel); |
| - | if(async_rr_start(data, async)) |
| - | infof(data, "Failed HTTPS RR operation"); |
| + | Curl_thrdq_trace(data->multi->resolv_thrdq, data); |
| #endif | |
| out: | |
| if(result) | |
| - | CURL_TRC_DNS(data, "[async] error queueing %s:%d -> %d", |
| + | CURL_TRC_DNS(data, "error queueing query %s:%d -> %d", |
| async->hostname, async->port, result); | |
| return result; | |
| } | |
| - | CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps) |
| + | CURLcode Curl_async_pollset(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async, |
| + | struct easy_pollset *ps) |
| { | |
| - | struct Curl_resolv_async *async = data->state.async; |
| - | struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; |
| + | timediff_t timeout_ms; |
| - | if(!thrdd) |
| - | return CURLE_OK; |
| + | timeout_ms = Curl_async_timeleft_ms(data, async); |
| #ifdef USE_HTTPSRR_ARES | |
| - | if(thrdd->rr.channel) { |
| - | CURLcode result = Curl_ares_pollset(data, thrdd->rr.channel, ps); |
| + | if(async->thrdd.rr.channel) { |
| + | CURLcode result = Curl_ares_pollset(data, async->thrdd.rr.channel, ps); |
| if(result) | |
| return result; | |
| + | timeout_ms = Curl_ares_timeout_ms(data, async, async->thrdd.rr.channel); |
| } | |
| #else | |
| (void)ps; | |
| #endif | |
| - | if(!thrdd->done) { |
| - | #ifdef ENABLE_WAKEUP |
| - | /* The multi "wakeup" socket pair triggers result processing, |
| - | * no need for an extra timer. */ |
| - | (void)data; |
| - | #else |
| - | timediff_t milli; |
| - | timediff_t ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); |
| - | if(ms < 3) |
| - | milli = 1; |
| - | else if(ms <= 50) |
| - | milli = ms / 3; |
| - | else if(ms <= 250) |
| - | milli = 50; |
| + | if(!async->done) { |
| + | #ifndef ENABLE_WAKEUP |
| + | timediff_t stutter_ms, elapsed_ms; |
| + | elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); |
| + | if(elapsed_ms < 3) |
| + | stutter_ms = 1; |
| + | else if(elapsed_ms <= 50) |
| + | stutter_ms = elapsed_ms / 3; |
| + | else if(elapsed_ms <= 250) |
| + | stutter_ms = 50; |
| else | |
| - | milli = 200; |
| - | Curl_expire(data, milli, EXPIRE_ASYNC_NAME); |
| + | stutter_ms = 200; |
| + | timeout_ms = CURLMIN(stutter_ms, timeout_ms); |
| #endif | |
| + | Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME); |
| } | |
| return CURLE_OK; | |
| } | |
| @@ -677,11 +676,12 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, | |
| struct Curl_dns_entry **pdns) | |
| { | |
| struct async_thrdd_ctx *thrdd = &async->thrdd; | |
| + | struct Curl_dns_entry *dns = NULL; |
| CURLcode result = CURLE_OK; | |
| DEBUGASSERT(pdns); | |
| *pdns = NULL; | |
| - | if(!thrdd->queued && !thrdd->done) { |
| + | if(!async->queries_ongoing && !async->done) { |
| DEBUGASSERT(0); | |
| return CURLE_FAILED_INIT; | |
| } | |
| @@ -692,52 +692,60 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, | |
| (void)Curl_ares_perform(thrdd->rr.channel, 0); | |
| #endif | |
| - | if(!thrdd->done) |
| + | if(!async->done) |
| return CURLE_AGAIN; | |
| Curl_expire_done(data, EXPIRE_ASYNC_NAME); | |
| + | if(async->result) |
| + | goto out; |
| + | |
| if((thrdd->res_A && thrdd->res_A->res) || | |
| (thrdd->res_AAAA && thrdd->res_AAAA->res)) { | |
| - | struct Curl_dns_entry *dns = |
| - | Curl_dnscache_mk_entry2(data, |
| - | thrdd->res_A ? &thrdd->res_A->res : NULL, |
| - | thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL, |
| - | async->hostname, async->port, async->ip_version); |
| - | if(!dns) |
| + | dns = Curl_dnscache_mk_entry2( |
| + | data, async->dns_queries, |
| + | thrdd->res_A ? &thrdd->res_A->res : NULL, |
| + | thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL, |
| + | async->hostname, async->port); |
| + | if(!dns) { |
| result = CURLE_OUT_OF_MEMORY; | |
| + | goto out; |
| + | } |
| #ifdef USE_HTTPSRR_ARES | |
| - | if(!result && thrdd->rr.channel) { |
| - | result = thrdd->rr.result; |
| - | if(!result) { |
| - | struct Curl_https_rrinfo *lhrr; |
| + | 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) |
| + | if(!lhrr) { |
| result = CURLE_OUT_OF_MEMORY; | |
| - | else |
| - | dns->hinfo = lhrr; |
| + | goto out; |
| + | } |
| } | |
| + | Curl_httpsrr_trace(data, lhrr); |
| + | Curl_dns_entry_set_https_rr(dns, lhrr); |
| } | |
| #endif | |
| - | if(!result && dns) { |
| - | CURL_TRC_DNS(data, "[async] resolving complete"); |
| - | *pdns = dns; |
| - | dns = NULL; |
| - | } |
| - | Curl_dns_entry_unlink(data, &dns); |
| } | |
| + | if(dns) { |
| + | CURL_TRC_DNS(data, "resolving complete"); |
| + | *pdns = dns; |
| + | dns = NULL; |
| + | } |
| #ifdef CURLVERBOSE | |
| - | Curl_thrdq_trace(data->multi->resolv_thrdq, data, &Curl_trc_feat_dns); |
| + | Curl_thrdq_trace(data->multi->resolv_thrdq, data); |
| #endif | |
| + | |
| + | out: |
| + | Curl_dns_entry_unlink(data, &dns); |
| + | Curl_async_thrdd_shutdown(data, async); |
| if(!result && !*pdns) | |
| result = Curl_resolver_error(data, NULL); | |
| - | Curl_async_thrdd_shutdown(data, async); |
| if(result && | |
| (result != CURLE_COULDNT_RESOLVE_HOST) && | |
| (result != CURLE_COULDNT_RESOLVE_PROXY)) { | |
| - | CURL_TRC_DNS(data, "[async] %s:%d: error %d", |
| - | async->hostname, async->port, result); |
| + | CURL_TRC_DNS(data, "Error %d resolving %s:%d", |
| + | result, async->hostname, async->port); |
| } | |
| return result; | |
| } | |
| @@ -780,4 +788,30 @@ Curl_async_get_ai(struct Curl_easy *data, | |
| return NULL; | |
| } | |
| + | #ifdef USE_HTTPSRR |
| + | const struct Curl_https_rrinfo * |
| + | Curl_async_get_https(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async) |
| + | { |
| + | #ifdef USE_HTTPSRR_ARES |
| + | if(Curl_async_knows_https(data, async)) |
| + | return &async->thrdd.rr.hinfo; |
| + | #else |
| + | (void)data; |
| + | (void)async; |
| + | #endif |
| + | return NULL; |
| + | } |
| + | |
| + | bool Curl_async_knows_https(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async) |
| + | { |
| + | (void)data; |
| + | if(async->dns_queries & CURL_DNSQ_HTTPS) |
| + | return ((async->dns_responses & CURL_DNSQ_HTTPS) || async->done); |
| + | return TRUE; /* we know it will never come */ |
| + | } |
| + | |
| + | #endif /* USE_HTTPSRR */ |
| + | |
| #endif /* USE_RESOLV_THREADED */ | |
| @@ -33,6 +33,7 @@ struct Curl_easy; | |
| struct Curl_dns_entry; | |
| struct Curl_resolv_async; | |
| struct Curl_multi; | |
| + | struct easy_pollset; |
| #ifdef CURLRES_ASYNCH | |
| @@ -67,49 +68,6 @@ int Curl_async_global_init(void); | |
| */ | |
| void Curl_async_global_cleanup(void); | |
| - | /* |
| - | * Curl_async_get_impl() |
| - | * Get the resolver implementation instance (c-ares channel) or NULL |
| - | * for passing to application callback. |
| - | */ |
| - | CURLcode Curl_async_get_impl(struct Curl_easy *easy, |
| - | struct Curl_resolv_async *async, |
| - | void **impl); |
| - | |
| - | /* Curl_async_pollset() |
| - | * |
| - | * This function is called from the Curl_multi_pollset() function. 'sock' is a |
| - | * pointer to an array to hold the file descriptors, with 'numsock' being the |
| - | * size of that array (in number of entries). This function is supposed to |
| - | * return bitmask indicating what file descriptors (referring to array indexes |
| - | * in the 'sock' array) to wait for, read/write. |
| - | */ |
| - | CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps); |
| - | |
| - | /* |
| - | * Take the result of an async resolve operation. |
| - | * Returns CURLE_OK with `*pdns` != NULL, CURLE_AGAIN while still |
| - | * ongoing or an error code for a failed resolve. |
| - | */ |
| - | CURLcode Curl_async_take_result(struct Curl_easy *data, |
| - | struct Curl_resolv_async *async, |
| - | struct Curl_dns_entry **pdns); |
| - | |
| - | /* |
| - | * Curl_async_await() |
| - | * |
| - | * Waits for a resolve to finish. This function should be avoided since using |
| - | * this risk getting the multi interface to "hang". |
| - | * |
| - | * On return 'entry' is assigned the resolved dns (CURLE_OK or NULL otherwise. |
| - | * |
| - | * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, |
| - | * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. |
| - | */ |
| - | CURLcode Curl_async_await(struct Curl_easy *data, |
| - | struct Curl_resolv_async *async, |
| - | struct Curl_dns_entry **pdns); |
| - | |
| /* | |
| * Curl_async_getaddrinfo() - when using this resolver | |
| * | |
| @@ -129,6 +87,14 @@ Curl_async_get_ai(struct Curl_easy *data, | |
| struct Curl_resolv_async *async, | |
| int ai_family, unsigned int index); | |
| + | #ifdef USE_HTTPSRR |
| + | const struct Curl_https_rrinfo * |
| + | Curl_async_get_https(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async); |
| + | bool Curl_async_knows_https(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async); |
| + | #endif /* USE_HTTPSRR */ |
| + | |
| #ifdef USE_ARES | |
| /* common functions for c-ares and threaded resolver with HTTPSRR */ | |
| #include <ares.h> | |
| @@ -137,6 +103,10 @@ CURLcode Curl_ares_pollset(struct Curl_easy *data, | |
| ares_channel channel, | |
| struct easy_pollset *ps); | |
| + | timediff_t Curl_ares_timeout_ms(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async, |
| + | ares_channel channel); |
| + | |
| int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms); | |
| #endif | |
| @@ -144,7 +114,6 @@ int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms); | |
| /* async resolving implementation using c-ares alone */ | |
| struct async_ares_ctx { | |
| ares_channel channel; | |
| - | int num_pending; /* number of outstanding c-ares requests */ |
| struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares | |
| parts */ | |
| int ares_status; /* ARES_SUCCESS, ARES_ENOTFOUND, etc. */ | |
| @@ -160,18 +129,6 @@ void Curl_async_ares_shutdown(struct Curl_easy *data, | |
| void Curl_async_ares_destroy(struct Curl_easy *data, | |
| struct Curl_resolv_async *async); | |
| - | /* Set the DNS server to use by ares, from `data` settings. */ |
| - | CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data); |
| - | |
| - | /* Set the DNS interfacer to use by ares, from `data` settings. */ |
| - | CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data); |
| - | |
| - | /* Set the local ipv4 address to use by ares, from `data` settings. */ |
| - | CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data); |
| - | |
| - | /* Set the local ipv6 address to use by ares, from `data` settings. */ |
| - | CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data); |
| - | |
| #endif /* USE_RESOLV_ARES */ | |
| #ifdef USE_RESOLV_THREADED | |
| @@ -186,12 +143,8 @@ struct async_thrdd_ctx { | |
| struct { | |
| ares_channel channel; | |
| struct Curl_https_rrinfo hinfo; | |
| - | CURLcode result; |
| - | BIT(done); |
| } rr; | |
| #endif | |
| - | uint32_t queued; |
| - | BIT(done); |
| }; | |
| void Curl_async_thrdd_shutdown(struct Curl_easy *data, | |
| @@ -217,15 +170,50 @@ CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi, | |
| struct doh_probes; | |
| #endif | |
| + | /* |
| + | * Curl_async_await() |
| + | * |
| + | * Waits for a resolve to finish. This function should be avoided since using |
| + | * this risk getting the multi interface to "hang". |
| + | * |
| + | * On return 'entry' is assigned the resolved dns (CURLE_OK or NULL otherwise. |
| + | * |
| + | * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, |
| + | * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. |
| + | */ |
| + | CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, |
| + | struct Curl_dns_entry **pdns); |
| + | |
| + | /* |
| + | * Take the result of an async resolve operation. |
| + | * Returns CURLE_OK with `*pdns` != NULL, CURLE_AGAIN while still |
| + | * ongoing or an error code for a failed resolve. |
| + | */ |
| + | CURLcode Curl_async_take_result(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async, |
| + | struct Curl_dns_entry **pdns); |
| + | |
| + | /* Curl_async_pollset() |
| + | * |
| + | * This function is called from the Curl_multi_pollset() function. 'sock' is a |
| + | * pointer to an array to hold the file descriptors, with 'numsock' being the |
| + | * size of that array (in number of entries). This function is supposed to |
| + | * return bitmask indicating what file descriptors (referring to array indexes |
| + | * in the 'sock' array) to wait for, read/write. |
| + | */ |
| + | CURLcode Curl_async_pollset(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async, |
| + | struct easy_pollset *ps); |
| + | |
| #else /* CURLRES_ASYNCH */ | |
| /* convert these functions if an asynch resolver is not used */ | |
| - | #define Curl_async_get_impl(x, y, z) (*(z) = NULL, CURLE_OK) |
| - | #define Curl_async_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST |
| - | #define Curl_async_await(x, y, z) CURLE_COULDNT_RESOLVE_HOST |
| #define Curl_async_global_init() CURLE_OK | |
| #define Curl_async_global_cleanup() Curl_nop_stmt | |
| #define Curl_async_get_ai(a,b,c,d) NULL | |
| + | #define Curl_async_await(a,b,c) CURLE_COULDNT_RESOLVE_HOST |
| + | #define Curl_async_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST |
| + | #define Curl_async_pollset(x, y, z) CURLE_OK |
| #endif /* !CURLRES_ASYNCH */ | |
| #if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH) | |
| @@ -233,7 +221,9 @@ struct doh_probes; | |
| #endif | |
| #ifdef USE_CURL_ASYNC | |
| + | |
| struct Curl_resolv_async { | |
| + | struct Curl_resolv_async *next; |
| #ifdef USE_RESOLV_ARES | |
| struct async_ares_ctx ares; | |
| #elif defined(USE_RESOLV_THREADED) | |
| @@ -245,31 +235,35 @@ struct Curl_resolv_async { | |
| struct curltime start; | |
| timediff_t interval_end; | |
| timediff_t timeout_ms; | |
| + | CURLcode result; |
| uint32_t poll_interval; | |
| uint32_t id; /* unique id per easy handle of the resolve operation */ | |
| /* what is being resolved */ | |
| uint16_t port; | |
| - | uint8_t ip_version; |
| + | uint8_t dns_queries; /* what queries are being performed */ |
| + | uint8_t dns_responses; /* what queries had responses so far. */ |
| uint8_t transport; | |
| + | uint8_t queries_ongoing; |
| + | BIT(is_ipaddr); |
| + | BIT(is_ipv4addr); |
| + | BIT(done); |
| + | BIT(shutdown); |
| char hostname[1]; | |
| }; | |
| - | /* |
| - | * Curl_async_shutdown(). |
| - | * |
| - | * This shuts down all ongoing operations. |
| - | */ |
| - | void Curl_async_shutdown(struct Curl_easy *data); |
| + | timediff_t Curl_async_timeleft_ms(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async); |
| + | |
| + | /* Shut down the given async resolve. */ |
| + | void Curl_async_shutdown(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async); |
| + | |
| + | /* Frees the resources of the given async resolve and the struct itself. */ |
| + | void Curl_async_destroy(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async); |
| - | /* |
| - | * Curl_async_destroy(). |
| - | * |
| - | * This frees the resources of any async resolve. |
| - | */ |
| - | void Curl_async_destroy(struct Curl_easy *data); |
| #else /* !USE_CURL_ASYNC */ | |
| - | #define Curl_async_shutdown(x) Curl_nop_stmt |
| - | #define Curl_async_destroy(x) Curl_nop_stmt |
| + | #define Curl_async_shutdown(x,y) Curl_nop_stmt |
| #endif /* USE_CURL_ASYNC */ | |
| /********** end of generic resolver interface functions *****************/ | |
| @@ -37,8 +37,9 @@ | |
| struct cf_dns_ctx { | |
| struct Curl_dns_entry *dns; | |
| CURLcode resolv_result; | |
| + | uint32_t resolv_id; |
| uint16_t port; | |
| - | uint8_t ip_version; |
| + | uint8_t dns_queries; |
| uint8_t transport; | |
| BIT(started); | |
| BIT(announced); | |
| @@ -47,9 +48,8 @@ struct cf_dns_ctx { | |
| }; | |
| static struct cf_dns_ctx * | |
| - | cf_dns_ctx_create(struct Curl_easy *data, |
| - | const char *hostname, uint16_t port, |
| - | uint8_t ip_version, uint8_t transport, |
| + | cf_dns_ctx_create(struct Curl_easy *data, uint8_t dns_queries, |
| + | const char *hostname, uint16_t port, uint8_t transport, |
| bool abstract_unix_socket, | |
| struct Curl_dns_entry *dns) | |
| { | |
| @@ -61,7 +61,7 @@ cf_dns_ctx_create(struct Curl_easy *data, | |
| return NULL; | |
| ctx->port = port; | |
| - | ctx->ip_version = ip_version; |
| + | ctx->dns_queries = dns_queries; |
| ctx->transport = transport; | |
| ctx->abstract_unix_socket = abstract_unix_socket; | |
| ctx->dns = Curl_dns_entry_link(data, dns); | |
| @@ -168,8 +168,9 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, | |
| /* Resolve target host right on */ | |
| CURL_TRC_CF(data, cf, "resolve host %s:%u", ctx->hostname, ctx->port); | |
| - | result = Curl_resolv(data, ctx->hostname, ctx->port, ctx->ip_version, |
| - | ctx->transport, timeout_ms, pdns); |
| + | result = Curl_resolv(data, ctx->dns_queries, |
| + | ctx->hostname, ctx->port, ctx->transport, |
| + | timeout_ms, &ctx->resolv_id, pdns); |
| DEBUGASSERT(!result || !*pdns); | |
| if(!result) { /* resolved right away, either sync or from dnscache */ | |
| DEBUGASSERT(*pdns); | |
| @@ -210,9 +211,11 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, | |
| } | |
| if(!ctx->dns && !ctx->resolv_result) { | |
| - | ctx->resolv_result = Curl_resolv_take_result(data, &ctx->dns); |
| + | ctx->resolv_result = |
| + | Curl_resolv_take_result(data, ctx->resolv_id, &ctx->dns); |
| if(!ctx->dns && !ctx->resolv_result) | |
| - | CURL_TRC_CF(data, cf, "DNS resolution not complete yet"); |
| + | CURL_TRC_CF(data, cf, "DNS resolution ongoing for %s:%u", |
| + | ctx->hostname, ctx->port); |
| } | |
| if(ctx->resolv_result) { | |
| @@ -242,7 +245,7 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, | |
| * that one's lock. */ | |
| DEBUGASSERT(*done); | |
| cf->connected = TRUE; | |
| - | Curl_async_shutdown(data); |
| + | Curl_resolv_destroy(data, ctx->resolv_id); |
| Curl_dns_entry_unlink(data, &ctx->dns); | |
| return CURLE_OK; | |
| } | |
| @@ -266,8 +269,14 @@ static CURLcode cf_dns_adjust_pollset(struct Curl_cfilter *cf, | |
| struct Curl_easy *data, | |
| struct easy_pollset *ps) | |
| { | |
| + | #ifdef USE_CURL_ASYNC |
| if(!cf->connected) | |
| return Curl_resolv_pollset(data, ps); | |
| + | #else |
| + | (void)cf; |
| + | (void)data; |
| + | (void)ps; |
| + | #endif |
| return CURLE_OK; | |
| } | |
| @@ -315,9 +324,9 @@ struct Curl_cftype Curl_cft_dns = { | |
| static CURLcode cf_dns_create(struct Curl_cfilter **pcf, | |
| struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport, | |
| bool abstract_unix_socket, | |
| struct Curl_dns_entry *dns) | |
| @@ -327,7 +336,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, | |
| CURLcode result = CURLE_OK; | |
| (void)data; | |
| - | ctx = cf_dns_ctx_create(data, hostname, port, ip_version, transport, |
| + | ctx = cf_dns_ctx_create(data, dns_queries, hostname, port, transport, |
| abstract_unix_socket, dns); | |
| if(!ctx) { | |
| result = CURLE_OUT_OF_MEMORY; | |
| @@ -347,13 +356,13 @@ out: | |
| * out the hostname/path and port where to connect to. */ | |
| static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, | |
| struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| uint8_t transport, | |
| struct Curl_dns_entry *dns) | |
| { | |
| struct connectdata *conn = data->conn; | |
| const char *hostname = NULL; | |
| uint16_t port = 0; | |
| - | uint8_t ip_version = conn->ip_version; |
| bool abstract_unix_socket = FALSE; | |
| #ifdef USE_UNIX_SOCKETS | |
| @@ -391,8 +400,9 @@ static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, | |
| DEBUGASSERT(0); | |
| return CURLE_FAILED_INIT; | |
| } | |
| - | return cf_dns_create(pcf, data, hostname, port, ip_version, |
| - | transport, abstract_unix_socket, dns); |
| + | return cf_dns_create(pcf, data, dns_queries, |
| + | hostname, port, transport, |
| + | abstract_unix_socket, dns); |
| } | |
| /* Adds a "resolv" filter at the top of the connection's filter chain. | |
| @@ -404,6 +414,7 @@ static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, | |
| CURLcode Curl_cf_dns_add(struct Curl_easy *data, | |
| struct connectdata *conn, | |
| int sockindex, | |
| + | uint8_t dns_queries, |
| uint8_t transport, | |
| struct Curl_dns_entry *dns) | |
| { | |
| @@ -412,10 +423,11 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, | |
| DEBUGASSERT(data); | |
| if(sockindex == FIRSTSOCKET) | |
| - | result = cf_dns_conn_create(&cf, data, transport, dns); |
| + | result = cf_dns_conn_create(&cf, data, dns_queries, transport, dns); |
| else if(dns) { | |
| - | result = cf_dns_create(&cf, data, dns->hostname, dns->port, |
| - | dns->ip_version, transport, FALSE, dns); |
| + | result = cf_dns_create(&cf, data, dns_queries, |
| + | dns->hostname, dns->port, transport, |
| + | FALSE, dns); |
| } | |
| else { | |
| DEBUGASSERT(0); | |
| @@ -436,16 +448,17 @@ out: | |
| */ | |
| CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, | |
| struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport) | |
| { | |
| struct Curl_cfilter *cf; | |
| CURLcode result; | |
| - | result = cf_dns_create(&cf, data, hostname, port, ip_version, |
| - | transport, FALSE, NULL); |
| + | result = cf_dns_create(&cf, data, dns_queries, |
| + | hostname, port, transport, |
| + | FALSE, NULL); |
| if(result) | |
| return result; | |
| @@ -515,7 +528,7 @@ Curl_cf_dns_get_ai(struct Curl_cfilter *cf, | |
| else if(ctx->dns) | |
| return cf_dns_get_nth_ai(ctx->dns->addr, ai_family, index); | |
| else | |
| - | return Curl_resolv_get_ai(data, ai_family, index); |
| + | return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index); |
| } | |
| } | |
| return NULL; | |
| @@ -548,9 +561,28 @@ Curl_conn_dns_get_https(struct Curl_easy *data, int sockindex) | |
| for(; cf; cf = cf->next) { | |
| if(cf->cft == &Curl_cft_dns) { | |
| struct cf_dns_ctx *ctx = cf->ctx; | |
| - | return ctx->dns ? ctx->dns->hinfo : NULL; |
| + | if(ctx->dns) |
| + | return ctx->dns->hinfo; |
| + | else |
| + | return Curl_resolv_get_https(data, ctx->resolv_id); |
| } | |
| } | |
| return NULL; | |
| } | |
| + | |
| + | bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex) |
| + | { |
| + | struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; |
| + | for(; cf; cf = cf->next) { |
| + | if(cf->cft == &Curl_cft_dns) { |
| + | struct cf_dns_ctx *ctx = cf->ctx; |
| + | if(ctx->dns) |
| + | return TRUE; |
| + | else |
| + | return Curl_resolv_knows_https(data, ctx->resolv_id); |
| + | } |
| + | } |
| + | return FALSE; |
| + | } |
| + | |
| #endif /* USE_HTTPSRR */ | |
| @@ -33,14 +33,15 @@ struct Curl_addrinfo; | |
| CURLcode Curl_cf_dns_add(struct Curl_easy *data, | |
| struct connectdata *conn, | |
| int sockindex, | |
| + | uint8_t dns_queries, |
| uint8_t transport, | |
| struct Curl_dns_entry *dns); | |
| CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, | |
| struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport); | |
| CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex); | |
| @@ -62,6 +63,10 @@ Curl_cf_dns_get_ai(struct Curl_cfilter *cf, | |
| #ifdef USE_HTTPSRR | |
| const struct Curl_https_rrinfo * | |
| Curl_conn_dns_get_https(struct Curl_easy *data, int sockindex); | |
| + | bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex); |
| + | #else |
| + | #define Curl_conn_dns_get_https(a,b) NULL |
| + | #define Curl_conn_dns_resolved_https(a,b) TRUE |
| #endif | |
| @@ -330,13 +330,6 @@ static CURLcode cf_hc_resolv(struct Curl_cfilter *cf, | |
| * can no longer change that. Any HTTPSRR advice for other hosts and ports | |
| * we need to ignore. */ | |
| const struct Curl_https_rrinfo *rr; | |
| - | bool need_https_rr = FALSE; |
| - | |
| - | if(need_https_rr) { |
| - | result = Curl_conn_dns_result(cf->conn, cf->sockindex); |
| - | if(result) |
| - | return result; |
| - | } |
| /* Do we have HTTPS-RR information? */ | |
| rr = Curl_conn_dns_get_https(data, cf->sockindex); | |
| @@ -346,7 +339,7 @@ static CURLcode cf_hc_resolv(struct Curl_cfilter *cf, | |
| !rr->target[0] || | |
| (rr->target[0] == '.' && | |
| !rr->target[1])) && | |
| - | (rr->port < 0 || /* for same port */ |
| + | (!rr->port_set || /* for same port */ |
| rr->port == cf->conn->remote_port)) { | |
| for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) && | |
| alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) { | |
| @@ -637,14 +637,14 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, | |
| * of the connection. The resolve functions should really be changed | |
| * to take a type parameter instead. | |
| */ | |
| - | uint8_t ip_version = (af == AF_INET) ? |
| - | CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_WHATEVER; |
| + | uint8_t dns_queries = (af == AF_INET) ? |
| + | CURL_DNSQ_A : (CURL_DNSQ_A|CURL_DNSQ_AAAA); |
| #ifdef USE_IPV6 | |
| if(af == AF_INET6) | |
| - | ip_version = CURL_IPRESOLVE_V6; |
| + | dns_queries = CURL_DNSQ_AAAA; |
| #endif | |
| - | (void)Curl_resolv_blocking(data, host, 80, ip_version, transport, &h); |
| + | (void)Curl_resolv_blocking(data, dns_queries, host, 80, transport, &h); |
| if(h) { | |
| int h_af = h->addr->ai_family; | |
| /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ | |
| @@ -557,6 +557,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, | |
| int ssl_mode) | |
| { | |
| CURLcode result = CURLE_OK; | |
| + | uint8_t dns_queries; |
| DEBUGASSERT(data); | |
| DEBUGASSERT(conn->scheme); | |
| @@ -580,7 +581,12 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, | |
| goto out; | |
| } | |
| - | result = Curl_cf_dns_add(data, conn, sockindex, |
| + | 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, dns_queries, |
| conn->transport_wanted, dns); | |
| DEBUGASSERT(conn->cfilter[sockindex]); | |
| out: | |
| @@ -223,6 +223,12 @@ struct curl_trc_feat Curl_trc_feat_timer = { | |
| "TIMER", | |
| CURL_LOG_LVL_NONE, | |
| }; | |
| + | #ifdef USE_THREADS |
| + | struct curl_trc_feat Curl_trc_feat_threads = { |
| + | "THREADS", |
| + | CURL_LOG_LVL_NONE, |
| + | }; |
| + | #endif |
| #endif | |
| #ifndef CURL_DISABLE_VERBOSE_STRINGS | |
| @@ -525,6 +531,9 @@ static struct trc_feat_def trc_feats[] = { | |
| { &Curl_trc_feat_write, TRC_CT_NONE }, | |
| { &Curl_trc_feat_dns, TRC_CT_NETWORK }, | |
| { &Curl_trc_feat_timer, TRC_CT_NETWORK }, | |
| + | #ifdef USE_THREADS |
| + | { &Curl_trc_feat_threads, TRC_CT_NONE }, |
| + | #endif |
| #ifndef CURL_DISABLE_FTP | |
| { &Curl_trc_feat_ftp, TRC_CT_PROTOCOL }, | |
| #endif | |
| @@ -308,6 +308,9 @@ extern struct curl_trc_feat Curl_trc_feat_read; | |
| extern struct curl_trc_feat Curl_trc_feat_write; | |
| extern struct curl_trc_feat Curl_trc_feat_dns; | |
| extern struct curl_trc_feat Curl_trc_feat_timer; | |
| + | #ifdef USE_THREADS |
| + | extern struct curl_trc_feat Curl_trc_feat_threads; |
| + | #endif |
| #endif | |
| #ifndef CURL_DISABLE_VERBOSE_STRINGS | |
| @@ -46,6 +46,7 @@ | |
| #include "curl_trc.h" | |
| #include "dnscache.h" | |
| #include "hash.h" | |
| + | #include "hostip.h" |
| #include "httpsrr.h" | |
| #include "progress.h" | |
| #include "rand.h" | |
| @@ -59,25 +60,6 @@ | |
| #define MAX_DNS_CACHE_SIZE 29999 | |
| - | #ifdef CURLVERBOSE |
| - | static const char *dnscache_ipv_str(uint8_t ip_version) |
| - | { |
| - | switch(ip_version) { |
| - | case CURL_IPRESOLVE_WHATEVER: |
| - | return "A+AAAA"; |
| - | case CURL_IPRESOLVE_V4: |
| - | return "A"; |
| - | #ifdef PF_INET6 |
| - | case CURL_IPRESOLVE_V6: |
| - | return "AAAA"; |
| - | #endif |
| - | default: |
| - | DEBUGASSERT(0); |
| - | return "???"; |
| - | } |
| - | } |
| - | #endif |
| - | |
| static void dnscache_entry_free(struct Curl_dns_entry *dns) | |
| { | |
| Curl_freeaddrinfo(dns->addr); | |
| @@ -229,9 +211,9 @@ void Curl_dnscache_clear(struct Curl_easy *data) | |
| /* lookup address, returns entry if found and not stale */ | |
| static CURLcode fetch_addr(struct Curl_easy *data, | |
| struct Curl_dnscache *dnscache, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| struct Curl_dns_entry **pdns) | |
| { | |
| struct Curl_dns_entry *dns = NULL; | |
| @@ -273,43 +255,17 @@ static CURLcode fetch_addr(struct Curl_easy *data, | |
| } | |
| } | |
| - | if(dns && dns->ip_version != ip_version) { |
| - | switch(dns->ip_version) { |
| - | case CURL_IPRESOLVE_WHATEVER: { |
| - | /* Do we have addresses that match the requested ip version? */ |
| - | int pf = PF_INET; |
| - | bool found = FALSE; |
| - | struct Curl_addrinfo *addr = dns->addr; |
| - | |
| - | #ifdef PF_INET6 |
| - | if(ip_version == CURL_IPRESOLVE_V6) |
| - | pf = PF_INET6; |
| - | #endif |
| - | |
| - | while(addr) { |
| - | if(addr->ai_family == pf) { |
| - | found = TRUE; |
| - | break; |
| - | } |
| - | addr = addr->ai_next; |
| - | } |
| - | |
| - | if(!found) { |
| - | /* We assume that CURL_IPRESOLVE_WHATEVER means we tried to |
| - | * get addresses for all supported types, but there are none |
| - | * for the ip version we need. This is a negative resolve. */ |
| - | CURL_TRC_DNS(data, "cache entry does not have type=%s addresses", |
| - | dnscache_ipv_str(ip_version)); |
| - | dns = NULL; |
| - | result = CURLE_COULDNT_RESOLVE_HOST; |
| - | } |
| - | break; |
| + | if(dns) { |
| + | if((dns->dns_queries & dns_queries) != dns_queries) { |
| + | /* The entry does not cover all wanted DNS queries, a miss. */ |
| + | dns = NULL; |
| } | |
| - | default: |
| - | /* different families, we return NULL + OK, so a new resolve |
| - | * attempt may get started. */ |
| + | else if(!(dns->dns_responses & dns_queries)) { |
| + | /* The entry has no responses for the wanted DNS queries. */ |
| + | CURL_TRC_DNS(data, "cache entry does not have type=%s addresses", |
| + | Curl_resolv_query_str(dns_queries)); |
| dns = NULL; | |
| - | break; |
| + | result = CURLE_COULDNT_RESOLVE_HOST; |
| } | |
| } | |
| @@ -336,9 +292,9 @@ static CURLcode fetch_addr(struct Curl_easy *data, | |
| * use, or we will leak memory! | |
| */ | |
| CURLcode Curl_dnscache_get(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| struct Curl_dns_entry **pentry) | |
| { | |
| struct Curl_dnscache *dnscache = dnscache_get(data); | |
| @@ -346,7 +302,7 @@ CURLcode Curl_dnscache_get(struct Curl_easy *data, | |
| CURLcode result = CURLE_OK; | |
| dnscache_lock(data, dnscache); | |
| - | result = fetch_addr(data, dnscache, hostname, port, ip_version, &dns); |
| + | result = fetch_addr(data, dnscache, dns_queries, hostname, port, &dns); |
| if(!result && dns) | |
| dns->refcount++; /* we pass out a reference */ | |
| else if(result) { | |
| @@ -441,14 +397,24 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, | |
| } | |
| #endif | |
| + | static bool dnscache_ai_has_family(struct Curl_addrinfo *ai, |
| + | int ai_family) |
| + | { |
| + | for(; ai; ai = ai->ai_next) { |
| + | if(ai->ai_family == ai_family) |
| + | return TRUE; |
| + | } |
| + | return FALSE; |
| + | } |
| + | |
| static struct Curl_dns_entry * | |
| dnscache_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, | |
| - | uint8_t ip_version, |
| bool permanent) | |
| { | |
| struct Curl_dns_entry *dns = NULL; | |
| @@ -459,6 +425,11 @@ dnscache_entry_create(struct Curl_easy *data, | |
| 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); |
| + | |
| if(permanent) { | |
| dns->timestamp.tv_sec = 0; /* an entry that never goes stale */ | |
| dns->timestamp.tv_usec = 0; /* an entry that never goes stale */ | |
| @@ -466,10 +437,6 @@ dnscache_entry_create(struct Curl_easy *data, | |
| else { | |
| dns->timestamp = *Curl_pgrs_now(data); | |
| } | |
| - | dns->port = port; |
| - | dns->ip_version = ip_version; |
| - | if(hostlen) |
| - | memcpy(dns->hostname, hostname, hostlen); |
| /* Take the given address lists into the entry */ | |
| if(paddr1 && *paddr1) { | |
| @@ -484,6 +451,16 @@ dnscache_entry_create(struct Curl_easy *data, | |
| *paddr2 = NULL; | |
| } | |
| + | if((dns_queries & CURL_DNSQ_A) && |
| + | dnscache_ai_has_family(dns->addr, PF_INET)) |
| + | dns->dns_responses |= CURL_DNSQ_A; |
| + | |
| + | #ifdef USE_IPV6 |
| + | if((dns_queries & CURL_DNSQ_AAAA) && |
| + | dnscache_ai_has_family(dns->addr, PF_INET6)) |
| + | dns->dns_responses |= CURL_DNSQ_AAAA; |
| + | #endif /* USE_IPV6 */ |
| + | |
| #ifndef CURL_DISABLE_SHUFFLE_DNS | |
| /* shuffle addresses if requested */ | |
| if(data->set.dns_shuffle_addresses && dns->addr) { | |
| @@ -513,36 +490,54 @@ out: | |
| struct Curl_dns_entry * | |
| Curl_dnscache_mk_entry(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| struct Curl_addrinfo **paddr, | |
| const char *hostname, | |
| - | uint16_t port, |
| - | uint8_t ip_version) |
| + | uint16_t port) |
| { | |
| - | return dnscache_entry_create(data, paddr, NULL, hostname, |
| + | return dnscache_entry_create(data, dns_queries, paddr, NULL, hostname, |
| hostname ? strlen(hostname) : 0, | |
| - | port, ip_version, FALSE); |
| + | port, FALSE); |
| } | |
| struct Curl_dns_entry * | |
| Curl_dnscache_mk_entry2(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| struct Curl_addrinfo **paddr1, | |
| struct Curl_addrinfo **paddr2, | |
| const char *hostname, | |
| - | uint16_t port, uint8_t ip_version) |
| + | uint16_t port) |
| { | |
| - | return dnscache_entry_create(data, paddr1, paddr2, hostname, |
| + | return dnscache_entry_create(data, dns_queries, paddr1, paddr2, hostname, |
| hostname ? strlen(hostname) : 0, | |
| - | port, ip_version, FALSE); |
| + | port, FALSE); |
| + | } |
| + | |
| + | #ifdef USE_HTTPSRR |
| + | void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, |
| + | struct Curl_https_rrinfo *hinfo) |
| + | { |
| + | /* 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); |
| + | if(dns->hinfo) { |
| + | Curl_httpsrr_cleanup(dns->hinfo); |
| + | curlx_free(dns->hinfo); |
| + | } |
| + | dns->hinfo = hinfo; |
| + | dns->dns_responses |= CURL_DNSQ_HTTPS; |
| } | |
| + | #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, | |
| - | uint8_t ip_version, |
| bool permanent) | |
| { | |
| char entry_id[MAX_HOSTCACHE_LEN]; | |
| @@ -550,8 +545,8 @@ dnscache_add_addr(struct Curl_easy *data, | |
| struct Curl_dns_entry *dns; | |
| struct Curl_dns_entry *dns2; | |
| - | dns = dnscache_entry_create(data, paddr, NULL, hostname, hlen, port, |
| - | ip_version, permanent); |
| + | dns = dnscache_entry_create(data, dns_queries, paddr, NULL, |
| + | hostname, hlen, port, permanent); |
| if(!dns) | |
| return NULL; | |
| @@ -596,9 +591,9 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data, | |
| } | |
| CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *host, | |
| - | uint16_t port, |
| - | uint8_t ip_version) |
| + | uint16_t port) |
| { | |
| struct Curl_dnscache *dnscache = dnscache_get(data); | |
| struct Curl_dns_entry *dns; | |
| @@ -607,14 +602,14 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, | |
| return CURLE_FAILED_INIT; | |
| /* put this new host in the cache */ | |
| - | dns = dnscache_add_addr(data, dnscache, NULL, host, strlen(host), |
| - | port, ip_version, FALSE); |
| + | dns = dnscache_add_addr(data, dnscache, dns_queries, NULL, |
| + | host, strlen(host), port, FALSE); |
| if(dns) { | |
| /* release the returned reference; the cache itself will keep the | |
| * entry alive: */ | |
| dns->refcount--; | |
| CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s", | |
| - | host, port, dnscache_ipv_str(ip_version)); |
| + | host, port, Curl_resolv_query_str(dns_queries)); |
| return CURLE_OK; | |
| } | |
| return CURLE_OUT_OF_MEMORY; | |
| @@ -846,10 +841,10 @@ err: | |
| Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); | |
| } | |
| - | /* put this new host in the cache */ |
| - | dns = dnscache_add_addr(data, dnscache, &head, curlx_str(&source), |
| - | curlx_strlen(&source), port, |
| - | CURL_IPRESOLVE_WHATEVER, permanent); |
| + | /* put this new host in the cache, an overridy for ALL dns queries */ |
| + | dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL, |
| + | &head, curlx_str(&source), |
| + | curlx_strlen(&source), port, permanent); |
| if(dns) | |
| /* release the returned reference; the cache itself will keep the | |
| * entry alive: */ | |
| @@ -47,7 +47,8 @@ struct Curl_dns_entry { | |
| uint32_t refcount; | |
| /* hostname port number that resolved to addr. */ | |
| uint16_t port; | |
| - | uint8_t ip_version; |
| + | 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). */ | |
| char hostname[1]; | |
| }; | |
| @@ -63,17 +64,23 @@ struct Curl_dns_entry { | |
| */ | |
| struct Curl_dns_entry * | |
| Curl_dnscache_mk_entry(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| struct Curl_addrinfo **paddr, | |
| const char *hostname, | |
| - | uint16_t port, |
| - | uint8_t ip_version); |
| + | uint16_t port); |
| struct Curl_dns_entry * | |
| Curl_dnscache_mk_entry2(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| struct Curl_addrinfo **paddr1, | |
| struct Curl_addrinfo **paddr2, | |
| const char *hostname, | |
| - | uint16_t port, uint8_t ip_version); |
| + | uint16_t port); |
| + | |
| + | #ifdef USE_HTTPSRR |
| + | void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, |
| + | struct Curl_https_rrinfo *hinfo); |
| + | #endif /* USE_HTTPSRR */ |
| /* Increase the ref counter and return it for storing in another place. | |
| * May be called with NULL, in which case it returns NULL. */ | |
| @@ -112,9 +119,9 @@ void Curl_dnscache_clear(struct Curl_easy *data); | |
| * entry was in the cache. | |
| */ | |
| CURLcode Curl_dnscache_get(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| struct Curl_dns_entry **pentry); | |
| /* | |
| @@ -127,9 +134,9 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data, | |
| /* Store a "negative" entry for host:port, e.g. remember that | |
| * it could not be resolved. */ | |
| CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *host, | |
| - | uint16_t port, |
| - | uint8_t ip_version); |
| + | uint16_t port); |
| /* | |
| * Populate the cache with specified entries from CURLOPT_RESOLVE. | |
| @@ -40,6 +40,9 @@ | |
| #define DNS_CLASS_IN 0x01 | |
| + | static void doh_close(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async); |
| + | |
| #ifdef CURLVERBOSE | |
| static const char * const errors[] = { | |
| "", | |
| @@ -216,22 +219,24 @@ static void doh_print_buf(struct Curl_easy *data, | |
| static void doh_probe_done(struct Curl_easy *data, | |
| struct Curl_easy *doh, CURLcode result) | |
| { | |
| - | struct Curl_resolv_async *async = data->state.async; |
| - | struct doh_probes *dohp = async ? async->doh : NULL; |
| + | struct Curl_resolv_async *async = NULL; |
| + | struct doh_probes *dohp = NULL; |
| struct doh_request *doh_req = NULL; | |
| int i; | |
| - | if(!dohp) { |
| + | doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE); |
| + | if(!doh_req) { |
| DEBUGASSERT(0); | |
| return; | |
| } | |
| - | doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE); |
| - | /* A DoH response may arrive for a resolve operation already cancelled. */ |
| - | if(doh_req && (doh_req->async_id != async->id)) { |
| - | CURL_TRC_DNS(data, "ignoring DoH response from a previous resolve"); |
| + | async = Curl_async_get(data, doh_req->resolv_id); |
| + | if(!async) { |
| + | CURL_TRC_DNS(data, "[%u] ignoring outdated DoH response", |
| + | doh_req->resolv_id); |
| return; | |
| } | |
| + | dohp = async->doh; |
| for(i = 0; i < DOH_SLOT_COUNT; ++i) { | |
| if(dohp->probe_resp[i].probe_mid == doh->mid) | |
| @@ -295,7 +300,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, | |
| DNStype dnstype, | |
| const char *host, | |
| const char *url, CURLM *multi, | |
| - | uint32_t async_id, |
| + | uint32_t resolv_id, |
| uint32_t *pmid) | |
| { | |
| struct Curl_easy *doh = NULL; | |
| @@ -309,7 +314,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, | |
| doh_req = curlx_calloc(1, sizeof(*doh_req)); | |
| if(!doh_req) | |
| return CURLE_OUT_OF_MEMORY; | |
| - | doh_req->async_id = async_id; |
| + | doh_req->resolv_id = resolv_id; |
| doh_req->dnstype = dnstype; | |
| curlx_dyn_init(&doh_req->resp_body, DYN_DOH_RESPONSE); | |
| @@ -482,16 +487,18 @@ CURLcode Curl_doh(struct Curl_easy *data, | |
| data->sub_xfer_done = doh_probe_done; | |
| /* create IPv4 DoH request */ | |
| - | result = doh_probe_run(data, CURL_DNS_TYPE_A, |
| - | async->hostname, data->set.str[STRING_DOH], |
| - | data->multi, async->id, |
| - | &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid); |
| - | if(result) |
| - | goto error; |
| - | dohp->pending++; |
| + | if(async->dns_queries & CURL_DNSQ_A) { |
| + | result = doh_probe_run(data, CURL_DNS_TYPE_A, |
| + | async->hostname, data->set.str[STRING_DOH], |
| + | data->multi, async->id, |
| + | &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid); |
| + | if(result) |
| + | goto error; |
| + | dohp->pending++; |
| + | } |
| #ifdef USE_IPV6 | |
| - | if((async->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { |
| + | if(async->dns_queries & CURL_DNSQ_AAAA) { |
| /* create IPv6 DoH request */ | |
| result = doh_probe_run(data, CURL_DNS_TYPE_AAAA, | |
| async->hostname, data->set.str[STRING_DOH], | |
| @@ -504,8 +511,7 @@ CURLcode Curl_doh(struct Curl_easy *data, | |
| #endif | |
| #ifdef USE_HTTPSRR | |
| - | if(conn->scheme->protocol & PROTO_FAMILY_HTTP) { |
| - | /* Only use HTTPS RR for HTTP(S) transfers */ |
| + | if(async->dns_queries & CURL_DNSQ_HTTPS) { |
| char *qname = NULL; | |
| if(async->port != PORT_HTTPS) { | |
| qname = curl_maprintf("_%d._https.%s", async->port, async->hostname); | |
| @@ -1130,6 +1136,7 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, | |
| CURLcode result = CURLE_OUT_OF_MEMORY; | |
| size_t olen; | |
| + | (void)data; |
| *hrr = NULL; | |
| if(len <= 2) | |
| return CURLE_BAD_FUNCTION_ARGUMENT; | |
| @@ -1147,7 +1154,6 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, | |
| result = CURLE_WEIRD_SERVER_REPLY; | |
| goto err; | |
| } | |
| - | lhrr->port = -1; /* until set */ |
| while(len >= 4) { | |
| pcode = doh_get16bit(cp, 0); | |
| plen = doh_get16bit(cp, 2); | |
| @@ -1157,9 +1163,10 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, | |
| result = CURLE_WEIRD_SERVER_REPLY; | |
| goto err; | |
| } | |
| - | result = Curl_httpsrr_set(data, lhrr, pcode, cp, plen); |
| + | result = Curl_httpsrr_set(lhrr, pcode, cp, plen); |
| if(result) | |
| goto err; | |
| + | Curl_httpsrr_trace(data, lhrr); |
| cp += plen; | |
| len -= plen; | |
| expected_min_pcode = pcode + 1; | |
| @@ -1214,10 +1221,10 @@ UNITTEST void doh_print_httpsrr(struct Curl_easy *data, | |
| #endif | |
| CURLcode Curl_doh_take_result(struct Curl_easy *data, | |
| + | struct Curl_resolv_async *async, |
| struct Curl_dns_entry **pdns) | |
| { | |
| - | struct Curl_resolv_async *async = data->state.async; |
| - | struct doh_probes *dohp = async ? async->doh : NULL; |
| + | struct doh_probes *dohp = async->doh; |
| CURLcode result = CURLE_OK; | |
| struct dohentry de; | |
| @@ -1237,7 +1244,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, | |
| memset(rc, 0, sizeof(rc)); | |
| /* remove DoH handles from multi handle and close them */ | |
| - | Curl_doh_close(data); |
| + | doh_close(data, async); |
| /* parse the responses, create the struct and return it! */ | |
| de_init(&de); | |
| for(slot = 0; slot < DOH_SLOT_COUNT; slot++) { | |
| @@ -1268,8 +1275,8 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, | |
| goto error; | |
| /* we got a response, create a dns entry. */ | |
| - | dns = Curl_dnscache_mk_entry(data, &ai, dohp->host, dohp->port, |
| - | async->ip_version); |
| + | dns = Curl_dnscache_mk_entry(data, async->dns_queries, |
| + | &ai, dohp->host, dohp->port); |
| if(!dns) { | |
| result = CURLE_OUT_OF_MEMORY; | |
| goto error; | |
| @@ -1290,9 +1297,10 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data, | |
| #if defined(DEBUGBUILD) && defined(CURLVERBOSE) | |
| doh_print_httpsrr(data, hrr); | |
| #endif | |
| - | dns->hinfo = hrr; |
| + | Curl_dns_entry_set_https_rr(dns, hrr); |
| } | |
| #endif /* USE_HTTPSRR */ | |
| + | |
| /* and add the entry to the cache */ | |
| result = Curl_dnscache_add(data, dns); | |
| *pdns = dns; | |
| @@ -1313,9 +1321,9 @@ error: | |
| return result; | |
| } | |
| - | void Curl_doh_close(struct Curl_easy *data) |
| + | static void doh_close(struct Curl_easy *data, |
| + | struct Curl_resolv_async *async) |
| { | |
| - | struct Curl_resolv_async *async = data->state.async; |
| struct doh_probes *doh = async ? async->doh : NULL; | |
| if(doh && data->multi) { | |
| struct Curl_easy *probe_data; | |
| @@ -1348,7 +1356,7 @@ void Curl_doh_cleanup(struct Curl_easy *data, | |
| struct doh_probes *dohp = async->doh; | |
| if(dohp) { | |
| int i; | |
| - | Curl_doh_close(data); |
| + | doh_close(data, async); |
| for(i = 0; i < DOH_SLOT_COUNT; ++i) { | |
| curlx_dyn_free(&dohp->probe_resp[i].body); | |
| } | |
| @@ -93,7 +93,7 @@ struct doh_request { | |
| struct curl_slist *req_hds; | |
| struct dynbuf resp_body; | |
| size_t req_body_len; | |
| - | uint32_t async_id; /* transfer specific id of the resolve operation */ |
| + | uint32_t resolv_id; /* id of the resolve operation */ |
| DNStype dnstype; | |
| }; | |
| @@ -122,6 +122,7 @@ CURLcode Curl_doh(struct Curl_easy *data, | |
| struct Curl_resolv_async *async); | |
| CURLcode Curl_doh_take_result(struct Curl_easy *data, | |
| + | struct Curl_resolv_async *async, |
| struct Curl_dns_entry **dns); | |
| #define DOH_MAX_ADDR 24 | |
| @@ -163,7 +164,6 @@ struct dohentry { | |
| #endif | |
| }; | |
| - | void Curl_doh_close(struct Curl_easy *data); |
| void Curl_doh_cleanup(struct Curl_easy *data, | |
| struct Curl_resolv_async *async); | |
| #define Curl_doh_wanted(d) (!!(d)->set.doh) | |
| @@ -171,7 +171,7 @@ void Curl_doh_cleanup(struct Curl_easy *data, | |
| #else /* CURL_DISABLE_DOH */ | |
| #define Curl_doh(a, b) NULL | |
| - | #define Curl_doh_take_result(x, y) CURLE_COULDNT_RESOLVE_HOST |
| + | #define Curl_doh_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST |
| #define Curl_doh_wanted(d) FALSE | |
| #endif /* !CURL_DISABLE_DOH */ | |
| @@ -1092,8 +1092,6 @@ void curl_easy_reset(CURL *d) | |
| /* clear all meta data */ | |
| Curl_meta_reset(data); | |
| - | /* clear any resolve data */ |
| - | Curl_async_shutdown(data); |
| /* zero out UserDefined data: */ | |
| Curl_freeset(data); | |
| memset(&data->set, 0, sizeof(struct UserDefined)); | |
| @@ -1062,9 +1062,9 @@ static CURLcode ftp_port_resolve_host(struct Curl_easy *data, | |
| CURLcode result; | |
| *resp = NULL; | |
| - | result = Curl_resolv_blocking(data, host, 0, conn->ip_version, |
| - | Curl_conn_get_transport(data, conn), |
| - | dns_entryp); |
| + | result = Curl_resolv_blocking( |
| + | data, Curl_resolv_dns_queries(data, conn->ip_version), |
| + | host, 0, Curl_conn_get_transport(data, conn), dns_entryp); |
| if(result) | |
| failf(data, "failed to resolve the address provided to PORT: %s", host); | |
| else { | |
| @@ -2162,10 +2162,10 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, | |
| if(result) | |
| goto error; | |
| - | (void)Curl_resolv_blocking(data, host_name, ipquad.remote_port, |
| - | is_ipv6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_V4, |
| - | Curl_conn_get_transport(data, conn), |
| - | &dns); |
| + | (void)Curl_resolv_blocking( |
| + | data, is_ipv6 ? CURL_DNSQ_AAAA : CURL_DNSQ_A, |
| + | host_name, ipquad.remote_port, Curl_conn_get_transport(data, conn), |
| + | &dns); |
| /* we connect to the proxy's port */ | |
| connectport = (unsigned short)ipquad.remote_port; | |
| @@ -2189,9 +2189,9 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, | |
| goto error; | |
| } | |
| - | (void)Curl_resolv_blocking(data, newhost, newport, conn->ip_version, |
| - | Curl_conn_get_transport(data, conn), |
| - | &dns); |
| + | (void)Curl_resolv_blocking( |
| + | data, Curl_resolv_dns_queries(data, conn->ip_version), |
| + | newhost, newport, Curl_conn_get_transport(data, conn), &dns); |
| connectport = newport; /* we connect to the remote port */ | |
| if(!dns) { | |
| @@ -118,6 +118,48 @@ | |
| * | |
| * If the conversion fails, the target buffer is empty. | |
| */ | |
| + | |
| + | uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version) |
| + | { |
| + | (void)data; |
| + | switch(ip_version) { |
| + | case CURL_IPRESOLVE_V6: |
| + | return CURL_DNSQ_AAAA; |
| + | case CURL_IPRESOLVE_V4: |
| + | return CURL_DNSQ_A; |
| + | default: |
| + | if(Curl_ipv6works(data)) |
| + | return (CURL_DNSQ_A|CURL_DNSQ_AAAA); |
| + | else |
| + | return CURL_DNSQ_A; |
| + | } |
| + | } |
| + | |
| + | #ifdef CURLVERBOSE |
| + | const char *Curl_resolv_query_str(uint8_t dns_queries) |
| + | { |
| + | switch(dns_queries) { |
| + | case (CURL_DNSQ_A|CURL_DNSQ_AAAA|CURL_DNSQ_HTTPS): |
| + | return "A+AAAA+HTTPS"; |
| + | case (CURL_DNSQ_A|CURL_DNSQ_AAAA): |
| + | return "A+AAAA"; |
| + | case (CURL_DNSQ_AAAA|CURL_DNSQ_HTTPS): |
| + | return "AAAA+HTTPS"; |
| + | case (CURL_DNSQ_AAAA): |
| + | return "AAAA"; |
| + | case (CURL_DNSQ_A|CURL_DNSQ_HTTPS): |
| + | return "A+HTTPS"; |
| + | case (CURL_DNSQ_A): |
| + | return "A"; |
| + | case (CURL_DNSQ_HTTPS): |
| + | return "HTTPS"; |
| + | default: |
| + | DEBUGASSERT(0); |
| + | return "???"; |
| + | } |
| + | } |
| + | #endif |
| + | |
| void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf, | |
| size_t bufsize) | |
| { | |
| @@ -287,57 +329,85 @@ static bool tailmatch(const char *full, size_t flen, | |
| return curl_strnequal(part, &full[flen - plen], plen); | |
| } | |
| - | static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version) |
| + | static bool can_resolve_dns_queries(struct Curl_easy *data, |
| + | uint8_t dns_queries) |
| { | |
| - | #ifdef CURLRES_IPV6 |
| - | if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data)) |
| - | return FALSE; |
| - | #elif defined(CURLRES_IPV4) |
| (void)data; | |
| - | if(ip_version == CURL_IPRESOLVE_V6) |
| + | if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && !Curl_ipv6works(data)) |
| return FALSE; | |
| - | #else |
| - | #error either CURLRES_IPV6 or CURLRES_IPV4 need to be defined |
| - | #endif |
| return TRUE; | |
| } | |
| + | CURLcode Curl_resolv_announce_start(struct Curl_easy *data, |
| + | void *resolver) |
| + | { |
| + | if(data->set.resolver_start) { |
| + | int rc; |
| + | |
| + | CURL_TRC_DNS(data, "announcing resolve to application"); |
| + | Curl_set_in_callback(data, TRUE); |
| + | rc = data->set.resolver_start(resolver, NULL, |
| + | data->set.resolver_start_client); |
| + | Curl_set_in_callback(data, FALSE); |
| + | if(rc) { |
| + | CURL_TRC_DNS(data, "application aborted resolve"); |
| + | return CURLE_ABORTED_BY_CALLBACK; |
| + | } |
| + | } |
| + | return CURLE_OK; |
| + | } |
| + | |
| #ifdef USE_CURL_ASYNC | |
| - | static CURLcode hostip_async_new(struct Curl_easy *data, |
| - | const char *hostname, |
| - | uint16_t port, |
| - | uint8_t ip_version, |
| - | uint8_t transport, |
| - | timediff_t timeout_ms) |
| + | static struct Curl_resolv_async * |
| + | hostip_async_new(struct Curl_easy *data, |
| + | uint8_t dns_queries, |
| + | const char *hostname, |
| + | uint16_t port, |
| + | uint8_t transport, |
| + | timediff_t timeout_ms) |
| { | |
| struct Curl_resolv_async *async; | |
| size_t hostlen = strlen(hostname); | |
| - | DEBUGASSERT(!data->state.async); |
| + | if(!data->multi) { |
| + | DEBUGASSERT(0); |
| + | return NULL; |
| + | } |
| + | |
| /* struct size already includes the NUL for hostname */ | |
| async = curlx_calloc(1, sizeof(*async) + hostlen); | |
| if(!async) | |
| - | return CURLE_OUT_OF_MEMORY; |
| + | return NULL; |
| - | /* Even if this wraps (unlikely), it will be in time so far apart |
| - | * that it does not matter for all practical purposes. */ |
| - | async->id = data->state.next_async_id++; |
| + | /* Give every async resolve operation a "unique" id. This may |
| + | * wrap around after a long time, making collisions highly unlikely. |
| + | * As we keep the async structs at the easy handle, chances of |
| + | * easy `mid plus resolv->id` colliding should be astronomical. |
| + | * `resolv_id == 0` is never used. */ |
| + | if(data->multi->last_resolv_id == UINT32_MAX) |
| + | data->multi->last_resolv_id = 1; /* wrap around */ |
| + | else |
| + | data->multi->last_resolv_id++; |
| + | async->id = data->multi->last_resolv_id; |
| + | async->dns_queries = dns_queries; |
| async->port = port; | |
| - | async->ip_version = ip_version; |
| async->transport = transport; | |
| async->start = *Curl_pgrs_now(data); | |
| async->timeout_ms = timeout_ms; | |
| - | if(hostlen) |
| + | if(hostlen) { |
| memcpy(async->hostname, hostname, hostlen); | |
| + | async->is_ipaddr = Curl_is_ipaddr(async->hostname); |
| + | if(async->is_ipaddr) |
| + | async->is_ipv4addr = Curl_is_ipv4addr(async->hostname); |
| + | } |
| - | data->state.async = async; |
| - | return CURLE_OK; |
| + | return async; |
| } | |
| static CURLcode hostip_resolv_take_result(struct Curl_easy *data, | |
| + | struct Curl_resolv_async *async, |
| struct Curl_dns_entry **pdns) | |
| { | |
| - | struct Curl_resolv_async *async = data->state.async; |
| CURLcode result; | |
| /* If async resolving is ongoing, this must be set */ | |
| @@ -346,7 +416,7 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data, | |
| #ifndef CURL_DISABLE_DOH | |
| if(data->conn->bits.doh) | |
| - | result = Curl_doh_take_result(data, pdns); |
| + | result = Curl_doh_take_result(data, async, pdns); |
| else | |
| #endif | |
| result = Curl_async_take_result(data, async, pdns); | |
| @@ -362,87 +432,83 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data, | |
| } | |
| const struct Curl_addrinfo * | |
| - | Curl_resolv_get_ai(struct Curl_easy *data, int ai_family, |
| - | unsigned int index) |
| + | Curl_resolv_get_ai(struct Curl_easy *data, uint32_t resolv_id, |
| + | int ai_family, unsigned int index) |
| { | |
| #ifdef CURLRES_ASYNCH | |
| - | struct Curl_resolv_async *async = data->state.async; |
| + | struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); |
| if(async) | |
| return Curl_async_get_ai(data, async, ai_family, index); | |
| #else | |
| (void)data; | |
| + | (void)resolv_id; |
| (void)ai_family; | |
| (void)index; | |
| #endif | |
| return NULL; | |
| } | |
| - | #endif /* USE_CURL_ASYNC */ |
| - | |
| - | static CURLcode hostip_resolv_announce(struct Curl_easy *data, |
| - | const char *hostname, |
| - | uint16_t port, |
| - | uint8_t ip_version, |
| - | uint8_t transport, |
| - | timediff_t timeout_ms) |
| + | #ifdef USE_HTTPSRR |
| + | const struct Curl_https_rrinfo * |
| + | Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id) |
| { | |
| - | if(data->set.resolver_start) { |
| - | void *resolver = NULL; |
| - | int st; |
| #ifdef CURLRES_ASYNCH | |
| - | CURLcode result; |
| - | if(!data->state.async) { |
| - | result = hostip_async_new(data, hostname, port, ip_version, |
| - | transport, timeout_ms); |
| - | if(result) |
| - | return result; |
| - | } |
| + | struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); |
| + | if(async) |
| + | return Curl_async_get_https(data, async); |
| + | #else |
| + | (void)data; |
| + | (void)resolv_id; |
| + | #endif |
| + | return NULL; |
| + | } |
| - | result = Curl_async_get_impl(data, data->state.async, &resolver); |
| - | if(result) |
| - | return result; |
| + | bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id) |
| + | { |
| + | #ifdef CURLRES_ASYNCH |
| + | struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); |
| + | if(async) |
| + | return Curl_async_knows_https(data, async); |
| #else | |
| - | (void)hostname; |
| - | (void)port; |
| - | (void)ip_version; |
| - | (void)timeout_ms; |
| - | (void)transport; |
| + | (void)data; |
| + | (void)resolv_id; |
| #endif | |
| - | Curl_set_in_callback(data, TRUE); |
| - | st = data->set.resolver_start(resolver, NULL, |
| - | data->set.resolver_start_client); |
| - | Curl_set_in_callback(data, FALSE); |
| - | if(st) { |
| - | return CURLE_ABORTED_BY_CALLBACK; |
| - | } |
| - | } |
| - | return CURLE_OK; |
| + | return TRUE; |
| } | |
| + | #endif /* USE_HTTPSRR */ |
| + | |
| + | #endif /* USE_CURL_ASYNC */ |
| + | |
| static CURLcode hostip_resolv_start(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport, | |
| timediff_t timeout_ms, | |
| bool allowDOH, | |
| + | uint32_t *presolv_id, |
| struct Curl_dns_entry **pdns) | |
| { | |
| + | #ifdef USE_CURL_ASYNC |
| + | struct Curl_resolv_async *async = NULL; |
| + | #endif |
| struct Curl_addrinfo *addr = NULL; | |
| size_t hostname_len; | |
| CURLcode result = CURLE_OK; | |
| + | (void)timeout_ms; /* not in all ifdefs */ |
| + | *presolv_id = 0; |
| *pdns = NULL; | |
| - | /* really need to start a resolve operation */ |
| - | result = hostip_resolv_announce(data, hostname, port, ip_version, |
| - | transport, timeout_ms); |
| - | if(result) |
| - | goto out; |
| - | |
| /* Check for "known" things to resolve ourselves. */ | |
| #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) |
| + | goto out; |
| /* shortcut literal IP addresses, if we are not told to resolve them. */ | |
| result = Curl_str2addr(hostname, port, &addr); | |
| goto out; | |
| @@ -454,6 +520,9 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, | |
| 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; | |
| @@ -462,13 +531,18 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, | |
| #ifndef CURL_DISABLE_DOH | |
| if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) { | |
| - | if(!data->state.async) { |
| - | result = hostip_async_new(data, hostname, port, ip_version, |
| - | transport, timeout_ms); |
| - | if(result) |
| + | result = Curl_resolv_announce_start(data, NULL); |
| + | if(result) |
| + | goto out; |
| + | if(!async) { |
| + | async = hostip_async_new(data, dns_queries, hostname, port, |
| + | transport, timeout_ms); |
| + | if(!async) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| goto out; | |
| + | } |
| } | |
| - | result = Curl_doh(data, data->state.async); |
| + | result = Curl_doh(data, async); |
| goto out; | |
| } | |
| #else | |
| @@ -476,30 +550,35 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data, | |
| #endif | |
| /* Can we provide the requested IP specifics in resolving? */ | |
| - | if(!can_resolve_ip_version(data, ip_version)) { |
| + | if(!can_resolve_dns_queries(data, dns_queries)) { |
| result = CURLE_COULDNT_RESOLVE_HOST; | |
| goto out; | |
| } | |
| #ifdef CURLRES_ASYNCH | |
| (void)addr; | |
| - | if(!data->state.async) { |
| - | result = hostip_async_new(data, hostname, port, ip_version, |
| - | transport, timeout_ms); |
| - | if(result) |
| + | if(!async) { |
| + | async = hostip_async_new(data, dns_queries, hostname, port, |
| + | transport, timeout_ms); |
| + | if(!async) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| goto out; | |
| + | } |
| } | |
| - | result = Curl_async_getaddrinfo(data, data->state.async); |
| + | result = Curl_async_getaddrinfo(data, async); |
| if(result == CURLE_AGAIN) { | |
| /* the answer might be there already. Check. */ | |
| - | CURLcode r2 = hostip_resolv_take_result(data, pdns); |
| + | CURLcode r2 = hostip_resolv_take_result(data, async, pdns); |
| if(r2) | |
| result = r2; | |
| else if(*pdns) | |
| result = CURLE_OK; | |
| } | |
| #else | |
| - | addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version, transport); |
| + | result = Curl_resolv_announce_start(data, NULL); |
| + | if(result) |
| + | goto out; |
| + | addr = Curl_sync_getaddrinfo(data, dns_queries, hostname, port, transport); |
| if(!addr) | |
| result = CURLE_COULDNT_RESOLVE_HOST; | |
| #endif | |
| @@ -509,7 +588,7 @@ out: | |
| if(addr) { | |
| /* we got a response, create a dns entry, add to cache, return */ | |
| DEBUGASSERT(!*pdns); | |
| - | *pdns = Curl_dnscache_mk_entry(data, &addr, hostname, port, ip_version); |
| + | *pdns = Curl_dnscache_mk_entry(data, dns_queries, &addr, hostname, port); |
| if(!*pdns) | |
| result = CURLE_OUT_OF_MEMORY; | |
| } | |
| @@ -521,31 +600,39 @@ out: | |
| else if(addr) | |
| Curl_freeaddrinfo(addr); | |
| + | #ifdef USE_CURL_ASYNC |
| + | if(async) { |
| + | if(result == CURLE_AGAIN) { /* still need it, link, return id. */ |
| + | *presolv_id = async->id; |
| + | async->next = data->state.async; |
| + | data->state.async = async; |
| + | } |
| + | else { |
| + | Curl_async_destroy(data, async); |
| + | } |
| + | } |
| + | #endif |
| return result; | |
| } | |
| static CURLcode hostip_resolv(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport, | |
| timediff_t timeout_ms, | |
| bool allowDOH, | |
| + | uint32_t *presolv_id, |
| struct Curl_dns_entry **pdns) | |
| { | |
| size_t hostname_len; | |
| CURLcode result = CURLE_COULDNT_RESOLVE_HOST; | |
| bool cache_dns = FALSE; | |
| + | (void)timeout_ms; /* not used in all ifdefs */ |
| + | *presolv_id = 0; |
| *pdns = NULL; | |
| - | #ifdef USE_CURL_ASYNC |
| - | if(data->state.async) |
| - | Curl_async_destroy(data); |
| - | #else |
| - | (void)timeout_ms; |
| - | #endif |
| - | |
| #ifndef CURL_DISABLE_DOH | |
| data->conn->bits.doh = FALSE; /* default is not */ | |
| #else | |
| @@ -563,9 +650,9 @@ static CURLcode hostip_resolv(struct Curl_easy *data, | |
| } | |
| #ifdef DEBUGBUILD | |
| - | CURL_TRC_DNS(data, "hostip_resolv(%s:%u, ip=%x)", |
| - | hostname, port, ip_version); |
| - | if((ip_version == CURL_IPRESOLVE_V6) && |
| + | 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) && |
| getenv("CURL_DBG_RESOLV_FAIL_IPV6")) { | |
| infof(data, "DEBUG fail ipv6 resolve"); | |
| result = Curl_resolver_error(data, NULL); | |
| @@ -573,7 +660,7 @@ static CURLcode hostip_resolv(struct Curl_easy *data, | |
| } | |
| #endif | |
| /* Let's check our DNS cache first */ | |
| - | result = Curl_dnscache_get(data, hostname, port, ip_version, pdns); |
| + | result = Curl_dnscache_get(data, dns_queries, hostname, port, pdns); |
| if(*pdns) { | |
| infof(data, "Hostname %s was found in DNS cache", hostname); | |
| result = CURLE_OK; | |
| @@ -585,18 +672,18 @@ static CURLcode hostip_resolv(struct Curl_easy *data, | |
| else { | |
| /* No luck, we need to start resolving. */ | |
| cache_dns = TRUE; | |
| - | result = hostip_resolv_start(data, hostname, port, ip_version, |
| - | transport, timeout_ms, allowDOH, pdns); |
| + | result = hostip_resolv_start(data, dns_queries, hostname, port, |
| + | transport, timeout_ms, allowDOH, |
| + | presolv_id, pdns); |
| } | |
| out: | |
| if(result && (result != CURLE_AGAIN)) { | |
| Curl_dns_entry_unlink(data, pdns); | |
| - | Curl_async_shutdown(data); |
| if((result == CURLE_COULDNT_RESOLVE_HOST) || | |
| (result == CURLE_COULDNT_RESOLVE_PROXY)) { | |
| if(cache_dns) | |
| - | Curl_dnscache_add_negative(data, hostname, port, ip_version); |
| + | Curl_dnscache_add_negative(data, dns_queries, hostname, port); |
| failf(data, "Could not resolve: %s:%u", hostname, port); | |
| } | |
| else { | |
| @@ -609,40 +696,39 @@ out: | |
| Curl_dns_entry_unlink(data, pdns); | |
| } | |
| - | CURL_TRC_DNS(data, "hostip_resolv(%s:%u, ip=%x, timeout_ms=%" FMT_TIMEDIFF_T |
| - | ") -> %d, dns %sfound", |
| - | hostname, port, ip_version, timeout_ms, result, |
| - | *pdns ? "" : "not "); |
| return result; | |
| } | |
| CURLcode Curl_resolv_blocking(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport, | |
| struct Curl_dns_entry **pdns) | |
| { | |
| CURLcode result; | |
| + | uint32_t resolv_id; |
| DEBUGASSERT(hostname && *hostname); | |
| *pdns = NULL; | |
| /* We cannot do a blocking resolve using DoH currently */ | |
| - | result = hostip_resolv(data, hostname, port, ip_version, |
| - | transport, 0, FALSE, pdns); |
| + | result = hostip_resolv(data, dns_queries, |
| + | hostname, port, transport, 0, FALSE, |
| + | &resolv_id, pdns); |
| switch(result) { | |
| case CURLE_OK: | |
| DEBUGASSERT(*pdns); | |
| - | return CURLE_OK; |
| + | break; |
| #ifdef USE_CURL_ASYNC | |
| case CURLE_AGAIN: | |
| DEBUGASSERT(!*pdns); | |
| - | if(!data->state.async) |
| - | return CURLE_FAILED_INIT; |
| - | return Curl_async_await(data, data->state.async, pdns); |
| + | result = Curl_async_await(data, resolv_id, pdns); |
| + | Curl_resolv_destroy(data, resolv_id); |
| + | break; |
| #endif | |
| default: | |
| - | return result; |
| + | break; |
| } | |
| + | return result; |
| } | |
| #ifdef USE_ALARM_TIMEOUT | |
| @@ -661,11 +747,12 @@ CURL_NORETURN static void alarmfunc(int sig) | |
| #ifdef USE_ALARM_TIMEOUT | |
| static CURLcode resolv_alarm_timeout(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport, | |
| timediff_t timeout_ms, | |
| + | uint32_t *presolv_id, |
| struct Curl_dns_entry **entry) | |
| { | |
| #ifdef HAVE_SIGACTION | |
| @@ -742,8 +829,8 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data, | |
| /* Perform the actual name resolution. This might be interrupted by an | |
| * alarm if it takes too long. */ | |
| - | result = hostip_resolv(data, hostname, port, ip_version, transport, |
| - | timeout_ms, TRUE, entry); |
| + | result = hostip_resolv(data, dns_queries, hostname, port, transport, |
| + | timeout_ms, FALSE, presolv_id, entry); |
| clean_up: | |
| if(!prev_alarm) | |
| @@ -816,19 +903,23 @@ clean_up: | |
| * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL | |
| */ | |
| CURLcode Curl_resolv(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport, | |
| timediff_t timeout_ms, | |
| + | uint32_t *presolv_id, |
| struct Curl_dns_entry **entry) | |
| { | |
| DEBUGASSERT(hostname && *hostname); | |
| + | *presolv_id = 0; |
| *entry = NULL; | |
| if(timeout_ms < 0) | |
| /* got an already expired timeout */ | |
| return CURLE_OPERATION_TIMEDOUT; | |
| + | else if(!timeout_ms) |
| + | timeout_ms = CURL_TIMEOUT_RESOLVE_MS; |
| #ifdef USE_ALARM_TIMEOUT | |
| if(timeout_ms && data->set.no_signal) { | |
| @@ -836,8 +927,8 @@ CURLcode Curl_resolv(struct Curl_easy *data, | |
| timeout_ms = 0; | |
| } | |
| if(timeout_ms && !Curl_doh_wanted(data)) { | |
| - | return resolv_alarm_timeout(data, hostname, port, ip_version, |
| - | transport, timeout_ms, entry); |
| + | return resolv_alarm_timeout(data, dns_queries, hostname, port, transport, |
| + | timeout_ms, presolv_id, entry); |
| } | |
| #endif /* !USE_ALARM_TIMEOUT */ | |
| @@ -846,16 +937,28 @@ CURLcode Curl_resolv(struct Curl_easy *data, | |
| infof(data, "timeout on name lookup is not supported"); | |
| #endif | |
| - | return hostip_resolv(data, hostname, port, ip_version, transport, |
| - | timeout_ms, TRUE, entry); |
| + | return hostip_resolv(data, dns_queries, hostname, port, transport, |
| + | timeout_ms, TRUE, presolv_id, entry); |
| } | |
| #ifdef USE_CURL_ASYNC | |
| - | CURLcode Curl_resolv_take_result(struct Curl_easy *data, |
| - | struct Curl_dns_entry **pdns) |
| + | |
| + | struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data, |
| + | uint32_t resolv_id) |
| { | |
| struct Curl_resolv_async *async = data->state.async; | |
| + | for(; async; async = async->next) { |
| + | if(async->id == resolv_id) |
| + | return async; |
| + | } |
| + | return NULL; |
| + | } |
| + | |
| + | CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, |
| + | struct Curl_dns_entry **pdns) |
| + | { |
| + | struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); |
| CURLcode result; | |
| /* If async resolving is ongoing, this must be set */ | |
| @@ -863,20 +966,20 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, | |
| return CURLE_FAILED_INIT; | |
| /* check if we have the name resolved by now (from someone else) */ | |
| - | result = Curl_dnscache_get(data, async->hostname, async->port, |
| - | async->ip_version, pdns); |
| + | result = Curl_dnscache_get(data, async->dns_queries, |
| + | async->hostname, async->port, pdns); |
| if(*pdns) { | |
| /* Tell a possibly async resolver we no longer need the results. */ | |
| infof(data, "Hostname '%s' was found in DNS cache", async->hostname); | |
| - | Curl_async_shutdown(data); |
| + | Curl_async_shutdown(data, async); |
| return CURLE_OK; | |
| } | |
| else if(result) { | |
| - | Curl_async_shutdown(data); |
| + | Curl_async_shutdown(data, async); |
| return Curl_resolver_error(data, NULL); | |
| } | |
| - | result = hostip_resolv_take_result(data, pdns); |
| + | result = hostip_resolv_take_result(data, async, pdns); |
| if(*pdns) { | |
| /* Add to cache */ | |
| @@ -886,8 +989,8 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, | |
| } | |
| else if((result == CURLE_COULDNT_RESOLVE_HOST) || | |
| (result == CURLE_COULDNT_RESOLVE_PROXY)) { | |
| - | Curl_dnscache_add_negative(data, async->hostname, |
| - | async->port, async->ip_version); |
| + | Curl_dnscache_add_negative(data, async->dns_queries, |
| + | async->hostname, async->port); |
| failf(data, "Could not resolve: %s:%u", async->hostname, async->port); | |
| } | |
| else if(result) { | |
| @@ -897,26 +1000,57 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, | |
| return result; | |
| } | |
| - | #endif /* USE_CURL_ASYNC */ |
| - | |
| CURLcode Curl_resolv_pollset(struct Curl_easy *data, | |
| struct easy_pollset *ps) | |
| { | |
| - | #ifdef CURLRES_ASYNCH |
| - | #ifndef CURL_DISABLE_DOH |
| - | if(data->conn->bits.doh) |
| - | /* nothing to wait for during DoH resolve, those handles have their own |
| - | sockets */ |
| - | return CURLE_OK; |
| - | #endif |
| - | return Curl_async_pollset(data, ps); |
| - | #else |
| - | (void)data; |
| + | struct Curl_resolv_async *async = data->state.async; |
| + | CURLcode result = CURLE_OK; |
| + | |
| (void)ps; | |
| - | return CURLE_OK; |
| + | for(; async && !result; async = async->next) { |
| + | #ifndef CURL_DISABLE_DOH |
| + | if(async->doh) /* DoH has nothing for the pollset */ |
| + | continue; |
| #endif | |
| + | result = Curl_async_pollset(data, async, ps); |
| + | } |
| + | return result; |
| } | |
| + | void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id) |
| + | { |
| + | struct Curl_resolv_async **panchor = &data->state.async; |
| + | |
| + | for(; *panchor; panchor = &(*panchor)->next) { |
| + | struct Curl_resolv_async *async = *panchor; |
| + | if(async->id == resolv_id) { |
| + | *panchor = async->next; |
| + | Curl_async_destroy(data, async); |
| + | break; |
| + | } |
| + | } |
| + | } |
| + | |
| + | void Curl_resolv_shutdown_all(struct Curl_easy *data) |
| + | { |
| + | struct Curl_resolv_async *async = data->state.async; |
| + | for(; async; async = async->next) { |
| + | Curl_async_shutdown(data, async); |
| + | } |
| + | } |
| + | |
| + | void Curl_resolv_destroy_all(struct Curl_easy *data) |
| + | { |
| + | while(data->state.async) { |
| + | struct Curl_resolv_async *async = data->state.async; |
| + | data->state.async = async->next; |
| + | Curl_async_destroy(data, async); |
| + | } |
| + | } |
| + | |
| + | #endif /* USE_CURL_ASYNC */ |
| + | |
| + | |
| /* | |
| * Curl_resolver_error() calls failf() with the appropriate message after a | |
| * resolve error | |
| @@ -964,7 +1098,7 @@ CURLcode Curl_resolv_unix(struct Curl_easy *data, | |
| return result; | |
| } | |
| - | *pdns = Curl_dnscache_mk_entry(data, &addr, NULL, 0, 0); |
| + | *pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0); |
| return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY; | |
| } | |
| #endif /* USE_UNIX_SOCKETS */ | |
| @@ -35,8 +35,7 @@ | |
| */ | |
| #define CURL_HOSTENT_SIZE 9000 | |
| - | #define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this |
| - | many seconds for a name resolve */ |
| + | #define CURL_TIMEOUT_RESOLVE_MS (300 * 1000) |
| struct addrinfo; | |
| struct hostent; | |
| @@ -47,6 +46,21 @@ struct Curl_https_rrinfo; | |
| struct Curl_multi; | |
| struct Curl_dns_entry; | |
| + | /* DNS query types */ |
| + | #define CURL_DNSQ_A (1U << 0) |
| + | #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)) |
| + | |
| + | #ifdef CURLVERBOSE |
| + | const char *Curl_resolv_query_str(uint8_t dns_queries); |
| + | #endif |
| + | |
| + | /* Return CURL_DNSQ_* bits for the transfer and ip_version. */ |
| + | uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version); |
| + | |
| enum alpnid { | |
| ALPN_none = 0, | |
| ALPN_h1 = CURLALTSVC_H1, | |
| @@ -80,50 +94,79 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port); | |
| void Curl_printable_address(const struct Curl_addrinfo *ip, | |
| char *buf, size_t bufsize); | |
| - | /* |
| - | * Curl_resolv() returns an entry with the info for the specified host |
| - | * and port. |
| - | * |
| - | * The returned data *MUST* be "released" with Curl_dns_entry_unlink() after |
| - | * use, or we will leak memory! |
| + | /* Start DNS resolving for the given parameters. 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(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport, | |
| timediff_t timeout_ms, | |
| + | uint32_t *presolv_id, |
| struct Curl_dns_entry **pdns); | |
| CURLcode Curl_resolv_blocking(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport, | |
| struct Curl_dns_entry **pdns); | |
| - | CURLcode Curl_resolv_timeout(struct Curl_easy *data, |
| - | const char *hostname, int port, |
| - | int ip_version, |
| - | struct Curl_dns_entry **entry, |
| - | timediff_t timeoutms); |
| + | /* Announce start of a resolve operation to application callback, |
| + | * passing the resolver implementation (maybe NULL). */ |
| + | CURLcode Curl_resolv_announce_start(struct Curl_easy *data, |
| + | void *resolver); |
| #ifdef USE_CURL_ASYNC | |
| - | CURLcode Curl_resolv_take_result(struct Curl_easy *data, |
| - | struct Curl_dns_entry **pdns); |
| - | const struct Curl_addrinfo * |
| - | Curl_resolv_get_ai(struct Curl_easy *data, int ai_family, |
| - | unsigned int index); |
| - | #else |
| - | #define Curl_resolv_take_result(x, y) CURLE_NOT_BUILT_IN |
| - | #define Curl_resolv_get_ai(x,y,z) NULL |
| - | #endif |
| CURLcode Curl_resolv_pollset(struct Curl_easy *data, | |
| struct easy_pollset *ps); | |
| + | /* Get the `async` struct for the given `resolv_id`, if it exists. */ |
| + | struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data, |
| + | uint32_t resolv_id); |
| + | |
| + | /* Shut down all resolves of the given easy handle. */ |
| + | void Curl_resolv_shutdown_all(struct Curl_easy *data); |
| + | |
| + | /* Destroy all resolve resources of the given easy handle. */ |
| + | void Curl_resolv_destroy_all(struct Curl_easy *data); |
| + | |
| + | CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, |
| + | struct Curl_dns_entry **pdns); |
| + | |
| + | void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id); |
| + | |
| + | const struct Curl_addrinfo * |
| + | Curl_resolv_get_ai(struct Curl_easy *data, uint32_t resolv_id, |
| + | int ai_family, unsigned int index); |
| + | #ifdef USE_HTTPSRR |
| + | 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); |
| + | #endif /* USE_HTTPSRR */ |
| + | |
| + | #else /* USE_CURL_ASYNC */ |
| + | #define Curl_resolv_shutdown_all(x) Curl_nop_stmt |
| + | #define Curl_resolv_destroy_all(x) Curl_nop_stmt |
| + | #define Curl_resolv_take_result(x, y, z) CURLE_NOT_BUILT_IN |
| + | #define Curl_resolv_get_ai(x,y,z, a) NULL |
| + | #define Curl_resolv_get_https(x,y) NULL |
| + | #define Curl_resolv_knows_https(x,y) TRUE |
| + | #define Curl_resolv_pollset(x,y) CURLE_OK |
| + | #define Curl_resolv_destroy(x,y) Curl_nop_stmt |
| + | #endif /* USE_CURL_ASYNC, else */ |
| + | |
| + | |
| CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail); | |
| + | |
| + | |
| #ifdef CURLRES_SYNCH | |
| /* | |
| * Curl_sync_getaddrinfo() is the non-async low-level name resolve API. | |
| @@ -131,9 +174,9 @@ CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail); | |
| * support and platform. | |
| */ | |
| struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport); | |
| #endif | |
| @@ -68,14 +68,14 @@ | |
| * | |
| */ | |
| struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport) | |
| { | |
| struct Curl_addrinfo *ai = NULL; | |
| - | (void)ip_version; |
| + | (void)dns_queries; |
| (void)transport; | |
| ai = Curl_ipv4_resolve_r(hostname, port); | |
| @@ -63,9 +63,9 @@ | |
| * Curl_freeaddrinfo(), nothing else. | |
| */ | |
| struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, | |
| + | uint8_t dns_queries, |
| const char *hostname, | |
| uint16_t port, | |
| - | uint8_t ip_version, |
| uint8_t transport) | |
| { | |
| struct addrinfo hints; | |
| @@ -78,8 +78,7 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, | |
| #endif | |
| int pf = PF_INET; | |
| - | if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) |
| - | /* The stack seems to be IPv6-enabled */ |
| + | if(dns_queries & CURL_DNSQ_AAAA) |
| pf = PF_UNSPEC; | |
| memset(&hints, 0, sizeof(hints)); | |
| @@ -30,6 +30,7 @@ | |
| #include "connect.h" | |
| #include "curl_trc.h" | |
| #include "curlx/strdup.h" | |
| + | #include "curlx/inet_ntop.h" |
| static CURLcode httpsrr_decode_alpn(const uint8_t *cp, size_t len, | |
| unsigned char *alpns) | |
| @@ -68,25 +69,90 @@ static CURLcode httpsrr_decode_alpn(const uint8_t *cp, size_t len, | |
| return CURLE_OK; | |
| } | |
| - | CURLcode Curl_httpsrr_set(struct Curl_easy *data, |
| - | struct Curl_https_rrinfo *hi, |
| + | #ifdef CURLVERBOSE |
| + | static void httpsrr_report_addr(struct Curl_easy *data, int ai_family, |
| + | const uint8_t *addr, size_t total_len) |
| + | { |
| + | char buf[MAX_IPADR_LEN]; |
| + | struct dynbuf tmp; |
| + | size_t i, alen = (ai_family == AF_INET6) ? 16 : 4; |
| + | const char *sep = ""; |
| + | bool incomplete = FALSE; |
| + | CURLcode result; |
| + | |
| + | if(!CURL_TRC_DNS_is_verbose(data)) |
| + | return; |
| + | |
| + | curlx_dyn_init(&tmp, 1024); |
| + | for(i = 0; i < (total_len / alen); ++i) { |
| + | if(!curlx_inet_ntop(ai_family, addr + (i * alen), buf, sizeof(buf))) { |
| + | CURL_TRC_DNS(data, "[HTTPS-RR] error parsing address #%zu", i); |
| + | goto out; |
| + | } |
| + | result = curlx_dyn_addf(&tmp, "%s%s", sep, buf); |
| + | if(result) { |
| + | incomplete = TRUE; |
| + | break; |
| + | } |
| + | sep = ", "; |
| + | } |
| + | |
| + | CURL_TRC_DNS(data, "[HTTPS-RR] IPv%d: %s%s", |
| + | (ai_family == AF_INET6) ? 6 : 4, |
| + | curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)", |
| + | incomplete ? " ..." : ""); |
| + | out: |
| + | curlx_dyn_free(&tmp); |
| + | } |
| + | |
| + | void Curl_httpsrr_trace(struct Curl_easy *data, |
| + | struct Curl_https_rrinfo *hi) |
| + | { |
| + | if(!hi || !hi->complete) { |
| + | CURL_TRC_DNS(data, "[HTTPS-RR] not available"); |
| + | return; |
| + | } |
| + | |
| + | if(hi->target) |
| + | CURL_TRC_DNS(data, "[HTTPS-RR] target: %s", hi->target); |
| + | if(hi->priority) |
| + | CURL_TRC_DNS(data, "[HTTPS-RR] priority: %u", hi->priority); |
| + | if(hi->mandatory) |
| + | CURL_TRC_DNS(data, "[HTTPS-RR] MANDATORY present, but not supported"); |
| + | if(hi->alpns[0]) |
| + | CURL_TRC_DNS(data, "[HTTPS-RR] ALPN: %u %u %u %u", |
| + | hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]); |
| + | if(hi->port_set) |
| + | CURL_TRC_DNS(data, "[HTTPS-RR] port %u", hi->port); |
| + | if(hi->no_def_alpn) |
| + | CURL_TRC_DNS(data, "[HTTPS-RR] no-def-alpn"); |
| + | if(hi->ipv6hints_len) |
| + | httpsrr_report_addr(data, AF_INET6, hi->ipv6hints, hi->ipv6hints_len); |
| + | if(hi->ipv4hints_len) |
| + | httpsrr_report_addr(data, AF_INET, hi->ipv4hints, hi->ipv4hints_len); |
| + | if(hi->echconfiglist_len) |
| + | CURL_TRC_DNS(data, "[HTTPS-RR] ECH"); |
| + | } |
| + | |
| + | #else |
| + | #define httpsrr_report_addr(a,b,c,d) Curl_nop_stmt |
| + | #endif /* CURLVERBOSE */ |
| + | |
| + | CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *hi, |
| uint16_t rrkey, const uint8_t *val, size_t vlen) | |
| { | |
| CURLcode result = CURLE_OK; | |
| switch(rrkey) { | |
| case HTTPS_RR_CODE_MANDATORY: | |
| - | CURL_TRC_DNS(data, "HTTPS RR MANDATORY left to implement"); |
| + | hi->mandatory = TRUE; |
| break; | |
| case HTTPS_RR_CODE_ALPN: /* str_list */ | |
| result = httpsrr_decode_alpn(val, vlen, hi->alpns); | |
| - | CURL_TRC_DNS(data, "HTTPS RR ALPN: %u %u %u %u", |
| - | hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]); |
| break; | |
| case HTTPS_RR_CODE_NO_DEF_ALPN: | |
| if(vlen) /* no data */ | |
| return CURLE_BAD_FUNCTION_ARGUMENT; | |
| hi->no_def_alpn = TRUE; | |
| - | CURL_TRC_DNS(data, "HTTPS RR no-def-alpn"); |
| break; | |
| case HTTPS_RR_CODE_IPV4: /* addr4 list */ | |
| if(!vlen || (vlen & 3)) /* the size must be 4-byte aligned */ | |
| @@ -96,7 +162,6 @@ CURLcode Curl_httpsrr_set(struct Curl_easy *data, | |
| if(!hi->ipv4hints) | |
| return CURLE_OUT_OF_MEMORY; | |
| hi->ipv4hints_len = vlen; | |
| - | CURL_TRC_DNS(data, "HTTPS RR IPv4"); |
| break; | |
| case HTTPS_RR_CODE_ECH: | |
| if(!vlen) | |
| @@ -106,7 +171,6 @@ CURLcode Curl_httpsrr_set(struct Curl_easy *data, | |
| if(!hi->echconfiglist) | |
| return CURLE_OUT_OF_MEMORY; | |
| hi->echconfiglist_len = vlen; | |
| - | CURL_TRC_DNS(data, "HTTPS RR ECH"); |
| break; | |
| case HTTPS_RR_CODE_IPV6: /* addr6 list */ | |
| if(!vlen || (vlen & 15)) /* the size must be 16-byte aligned */ | |
| @@ -116,16 +180,15 @@ CURLcode Curl_httpsrr_set(struct Curl_easy *data, | |
| if(!hi->ipv6hints) | |
| return CURLE_OUT_OF_MEMORY; | |
| hi->ipv6hints_len = vlen; | |
| - | CURL_TRC_DNS(data, "HTTPS RR IPv6"); |
| break; | |
| case HTTPS_RR_CODE_PORT: | |
| if(vlen != 2) | |
| return CURLE_BAD_FUNCTION_ARGUMENT; | |
| - | hi->port = (unsigned short)((val[0] << 8) | val[1]); |
| - | CURL_TRC_DNS(data, "HTTPS RR port %u", hi->port); |
| + | hi->port = (uint16_t)((val[0] << 8) | val[1]); |
| + | hi->port_set = TRUE; |
| break; | |
| default: | |
| - | CURL_TRC_DNS(data, "HTTPS RR unknown code"); |
| + | /* unknown code */ |
| break; | |
| } | |
| return result; | |
| @@ -147,12 +210,12 @@ void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo) | |
| Curl_safefree(rrinfo->ipv4hints); | |
| Curl_safefree(rrinfo->ipv6hints); | |
| Curl_safefree(rrinfo->rrname); | |
| + | rrinfo->complete = FALSE; |
| } | |
| #ifdef USE_ARES | |
| - | static CURLcode httpsrr_opt(struct Curl_easy *data, |
| - | const ares_dns_rr_t *rr, |
| + | static CURLcode httpsrr_opt(const ares_dns_rr_t *rr, |
| ares_dns_rr_key_t key, size_t idx, | |
| struct Curl_https_rrinfo *hinfo) | |
| { | |
| @@ -161,11 +224,10 @@ static CURLcode httpsrr_opt(struct Curl_easy *data, | |
| size_t len = 0; | |
| code = ares_dns_rr_get_opt(rr, key, idx, &val, &len); | |
| - | return Curl_httpsrr_set(data, hinfo, code, val, len); |
| + | return Curl_httpsrr_set(hinfo, code, val, len); |
| } | |
| - | CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data, |
| - | const ares_dns_record_t *dnsrec, |
| + | CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, |
| struct Curl_https_rrinfo *hinfo) | |
| { | |
| CURLcode result = CURLE_OK; | |
| @@ -188,18 +250,17 @@ CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data, | |
| result = CURLE_OUT_OF_MEMORY; | |
| goto out; | |
| } | |
| - | CURL_TRC_DNS(data, "HTTPS RR target: %s", hinfo->target); |
| } | |
| - | CURL_TRC_DNS(data, "HTTPS RR priority: %u", |
| - | ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY)); |
| + | hinfo->priority = ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY); |
| for(opt = 0; opt < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS); | |
| opt++) { | |
| - | result = httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt, hinfo); |
| + | result = httpsrr_opt(rr, ARES_RR_HTTPS_PARAMS, opt, hinfo); |
| if(result) | |
| break; | |
| } | |
| } | |
| out: | |
| + | hinfo->complete = !result; |
| Curl_safefree(hinfo->rrname); | |
| return result; | |
| } | |
| @@ -51,13 +51,15 @@ struct Curl_https_rrinfo { | |
| size_t ipv6hints_len; | |
| unsigned char alpns[MAX_HTTPSRR_ALPNS]; /* keytag = 1 */ | |
| /* store parsed alpnid entries in the array, end with ALPN_none */ | |
| - | int port; /* -1 means not set */ |
| + | uint16_t port; /* -1 means not set */ |
| uint16_t priority; | |
| 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_easy *data, |
| - | struct Curl_https_rrinfo *hi, |
| + | CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *hi, |
| uint16_t rrkey, const uint8_t *val, size_t vlen); | |
| struct Curl_https_rrinfo * | |
| @@ -77,10 +79,17 @@ void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo); | |
| #define HTTPS_RR_CODE_IPV6 0x06 | |
| #ifdef USE_ARES | |
| - | CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data, |
| - | const ares_dns_record_t *dnsrec, |
| + | CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, |
| struct Curl_https_rrinfo *hinfo); | |
| #endif /* USE_ARES */ | |
| + | |
| + | #ifdef CURLVERBOSE |
| + | void Curl_httpsrr_trace(struct Curl_easy *data, |
| + | struct Curl_https_rrinfo *hi); |
| + | #else |
| + | #define Curl_httpsrr_trace(a,b) Curl_nop_stmt |
| + | #endif |
| + | |
| #endif /* USE_HTTPSRR */ | |
| #endif /* HEADER_CURL_HTTPSRR_H */ | |
| @@ -702,7 +702,7 @@ static CURLcode multi_done(struct Curl_easy *data, | |
| return CURLE_OK; | |
| /* Shut down any ongoing async resolver operation. */ | |
| - | Curl_async_shutdown(data); |
| + | Curl_resolv_shutdown_all(data); |
| /* Cleanup possible redirect junk */ | |
| Curl_safefree(data->req.newurl); | |
| @@ -175,6 +175,7 @@ struct Curl_multi { | |
| #ifdef DEBUGBUILD | |
| unsigned int now_access_count; | |
| #endif | |
| + | uint32_t last_resolv_id; /* id of the last DNS resolve operation */ |
| BIT(ipv6_works); | |
| BIT(multiplexing); /* multiplexing wanted */ | |
| BIT(recheckstate); /* see Curl_multi_connchanged */ | |
| @@ -102,9 +102,6 @@ CURLcode Curl_req_done(struct SingleRequest *req, | |
| if(!aborted) | |
| (void)req_flush(data); | |
| Curl_client_reset(data); | |
| - | #ifndef CURL_DISABLE_DOH |
| - | Curl_doh_close(data); |
| - | #endif |
| return CURLE_OK; | |
| } | |
| @@ -117,9 +114,8 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) | |
| if(req->sendbuf_init) | |
| Curl_bufq_reset(&req->sendbuf); | |
| - | #ifndef CURL_DISABLE_DOH |
| - | Curl_doh_close(data); |
| - | #endif |
| + | /* clear any resolve data */ |
| + | Curl_resolv_destroy_all(data); |
| /* Can no longer memset() this struct as we need to keep some state */ | |
| req->size = -1; | |
| req->maxdownload = -1; | |
| @@ -2386,28 +2386,16 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, | |
| #endif | |
| #ifdef USE_RESOLV_ARES | |
| case CURLOPT_DNS_SERVERS: | |
| - | result = Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); |
| - | if(result) |
| - | break; |
| - | return Curl_async_ares_set_dns_servers(data); |
| + | return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); |
| case CURLOPT_DNS_INTERFACE: | |
| - | result = Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); |
| - | if(result) |
| - | break; |
| - | return Curl_async_ares_set_dns_interface(data); |
| + | return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); |
| case CURLOPT_DNS_LOCAL_IP4: | |
| - | result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); |
| - | if(result) |
| - | break; |
| - | return Curl_async_ares_set_dns_local_ip4(data); |
| + | return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); |
| case CURLOPT_DNS_LOCAL_IP6: | |
| - | result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); |
| - | if(result) |
| - | break; |
| - | return Curl_async_ares_set_dns_local_ip6(data); |
| + | return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); |
| #endif | |
| #ifdef USE_UNIX_SOCKETS | |
| @@ -102,6 +102,7 @@ struct socks_state { | |
| const char *proxy_user; | |
| const char *proxy_password; | |
| CURLproxycode presult; | |
| + | uint32_t resolv_id; |
| unsigned char version; | |
| BIT(resolve_local); | |
| BIT(start_resolving); | |
| @@ -323,10 +324,11 @@ static CURLproxycode socks4_resolving(struct socks_state *sx, | |
| sx->start_resolving = FALSE; | |
| DEBUGASSERT(sx->hostname && *sx->hostname); | |
| - | result = Curl_resolv(data, sx->hostname, sx->remote_port, |
| - | cf->conn->ip_version, |
| + | 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, &dns); |
| + | 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; | |
| @@ -336,7 +338,7 @@ static CURLproxycode socks4_resolving(struct socks_state *sx, | |
| } | |
| else { | |
| /* check if we have the name resolved by now */ | |
| - | result = Curl_resolv_take_result(data, &dns); |
| + | result = Curl_resolv_take_result(data, sx->resolv_id, &dns); |
| if(!result && !dns) | |
| return CURLPX_OK; | |
| } | |
| @@ -858,10 +860,11 @@ static CURLproxycode socks5_resolving(struct socks_state *sx, | |
| sx->start_resolving = FALSE; | |
| DEBUGASSERT(sx->hostname && *sx->hostname); | |
| - | result = Curl_resolv(data, sx->hostname, sx->remote_port, |
| - | cf->conn->ip_version, |
| + | 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, &dns); |
| + | 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; | |
| @@ -871,7 +874,7 @@ static CURLproxycode socks5_resolving(struct socks_state *sx, | |
| } | |
| else { | |
| /* check if we have the name resolved by now */ | |
| - | result = Curl_resolv_take_result(data, &dns); |
| + | result = Curl_resolv_take_result(data, sx->resolv_id, &dns); |
| if(!result && !dns) | |
| return CURLPX_OK; | |
| } | |
| @@ -435,38 +435,38 @@ out: | |
| #ifdef CURLVERBOSE | |
| void Curl_thrdpool_trace(struct curl_thrdpool *tpool, | |
| - | struct Curl_easy *data, |
| - | struct curl_trc_feat *feat) |
| + | struct Curl_easy *data) |
| { | |
| + | struct curl_trc_feat *feat = &Curl_trc_feat_threads; |
| if(Curl_trc_ft_is_verbose(data, feat)) { | |
| struct Curl_llist_node *e; | |
| struct curltime now = curlx_now(); | |
| Curl_mutex_acquire(&tpool->lock); | |
| if(!Curl_llist_count(&tpool->slots)) { | |
| - | Curl_trc_feat_infof(data, feat, "[%s] [TPOOL] no threads running", |
| + | Curl_trc_feat_infof(data, feat, "[TPOOL-%s] no threads running", |
| tpool->name); | |
| } | |
| for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { | |
| struct thrdslot *tslot = Curl_node_elem(e); | |
| timediff_t elapsed_ms = curlx_ptimediff_ms(&now, &tslot->starttime); | |
| if(!tslot->running) { | |
| - | Curl_trc_feat_infof(data, feat, "[%s] [TPOOL] [%u]: not running", |
| + | Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: not running", |
| tpool->name, tslot->id); | |
| } | |
| else if(!tslot->starttime.tv_sec && !tslot->starttime.tv_usec) { | |
| - | Curl_trc_feat_infof(data, feat, "[%s] [TPOOL] [%u]: starting...", |
| + | Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: starting...", |
| tpool->name, tslot->id); | |
| } | |
| else if(tslot->idle) { | |
| - | Curl_trc_feat_infof(data, feat, "[%s] [TPOOL] [%u]: idle for %" |
| + | Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: idle for %" |
| FMT_TIMEDIFF_T "ms", | |
| tpool->name, tslot->id, elapsed_ms); | |
| } | |
| else { | |
| timediff_t remain_ms = tslot->work_timeout_ms ? | |
| (tslot->work_timeout_ms - elapsed_ms) : 0; | |
| - | Curl_trc_feat_infof(data, feat, "[%s] [TPOOL] [%u]: busy %" |
| + | Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: busy %" |
| FMT_TIMEDIFF_T "ms, timeout in %" FMT_TIMEDIFF_T | |
| "ms: %s", | |
| tpool->name, tslot->id, elapsed_ms, remain_ms, | |
| @@ -30,7 +30,6 @@ | |
| struct curl_thrdpool; | |
| struct Curl_easy; | |
| - | struct curl_trc_feat; |
| /* Invoked under thread pool lock to get an "item" to work on. Must | |
| * return NULL if there is nothing to do. | |
| @@ -99,8 +98,7 @@ CURLcode Curl_thrdpool_set_props(struct curl_thrdpool *tpool, | |
| #ifdef CURLVERBOSE | |
| void Curl_thrdpool_trace(struct curl_thrdpool *tpool, | |
| - | struct Curl_easy *data, |
| - | struct curl_trc_feat *feat); |
| + | struct Curl_easy *data); |
| #endif | |
| #endif /* USE_THREADS */ | |
| @@ -380,27 +380,27 @@ CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, | |
| #ifdef CURLVERBOSE | |
| void Curl_thrdq_trace(struct curl_thrdq *tqueue, | |
| - | struct Curl_easy *data, |
| - | struct curl_trc_feat *feat) |
| + | struct Curl_easy *data) |
| { | |
| + | struct curl_trc_feat *feat = &Curl_trc_feat_threads; |
| if(Curl_trc_ft_is_verbose(data, feat)) { | |
| struct Curl_llist_node *e; | |
| struct thrdq_item *qitem; | |
| - | Curl_thrdpool_trace(tqueue->tpool, data, feat); |
| + | Curl_thrdpool_trace(tqueue->tpool, data); |
| Curl_mutex_acquire(&tqueue->lock); | |
| if(!Curl_llist_count(&tqueue->sendq) && | |
| !Curl_llist_count(&tqueue->recvq)) { | |
| - | Curl_trc_feat_infof(data, feat, "[%s] [QUEUE] empty", tqueue->name); |
| + | Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] empty", tqueue->name); |
| } | |
| for(e = Curl_llist_head(&tqueue->sendq); e; e = Curl_node_next(e)) { | |
| qitem = Curl_node_elem(e); | |
| - | Curl_trc_feat_infof(data, feat, "[%s] [QUEUE] in: %s", |
| + | Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] in: %s", |
| tqueue->name, qitem->description); | |
| } | |
| for(e = Curl_llist_head(&tqueue->recvq); e; e = Curl_node_next(e)) { | |
| qitem = Curl_node_elem(e); | |
| - | Curl_trc_feat_infof(data, feat, "[%s] [QUEUE] out: %s", |
| + | Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] out: %s", |
| tqueue->name, qitem->description); | |
| } | |
| Curl_mutex_release(&tqueue->lock); | |
| @@ -29,7 +29,6 @@ | |
| #ifdef USE_THREADS | |
| struct Curl_easy; | |
| - | struct curl_trc_feat; |
| struct curl_thrdq; | |
| typedef enum { | |
| @@ -111,8 +110,7 @@ CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, | |
| #ifdef CURLVERBOSE | |
| void Curl_thrdq_trace(struct curl_thrdq *tqueue, | |
| - | struct Curl_easy *data, |
| - | struct curl_trc_feat *feat); |
| + | struct Curl_easy *data); |
| #endif | |
| #endif /* USE_THREADS */ | |
| @@ -239,7 +239,7 @@ CURLcode Curl_close(struct Curl_easy **datap) | |
| curlx_free(data->state.range); | |
| /* release any resolve information this transfer kept */ | |
| - | Curl_async_destroy(data); |
| + | Curl_resolv_destroy_all(data); |
| data->set.verbose = FALSE; /* no more calls to DEBUGFUNCTION */ | |
| data->magic = 0; /* force a clear AFTER the possibly enforced removal from | |
| @@ -723,7 +723,6 @@ struct UrlState { | |
| #ifdef USE_CURL_ASYNC | |
| struct Curl_resolv_async *async; /* asynchronous name resolver data */ | |
| - | uint32_t next_async_id; /* id of the next async resolve operation */ |
| #endif | |
| #ifdef USE_OPENSSL | |
| @@ -37,6 +37,7 @@ | |
| #include "curlx/inet_pton.h" | |
| #include "vtls/openssl.h" | |
| #include "connect.h" | |
| + | #include "cf-dns.h" |
| #include "progress.h" | |
| #include "vtls/vtls.h" | |
| #include "vtls/vtls_int.h" | |
| @@ -3423,6 +3424,16 @@ ossl_init_session_and_alpns(struct ossl_ctx *octx, | |
| } | |
| #ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST | |
| + | static bool ossl_ech_need_httpsrr(struct Curl_easy *data) |
| + | { |
| + | if(!CURLECH_ENABLED(data)) |
| + | return FALSE; |
| + | if((data->set.tls_ech & CURLECH_GREASE) || |
| + | (data->set.tls_ech & CURLECH_CLA_CFG)) |
| + | return FALSE; |
| + | return TRUE; |
| + | } |
| + | |
| static CURLcode ossl_init_ech(struct ossl_ctx *octx, | |
| struct Curl_cfilter *cf, | |
| struct Curl_easy *data, | |
| @@ -3488,49 +3499,32 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, | |
| infof(data, "ECH: ECHConfig from command line"); | |
| } | |
| else { | |
| - | struct Curl_dns_entry *dns = NULL; |
| - | |
| - | if(peer->hostname) |
| - | result = Curl_dnscache_get(data, peer->hostname, peer->port, |
| - | cf->conn->ip_version, &dns); |
| - | if(!dns) { |
| - | if(result) { |
| - | failf(data, "ECH: could not resolve %s:%d", |
| - | peer->hostname, peer->port); |
| - | return result; |
| - | } |
| - | infof(data, "ECH: requested but no DNS info available"); |
| - | if(data->set.tls_ech & CURLECH_HARD) |
| - | return CURLE_SSL_CONNECT_ERROR; |
| - | } |
| - | else { |
| - | struct Curl_https_rrinfo *rinfo = NULL; |
| - | |
| - | rinfo = dns->hinfo; |
| - | if(rinfo && rinfo->echconfiglist) { |
| - | unsigned char *ecl = rinfo->echconfiglist; |
| - | size_t elen = rinfo->echconfiglist_len; |
| - | |
| - | infof(data, "ECH: ECHConfig from DoH HTTPS RR"); |
| - | if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { |
| - | infof(data, "ECH: SSL_set1_ech_config_list failed"); |
| - | if(data->set.tls_ech & CURLECH_HARD) |
| - | return CURLE_SSL_CONNECT_ERROR; |
| - | } |
| - | else { |
| - | trying_ech_now = 1; |
| - | infof(data, "ECH: imported ECHConfigList of length %zu", elen); |
| - | } |
| - | } |
| - | else { |
| - | infof(data, "ECH: requested but no ECHConfig available"); |
| + | const struct Curl_https_rrinfo *rinfo = |
| + | Curl_conn_dns_get_https(data, cf->sockindex); |
| + | |
| + | if(rinfo && rinfo->echconfiglist) { |
| + | const unsigned char *ecl = rinfo->echconfiglist; |
| + | size_t elen = rinfo->echconfiglist_len; |
| + | |
| + | infof(data, "ECH: ECHConfig from DoH HTTPS RR"); |
| + | if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { |
| + | infof(data, "ECH: SSL_set1_ech_config_list failed"); |
| if(data->set.tls_ech & CURLECH_HARD) | |
| return CURLE_SSL_CONNECT_ERROR; | |
| } | |
| - | Curl_dns_entry_unlink(data, &dns); |
| + | else { |
| + | trying_ech_now = 1; |
| + | infof(data, "ECH: imported ECHConfigList of length %zu", elen); |
| + | } |
| + | } |
| + | else { |
| + | infof(data, "ECH: requested but no ECHConfig available"); |
| + | if(data->set.tls_ech & CURLECH_HARD) |
| + | return CURLE_SSL_CONNECT_ERROR; |
| } | |
| } | |
| #ifdef HAVE_BORINGSSL_LIKE | |
| + | (void)peer; |
| if(trying_ech_now && outername) { | |
| infof(data, "ECH: setting public_name not supported with BoringSSL"); | |
| return CURLE_SSL_CONNECT_ERROR; | |
| @@ -4949,6 +4943,15 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf, | |
| connssl->io_need = CURL_SSL_IO_NEED_NONE; | |
| if(ssl_connect_1 == connssl->connecting_state) { | |
| + | #ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST |
| + | /* if we do ECH and need the HTTPS-RR information for it, |
| + | * we delay the connect until it arrives or DNS resolve fails. */ |
| + | if(ossl_ech_need_httpsrr(data) && |
| + | !Curl_conn_dns_resolved_https(data, cf->sockindex)) { |
| + | CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); |
| + | return CURLE_OK; |
| + | } |
| + | #endif |
| CURL_TRC_CF(data, cf, "ossl_connect, step1"); | |
| result = ossl_connect_step1(cf, data); | |
| if(result) | |
| @@ -32,6 +32,7 @@ | |
| #include "curlx/fopen.h" | |
| #include "curlx/strerr.h" | |
| #include "urldata.h" | |
| + | #include "cf-dns.h" |
| #include "curl_trc.h" | |
| #include "httpsrr.h" | |
| #include "vtls/vtls.h" | |
| @@ -909,16 +910,26 @@ cleanup: | |
| } | |
| #ifdef USE_ECH | |
| + | |
| + | static bool cr_ech_need_httpsrr(struct Curl_easy *data) |
| + | { |
| + | if(!CURLECH_ENABLED(data)) |
| + | return FALSE; |
| + | if((data->set.tls_ech & CURLECH_GREASE) || |
| + | (data->set.tls_ech & CURLECH_CLA_CFG)) |
| + | return FALSE; |
| + | return TRUE; |
| + | } |
| + | |
| static CURLcode | |
| init_config_builder_ech(struct Curl_easy *data, | |
| - | const struct ssl_connect_data *connssl, |
| + | struct Curl_cfilter *cf, |
| struct rustls_client_config_builder *builder) | |
| { | |
| const rustls_hpke *hpke = rustls_supported_hpke(); | |
| unsigned char *ech_config = NULL; | |
| size_t ech_config_len = 0; | |
| struct Curl_dns_entry *dns = NULL; | |
| - | struct Curl_https_rrinfo *rinfo = NULL; |
| CURLcode result = CURLE_OK; | |
| rustls_result rr; | |
| @@ -963,22 +974,9 @@ init_config_builder_ech(struct Curl_easy *data, | |
| } | |
| } | |
| else { | |
| - | if(connssl->peer.hostname) { |
| - | result = Curl_dnscache_get(data, connssl->peer.hostname, |
| - | connssl->peer.port, data->conn->ip_version, |
| - | &dns); |
| - | } |
| - | if(!dns) { |
| - | if(result) { |
| - | failf(data, "ECH: could not resolve %s:%d", |
| - | connssl->peer.hostname, connssl->peer.port); |
| - | return result; |
| - | } |
| - | failf(data, "rustls: ECH requested but no DNS info available"); |
| - | result = CURLE_SSL_CONNECT_ERROR; |
| - | goto cleanup; |
| - | } |
| - | rinfo = dns->hinfo; |
| + | const struct Curl_https_rrinfo *rinfo = |
| + | Curl_conn_dns_get_https(data, cf->sockindex); |
| + | |
| if(!rinfo || !rinfo->echconfiglist) { | |
| failf(data, "rustls: ECH requested but no ECHConfig available"); | |
| result = CURLE_SSL_CONNECT_ERROR; | |
| @@ -1075,7 +1073,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, | |
| #ifdef USE_ECH | |
| if(CURLECH_ENABLED(data)) { | |
| - | result = init_config_builder_ech(data, connssl, config_builder); |
| + | result = init_config_builder_ech(data, cf, config_builder); |
| if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) { | |
| rustls_client_config_builder_free(config_builder); | |
| return result; | |
| @@ -1146,6 +1144,16 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, | |
| CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state); | |
| *done = FALSE; | |
| + | #ifdef USE_ECH |
| + | /* if we do ECH and need the HTTPS-RR information for it, |
| + | * we delay the connect until it arrives or DNS resolve fails. */ |
| + | if(cr_ech_need_httpsrr(data) && |
| + | !Curl_conn_dns_resolved_https(data, cf->sockindex)) { |
| + | CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); |
| + | return CURLE_OK; |
| + | } |
| + | #endif /* USE_ECH */ |
| + | |
| if(!backend->conn) { | |
| result = cr_init_backend(cf, data, | |
| (struct rustls_ssl_backend_data *)connssl->backend); | |
| @@ -56,6 +56,7 @@ | |
| #include "urldata.h" | |
| #include "curl_trc.h" | |
| #include "httpsrr.h" | |
| + | #include "cf-dns.h" |
| #include "vtls/vtls.h" | |
| #include "vtls/vtls_int.h" | |
| #include "vtls/vtls_scache.h" | |
| @@ -1289,55 +1290,31 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, | |
| } | |
| } | |
| else { | |
| - | struct ssl_connect_data *connssl = cf->ctx; |
| - | struct Curl_dns_entry *dns = NULL; |
| - | CURLcode result; |
| - | |
| - | result = Curl_dnscache_get(data, connssl->peer.hostname, |
| - | connssl->peer.port, cf->conn->ip_version, |
| - | &dns); |
| - | if(!dns) { |
| - | if(result) { |
| - | failf(data, "ECH: could not resolve %s:%d", |
| - | connssl->peer.hostname, |
| - | connssl->peer.port); |
| - | return result; |
| - | } |
| - | infof(data, "ECH: requested but no DNS info" |
| - | " available"); |
| - | if(data->set.tls_ech & CURLECH_HARD) |
| - | return CURLE_SSL_CONNECT_ERROR; |
| - | } |
| - | else { |
| - | struct Curl_https_rrinfo *rinfo = NULL; |
| - | |
| - | rinfo = dns->hinfo; |
| - | if(rinfo && rinfo->echconfiglist) { |
| - | unsigned char *ecl = rinfo->echconfiglist; |
| - | size_t elen = rinfo->echconfiglist_len; |
| - | |
| - | infof(data, "ECH: ECHConfig from DoH HTTPS RR"); |
| - | if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) != |
| - | WOLFSSL_SUCCESS) { |
| - | infof(data, "ECH: wolfSSL_SetEchConfigs failed"); |
| - | if(data->set.tls_ech & CURLECH_HARD) { |
| - | Curl_dns_entry_unlink(data, &dns); |
| - | return CURLE_SSL_CONNECT_ERROR; |
| - | } |
| - | } |
| - | else { |
| - | trying_ech_now = 1; |
| - | infof(data, "ECH: imported ECHConfigList of length %zu", elen); |
| - | } |
| - | } |
| - | else { |
| - | infof(data, "ECH: requested but no ECHConfig available"); |
| + | const struct Curl_https_rrinfo *rinfo = |
| + | Curl_conn_dns_get_https(data, cf->sockindex); |
| + | |
| + | if(rinfo && rinfo->echconfiglist) { |
| + | const unsigned char *ecl = rinfo->echconfiglist; |
| + | size_t elen = rinfo->echconfiglist_len; |
| + | |
| + | infof(data, "ECH: ECHConfig from DoH HTTPS RR"); |
| + | if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) != |
| + | WOLFSSL_SUCCESS) { |
| + | infof(data, "ECH: wolfSSL_SetEchConfigs failed"); |
| if(data->set.tls_ech & CURLECH_HARD) { | |
| - | Curl_dns_entry_unlink(data, &dns); |
| return CURLE_SSL_CONNECT_ERROR; | |
| } | |
| } | |
| - | Curl_dns_entry_unlink(data, &dns); |
| + | else { |
| + | trying_ech_now = 1; |
| + | infof(data, "ECH: imported ECHConfigList of length %zu", elen); |
| + | } |
| + | } |
| + | else { |
| + | infof(data, "ECH: requested but no ECHConfig available"); |
| + | if(data->set.tls_ech & CURLECH_HARD) { |
| + | return CURLE_SSL_CONNECT_ERROR; |
| + | } |
| } | |
| } | |
| @@ -1510,6 +1487,18 @@ out: | |
| return result; | |
| } | |
| + | #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG |
| + | static bool wssl_ech_need_httpsrr(struct Curl_easy *data) |
| + | { |
| + | if(!CURLECH_ENABLED(data)) |
| + | return FALSE; |
| + | if((data->set.tls_ech & CURLECH_GREASE) || |
| + | (data->set.tls_ech & CURLECH_CLA_CFG)) |
| + | return FALSE; |
| + | return TRUE; |
| + | } |
| + | #endif |
| + | |
| /* | |
| * This function loads all the client/CA certificates and CRLs. Setup the TLS | |
| * layer and do all necessary magic. | |
| @@ -2179,6 +2168,15 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, | |
| connssl->io_need = CURL_SSL_IO_NEED_NONE; | |
| if(ssl_connect_1 == connssl->connecting_state) { | |
| + | #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG |
| + | /* if we do ECH and need the HTTPS-RR information for it, |
| + | * we delay the connect until it arrives or DNS resolve fails. */ |
| + | if(wssl_ech_need_httpsrr(data) && |
| + | !Curl_conn_dns_resolved_https(data, cf->sockindex)) { |
| + | CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); |
| + | return CURLE_OK; |
| + | } |
| + | #endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */ |
| result = wssl_connect_step1(cf, data); | |
| if(result) | |
| return result; | |
| @@ -23,6 +23,8 @@ | |
| ***************************************************************************/ | |
| #include "first.h" | |
| + | #include "testtrace.h" |
| + | |
| static const char *TEST_DATA_STRING = "Test data"; | |
| static int cb_count = 0; | |
| @@ -61,6 +63,9 @@ static CURLcode test_lib655(const char *URL) | |
| CURL *curl; | |
| CURLcode result = CURLE_OK; | |
| + | debug_config.nohex = TRUE; |
| + | debug_config.tracetime = TRUE; |
| + | |
| if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { | |
| curl_mfprintf(stderr, "curl_global_init() failed\n"); | |
| return TEST_ERR_MAJOR_BAD; | |
| @@ -74,6 +79,9 @@ static CURLcode test_lib655(const char *URL) | |
| /* Set the URL that is about to receive our first request. */ | |
| test_setopt(curl, CURLOPT_URL, URL); | |
| + | test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); |
| + | test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); |
| + | test_setopt(curl, CURLOPT_VERBOSE, 1L); |
| test_setopt(curl, CURLOPT_RESOLVER_START_DATA, TEST_DATA_STRING); | |
| test_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_fail); | |
| @@ -91,6 +99,9 @@ static CURLcode test_lib655(const char *URL) | |
| /* Set the URL that receives our second request. */ | |
| test_setopt(curl, CURLOPT_URL, libtest_arg2); | |
| + | test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); |
| + | test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); |
| + | test_setopt(curl, CURLOPT_VERBOSE, 1L); |
| test_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_pass); | |
| @@ -66,7 +66,7 @@ static void rrresults(struct Curl_https_rrinfo *rr, CURLcode res) | |
| curl_msnprintf(p, pend - p, "no-def-alpn|"); | |
| p += strlen(p); | |
| } | |
| - | if(rr->port >= 0) { |
| + | if(rr->port_set) { |
| curl_msnprintf(p, pend - p, "port:%d|", rr->port); | |
| p += strlen(p); | |
| } | |