curl-curl/lib/asyn-thrdd.c
Stefan Eissing 2b3dfb4ad4
lib: make resolving HTTPS DNS records reliable:
- allow to specify when they are wanted on starting a resolve
- match dns cache entries accordingly. An entry which never
  tried to get HTTPS-RRs is no answer for a resolve that wants
  it.
- fix late arrivals of resolve answers to match the "async"
  records that started them - if it still exists.
- provide for multiple "async" resolves in a transfer at the
  same time. We may need to resolve an IP interface while the
  main connection resolve has not finished yet.
- allow lookup of HTTPS-RR information as soon as it is
  available, even if A/AAAA queries are still ongoing.

For this, the "async" infrastructure is changed:

- Defined bits for DNS queries `CURL_DNSQ_A`, `CURL_DNSQ_AAAA`
  and `CURL_DNSQ_HTTPS`. These replace `ip_version` which says
  nothing about HTTPS.
  Use them in dns cache entries for matching.
- enhance the `async->id` to be a unique `uint32_t` for
  resolves inside one multi. This is weak, as the id may
  wrap around. However it is combined with the `mid` of
  the easy handle, making collisions highly unlikely.
  `data->state.async` is only accessed in few places where
  the mid/async-id match is performed.
- vtls: for ECH supporting TLS backends (openssl, rustls, wolfssl),
  retrieve the HTTPS-RR information from the dns connection filter.
  Delay the connect if the HTTPS-RR is needed, but has not
  been resolved yet.

The implementation of all this is complete for the threaded
resolver. c-ares resolver and DoH do not take advantage of
all new async features yet. To be done in separate PRs.

Details:

c-ares: cleanup settings and initialisation. Any ares channel
is only being created on starting a resolve and propagating
operations in setopt.c to the channel are not helpful.

Changed threaded+ares pollset handling so that they do not
overwrite each others `ASYNC_NAME` timeouts.

Add trace name 'threads' for tracing thread queue and
pool used by threaded resolver.

Closes #21175
2026-04-01 15:36:31 +02:00

818 lines
22 KiB
C

