resolving: dns error tracing

* Add more tracing information to c-ares errors.
* remove CURL_ASYNC_SUCCESS, rename `ares->last_status` to
  `ares->ares_status`. Give trace explanation for "common"
  errors
* add ares "csv" information to tracing on failure
* DoH: invoke `Curl_resolver_error()` on failure to populate
  error buf

Closes #18247
This commit is contained in:
Stefan Eissing
2025-08-11 11:39:03 +02:00
committed by Daniel Stenberg
parent 89490b16c7
commit 9cc4e24ad9
6 changed files with 59 additions and 30 deletions

View File

@@ -337,28 +337,55 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
Curl_resolv_unlink(data, &data->state.async.dns);
data->state.async.done = TRUE;
result = ares->result;
if(ares->last_status == CURL_ASYNC_SUCCESS && !result) {
if(ares->ares_status == ARES_SUCCESS && !result) {
data->state.async.dns =
Curl_dnscache_mk_entry(data, ares->temp_ai,
data->state.async.hostname, 0,
data->state.async.port, FALSE);
ares->temp_ai = NULL; /* temp_ai now owned by entry */
#ifdef HTTPSRR_WORKS
if(data->state.async.dns) {
struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
if(!lhrr)
result = CURLE_OUT_OF_MEMORY;
else
data->state.async.dns->hinfo = lhrr;
}
if(data->state.async.dns) {
struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
if(!lhrr)
result = CURLE_OUT_OF_MEMORY;
else
data->state.async.dns->hinfo = lhrr;
}
#endif
if(!result && data->state.async.dns)
result = Curl_dnscache_add(data, data->state.async.dns);
}
/* if we have not found anything, report the proper
* CURLE_COULDNT_RESOLVE_* code */
if(!result && !data->state.async.dns)
if(!result && !data->state.async.dns) {
result = Curl_resolver_error(data);
if(ares->ares_status != ARES_SUCCESS) {
const char *msg;
switch(ares->ares_status) {
case ARES_ECONNREFUSED:
msg = "connection to DNS server refused";
break;
case ARES_ETIMEOUT:
msg = "query to DNS server timed out";
break;
case ARES_ENOTFOUND:
msg = "DNS server did not find the address";
break;
case ARES_EREFUSED:
msg = "DNS server refused query";
break;
default:
msg = "resolve failed";
break;
}
CURL_TRC_DNS(data, "asyn-ares: %s (error %d)", msg, ares->ares_status);
#if ARES_VERSION >= 0x011800 /* >= v1.24.0 */
CURL_TRC_DNS(data, "asyn-ares config: %s",
ares_get_servers_csv(ares->channel));
#endif
}
}
if(result)
Curl_resolv_unlink(data, &data->state.async.dns);
*dns = data->state.async.dns;
@@ -511,14 +538,14 @@ static void async_ares_hostbyname_cb(void *user_data,
be valid so only defer it when we know the 'status' says its fine! */
return;
if(CURL_ASYNC_SUCCESS == status) {
ares->last_status = status; /* one success overrules any error */
if(ARES_SUCCESS == status) {
ares->ares_status = status; /* one success overrules any error */
async_addr_concat(&ares->temp_ai,
Curl_he2ai(hostent, data->state.async.port));
}
else if(ares->last_status != ARES_SUCCESS) {
/* no success so far, remember error */
ares->last_status = status;
else if(ares->ares_status != ARES_SUCCESS) {
/* no success so far, remember last error */
ares->ares_status = status;
}
ares->num_pending--;
@@ -666,21 +693,22 @@ async_ares_node2addr(struct ares_addrinfo_node *node)
}
static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts,
struct ares_addrinfo *result)
struct ares_addrinfo *ares_ai)
{
struct Curl_easy *data = (struct Curl_easy *)user_data;
struct async_ares_ctx *ares = &data->state.async.ares;
(void)timeouts;
CURL_TRC_DNS(data, "asyn-ares: addrinfo callback, status=%d", status);
if(ARES_SUCCESS == status) {
ares->temp_ai = async_ares_node2addr(result->nodes);
ares->last_status = CURL_ASYNC_SUCCESS;
ares_freeaddrinfo(result);
if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */
ares->ares_status = status;
if(status == ARES_SUCCESS) {
ares->temp_ai = async_ares_node2addr(ares_ai->nodes);
ares_freeaddrinfo(ares_ai);
}
ares->num_pending--;
CURL_TRC_DNS(data, "ares: addrinfo done, status=%d, pending=%d, "
"addr=%sfound",
status, ares->num_pending, ares->temp_ai ? "" : "not ");
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 ");
}
#endif
@@ -736,7 +764,8 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
return NULL;
/* initial status - failed */
ares->last_status = ARES_ENOTFOUND;
ares->ares_status = ARES_ENOTFOUND;
ares->result = CURLE_OK;
#ifdef HAVE_CARES_GETADDRINFO
{

View File

@@ -173,7 +173,7 @@ addr_ctx_create(const char *hostname, int port,
goto err_exit;
}
#endif
addr_ctx->sock_error = CURL_ASYNC_SUCCESS;
addr_ctx->sock_error = 0;
/* Copying hostname string because original can be destroyed by parent
* thread during gethostbyname execution.

View File

@@ -143,7 +143,7 @@ struct async_ares_ctx {
int num_pending; /* number of outstanding c-ares requests */
struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
parts */
int last_status;
int ares_status; /* ARES_SUCCESS, ARES_ENOTFOUND, etc. */
CURLcode result; /* CURLE_OK or error handling response */
#ifndef HAVE_CARES_GETADDRINFO
struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */

View File

@@ -1249,8 +1249,8 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
p->dnstype, &de);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
if(rc[slot]) {
infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
doh_type2name(p->dnstype), dohp->host);
CURL_TRC_DNS(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
doh_type2name(p->dnstype), dohp->host);
}
#endif
} /* next slot */

View File

@@ -1547,6 +1547,8 @@ CURLcode Curl_resolv_check(struct Curl_easy *data,
#ifndef CURL_DISABLE_DOH
if(data->conn->bits.doh) {
result = Curl_doh_is_resolved(data, dns);
if(result)
Curl_resolver_error(data);
}
else
#endif

View File

@@ -47,8 +47,6 @@
#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this
many seconds for a name resolve */
#define CURL_ASYNC_SUCCESS CURLE_OK
struct addrinfo;
struct hostent;
struct Curl_easy;