asyn-thread: use c-ares to resolve HTTPS RR

Allow building with c-ares and yet use threaded resolver for the main
host A/AAAA resolving:

  `--with-ares` provides the c-ares install path and defaults to use
  c-ares for name resolving

  `--with-threaded-resolver` still uses c-ares in the build (for HTTPS)
  but uses the threaded resolver for "normal" resolves.

It works similarly for cmake: ENABLE_ARES enables ares, and if
ENABLE_THREADED_RESOLVER also is set, c-ares is used for HTTPS RR and
the threaded resolver for "normal" resolves.

HTTPSRR and c-ares-rr are new features return by curl_version_info() and
thus shown by curl -V.

The c-ares-rr feature bit is there to make it possible to distinguish
between builds using c-ares for all name resolves and builds that use
the threaded resolves for the regular name resolves and c-ares for
HTTPSRR only. "c-ares-rr" means it does not use c-ares for "plain" name
resolves.

HTTPSRR support is EXPERIMENTAL only.

Closes #16054
This commit is contained in:
Daniel Stenberg 2025-01-21 11:42:20 +01:00
parent 520e67dd39
commit 0d4fdbf15d
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
18 changed files with 470 additions and 421 deletions

View file

@ -64,6 +64,14 @@
#include "inet_ntop.h"
#include "curl_threads.h"
#include "connect.h"
#ifdef USE_ARES
#include <ares.h>
#ifdef USE_HTTPSRR
#define USE_HTTPSRR_ARES 1 /* the combo */
#endif
#endif
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@ -145,32 +153,6 @@ static bool init_resolve_thread(struct Curl_easy *data,
const struct addrinfo *hints);
/* Data for synchronization between resolver thread and its parent */
struct thread_sync_data {
curl_mutex_t *mtx;
int done;
int port;
char *hostname; /* hostname to resolve, Curl_async.hostname
duplicate */
#ifndef CURL_DISABLE_SOCKETPAIR
struct Curl_easy *data;
curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */
#endif
int sock_error;
struct Curl_addrinfo *res;
#ifdef HAVE_GETADDRINFO
struct addrinfo hints;
#endif
struct thread_data *td; /* for thread-self cleanup */
};
struct thread_data {
curl_thread_t thread_hnd;
unsigned int poll_interval;
timediff_t interval_end;
struct thread_sync_data tsd;
};
static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
{
return &(data->state.async.tdata->tsd);
@ -220,7 +202,7 @@ int init_thread_sync_data(struct thread_data *td,
/* Treat the request as done until the thread actually starts so any early
* cleanup gets done properly.
*/
tsd->done = 1;
tsd->done = TRUE;
#ifdef HAVE_GETADDRINFO
DEBUGASSERT(hints);
tsd->hints = *hints;
@ -343,7 +325,7 @@ CURL_STDCALL getaddrinfo_thread(void *arg)
}
}
#endif
tsd->done = 1;
tsd->done = TRUE;
Curl_mutex_release(tsd->mtx);
}
@ -382,7 +364,7 @@ CURL_STDCALL gethostbyname_thread(void *arg)
free(td);
}
else {
tsd->done = 1;
tsd->done = TRUE;
Curl_mutex_release(tsd->mtx);
}
@ -398,19 +380,22 @@ static void destroy_async_data(struct Curl_async *async)
{
if(async->tdata) {
struct thread_data *td = async->tdata;
int done;
bool done;
#ifndef CURL_DISABLE_SOCKETPAIR
curl_socket_t sock_rd = td->tsd.sock_pair[0];
struct Curl_easy *data = td->tsd.data;
#endif
#ifdef USE_HTTPSRR_ARES
ares_destroy(data->state.async.tdata->channel);
#endif
/*
* if the thread is still blocking in the resolve syscall, detach it and
* let the thread do the cleanup...
*/
Curl_mutex_acquire(td->tsd.mtx);
done = td->tsd.done;
td->tsd.done = 1;
td->tsd.done = TRUE;
Curl_mutex_release(td->tsd.mtx);
if(!done) {
@ -439,6 +424,24 @@ static void destroy_async_data(struct Curl_async *async)
async->hostname = NULL;
}
#ifdef USE_HTTPSRR_ARES
static CURLcode resolve_httpsrr(struct Curl_easy *data,
struct Curl_async *asp)
{
int status = ares_init(&asp->tdata->channel);
if(status != ARES_SUCCESS)
return CURLE_FAILED_INIT;
memset(&asp->tdata->hinfo, 0, sizeof(struct Curl_https_rrinfo));
ares_query_dnsrec(asp->tdata->channel,
asp->hostname, ARES_CLASS_IN,
ARES_REC_TYPE_HTTPS,
Curl_dnsrec_done_cb, data, NULL);
return CURLE_OK;
}
#endif
/*
* init_resolve_thread() starts a new thread that performs the actual
* resolve. This function returns before the resolve is done.
@ -474,8 +477,8 @@ static bool init_resolve_thread(struct Curl_easy *data,
if(!asp->hostname)
goto err_exit;
/* The thread will set this to 1 when complete. */
td->tsd.done = 0;
/* The thread will set this TRUE when complete. */
td->tsd.done = FALSE;
#ifdef HAVE_GETADDRINFO
td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
@ -485,11 +488,14 @@ static bool init_resolve_thread(struct Curl_easy *data,
if(td->thread_hnd == curl_thread_t_null) {
/* The thread never started, so mark it as done here for proper cleanup. */
td->tsd.done = 1;
td->tsd.done = TRUE;
err = errno;
goto err_exit;
}
#ifdef USE_HTTPSRR_ARES
if(resolve_httpsrr(data, asp))
goto err_exit;
#endif
return TRUE;
err_exit:
@ -587,7 +593,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **entry)
{
struct thread_data *td = data->state.async.tdata;
int done = 0;
bool done = FALSE;
DEBUGASSERT(entry);
*entry = NULL;
@ -596,6 +602,10 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
DEBUGASSERT(td);
return CURLE_COULDNT_RESOLVE_HOST;
}
#ifdef USE_HTTPSRR_ARES
if(Curl_ares_perform(data->state.async.tdata->channel, 0) < 0)
return CURLE_UNRECOVERABLE_POLL;
#endif
Curl_mutex_acquire(td->tsd.mtx);
done = td->tsd.done;
@ -643,18 +653,28 @@ int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
timediff_t milli;
timediff_t ms;
struct resdata *reslv = (struct resdata *)data->state.async.resolver;
int socketi = 0;
#ifndef CURL_DISABLE_SOCKETPAIR
struct thread_data *td = data->state.async.tdata;
#else
(void)socks;
#endif
#ifdef USE_HTTPSRR_ARES
if(data->state.async.tdata) {
ret_val = Curl_ares_getsock(data, data->state.async.tdata->channel, socks);
for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++)
if(!ARES_GETSOCK_READABLE(ret_val, socketi) &&
!ARES_GETSOCK_WRITABLE(ret_val, socketi))
break;
}
#endif
#ifndef CURL_DISABLE_SOCKETPAIR
if(td) {
/* return read fd to client for polling the DNS resolution status */
socks[0] = td->tsd.sock_pair[0];
socks[socketi] = td->tsd.sock_pair[0];
td->tsd.data = data;
ret_val = GETSOCK_READSOCK(0);
ret_val = GETSOCK_READSOCK(socketi);
}
else {
#endif