/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
/***********************************************************************
* Only for threaded name resolves builds
**********************************************************************/
#ifdef USE_RESOLV_THREADED
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef __VMS
#include <in.h>
#include <inet.h>
#endif
#ifdef HAVE_GETADDRINFO
#define RESOLVER_ENOMEM EAI_MEMORY /* = WSA_NOT_ENOUGH_MEMORY on Windows */
#else
#define RESOLVER_ENOMEM SOCKENOMEM
#endif
#include "urldata.h"
#include "cfilters.h"
#include "curl_addrinfo.h"
#include "curl_trc.h"
#include "hostip.h"
#include "httpsrr.h"
#include "url.h"
#include "multiif.h"
#include "curl_threads.h"
#include "progress.h"
#include "rand.h"
#include "select.h"
#include "thrdqueue.h"
#include "curlx/strparse.h"
#include "curlx/wait.h"
#ifdef USE_ARES
#include <ares.h>
#ifdef USE_HTTPSRR
#define USE_HTTPSRR_ARES /* the combo */
#endif
#endif
/*
* Curl_async_global_init()
* Called from curl_global_init() to initialize global resolver environment.
* Does nothing here.
*/
int Curl_async_global_init(void)
{
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
if(ares_library_init(ARES_LIB_INIT_ALL)) {
return CURLE_FAILED_INIT;
}
#endif
return CURLE_OK;
}
/*
* Curl_async_global_cleanup()
* Called from curl_global_cleanup() to destroy global resolver environment.
* Does nothing here.
*/
void Curl_async_global_cleanup(void)
{
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
ares_library_cleanup();
#endif
}
#ifdef CURLVERBOSE
#define CURL_ASYN_ITEM_DESC_LEN 64
#define async_item_description(x) (x)->description
#else
#define async_item_description(x) NULL
#endif
struct async_thrdd_item {
struct Curl_addrinfo *res;
#ifdef CURLVERBOSE
char description[CURL_ASYN_ITEM_DESC_LEN];
#endif
int sock_error;
uint32_t mid;
uint32_t resolv_id;
uint16_t port;
uint8_t transport;
uint8_t dns_queries;
#ifdef DEBUGBUILD
uint32_t delay_ms;
uint32_t delay_fail_ms;
#endif
char hostname[1];
};
/* Give up reference to add_ctx */
static void async_thrdd_item_destroy(struct async_thrdd_item *item)
{
if(item) {
if(item->res)
Curl_freeaddrinfo(item->res);
curlx_free(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 transport)
{
size_t hostlen = strlen(hostname);
struct async_thrdd_item *item;
item = curlx_calloc(1, sizeof(*item) + hostlen);
if(!item)
return NULL;
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->transport = transport;
#ifdef CURLVERBOSE
curl_msnprintf(item->description, sizeof(item->description),
"[%" 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
{
const char *p = getenv("CURL_DBG_RESOLV_DELAY");
if(p) {
curl_off_t l;
if(!curlx_str_number(&p, &l, UINT32_MAX)) {
item->delay_ms = (uint32_t)l;
}
}
p = getenv("CURL_DBG_RESOLV_FAIL_DELAY");
if(p) {
curl_off_t l;
if(!curlx_str_number(&p, &l, UINT32_MAX)) {
unsigned char c = 0;
Curl_rand_bytes(data, FALSE, &c, 1);
item->delay_fail_ms = (uint32_t)l + c;
}
}
}
#endif
return item;
}
#ifdef USE_HTTPSRR_ARES
static void async_thrdd_rr_done(void *user_data, ares_status_t status,
size_t timeouts,
const ares_dns_record_t *dnsrec)
{
struct Curl_resolv_async *async = user_data;
struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
(void)timeouts;
if(!thrdd)
return;
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,
struct Curl_resolv_async *async)
{
struct async_thrdd_ctx *thrdd = &async->thrdd;
int status;
char *rrname = NULL;
DEBUGASSERT(!thrdd->rr.channel);
if(async->port != 443) {
rrname = curl_maprintf("_%d_.https.%s", async->port, async->hostname);
if(!rrname)
return CURLE_OUT_OF_MEMORY;
}
status = ares_init_options(&thrdd->rr.channel, NULL, 0);
if(status != ARES_SUCCESS) {
thrdd->rr.channel = NULL;
curlx_free(rrname);
return CURLE_FAILED_INIT;
}
#ifdef DEBUGBUILD
if(getenv("CURL_DNS_SERVER")) {
const char *servers = getenv("CURL_DNS_SERVER");
status = ares_set_servers_ports_csv(thrdd->rr.channel, servers);
if(status) {
curlx_free(rrname);
return CURLE_FAILED_INIT;
}
}
#endif
memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
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, async, NULL);
async->queries_ongoing++;
CURL_TRC_DNS(data, "[HTTPS-RR] initiated request for %s",
rrname ? rrname : async->hostname);
return CURLE_OK;
}
#endif
void Curl_async_thrdd_shutdown(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
Curl_async_thrdd_destroy(data, async);
}
static bool async_thrdd_match_item(void *qitem, void *match_data)
{
struct Curl_easy *data = match_data;
struct async_thrdd_item *item = qitem;
return item->mid == data->mid;
}
void Curl_async_thrdd_destroy(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
(void)data;
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,
async_thrdd_match_item, data);
}
#ifdef USE_HTTPSRR_ARES
if(async->thrdd.rr.channel) {
ares_destroy(async->thrdd.rr.channel);
async->thrdd.rr.channel = NULL;
}
Curl_httpsrr_cleanup(&async->thrdd.rr.hinfo);
#endif
async_thrdd_item_destroy(async->thrdd.res_A);
async->thrdd.res_A = NULL;
async_thrdd_item_destroy(async->thrdd.res_AAAA);
async->thrdd.res_AAAA = NULL;
}
/*
* 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, uint32_t resolv_id,
struct Curl_dns_entry **pdns)
{
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
timediff_t milli, ms;
if(!thrdd)
return CURLE_FAILED_INIT;
while(async->queries_ongoing && !async->done) {
Curl_async_thrdd_multi_process(data->multi);
if(async->done)
break;
ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
if(ms < 3)
milli = 0;
else if(ms <= 50)
milli = ms / 3;
else if(ms <= 250)
milli = 50;
else
milli = 200;
CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli);
curlx_wait_ms(milli);
}
return Curl_async_take_result(data, async, pdns);
}
#ifdef HAVE_GETADDRINFO
/* Process the item, using Curl_getaddrinfo_ex() */
static void async_thrdd_item_process(void *arg)
{
struct async_thrdd_item *item = arg;
struct addrinfo hints;
char service[12];
int pf = PF_INET;
int rc;
#ifdef DEBUGBUILD
if(item->delay_ms) {
curlx_wait_ms(item->delay_ms);
}
if(item->delay_fail_ms) {
curlx_wait_ms(item->delay_fail_ms);
return;
}
#endif
memset(&hints, 0, sizeof(hints));
#ifdef CURLRES_IPV6
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
hints.ai_flags |= AI_NUMERICSERV;
#endif
rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res);
if(rc) {
item->sock_error = SOCKERRNO ? SOCKERRNO : rc;
if(item->sock_error == 0)
item->sock_error = RESOLVER_ENOMEM;
}
else {
Curl_addrinfo_set_port(item->res, item->port);
}
}
#else /* HAVE_GETADDRINFO */
/* Process the item, using Curl_ipv4_resolve_r() */
static void async_thrdd_item_process(void *item)
{
struct async_thrdd_item *item = arg;
#ifdef DEBUGBUILD
if(item->delay_ms) {
curlx_wait_ms(item->delay_ms);
}
if(item->delay_fail_ms) {
curlx_wait_ms(item->delay_fail_ms);
return;
}
#endif
item->res = Curl_ipv4_resolve_r(item->hostname, item->port);
if(!item->res) {
item->sock_error = SOCKERRNO;
if(item->sock_error == 0)
item->sock_error = RESOLVER_ENOMEM;
}
}
#endif /* HAVE_GETADDRINFO */
#ifdef ENABLE_WAKEUP
static void async_thrdd_event(const struct curl_thrdq *tqueue,
Curl_thrdq_event ev,
void *user_data)
{
struct Curl_multi *multi = user_data;
(void)tqueue;
switch(ev) {
case CURL_THRDQ_EV_ITEM_DONE:
(void)curl_multi_wakeup(multi);
break;
default:
break;
}
}
#else
#define async_thrdd_event NULL
#endif
static void async_thrdd_item_free(void *item)
{
async_thrdd_item_destroy(item);
}
/* Create a thread queue for processing resolv items */
CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi,
uint32_t min_threads,
uint32_t max_threads,
uint32_t idle_time_ms)
{
CURLcode result;
DEBUGASSERT(!multi->resolv_thrdq);
result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0,
min_threads, max_threads, idle_time_ms,
async_thrdd_item_free,
async_thrdd_item_process,
async_thrdd_event,
multi);
#ifdef DEBUGBUILD
if(!result) {
const char *p = getenv("CURL_DBG_RESOLV_MAX_THREADS");
if(p) {
curl_off_t l;
if(!curlx_str_number(&p, &l, UINT32_MAX)) {
result = Curl_async_thrdd_multi_set_props(
multi, min_threads, (uint32_t)l, idle_time_ms);
}
}
}
#endif
return result;
}
/* Tear down the thread queue, joining active threads or detaching them */
void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join)
{
if(multi->resolv_thrdq) {
Curl_thrdq_destroy(multi->resolv_thrdq, join);
multi->resolv_thrdq = NULL;
}
}
#ifdef CURLVERBOSE
static void async_thrdd_report_item(struct Curl_easy *data,
struct async_thrdd_item *item)
{
char buf[MAX_IPADR_LEN];
struct dynbuf tmp;
const char *sep = "";
const struct Curl_addrinfo *ai = item->res;
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) {
Curl_printable_address(ai, buf, sizeof(buf));
result = curlx_dyn_addf(&tmp, "%s%s", sep, buf);
if(result) {
CURL_TRC_DNS(data, "too many IP, cannot show");
goto out;
}
sep = ", ";
}
}
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)"));
out:
curlx_dyn_free(&tmp);
}
#endif /* CURLVERBOSE */
/* Process the receiving end of the thread queue, dispatching
* processed items to their transfer when it can still be found
* and has an `async` state present. Otherwise, destroy the item. */
void Curl_async_thrdd_multi_process(struct Curl_multi *multi)
{
struct Curl_easy *data;
void *qitem;
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);
if(data)
async = Curl_async_get(data, item->resolv_id);
if(async) {
struct async_thrdd_item **pdest = &async->thrdd.res_A;
async->dns_responses |= item->dns_queries;
--async->queries_ongoing;
async->done = !async->queries_ongoing;
#ifdef CURLRES_IPV6
if(item->dns_queries & CURL_DNSQ_AAAA)
pdest = &async->thrdd.res_AAAA;
#endif
if(!*pdest) {
VERBOSE(async_thrdd_report_item(data, item));
*pdest = item;
item = NULL;
}
else
DEBUGASSERT(0); /* should not receive duplicates here */
Curl_multi_mark_dirty(data);
}
async_thrdd_item_free(item);
}
#ifdef CURLVERBOSE
Curl_thrdq_trace(multi->resolv_thrdq, multi->admin);
#endif
}
CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi,
uint32_t min_threads,
uint32_t max_threads,
uint32_t idle_time_ms)
{
return Curl_thrdq_set_props(multi->resolv_thrdq, 0,
min_threads, max_threads, idle_time_ms);
}
static CURLcode async_thrdd_query(struct Curl_easy *data,
struct Curl_resolv_async *async,
uint8_t dns_queries)
{
struct async_thrdd_item *item;
CURLcode result;
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, "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->queries_ongoing++;
out:
if(item)
async_thrdd_item_free(item);
return result;
}
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
CURLcode result = CURLE_FAILED_INIT;
void *resolver = NULL;
if(async->queries_ongoing || async->done)
return CURLE_FAILED_INIT;
#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
result = Curl_resolv_announce_start(data, resolver);
if(result)
return result;
#ifdef CURL_IPRESOLVE_V6
/* 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
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);
#endif
out:
if(result)
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 Curl_resolv_async *async,
struct easy_pollset *ps)
{
timediff_t timeout_ms;
timeout_ms = Curl_async_timeleft_ms(data, async);
#ifdef USE_HTTPSRR_ARES
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(!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
stutter_ms = 200;
timeout_ms = CURLMIN(stutter_ms, timeout_ms);
#endif
Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME);
}
return CURLE_OK;
}
/*
* Curl_async_take_result() is called repeatedly to check if a previous
* name resolve request has completed. It should also make sure to time-out if
* the operation seems to take too long.
*/
CURLcode Curl_async_take_result(struct Curl_easy *data,
struct Curl_resolv_async *async,
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(!async->queries_ongoing && !async->done) {
DEBUGASSERT(0);
return CURLE_FAILED_INIT;
}
#ifdef USE_HTTPSRR_ARES
/* best effort, ignore errors */
if(thrdd->rr.channel)
(void)Curl_ares_perform(thrdd->rr.channel, 0);
#endif
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)) {
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(thrdd->rr.channel) {
struct Curl_https_rrinfo *lhrr = NULL;
if(thrdd->rr.hinfo.complete) {
lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
if(!lhrr) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
Curl_httpsrr_trace(data, lhrr);
Curl_dns_entry_set_https_rr(dns, lhrr);
}
#endif
}
if(dns) {
CURL_TRC_DNS(data, "resolving complete");
*pdns = dns;
dns = NULL;
}
#ifdef CURLVERBOSE
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);
if(result &&
(result != CURLE_COULDNT_RESOLVE_HOST) &&
(result != CURLE_COULDNT_RESOLVE_PROXY)) {
CURL_TRC_DNS(data, "Error %d resolving %s:%d",
result, async->hostname, async->port);
}
return result;
}
static const struct Curl_addrinfo *
async_thrdd_get_ai(const struct Curl_addrinfo *ai,
int ai_family, unsigned int index)
{
unsigned int i = 0;
for(i = 0; ai; ai = ai->ai_next) {
if(ai->ai_family == ai_family) {
if(i == index)
return ai;
++i;
}
}
return NULL;
}
const struct Curl_addrinfo *
Curl_async_get_ai(struct Curl_easy *data,
struct Curl_resolv_async *async,
int ai_family, unsigned int index)
{
struct async_thrdd_ctx *thrdd = &async->thrdd;
(void)data;
switch(ai_family) {
case AF_INET:
if(thrdd->res_A)
return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index);
break;
case AF_INET6:
if(thrdd->res_AAAA)
return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index);
break;
default:
break;
}
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 */