Happy Eyeballs: add resolution time delay

HEv3 describes conditions on when first connect attempts shall be
started.
https://www.ietf.org/archive/id/draft-ietf-happy-happyeyeballs-v3-01.html
Chapter 4.2

libcurl now waits 50ms for AAAA and HTTPS results (when requested) to
return before continuing with the connect.

Added HTTPS-RR to the "was resolved" information info message. Changed
logging of HTTPS-RR to a one-liner with RFC 9460 like formatting. This
way the user can see if/what was resolved and used in connecting.

Closes #21354
This commit is contained in:
Stefan Eissing 2026-04-17 10:26:25 +02:00 committed by Daniel Stenberg
parent 70a159527c
commit 809dda3a37
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
13 changed files with 272 additions and 158 deletions

View file

@ -431,10 +431,11 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data,
result = Curl_async_take_result(data, async, pdns);
if(result == CURLE_AGAIN) {
CURL_TRC_DNS(data, "result incomplete, queries=%s, responses=%s, "
"ongoing=%d", Curl_resolv_query_str(async->dns_queries),
CURL_TRC_DNS(data, "resolve incomplete, queries=%s, responses=%s, "
"ongoing=%d for %s:%d",
Curl_resolv_query_str(async->dns_queries),
Curl_resolv_query_str(async->dns_responses),
async->queries_ongoing);
async->queries_ongoing, async->hostname, async->port);
result = CURLE_OK;
}
else if(result) {
@ -442,64 +443,74 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data,
Curl_resolver_error(data, NULL);
}
else {
CURL_TRC_DNS(data, "result complete");
CURL_TRC_DNS(data, "resolve complete for %s:%u",
async->hostname, async->port);
DEBUGASSERT(*pdns);
}
return result;
}
timediff_t Curl_resolv_elapsed_ms(struct Curl_easy *data,
uint32_t resolv_id)
{
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
if(!async)
return CURL_TIMEOUT_RESOLVE_MS;
return curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
}
bool Curl_resolv_has_answers(struct Curl_easy *data,
uint32_t resolv_id, uint8_t dns_queries)
{
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
uint8_t check_queries;
/* a no longer existing/running resolve has all answers. */
if(!async || async->done)
return TRUE;
/* Relevant are only queries undertaken. Others are considered answered. */
check_queries = (dns_queries & async->dns_queries);
if((check_queries & async->dns_responses) != check_queries) {
return FALSE;
}
return TRUE;
}
const struct Curl_addrinfo *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 = Curl_async_get(data, resolv_id);
if(async) {
if((ai_family == AF_INET) && !(async->dns_queries & CURL_DNSQ_A))
return NULL;
#ifdef USE_IPV6
if((ai_family == AF_INET6) && !(async->dns_queries & CURL_DNSQ_AAAA))
return NULL;
#endif
return Curl_async_get_ai(data, async, ai_family, index);
}
#else
(void)data;
(void)resolv_id;
(void)ai_family;
(void)index;
if(!async)
return NULL;
if((ai_family == AF_INET) && !(async->dns_queries & CURL_DNSQ_A))
return NULL;
#ifdef USE_IPV6
if((ai_family == AF_INET6) && !(async->dns_queries & CURL_DNSQ_AAAA))
return NULL;
#endif
return NULL;
return Curl_async_get_ai(data, async, ai_family, index);
}
#ifdef USE_HTTPSRR
const struct Curl_https_rrinfo *
Curl_resolv_get_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_get_https(data, async);
#else
(void)data;
(void)resolv_id;
#endif
return NULL;
if(!async)
return NULL;
return Curl_async_get_https(data, async);
}
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)data;
(void)resolv_id;
#endif
return TRUE;
if(!async)
return TRUE;
return Curl_async_knows_https(data, async);
}
#endif /* USE_HTTPSRR */