Happy Eyeballs: add resolution time delay
No binary (build failed)
—
(log yok)
| @@ -563,6 +563,7 @@ static void async_ares_A_cb(void *user_data, int status, int timeouts, | |
| async->dns_responses |= CURL_DNSQ_A; | |
| async->queries_ongoing--; | |
| + | async->done = !async->queries_ongoing; |
| if(status == ARES_SUCCESS) { | |
| ares->ares_status = ARES_SUCCESS; | |
| ares->res_A = async_ares_node2addr(ares_ai->nodes); | |
| @@ -585,6 +586,7 @@ static void async_ares_AAAA_cb(void *user_data, int status, int timeouts, | |
| async->dns_responses |= CURL_DNSQ_AAAA; | |
| async->queries_ongoing--; | |
| + | async->done = !async->queries_ongoing; |
| if(status == ARES_SUCCESS) { | |
| ares->ares_status = ARES_SUCCESS; | |
| ares->res_AAAA = async_ares_node2addr(ares_ai->nodes); | |
| @@ -609,6 +611,7 @@ static void async_ares_rr_done(void *user_data, ares_status_t status, | |
| (void)timeouts; | |
| async->dns_responses |= CURL_DNSQ_HTTPS; | |
| async->queries_ongoing--; | |
| + | async->done = !async->queries_ongoing; |
| if((ARES_SUCCESS != status) || !dnsrec) | |
| return; | |
| ares->result = Curl_httpsrr_from_ares(dnsrec, &ares->hinfo); | |
| @@ -484,7 +484,7 @@ static void async_thrdd_report_item(struct Curl_easy *data, | |
| int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET; | |
| CURLcode result; | |
| - | if(!Curl_trc_is_verbose(data)) |
| + | if(!CURL_TRC_DNS_is_verbose(data)) |
| return; | |
| curlx_dyn_init(&tmp, 1024); | |
| @@ -500,9 +500,10 @@ static void async_thrdd_report_item(struct Curl_easy *data, | |
| } | |
| } | |
| - | infof(data, "Host %s:%u resolved IPv%c: %s", item->hostname, item->port, |
| - | (item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4', |
| - | (curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)")); |
| + | CURL_TRC_DNS(data, "Host %s:%u resolved IPv%c: %s", |
| + | item->hostname, item->port, |
| + | (item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4', |
| + | (curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)")); |
| out: | |
| curlx_dyn_free(&tmp); | |
| } | |
| @@ -707,8 +708,10 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, | |
| return CURLE_AGAIN; | |
| Curl_expire_done(data, EXPIRE_ASYNC_NAME); | |
| - | if(async->result) |
| + | if(async->result) { |
| + | result = async->result; |
| goto out; | |
| + | } |
| if((thrdd->res_A && thrdd->res_A->res) || | |
| (thrdd->res_AAAA && thrdd->res_AAAA->res)) { | |
| @@ -739,7 +742,6 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, | |
| } | |
| if(dns) { | |
| - | CURL_TRC_DNS(data, "resolving complete"); |
| *pdns = dns; | |
| dns = NULL; | |
| } | |
| @@ -214,6 +214,8 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, | |
| #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 | |
| + | #define Curl_async_get_https(x, y) NULL |
| + | #define Curl_async_knows_https(x, y) TRUE |
| #endif /* !CURLRES_ASYNCH */ | |
| #if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH) | |
| @@ -28,6 +28,7 @@ | |
| #include "cfilters.h" | |
| #include "connect.h" | |
| #include "dnscache.h" | |
| + | #include "httpsrr.h" |
| #include "curl_trc.h" | |
| #include "progress.h" | |
| #include "url.h" | |
| @@ -138,11 +139,24 @@ static void cf_dns_report(struct Curl_cfilter *cf, | |
| break; | |
| default: | |
| curlx_dyn_init(&tmp, 1024); | |
| - | infof(data, "Host %s:%d was resolved.", dns->hostname, dns->port); |
| + | infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port); |
| #ifdef CURLRES_IPV6 | |
| cf_dns_report_addr(data, &tmp, "IPv6: ", AF_INET6, dns->addr); | |
| #endif | |
| cf_dns_report_addr(data, &tmp, "IPv4: ", AF_INET, dns->addr); | |
| + | #ifdef USE_HTTPSRR |
| + | if(!dns->hinfo) |
| + | infof(data, "HTTPS-RR: -"); |
| + | else if(!Curl_httpsrr_applicable(data, dns->hinfo)) |
| + | infof(data, "HTTPS-RR: not applicable"); |
| + | else { |
| + | CURLcode result = Curl_httpsrr_print(&tmp, dns->hinfo); |
| + | if(!result) |
| + | infof(data, "HTTPS-RR: %s", curlx_dyn_ptr(&tmp)); |
| + | else |
| + | infof(data, "Error printing HTTPS-RR information"); |
| + | } |
| + | #endif |
| curlx_dyn_free(&tmp); | |
| break; | |
| } | |
| @@ -205,6 +219,41 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, | |
| } | |
| } | |
| + | #define CURL_HEV3_RESOLVE_DELAY_MS 50 |
| + | |
| + | static bool cf_dns_ready_to_connect(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| + | { |
| + | struct cf_dns_ctx *ctx = cf->ctx; |
| + | |
| + | if(ctx->resolv_result) |
| + | return TRUE; |
| + | else if(ctx->dns) |
| + | return TRUE; |
| + | #ifdef USE_CURL_ASYNC |
| + | else { |
| + | /* We want AAAA answer as we prefer ipv6. If a sub-filter desires |
| + | * HTTPS-RR, we check for that query as well. */ |
| + | uint8_t wanted_answers = CURL_DNSQ_AAAA; |
| + | if(Curl_conn_cf_wants_httpsrr(cf, data)) |
| + | wanted_answers |= CURL_DNSQ_HTTPS; |
| + | |
| + | /* Note: if a query was never started, it is considered to have |
| + | * an answer (e.g. a negative one). */ |
| + | if(Curl_resolv_has_answers(data, ctx->resolv_id, wanted_answers)) |
| + | return TRUE; |
| + | /* If the wanted answers are not available after a delay, |
| + | * we let the connect attempts start anyway. */ |
| + | return Curl_resolv_elapsed_ms(data, ctx->resolv_id) >= |
| + | CURL_HEV3_RESOLVE_DELAY_MS; |
| + | } |
| + | #else |
| + | (void)data; |
| + | DEBUGASSERT(0); /* We should not come here */ |
| + | return FALSE; |
| + | #endif /* USE_CURL_ASYNC */ |
| + | } |
| + | |
| static CURLcode cf_dns_connect(struct Curl_cfilter *cf, | |
| struct Curl_easy *data, | |
| bool *done) | |
| @@ -225,9 +274,6 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, | |
| if(!ctx->dns && !ctx->resolv_result) { | |
| 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 ongoing for %s:%u", |
| - | ctx->hostname, ctx->port); |
| } | |
| if(ctx->resolv_result) { | |
| @@ -244,19 +290,23 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, | |
| cf_dns_report(cf, data, ctx->dns); | |
| } | |
| + | if(!cf_dns_ready_to_connect(cf, data)) { |
| + | return CURLE_OK; |
| + | } |
| + | |
| if(cf->next && !cf->next->connected) { | |
| bool sub_done; | |
| CURLcode result = Curl_conn_cf_connect(cf->next, data, &sub_done); | |
| - | CURL_TRC_CF(data, cf, "connect subfilters -> %d, done=%d", |
| - | result, sub_done); |
| if(result || !sub_done) | |
| return result; | |
| DEBUGASSERT(sub_done); | |
| } | |
| /* sub filter chain is connected */ | |
| + | CURL_TRC_CF(data, cf, "connected filter chain below"); |
| if(ctx->complete_resolve && !ctx->dns && !ctx->resolv_result) { | |
| /* This filter only connects when it has resolved everything. */ | |
| + | CURL_TRC_CF(data, cf, "delay connect until resolve complete"); |
| return CURLE_OK; | |
| } | |
| *done = TRUE; | |
| @@ -536,30 +586,6 @@ static const struct Curl_addrinfo *cf_dns_get_nth_ai( | |
| return NULL; | |
| } | |
| - | bool Curl_conn_dns_has_any_ai(struct Curl_easy *data, int sockindex) |
| - | { |
| - | struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; |
| - | |
| - | (void)data; |
| - | for(; cf; cf = cf->next) { |
| - | if(cf->cft == &Curl_cft_dns) { |
| - | struct cf_dns_ctx *ctx = cf->ctx; |
| - | if(ctx->resolv_result) |
| - | return FALSE; |
| - | else if(ctx->dns) |
| - | return !!ctx->dns->addr; |
| - | else |
| - | #ifdef USE_IPV6 |
| - | return Curl_resolv_get_ai(data, ctx->resolv_id, AF_INET, 0) || |
| - | Curl_resolv_get_ai(data, ctx->resolv_id, AF_INET6, 0); |
| - | #else |
| - | return !!Curl_resolv_get_ai(data, ctx->resolv_id, AF_INET, 0); |
| - | #endif |
| - | } |
| - | } |
| - | return FALSE; |
| - | } |
| - | |
| /* Return the addrinfo at `index` for the given `family` from the | |
| * first "resolve" filter underneath `cf`. If the DNS resolving is | |
| * not done yet or if no address for the family exists, returns NULL. | |
| @@ -47,10 +47,6 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, | |
| CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex); | |
| - | /* Returns TRUE if any addressinfo is available via |
| - | * `Curl_conn_dns_get_ai()`. */ |
| - | bool Curl_conn_dns_has_any_ai(struct Curl_easy *data, int sockindex); |
| - | |
| const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, | |
| int sockindex, | |
| int ai_family, | |
| @@ -306,13 +306,8 @@ static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, | |
| /* Do we have HTTPS-RR information? */ | |
| rr = Curl_conn_dns_get_https(data, cf->sockindex); | |
| - | if(rr && !rr->no_def_alpn && /* ALPNs are defaults */ |
| - | (!rr->target || /* for same host */ |
| - | !rr->target[0] || |
| - | (rr->target[0] == '.' && |
| - | !rr->target[1])) && |
| - | (!rr->port_set || /* for same port */ |
| - | rr->port == cf->conn->remote_port)) { |
| + | /* We do not support `rr->no_def_alpn`. */ |
| + | if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) { |
| for(i = 0; i < CURL_ARRAYSIZE(rr->alpns); ++i) { | |
| enum alpnid alpn_rr = (enum alpnid)rr->alpns[i]; | |
| if(alpn_rr == not_this_one) /* don't want this one */ | |
| @@ -509,9 +504,6 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, | |
| switch(ctx->state) { | |
| case CF_HC_RESOLV: | |
| - | /* Without any addressinfo, delay the start of balling. */ |
| - | if(!Curl_conn_dns_has_any_ai(data, cf->sockindex)) |
| - | return CURLE_OK; |
| ctx->state = CF_HC_INIT; | |
| FALLTHROUGH(); | |
| @@ -761,7 +753,7 @@ static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) | |
| struct Curl_cftype Curl_cft_http_connect = { | |
| "HTTPS-CONNECT", | |
| - | CF_TYPE_SETUP, |
| + | CF_TYPE_SETUP | CF_TYPE_HTTPSRR, |
| CURL_LOG_LVL_NONE, | |
| cf_hc_destroy, | |
| cf_hc_connect, | |
| @@ -814,8 +814,9 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, | |
| result = Curl_conn_dns_result(cf->conn, cf->sockindex); | |
| if(!result) | |
| ctx->dns_resolved = TRUE; | |
| - | else if(result == CURLE_AGAIN) /* not complete yet */ |
| + | else if(result == CURLE_AGAIN) { |
| result = CURLE_OK; | |
| + | } |
| else /* real error */ | |
| goto out; | |
| } | |
| @@ -748,6 +748,17 @@ int Curl_protocol_for_transport(uint8_t transport) | |
| } | |
| } | |
| + | bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| + | { |
| + | (void)data; |
| + | for(; cf; cf = cf->next) { |
| + | if(cf->cft->flags & CF_TYPE_HTTPSRR) |
| + | return TRUE; |
| + | } |
| + | return FALSE; |
| + | } |
| + | |
| const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, | |
| struct connectdata *conn) | |
| { | |
| @@ -201,6 +201,7 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, | |
| * CF_TYPE_HTTP implement a version of the HTTP protocol | |
| * CF_TYPE_SETUP filter is only needed for connection setup and | |
| * can be removed once connected | |
| + | * CF_TYPE_HTTPSRR filter that wants HTTPS-RR information |
| */ | |
| #define CF_TYPE_IP_CONNECT (1 << 0) | |
| #define CF_TYPE_SSL (1 << 1) | |
| @@ -208,6 +209,7 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, | |
| #define CF_TYPE_PROXY (1 << 3) | |
| #define CF_TYPE_HTTP (1 << 4) | |
| #define CF_TYPE_SETUP (1 << 5) | |
| + | #define CF_TYPE_HTTPSRR (1 << 6) |
| /* A connection filter type, e.g. specific implementation. */ | |
| struct Curl_cftype { | |
| @@ -354,6 +356,10 @@ int Curl_protocol_for_transport(uint8_t transport); | |
| const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf, | |
| struct Curl_easy *data); | |
| + | /* The filter (or one of its sub-filters) wants HTTPS-RR information. */ |
| + | bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data); |
| + | |
| #define CURL_CF_SSL_DEFAULT (-1) | |
| #define CURL_CF_SSL_DISABLE 0 | |
| #define CURL_CF_SSL_ENABLE 1 | |
| @@ -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 */ | |
| @@ -136,6 +136,17 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, | |
| void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id); | |
| + | /* How much time has gone by since start of resolve. |
| + | * Returns CURL_TIMEOUT_RESOLVE_MS if `resolv_id` is no longer valid. */ |
| + | timediff_t Curl_resolv_elapsed_ms(struct Curl_easy *data, |
| + | uint32_t resolv_id); |
| + | |
| + | /* Return TRUE if `resolv_id` has answers (positive or negative) to |
| + | * all queries in `dns_queries`. |
| + | * Queries not requested are considered answered. */ |
| + | bool Curl_resolv_has_answers(struct Curl_easy *data, |
| + | uint32_t resolv_id, uint8_t dns_queries); |
| + | |
| const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, | |
| uint32_t resolv_id, | |
| int ai_family, | |
| @@ -150,6 +161,8 @@ bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id); | |
| #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_elapsed_ms(x, y) CURL_TIMEOUT_RESOLVE_MS |
| + | #define Curl_resolv_has_answers(x, y, z) TRUE |
| #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 | |
| @@ -70,122 +70,153 @@ static CURLcode httpsrr_decode_alpn(const uint8_t *cp, size_t len, | |
| } | |
| #ifdef CURLVERBOSE | |
| - | static void httpsrr_report_addr(struct Curl_easy *data, int ai_family, |
| - | const uint8_t *addr, size_t total_len) |
| + | |
| + | static CURLcode httpsrr_print_addr(struct dynbuf *dyn, |
| + | 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; |
| + | CURLcode result = CURLE_OK; |
| - | 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 = ", "; |
| + | for(i = 0; (i < (total_len / alen)) && !result; ++i) { |
| + | if(!curlx_inet_ntop(ai_family, addr + (i * alen), buf, sizeof(buf))) |
| + | result = curlx_dyn_add(dyn, "<error parsing address>"); |
| + | else |
| + | result = curlx_dyn_addf(dyn, "%s%s", sep, buf); |
| + | 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); |
| + | return result; |
| } | |
| void Curl_httpsrr_trace(struct Curl_easy *data, | |
| - | struct Curl_https_rrinfo *hi) |
| + | struct Curl_https_rrinfo *rr) |
| { | |
| - | if(!hi || !hi->complete) { |
| + | struct dynbuf tmp; |
| + | CURLcode result; |
| + | |
| + | if(!rr || !rr->complete) { |
| CURL_TRC_DNS(data, "[HTTPS-RR] not available"); | |
| return; | |
| } | |
| + | curlx_dyn_init(&tmp, 1024); |
| + | result = Curl_httpsrr_print(&tmp, rr); |
| + | if(!result) |
| + | CURL_TRC_DNS(data, "HTTPS-RR: %s", curlx_dyn_ptr(&tmp)); |
| + | else |
| + | CURL_TRC_DNS(data, "Error printing HTTPS-RR information"); |
| + | curlx_dyn_free(&tmp); |
| + | } |
| - | 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"); |
| + | CURLcode Curl_httpsrr_print(struct dynbuf *tmp, |
| + | struct Curl_https_rrinfo *rr) |
| + | { |
| + | CURLcode result; |
| + | int i; |
| + | |
| + | curlx_dyn_reset(tmp); |
| + | result = curlx_dyn_addf(tmp, "%u %s", rr->priority, |
| + | rr->target ? rr->target : "."); |
| + | if(!result && rr->mandatory) |
| + | result = curlx_dyn_add(tmp, " mandatory-keys(ignored)"); |
| + | if(!result && rr->alpns[0]) { |
| + | const char *sep = "", *name; |
| + | result = curlx_dyn_add(tmp, " alpn="); |
| + | for(i = 0; !result && (i < 4); ++i) { |
| + | switch(rr->alpns[i]) { |
| + | case ALPN_h1: |
| + | name = "http/1.1"; |
| + | break; |
| + | case ALPN_h2: |
| + | name = "h2"; |
| + | break; |
| + | case ALPN_h3: |
| + | name = "h3"; |
| + | break; |
| + | default: |
| + | name = NULL; |
| + | } |
| + | if(name) { |
| + | result = curlx_dyn_addf(tmp, "%s%s", sep, name); |
| + | sep = ","; |
| + | } |
| + | } |
| + | } |
| + | if(!result && rr->port_set) { |
| + | result = curlx_dyn_addf(tmp, " port=%u", rr->port); |
| + | } |
| + | if(!result && rr->no_def_alpn) |
| + | result = curlx_dyn_add(tmp, " no-default-alpn"); |
| + | if(!result && rr->ipv6hints_len) { |
| + | result = curlx_dyn_add(tmp, " ipv6hint="); |
| + | if(!result) |
| + | result = httpsrr_print_addr( |
| + | tmp, AF_INET6, rr->ipv6hints, rr->ipv6hints_len); |
| + | } |
| + | if(!result && rr->ipv4hints_len) { |
| + | result = curlx_dyn_add(tmp, " ipv4hint="); |
| + | if(!result) |
| + | result = httpsrr_print_addr( |
| + | tmp, AF_INET, rr->ipv4hints, rr->ipv4hints_len); |
| + | } |
| + | if(!result && rr->echconfiglist_len) |
| + | result = curlx_dyn_addf(tmp, " ech=<%zu bytes>", rr->echconfiglist_len); |
| + | |
| + | return result; |
| } | |
| - | #else |
| - | #define httpsrr_report_addr(a, b, c, d) Curl_nop_stmt |
| #endif /* CURLVERBOSE */ | |
| - | CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *hi, |
| + | CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr, |
| uint16_t rrkey, const uint8_t *val, size_t vlen) | |
| { | |
| CURLcode result = CURLE_OK; | |
| switch(rrkey) { | |
| case HTTPS_RR_CODE_MANDATORY: | |
| - | hi->mandatory = TRUE; |
| + | rr->mandatory = TRUE; |
| break; | |
| case HTTPS_RR_CODE_ALPN: /* str_list */ | |
| - | result = httpsrr_decode_alpn(val, vlen, hi->alpns); |
| + | result = httpsrr_decode_alpn(val, vlen, rr->alpns); |
| break; | |
| case HTTPS_RR_CODE_NO_DEF_ALPN: | |
| if(vlen) /* no data */ | |
| return CURLE_BAD_FUNCTION_ARGUMENT; | |
| - | hi->no_def_alpn = TRUE; |
| + | rr->no_def_alpn = TRUE; |
| break; | |
| case HTTPS_RR_CODE_IPV4: /* addr4 list */ | |
| if(!vlen || (vlen & 3)) /* the size must be 4-byte aligned */ | |
| return CURLE_BAD_FUNCTION_ARGUMENT; | |
| - | curlx_free(hi->ipv4hints); |
| - | hi->ipv4hints = curlx_memdup(val, vlen); |
| - | if(!hi->ipv4hints) |
| + | curlx_free(rr->ipv4hints); |
| + | rr->ipv4hints = curlx_memdup(val, vlen); |
| + | if(!rr->ipv4hints) |
| return CURLE_OUT_OF_MEMORY; | |
| - | hi->ipv4hints_len = vlen; |
| + | rr->ipv4hints_len = vlen; |
| break; | |
| case HTTPS_RR_CODE_ECH: | |
| if(!vlen) | |
| return CURLE_BAD_FUNCTION_ARGUMENT; | |
| - | curlx_free(hi->echconfiglist); |
| - | hi->echconfiglist = curlx_memdup(val, vlen); |
| - | if(!hi->echconfiglist) |
| + | curlx_free(rr->echconfiglist); |
| + | rr->echconfiglist = curlx_memdup(val, vlen); |
| + | if(!rr->echconfiglist) |
| return CURLE_OUT_OF_MEMORY; | |
| - | hi->echconfiglist_len = vlen; |
| + | rr->echconfiglist_len = vlen; |
| break; | |
| case HTTPS_RR_CODE_IPV6: /* addr6 list */ | |
| if(!vlen || (vlen & 15)) /* the size must be 16-byte aligned */ | |
| return CURLE_BAD_FUNCTION_ARGUMENT; | |
| - | curlx_free(hi->ipv6hints); |
| - | hi->ipv6hints = curlx_memdup(val, vlen); |
| - | if(!hi->ipv6hints) |
| + | curlx_free(rr->ipv6hints); |
| + | rr->ipv6hints = curlx_memdup(val, vlen); |
| + | if(!rr->ipv6hints) |
| return CURLE_OUT_OF_MEMORY; | |
| - | hi->ipv6hints_len = vlen; |
| + | rr->ipv6hints_len = vlen; |
| break; | |
| case HTTPS_RR_CODE_PORT: | |
| if(vlen != 2) | |
| return CURLE_BAD_FUNCTION_ARGUMENT; | |
| - | hi->port = (uint16_t)((val[0] << 8) | val[1]); |
| - | hi->port_set = TRUE; |
| + | rr->port = (uint16_t)((val[0] << 8) | val[1]); |
| + | rr->port_set = TRUE; |
| break; | |
| default: | |
| /* unknown code */ | |
| @@ -213,6 +244,17 @@ void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo) | |
| rrinfo->complete = FALSE; | |
| } | |
| + | bool Curl_httpsrr_applicable(struct Curl_easy *data, |
| + | const struct Curl_https_rrinfo *rr) |
| + | { |
| + | if(!data->conn || !rr) |
| + | return FALSE; |
| + | return (data->conn && rr && |
| + | (!rr->target || !rr->target[0] || |
| + | (rr->target[0] == '.' && !rr->target[1])) && |
| + | (!rr->port_set || rr->port == data->conn->remote_port)); |
| + | } |
| + | |
| #ifdef USE_ARES | |
| static CURLcode httpsrr_opt(const ares_dns_rr_t *rr, | |
| @@ -35,6 +35,7 @@ | |
| #define MAX_HTTPSRR_ALPNS 4 | |
| struct Curl_easy; | |
| + | struct dynbuf; |
| struct Curl_https_rrinfo { | |
| char *rrname; /* if NULL, the same as the URL hostname */ | |
| @@ -59,7 +60,7 @@ struct Curl_https_rrinfo { | |
| BIT(complete); /* values have been successfully assigned */ | |
| }; | |
| - | CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *hi, |
| + | CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr, |
| uint16_t rrkey, const uint8_t *val, size_t vlen); | |
| struct Curl_https_rrinfo *Curl_httpsrr_dup_move( | |
| @@ -67,6 +68,11 @@ struct Curl_https_rrinfo *Curl_httpsrr_dup_move( | |
| void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo); | |
| + | /* TRUE if the record is applicable to the transfer and its connection. */ |
| + | bool Curl_httpsrr_applicable(struct Curl_easy *data, |
| + | const struct Curl_https_rrinfo *rr); |
| + | |
| + | |
| /* | |
| * Code points for DNS wire format SvcParams as per RFC 9460 | |
| */ | |
| @@ -84,8 +90,11 @@ CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, | |
| #endif /* USE_ARES */ | |
| #ifdef CURLVERBOSE | |
| + | |
| + | CURLcode Curl_httpsrr_print(struct dynbuf *tmp, |
| + | struct Curl_https_rrinfo *rr); |
| void Curl_httpsrr_trace(struct Curl_easy *data, | |
| - | struct Curl_https_rrinfo *hi); |
| + | struct Curl_https_rrinfo *rr); |
| #else | |
| #define Curl_httpsrr_trace(a, b) Curl_nop_stmt | |
| #endif | |