diff --git a/docs/libcurl/curl_global_trace.md b/docs/libcurl/curl_global_trace.md index 87edb8ecf08f..acf966f3ce20 100644 --- a/docs/libcurl/curl_global_trace.md +++ b/docs/libcurl/curl_global_trace.md @@ -97,9 +97,13 @@ In order to find out all components involved in a transfer, run it with "all" configured. You can then see all names involved in your libcurl version in the trace. +## `dns` + +Tracing of DNS operations to resolve hostnames and HTTPS records. + ## `doh` -Tracing of DNS-over-HTTP operations to resolve hostnames. +Former name for DNS-over-HTTP operations. Now an alias for `dns`. ## `read` diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 50f3bc741883..86cd983414fc 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -419,14 +419,14 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, them */ res->temp_ai = NULL; + result = res->result; if(!data->state.async.dns) result = Curl_resolver_error(data); - else { + if(!result) { *dns = data->state.async.dns; #ifdef USE_HTTPSRR_ARES { - struct Curl_https_rrinfo *lhrr = - Curl_memdup(&res->hinfo, sizeof(struct Curl_https_rrinfo)); + struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&res->hinfo); if(!lhrr) result = CURLE_OUT_OF_MEMORY; else diff --git a/lib/asyn-thread.c b/lib/asyn-thread.c index 8a938c425ab9..de056c672f83 100644 --- a/lib/asyn-thread.c +++ b/lib/asyn-thread.c @@ -585,17 +585,19 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, Curl_mutex_release(&td->tsd.mutx); if(done) { + CURLcode result = td->result; getaddrinfo_complete(data); - if(!data->state.async.dns) { - CURLcode result = Curl_resolver_error(data); + if(!result && !data->state.async.dns) + result = Curl_resolver_error(data); + + if(result) { destroy_async_data(data); return result; } #ifdef USE_HTTPSRR_ARES { - struct Curl_https_rrinfo *lhrr = - Curl_memdup(&td->hinfo, sizeof(struct Curl_https_rrinfo)); + struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&td->hinfo); if(!lhrr) { destroy_async_data(data); return CURLE_OUT_OF_MEMORY; diff --git a/lib/asyn.h b/lib/asyn.h index 6e8e2debde70..d70bc86aaf3d 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -60,6 +60,7 @@ struct thread_data { timediff_t interval_end; struct curltime start; struct thread_sync_data tsd; + CURLcode result; /* CURLE_OK or error handling response */ #if defined(USE_HTTPSRR) && defined(USE_ARES) struct Curl_https_rrinfo hinfo; ares_channel channel; @@ -74,6 +75,7 @@ struct thread_data { struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */ int last_status; + CURLcode result; /* CURLE_OK or error handling response */ #ifndef HAVE_CARES_GETADDRINFO struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ #endif diff --git a/lib/curl_trc.c b/lib/curl_trc.c index e5f9b517dc5e..fb3654e53bbf 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -176,6 +176,11 @@ struct curl_trc_feat Curl_trc_feat_write = { "WRITE", CURL_LOG_LVL_NONE, }; +struct curl_trc_feat Curl_trc_feat_dns = { + "DNS", + CURL_LOG_LVL_NONE, +}; + void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...) { @@ -199,6 +204,17 @@ void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...) } } +void Curl_trc_dns(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_dns, fmt, ap); + va_end(ap); + } +} + #ifndef CURL_DISABLE_FTP struct curl_trc_feat Curl_trc_feat_ftp = { "FTP", @@ -284,11 +300,11 @@ struct trc_feat_def { static struct trc_feat_def trc_feats[] = { { &Curl_trc_feat_read, TRC_CT_NONE }, { &Curl_trc_feat_write, TRC_CT_NONE }, + { &Curl_trc_feat_dns, TRC_CT_NETWORK }, #ifndef CURL_DISABLE_FTP { &Curl_trc_feat_ftp, TRC_CT_PROTOCOL }, #endif #ifndef CURL_DISABLE_DOH - { &Curl_doh_trc, TRC_CT_NETWORK }, #endif #ifndef CURL_DISABLE_SMTP { &Curl_trc_feat_smtp, TRC_CT_PROTOCOL }, @@ -395,6 +411,10 @@ static CURLcode trc_opt(const char *config) trc_apply_level_by_category(TRC_CT_NETWORK, lvl); else if(Curl_str_casecompare(&out, "proxy")) trc_apply_level_by_category(TRC_CT_PROXY, lvl); + else if(Curl_str_casecompare(&out, "doh")) { + struct Curl_str dns = { "dns", 3 }; + trc_apply_level_by_name(&dns, lvl); + } else trc_apply_level_by_name(&out, lvl); diff --git a/lib/curl_trc.h b/lib/curl_trc.h index 9b4e36eef993..5412c7a5f885 100644 --- a/lib/curl_trc.h +++ b/lib/curl_trc.h @@ -86,6 +86,8 @@ void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...) CURL_PRINTF(2, 3); void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...) CURL_PRINTF(2, 3); +void Curl_trc_dns(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); #ifndef CURL_DISABLE_FTP extern struct curl_trc_feat Curl_trc_feat_ftp; @@ -121,6 +123,9 @@ void Curl_trc_ws(struct Curl_easy *data, #define CURL_TRC_READ(data, ...) \ do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) \ Curl_trc_read(data, __VA_ARGS__); } while(0) +#define CURL_TRC_DNS(data, ...) \ + do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) \ + Curl_trc_dns(data, __VA_ARGS__); } while(0) #ifndef CURL_DISABLE_FTP #define CURL_TRC_FTP(data, ...) \ @@ -149,6 +154,7 @@ void Curl_trc_ws(struct Curl_easy *data, #define CURL_TRC_CF Curl_trc_cf_infof #define CURL_TRC_WRITE Curl_trc_write #define CURL_TRC_READ Curl_trc_read +#define CURL_TRC_DNS Curl_trc_dns #ifndef CURL_DISABLE_FTP #define CURL_TRC_FTP Curl_trc_ftp @@ -174,6 +180,7 @@ struct curl_trc_feat { }; 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; #define Curl_trc_is_verbose(data) \ ((data) && (data)->set.verbose && \ diff --git a/lib/doh.c b/lib/doh.c index eca071138055..f1d2bbe47eb6 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -71,10 +71,6 @@ static const char *doh_strerror(DOHcode code) return "bad error code"; } -struct curl_trc_feat Curl_doh_trc = { - "DoH", - CURL_LOG_LVL_NONE, -}; #endif /* !CURL_DISABLE_VERBOSE_STRINGS */ /* @unittest 1655 @@ -281,7 +277,7 @@ static CURLcode doh_run_probe(struct Curl_easy *data, the gcc typecheck helpers */ doh->state.internal = TRUE; #ifndef CURL_DISABLE_VERBOSE_STRINGS - doh->state.feat = &Curl_doh_trc; + doh->state.feat = &Curl_trc_feat_dns; #endif ERROR_CHECK_SETOPT(CURLOPT_URL, url); ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https"); @@ -305,7 +301,7 @@ static CURLcode doh_run_probe(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_SHARE, (CURLSH *)data->share); if(data->set.err && data->set.err != stderr) ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err); - if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L); if(data->set.no_signal) ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L); @@ -1087,12 +1083,14 @@ static CURLcode doh_test_alpn_escapes(void) } #endif -static CURLcode doh_resp_decode_httpsrr(unsigned char *cp, size_t len, +static CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, + unsigned char *cp, size_t len, struct Curl_https_rrinfo **hrr) { uint16_t pcode = 0, plen = 0; struct Curl_https_rrinfo *lhrr = NULL; char *dnsname = NULL; + CURLcode result = CURLE_OUT_OF_MEMORY; #ifdef DEBUGBUILD /* a few tests of escaping, should not be here but ok for now */ @@ -1116,44 +1114,9 @@ static CURLcode doh_resp_decode_httpsrr(unsigned char *cp, size_t len, plen = doh_get16bit(cp, 2); cp += 4; len -= 4; - switch(pcode) { - case HTTPS_RR_CODE_ALPN: - if(Curl_httpsrr_decode_alpn(cp, plen, lhrr->alpns) != CURLE_OK) - goto err; - break; - case HTTPS_RR_CODE_NO_DEF_ALPN: - lhrr->no_def_alpn = TRUE; - break; - case HTTPS_RR_CODE_IPV4: - if(!plen) - goto err; - lhrr->ipv4hints = Curl_memdup(cp, plen); - if(!lhrr->ipv4hints) - goto err; - lhrr->ipv4hints_len = (size_t)plen; - break; - case HTTPS_RR_CODE_ECH: - if(!plen) - goto err; - lhrr->echconfiglist = Curl_memdup(cp, plen); - if(!lhrr->echconfiglist) - goto err; - lhrr->echconfiglist_len = (size_t)plen; - break; - case HTTPS_RR_CODE_IPV6: - if(!plen) - goto err; - lhrr->ipv6hints = Curl_memdup(cp, plen); - if(!lhrr->ipv6hints) - goto err; - lhrr->ipv6hints_len = (size_t)plen; - break; - case HTTPS_RR_CODE_PORT: - lhrr->port = doh_get16bit(cp, 0); - break; - default: - break; - } + result = Curl_httpsrr_set(data, lhrr, pcode, cp, plen); + if(result) + goto err; if(plen > 0 && plen <= len) { cp += plen; len -= plen; @@ -1163,10 +1126,9 @@ static CURLcode doh_resp_decode_httpsrr(unsigned char *cp, size_t len, *hrr = lhrr; return CURLE_OK; err: - Curl_safefree(lhrr->target); - Curl_safefree(lhrr->echconfiglist); + Curl_httpsrr_cleanup(lhrr); Curl_safefree(lhrr); - return CURLE_OUT_OF_MEMORY; + return result; } # ifdef DEBUGBUILD @@ -1256,8 +1218,8 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, struct Curl_addrinfo *ai; - if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) { - infof(data, "[DoH] hostname: %s", dohp->host); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) { + CURL_TRC_DNS(data, "hostname: %s", dohp->host); doh_show(data, &de); } @@ -1291,8 +1253,8 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, #ifdef USE_HTTPSRR if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) { struct Curl_https_rrinfo *hrr = NULL; - result = doh_resp_decode_httpsrr(de.https_rrs->val, de.https_rrs->len, - &hrr); + result = doh_resp_decode_httpsrr(data, de.https_rrs->val, + de.https_rrs->len, &hrr); if(result) { infof(data, "Failed to decode HTTPS RR"); return result; diff --git a/lib/doh.h b/lib/doh.h index 53644863e6f5..9c41ed818eaa 100644 --- a/lib/doh.h +++ b/lib/doh.h @@ -167,8 +167,6 @@ UNITTEST void de_init(struct dohentry *d); UNITTEST void de_cleanup(struct dohentry *d); #endif -extern struct curl_trc_feat Curl_doh_trc; - #else /* if DoH is disabled */ #define Curl_doh(a,b,c,d) NULL #define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST diff --git a/lib/hostip.c b/lib/hostip.c index 17d335564f75..9d0dd104041b 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -1081,10 +1081,7 @@ static void hostcache_unlink_entry(void *entry) Curl_freeaddrinfo(dns->addr); #ifdef USE_HTTPSRR if(dns->hinfo) { - free(dns->hinfo->target); - free(dns->hinfo->ipv4hints); - free(dns->hinfo->echconfiglist); - free(dns->hinfo->ipv6hints); + Curl_httpsrr_cleanup(dns->hinfo); free(dns->hinfo); } #endif diff --git a/lib/httpsrr.c b/lib/httpsrr.c index 3072f60bb1d0..7a2ecdbe4dc2 100644 --- a/lib/httpsrr.c +++ b/lib/httpsrr.c @@ -31,6 +31,7 @@ #include "httpsrr.h" #include "connect.h" #include "sendf.h" +#include "strdup.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -91,44 +92,92 @@ CURLcode Curl_httpsrr_decode_alpn(const unsigned char *cp, size_t len, return CURLE_BAD_CONTENT_ENCODING; } -#ifdef USE_ARES - -static void httpsrr_opt(struct Curl_easy *data, - const ares_dns_rr_t *rr, - ares_dns_rr_key_t key, size_t idx) +CURLcode Curl_httpsrr_set(struct Curl_easy *data, + struct Curl_https_rrinfo *hi, + uint16_t rrkey, const uint8_t *val, size_t vlen) { - size_t len = 0; - const unsigned char *val = NULL; - unsigned short code; - struct thread_data *res = &data->state.async.thdata; - struct Curl_https_rrinfo *hi = &res->hinfo; - code = ares_dns_rr_get_opt(rr, key, idx, &val, &len); - - switch(code) { + switch(rrkey) { case HTTPS_RR_CODE_ALPN: /* str_list */ - Curl_httpsrr_decode_alpn(val, len, hi->alpns); - infof(data, "HTTPS RR ALPN: %u %u %u %u", - hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]); + Curl_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: - infof(data, "HTTPS RR no-def-alpn"); + hi->no_def_alpn = TRUE; + CURL_TRC_DNS(data, "HTTPS RR no-def-alpn"); break; case HTTPS_RR_CODE_IPV4: /* addr4 list */ - infof(data, "HTTPS RR IPv4"); + if(!vlen) + return CURLE_BAD_FUNCTION_ARGUMENT; + hi->ipv4hints = Curl_memdup(val, vlen); + 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: - infof(data, "HTTPS RR ECH"); + if(!vlen) + return CURLE_BAD_FUNCTION_ARGUMENT; + hi->echconfiglist = Curl_memdup(val, vlen); + 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 */ - infof(data, "HTTPS RR IPv6"); + if(!vlen) + return CURLE_BAD_FUNCTION_ARGUMENT; + hi->ipv6hints = Curl_memdup(val, vlen); + 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: - infof(data, "HTTPS RR 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); break; default: - infof(data, "HTTPS RR unknown code"); + CURL_TRC_DNS(data, "HTTPS RR unknown code"); break; } + return CURLE_OK; +} + +struct Curl_https_rrinfo * +Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo) +{ + struct Curl_https_rrinfo *dup = Curl_memdup(rrinfo, sizeof(*rrinfo)); + if(dup) + memset(rrinfo, 0, sizeof(*rrinfo)); + return dup; +} + +void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo) +{ + Curl_safefree(rrinfo->target); + Curl_safefree(rrinfo->echconfiglist); + Curl_safefree(rrinfo->ipv4hints); + Curl_safefree(rrinfo->ipv6hints); +} + + +#ifdef USE_ARES + +static CURLcode httpsrr_opt(struct Curl_easy *data, + const ares_dns_rr_t *rr, + ares_dns_rr_key_t key, size_t idx) +{ + size_t len = 0; + const unsigned char *val = NULL; + unsigned short code; + struct thread_data *res = &data->state.async.thdata; + struct Curl_https_rrinfo *hi = &res->hinfo; + + code = ares_dns_rr_get_opt(rr, key, idx, &val, &len); + return Curl_httpsrr_set(data, hi, code, val, len); } void Curl_dnsrec_done_cb(void *arg, ares_status_t status, @@ -136,6 +185,7 @@ void Curl_dnsrec_done_cb(void *arg, ares_status_t status, const ares_dns_record_t *dnsrec) { struct Curl_easy *data = arg; + CURLcode result = CURLE_OK; size_t i; #ifdef CURLRES_ARES struct thread_data *res = &data->state.async.thdata; @@ -147,6 +197,7 @@ void Curl_dnsrec_done_cb(void *arg, ares_status_t status, return; for(i = 0; i < ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); i++) { + const char *target; size_t opt; const ares_dns_rr_t *rr = ares_dns_record_rr_get_const(dnsrec, ARES_SECTION_ANSWER, i); @@ -154,12 +205,26 @@ void Curl_dnsrec_done_cb(void *arg, ares_status_t status, continue; /* When SvcPriority is 0, the SVCB record is in AliasMode. Otherwise, it is in ServiceMode */ - infof(data, "HTTPS RR priority: %u", - ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY)); + target = ares_dns_rr_get_str(rr, ARES_RR_HTTPS_TARGET); + if(target && target[0]) { + res->hinfo.target = strdup(target); + if(!res->hinfo.target) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + CURL_TRC_DNS(data, "HTTPS RR target: %s", res->hinfo.target); + } + CURL_TRC_DNS(data, "HTTPS RR priority: %u", + 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++) - httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt); + opt++) { + result = httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt); + if(result) + break; + } } +out: + res->result = result; } #endif /* USE_ARES */ diff --git a/lib/httpsrr.h b/lib/httpsrr.h index ade2126f0f1d..847aa9abf637 100644 --- a/lib/httpsrr.h +++ b/lib/httpsrr.h @@ -35,6 +35,8 @@ #define CURL_MAXLEN_host_name 253 #define MAX_HTTPSRR_ALPNS 4 +struct Curl_easy; + struct Curl_https_rrinfo { /* * Fields from HTTPS RR. The only mandatory fields are priority and target. @@ -53,7 +55,15 @@ struct Curl_https_rrinfo { uint16_t priority; bool no_def_alpn; /* keytag = 2 */ }; -#endif + +CURLcode Curl_httpsrr_set(struct Curl_easy *data, + struct Curl_https_rrinfo *hi, + uint16_t rrkey, const uint8_t *val, size_t vlen); + +struct Curl_https_rrinfo * +Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo); + +void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo); /* * Code points for DNS wire format SvcParams as per RFC 9460 @@ -68,9 +78,12 @@ struct Curl_https_rrinfo { CURLcode Curl_httpsrr_decode_alpn(const unsigned char *cp, size_t len, unsigned char *alpns); -#if defined(USE_ARES) && defined(USE_HTTPSRR) +#if defined(USE_ARES) void Curl_dnsrec_done_cb(void *arg, ares_status_t status, size_t timeouts, const ares_dns_record_t *dnsrec); -#endif + +#endif /* USE_ARES */ +#endif /* USE_HTTPSRR */ + #endif /* HEADER_CURL_HTTPSRR_H */ diff --git a/scripts/singleuse.pl b/scripts/singleuse.pl index 06cf8b56e046..7732387226cc 100755 --- a/scripts/singleuse.pl +++ b/scripts/singleuse.pl @@ -47,6 +47,7 @@ 'Curl_creader_def_close' => 'internal api', 'Curl_creader_def_read' => 'internal api', 'Curl_creader_def_total_length' => 'internal api', + 'Curl_trc_dns' => 'internal api', ); my %api = (