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
This commit is contained in:
Stefan Eissing 2026-03-31 11:45:21 +02:00 committed by Daniel Stenberg
parent 03a792b186
commit 2b3dfb4ad4
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
40 changed files with 1242 additions and 873 deletions

View File

@ -142,6 +142,10 @@ Tracing of SSL Session handling, e.g. caching/import/export.
Tracing of SMTP operations when this protocol is enabled in your build.
## `threads`
Tracing of thread queue and pools, used in threaded DNS resolving.
## `timer`
Tracing of timers set for transfers.

View File

@ -76,7 +76,13 @@
static int ares_ver = 0;
static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
bool reset_on_null);
struct Curl_resolv_async *async);
static CURLcode async_ares_set_dns_interface(struct Curl_easy *data,
struct Curl_resolv_async *async);
static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data,
struct Curl_resolv_async *async);
static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data,
struct Curl_resolv_async *async);
/*
* Curl_async_global_init() - the generic low-level asynchronous name
@ -152,19 +158,19 @@ static CURLcode async_ares_init(struct Curl_easy *data,
goto out;
}
rc = async_ares_set_dns_servers(data, FALSE);
rc = async_ares_set_dns_servers(data, async);
if(rc && rc != CURLE_NOT_BUILT_IN)
goto out;
rc = Curl_async_ares_set_dns_interface(data);
rc = async_ares_set_dns_interface(data, async);
if(rc && rc != CURLE_NOT_BUILT_IN)
goto out;
rc = Curl_async_ares_set_dns_local_ip4(data);
rc = async_ares_set_dns_local_ip4(data, async);
if(rc && rc != CURLE_NOT_BUILT_IN)
goto out;
rc = Curl_async_ares_set_dns_local_ip6(data);
rc = async_ares_set_dns_local_ip6(data, async);
if(rc && rc != CURLE_NOT_BUILT_IN)
goto out;
@ -178,28 +184,6 @@ out:
return rc;
}
static CURLcode async_ares_init_lazy(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct async_ares_ctx *ares = &async->ares;
if(!ares->channel)
return async_ares_init(data, async);
return CURLE_OK;
}
CURLcode Curl_async_get_impl(struct Curl_easy *data,
struct Curl_resolv_async *async,
void **impl)
{
struct async_ares_ctx *ares = &async->ares;
CURLcode result = CURLE_OK;
if(!ares->channel) {
result = async_ares_init(data, async);
}
*impl = ares->channel;
return result;
}
/*
* async_ares_cleanup() cleans up async resolver data.
*/
@ -238,18 +222,21 @@ void Curl_async_ares_destroy(struct Curl_easy *data,
async_ares_cleanup(async);
}
/*
* Curl_async_pollset() is called when someone from the outside world
* (using curl_multi_fdset()) wants to get our fd_set setup.
*/
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
CURLcode Curl_async_pollset(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct easy_pollset *ps)
{
struct Curl_resolv_async *async = data->state.async;
struct async_ares_ctx *ares = async ? &async->ares : NULL;
if(ares && ares->channel)
return Curl_ares_pollset(data, ares->channel, ps);
return CURLE_OK;
struct async_ares_ctx *ares = &async->ares;
CURLcode result = CURLE_OK;
if(ares->channel) {
result = Curl_ares_pollset(data, ares->channel, ps);
if(!result) {
timediff_t ms = Curl_ares_timeout_ms(data, async, ares->channel);
Curl_expire(data, ms, EXPIRE_ASYNC_NAME);
}
}
return result;
}
/*
@ -276,25 +263,28 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
goto out;
}
if(!ares->num_pending) {
if(!async->queries_ongoing) {
/* all c-ares operations done, what is the result to report? */
result = ares->result;
if(ares->ares_status == ARES_SUCCESS && !result) {
struct Curl_dns_entry *dns =
Curl_dnscache_mk_entry(data, &ares->temp_ai,
async->hostname, async->port,
async->ip_version);
Curl_dnscache_mk_entry(data, async->dns_queries, &ares->temp_ai,
async->hostname, async->port);
if(!dns) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
#ifdef HTTPSRR_WORKS
{
struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
if(!lhrr)
result = CURLE_OUT_OF_MEMORY;
if(async->dns_queries & CURL_DNSQ_HTTPS) {
if(ares->hinfo.complete) {
struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
if(!lhrr)
result = CURLE_OUT_OF_MEMORY;
else
Curl_dns_entry_set_https_rr(dns, lhrr);
}
else
dns->hinfo = lhrr;
Curl_dns_entry_set_https_rr(dns, NULL);
}
#endif
if(!result) {
@ -362,6 +352,27 @@ Curl_async_get_ai(struct Curl_easy *data,
return NULL;
}
#ifdef USE_HTTPSRR
const struct Curl_https_rrinfo *
Curl_async_get_https(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
if(Curl_async_knows_https(data, async))
return &async->ares.hinfo;
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->queries_ongoing;
return TRUE; /* we know it will never come */
}
#endif /* USE_HTTPSRR */
/*
* Curl_async_await()
*
@ -373,17 +384,20 @@ Curl_async_get_ai(struct Curl_easy *data,
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
CURLcode Curl_async_await(struct Curl_easy *data,
struct Curl_resolv_async *async,
CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id,
struct Curl_dns_entry **pdns)
{
struct async_ares_ctx *ares = &async->ares;
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
struct async_ares_ctx *ares = async ? &async->ares : NULL;
struct curltime start = *Curl_pgrs_now(data);
CURLcode result = CURLE_OK;
DEBUGASSERT(pdns);
*pdns = NULL; /* clear on entry */
if(!ares)
return CURLE_FAILED_INIT;
/* Wait for the name resolve query to complete or time out. */
while(!result) {
timediff_t timeout_ms;
@ -391,8 +405,8 @@ CURLcode Curl_async_await(struct Curl_easy *data,
timeout_ms = Curl_timeleft_ms(data);
if(!timeout_ms) { /* no applicable timeout from `data`*/
timediff_t elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &start);
if(elapsed_ms < (CURL_TIMEOUT_RESOLVE * 1000))
timeout_ms = (CURL_TIMEOUT_RESOLVE * 1000) - elapsed_ms;
if(elapsed_ms < CURL_TIMEOUT_RESOLVE_MS)
timeout_ms = CURL_TIMEOUT_RESOLVE_MS - elapsed_ms;
else
timeout_ms = -1;
}
@ -503,8 +517,7 @@ async_ares_node2addr(struct ares_addrinfo_node *node)
static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts,
struct ares_addrinfo *ares_ai)
{
struct Curl_easy *data = (struct Curl_easy *)user_data;
struct Curl_resolv_async *async = data->state.async;
struct Curl_resolv_async *async = user_data;
struct async_ares_ctx *ares = async ? &async->ares : NULL;
(void)timeouts;
@ -516,11 +529,11 @@ static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts,
ares->temp_ai = async_ares_node2addr(ares_ai->nodes);
ares_freeaddrinfo(ares_ai);
}
ares->num_pending--;
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 ");
if(async->dns_queries & CURL_DNSQ_A)
async->dns_responses |= CURL_DNSQ_A;
if(async->dns_queries & CURL_DNSQ_AAAA)
async->dns_responses |= CURL_DNSQ_AAAA;
async->queries_ongoing--;
}
#ifdef USE_HTTPSRR
@ -528,23 +541,17 @@ static void async_ares_rr_done(void *user_data, ares_status_t status,
size_t timeouts,
const ares_dns_record_t *dnsrec)
{
struct Curl_easy *data = user_data;
struct Curl_resolv_async *async = data->state.async;
struct Curl_resolv_async *async = user_data;
struct async_ares_ctx *ares = async ? &async->ares : NULL;
if(!ares)
return;
(void)timeouts;
--ares->num_pending;
CURL_TRC_DNS(data, "ares: httpsrr done, status=%d, pending=%d, "
"dnsres=%sfound",
status, ares->num_pending,
(dnsrec &&
ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)) ?
"" : "not ");
async->dns_responses |= CURL_DNSQ_HTTPS;
async->queries_ongoing--;
if((ARES_SUCCESS != status) || !dnsrec)
return;
ares->result = Curl_httpsrr_from_ares(data, dnsrec, &ares->hinfo);
ares->result = Curl_httpsrr_from_ares(dnsrec, &ares->hinfo);
}
#endif /* USE_HTTPSRR */
@ -557,25 +564,24 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct async_ares_ctx *ares = &async->ares;
#ifdef USE_HTTPSRR
char *rrname = NULL;
#endif
if(async_ares_init_lazy(data, async))
CURL_TRC_DNS(data, "ares getaddrinfo(%s:%u)", async->hostname, async->port);
if(ares->channel) {
DEBUGASSERT(0);
return CURLE_FAILED_INIT;
#ifdef USE_HTTPSRR
if(async->port != 443) {
rrname = curl_maprintf("_%d._https.%s", async->port, async->hostname);
if(!rrname)
return CURLE_OUT_OF_MEMORY;
}
#endif
if(async_ares_init(data, async))
return CURLE_FAILED_INIT;
/* initial status - failed */
ares->ares_status = ARES_ENOTFOUND;
ares->result = CURLE_OK;
ares->result = Curl_resolv_announce_start(data, ares->channel);
if(ares->result)
return ares->result;
#if defined(CURLVERBOSE) && ARES_VERSION >= 0x011800 /* >= v1.24.0 */
if(CURL_TRC_DNS_is_verbose(data)) {
char *csv = ares_get_servers_csv(ares->channel);
@ -590,12 +596,11 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
int pf = PF_INET;
memset(&hints, 0, sizeof(hints));
#ifdef CURLRES_IPV6
if((async->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* The stack seems to be IPv6-enabled */
if(async->ip_version == CURL_IPRESOLVE_V6)
pf = PF_INET6;
else
if(async->dns_queries & CURL_DNSQ_AAAA) {
if(async->dns_queries & CURL_DNSQ_A)
pf = PF_UNSPEC;
else
pf = PF_INET6;
}
#endif /* CURLRES_IPV6 */
CURL_TRC_DNS(data, "asyn-ares: fire off getaddrinfo for %s",
@ -610,23 +615,28 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
*/
hints.ai_flags = ARES_AI_NUMERICSERV;
curl_msnprintf(service, sizeof(service), "%d", async->port);
ares->num_pending = 1;
async->queries_ongoing = 1;
ares_getaddrinfo(ares->channel, async->hostname,
service, &hints, async_ares_addrinfo_cb, data);
service, &hints, async_ares_addrinfo_cb, async);
}
#ifdef USE_HTTPSRR
{
memset(&ares->hinfo, 0, sizeof(ares->hinfo));
if(async->dns_queries & CURL_DNSQ_HTTPS) {
char *rrname = NULL;
if(async->port != 443) {
rrname = curl_maprintf("_%d._https.%s", async->port, async->hostname);
if(!rrname)
return CURLE_OUT_OF_MEMORY;
}
CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR: %s",
rrname ? rrname : async->hostname);
memset(&ares->hinfo, 0, sizeof(ares->hinfo));
ares->hinfo.port = -1;
ares->hinfo.rrname = rrname;
ares->num_pending++; /* one more */
async->queries_ongoing++;
ares_query_dnsrec(ares->channel,
rrname ? rrname : async->hostname,
ARES_CLASS_IN, ARES_REC_TYPE_HTTPS,
async_ares_rr_done, data, NULL);
async_ares_rr_done, async, NULL);
}
#endif
@ -640,9 +650,8 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
* 2. When we lazy init the ares channel and NULL means that there
* are no preferences and we do not reset any existing channel. */
static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
bool reset_on_null)
struct Curl_resolv_async *async)
{
struct Curl_resolv_async *async = data->state.async;
struct async_ares_ctx *ares = async ? &async->ares : NULL;
CURLcode result = CURLE_NOT_BUILT_IN;
const char *servers = data->set.str[STRING_DNS_SERVERS];
@ -653,12 +662,8 @@ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
servers = getenv("CURL_DNS_SERVER");
#endif
if(!servers) {
if(reset_on_null) {
Curl_async_destroy(data);
}
if(!servers)
return CURLE_OK;
}
/* if channel is not there, this is a parameter check */
if(ares && ares->channel)
@ -681,14 +686,9 @@ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
return result;
}
CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data)
static CURLcode async_ares_set_dns_interface(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
return async_ares_set_dns_servers(data, TRUE);
}
CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data)
{
struct Curl_resolv_async *async = data->state.async;
struct async_ares_ctx *ares = async ? &async->ares : NULL;
const char *interf = data->set.str[STRING_DNS_INTERFACE];
@ -702,9 +702,9 @@ CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data)
return CURLE_OK;
}
CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct Curl_resolv_async *async = data->state.async;
struct async_ares_ctx *ares = async ? &async->ares : NULL;
struct in_addr a4;
const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4];
@ -726,10 +726,10 @@ CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
return CURLE_OK;
}
CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data)
static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
#ifdef USE_IPV6
struct Curl_resolv_async *async = data->state.async;
struct async_ares_ctx *ares = async ? &async->ares : NULL;
unsigned char a6[INET6_ADDRSTRLEN];
const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6];
@ -752,6 +752,7 @@ CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data)
return CURLE_OK;
#else /* no IPv6 support */
(void)data;
(void)async;
return CURLE_NOT_BUILT_IN;
#endif
}

View File

@ -42,9 +42,11 @@
#endif
#include "urldata.h"
#include "connect.h"
#include "curl_trc.h"
#include "hostip.h"
#include "multiif.h"
#include "progress.h"
#include "select.h"
#include "url.h"
@ -53,6 +55,17 @@
**********************************************************************/
#ifdef CURLRES_ASYNCH
timediff_t Curl_async_timeleft_ms(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
if(async->timeout_ms) {
timediff_t elapsed_ms =
curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
return async->timeout_ms - elapsed_ms;
}
return Curl_timeleft_ms(data);
}
#ifdef USE_ARES
#if ARES_VERSION < 0x011000
@ -71,12 +84,8 @@ CURLcode Curl_ares_pollset(struct Curl_easy *data,
ares_channel channel,
struct easy_pollset *ps)
{
struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 };
struct timeval timebuf;
curl_socket_t sockets[16]; /* ARES documented limit */
unsigned int bitmap, i;
struct timeval *timeout;
timediff_t milli;
CURLcode result = CURLE_OK;
DEBUGASSERT(channel);
@ -97,15 +106,33 @@ CURLcode Curl_ares_pollset(struct Curl_easy *data,
if(result)
return result;
}
timeout = ares_timeout(channel, &maxtime, &timebuf);
if(!timeout)
timeout = &maxtime;
milli = curlx_tvtoms(timeout);
Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
return result;
}
timediff_t Curl_ares_timeout_ms(struct Curl_easy *data,
struct Curl_resolv_async *async,
ares_channel channel)
{
timediff_t async_timeout_ms;
DEBUGASSERT(channel);
if(!channel)
return -1;
async_timeout_ms = Curl_async_timeleft_ms(data, async);
if((async_timeout_ms > 0) && (async_timeout_ms < INT_MAX)) {
struct timeval timebuf;
struct timeval *timeout;
struct timeval end = { (int)async_timeout_ms / 1000,
((int)async_timeout_ms % 1000) * 1000 };
timeout = ares_timeout(channel, &end, &timebuf);
if(timeout)
return curlx_tvtoms(timeout);
}
return async_timeout_ms;
}
/*
* Curl_ares_perform()
*
@ -178,36 +205,40 @@ int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms)
#include "doh.h"
void Curl_async_shutdown(struct Curl_easy *data)
void Curl_async_shutdown(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
if(data->state.async) {
CURL_TRC_DNS(data, "shutdown async");
if(async) {
CURL_TRC_DNS(data, "[%u] shutdown async", async->id);
async->shutdown = TRUE;
#ifdef USE_RESOLV_ARES
Curl_async_ares_shutdown(data, data->state.async);
Curl_async_ares_shutdown(data, async);
#endif
#ifdef USE_RESOLV_THREADED
Curl_async_thrdd_shutdown(data, data->state.async);
Curl_async_thrdd_shutdown(data, async);
#endif
#ifndef CURL_DISABLE_DOH
Curl_doh_cleanup(data, data->state.async);
Curl_doh_cleanup(data, async);
#endif
}
}
void Curl_async_destroy(struct Curl_easy *data)
void Curl_async_destroy(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
if(data->state.async) {
CURL_TRC_DNS(data, "destroy async");
if(async) {
CURL_TRC_DNS(data, "[%u] destroy async", async->id);
async->shutdown = TRUE;
#ifdef USE_RESOLV_ARES
Curl_async_ares_destroy(data, data->state.async);
Curl_async_ares_destroy(data, async);
#endif
#ifdef USE_RESOLV_THREADED
Curl_async_thrdd_destroy(data, data->state.async);
Curl_async_thrdd_destroy(data, async);
#endif
#ifndef CURL_DISABLE_DOH
Curl_doh_cleanup(data, data->state.async);
Curl_doh_cleanup(data, async);
#endif
Curl_safefree(data->state.async);
Curl_safefree(async);
}
}

View File

@ -99,16 +99,6 @@ void Curl_async_global_cleanup(void)
#endif
}
CURLcode Curl_async_get_impl(struct Curl_easy *data,
struct Curl_resolv_async *async,
void **impl)
{
(void)data;
(void)async;
*impl = NULL;
return CURLE_OK;
}
#ifdef CURLVERBOSE
#define CURL_ASYN_ITEM_DESC_LEN 64
#define async_item_description(x) (x)->description
@ -123,10 +113,10 @@ struct async_thrdd_item {
#endif
int sock_error;
uint32_t mid;
uint32_t async_id;
uint32_t resolv_id;
uint16_t port;
uint8_t ip_version;
uint8_t transport;
uint8_t dns_queries;
#ifdef DEBUGBUILD
uint32_t delay_ms;
uint32_t delay_fail_ms;
@ -147,13 +137,12 @@ static void async_thrdd_item_destroy(struct async_thrdd_item *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 ip_version, uint8_t transport,
uint32_t async_id)
uint8_t transport)
{
size_t hostlen = strlen(hostname);
struct async_thrdd_item *item;
VERBOSE(const char *qtype);
item = curlx_calloc(1, sizeof(*item) + hostlen);
if(!item)
@ -161,18 +150,18 @@ async_thrdd_item_create(struct Curl_easy *data,
if(hostlen) /* NUL byte of name already in struct size */
memcpy(item->hostname, hostname, hostlen);
item->port = port;
item->ip_version = ip_version;
item->transport = transport;
item->mid = data->mid;
item->async_id = async_id;
item->resolv_id = resolv_id;
item->dns_queries = dns_queries;
item->port = port;
item->transport = transport;
#ifdef CURLVERBOSE
qtype = (ip_version == CURL_IPRESOLVE_WHATEVER) ? "A+AAAA":
((ip_version == CURL_IPRESOLVE_V6) ? "AAAA" : "A");
curl_msnprintf(item->description, sizeof(item->description),
"[%" FMT_OFF_T "/%d] %s %s:%u",
data->id, item->async_id, qtype, item->hostname, item->port);
"[%" 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
@ -205,14 +194,18 @@ static void async_thrdd_rr_done(void *user_data, ares_status_t status,
size_t timeouts,
const ares_dns_record_t *dnsrec)
{
struct Curl_easy *data = user_data;
struct async_thrdd_ctx *thrdd = &data->state.async->thrdd;
struct Curl_resolv_async *async = user_data;
struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
(void)timeouts;
thrdd->rr.done = TRUE;
if((ARES_SUCCESS != status) || !dnsrec)
if(!thrdd)
return;
thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo);
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,
@ -246,13 +239,13 @@ static CURLcode async_rr_start(struct Curl_easy *data,
#endif
memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
thrdd->rr.hinfo.port = -1;
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, data, NULL);
CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s",
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;
}
@ -275,7 +268,7 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
(void)data;
if(async->thrdd.queued && !async->thrdd.done &&
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,
@ -298,17 +291,19 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data,
* 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,
struct Curl_resolv_async *async,
CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id,
struct Curl_dns_entry **pdns)
{
struct async_thrdd_ctx *thrdd = &async->thrdd;
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
timediff_t milli, ms;
CURL_TRC_DNS(data, "[async] await %s", async->hostname);
while(thrdd->queued && !thrdd->done) {
if(!thrdd)
return CURLE_FAILED_INIT;
while(async->queries_ongoing && !async->done) {
Curl_async_thrdd_multi_process(data->multi);
if(thrdd->done)
if(async->done)
break;
ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
@ -320,8 +315,7 @@ CURLcode Curl_async_await(struct Curl_easy *data,
milli = 50;
else
milli = 200;
CURL_TRC_DNS(data, "[async] await, waiting %" FMT_TIMEDIFF_T "ms",
milli);
CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli);
curlx_wait_ms(milli);
}
return Curl_async_take_result(data, async, pdns);
@ -350,19 +344,23 @@ static void async_thrdd_item_process(void *arg)
memset(&hints, 0, sizeof(hints));
#ifdef CURLRES_IPV6
if(item->ip_version != CURL_IPRESOLVE_V4) {
pf = (item->ip_version == CURL_IPRESOLVE_V6) ? PF_INET6 : PF_UNSPEC;
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
/* Without service and flags, resolvers might lookup up in more
* places than we want them to, causing a delay. */
hints.ai_flags |= AI_NUMERICSERV;
#endif
curl_msnprintf(service, sizeof(service), "%u", item->port);
rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res);
if(rc) {
@ -433,7 +431,7 @@ CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi,
{
CURLcode result;
DEBUGASSERT(!multi->resolv_thrdq);
result = Curl_thrdq_create(&multi->resolv_thrdq, "async", 0,
result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0,
min_threads, max_threads, idle_time_ms,
async_thrdd_item_free,
async_thrdd_item_process,
@ -471,9 +469,12 @@ static void async_thrdd_report_item(struct Curl_easy *data,
struct dynbuf tmp;
const char *sep = "";
const struct Curl_addrinfo *ai = item->res;
int ai_family = (item->ip_version == CURL_IPRESOLVE_V6) ? AF_INET6 : AF_INET;
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) {
@ -489,7 +490,7 @@ static void async_thrdd_report_item(struct Curl_easy *data,
infof(data, "Host %s:%u resolved IPv%c: %s",
item->hostname, item->port,
(item->ip_version == CURL_IPRESOLVE_V6) ? '6' : '4',
(item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4',
(curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)"));
out:
curlx_dyn_free(&tmp);
@ -507,20 +508,20 @@ void Curl_async_thrdd_multi_process(struct Curl_multi *multi)
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);
/* there is a chance that the original resolve was discarded and
* either no new, or a new resolve with a different id is ongoing. */
if(data && data->conn && data->state.async &&
(data->state.async->id == item->async_id)) {
struct Curl_resolv_async *async = data->state.async;
if(data)
async = Curl_async_get(data, item->resolv_id);
if(async) {
struct async_thrdd_item **pdest = &async->thrdd.res_A;
--async->thrdd.queued;
async->thrdd.done = !async->thrdd.queued;
async->dns_responses |= item->dns_queries;
--async->queries_ongoing;
async->done = !async->queries_ongoing;
#ifdef CURLRES_IPV6
if(item->ip_version == CURL_IPRESOLVE_V6)
if(item->dns_queries & CURL_DNSQ_AAAA)
pdest = &async->thrdd.res_AAAA;
#endif
if(!*pdest) {
@ -535,7 +536,7 @@ void Curl_async_thrdd_multi_process(struct Curl_multi *multi)
async_thrdd_item_free(item);
}
#ifdef CURLVERBOSE
Curl_thrdq_trace(multi->resolv_thrdq, multi->admin, &Curl_trc_feat_dns);
Curl_thrdq_trace(multi->resolv_thrdq, multi->admin);
#endif
}
@ -550,25 +551,25 @@ CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi,
static CURLcode async_thrdd_query(struct Curl_easy *data,
struct Curl_resolv_async *async,
uint8_t query_version)
uint8_t dns_queries)
{
struct async_thrdd_item *item;
CURLcode result;
item = async_thrdd_item_create(data, async->hostname, async->port,
query_version, async->transport,
async->id);
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, "[async] queueing %s", item->description);
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->thrdd.queued++;
async->queries_ongoing++;
out:
if(item)
@ -580,89 +581,87 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
CURLcode result = CURLE_FAILED_INIT;
void *resolver = NULL;
if(async->thrdd.queued || async->thrdd.done)
if(async->queries_ongoing || async->done)
return CURLE_FAILED_INIT;
switch(async->ip_version) {
#ifdef CURL_IPRESOLVE_V6
case CURL_IPRESOLVE_V6:
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V6);
break;
#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
case CURL_IPRESOLVE_V4:
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V4);
break;
default:
result = Curl_resolv_announce_start(data, resolver);
if(result)
return result;
#ifdef CURL_IPRESOLVE_V6
/* When resolving IP addresses, some resolvers (e.g. macOS) will
* happily "embed" an IPv4 address into the IPv6 equivalent.
* This will then confuse FTP that has been told an IPv4 for
* DATA, but suddenly sees IPv6. */
if(Curl_ipv6works(data) && !Curl_is_ipv4addr(async->hostname)) {
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V6);
if(result)
goto out;
}
/* 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
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V4);
break;
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, &Curl_trc_feat_dns);
#endif
#ifdef USE_HTTPSRR_ARES
DEBUGASSERT(!async->thrdd.rr.channel);
if(async_rr_start(data, async))
infof(data, "Failed HTTPS RR operation");
Curl_thrdq_trace(data->multi->resolv_thrdq, data);
#endif
out:
if(result)
CURL_TRC_DNS(data, "[async] error queueing %s:%d -> %d",
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 easy_pollset *ps)
CURLcode Curl_async_pollset(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct easy_pollset *ps)
{
struct Curl_resolv_async *async = data->state.async;
struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
timediff_t timeout_ms;
if(!thrdd)
return CURLE_OK;
timeout_ms = Curl_async_timeleft_ms(data, async);
#ifdef USE_HTTPSRR_ARES
if(thrdd->rr.channel) {
CURLcode result = Curl_ares_pollset(data, thrdd->rr.channel, ps);
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(!thrdd->done) {
#ifdef ENABLE_WAKEUP
/* The multi "wakeup" socket pair triggers result processing,
* no need for an extra timer. */
(void)data;
#else
timediff_t milli;
timediff_t ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
if(ms < 3)
milli = 1;
else if(ms <= 50)
milli = ms / 3;
else if(ms <= 250)
milli = 50;
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
milli = 200;
Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
stutter_ms = 200;
timeout_ms = CURLMIN(stutter_ms, timeout_ms);
#endif
Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME);
}
return CURLE_OK;
}
@ -677,11 +676,12 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
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(!thrdd->queued && !thrdd->done) {
if(!async->queries_ongoing && !async->done) {
DEBUGASSERT(0);
return CURLE_FAILED_INIT;
}
@ -692,52 +692,60 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
(void)Curl_ares_perform(thrdd->rr.channel, 0);
#endif
if(!thrdd->done)
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)) {
struct Curl_dns_entry *dns =
Curl_dnscache_mk_entry2(data,
thrdd->res_A ? &thrdd->res_A->res : NULL,
thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL,
async->hostname, async->port, async->ip_version);
if(!dns)
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(!result && thrdd->rr.channel) {
result = thrdd->rr.result;
if(!result) {
struct Curl_https_rrinfo *lhrr;
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)
if(!lhrr) {
result = CURLE_OUT_OF_MEMORY;
else
dns->hinfo = lhrr;
goto out;
}
}
Curl_httpsrr_trace(data, lhrr);
Curl_dns_entry_set_https_rr(dns, lhrr);
}
#endif
if(!result && dns) {
CURL_TRC_DNS(data, "[async] resolving complete");
*pdns = dns;
dns = NULL;
}
Curl_dns_entry_unlink(data, &dns);
}
if(dns) {
CURL_TRC_DNS(data, "resolving complete");
*pdns = dns;
dns = NULL;
}
#ifdef CURLVERBOSE
Curl_thrdq_trace(data->multi->resolv_thrdq, data, &Curl_trc_feat_dns);
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);
Curl_async_thrdd_shutdown(data, async);
if(result &&
(result != CURLE_COULDNT_RESOLVE_HOST) &&
(result != CURLE_COULDNT_RESOLVE_PROXY)) {
CURL_TRC_DNS(data, "[async] %s:%d: error %d",
async->hostname, async->port, result);
CURL_TRC_DNS(data, "Error %d resolving %s:%d",
result, async->hostname, async->port);
}
return result;
}
@ -780,4 +788,30 @@ Curl_async_get_ai(struct Curl_easy *data,
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 */

View File

@ -33,6 +33,7 @@ struct Curl_easy;
struct Curl_dns_entry;
struct Curl_resolv_async;
struct Curl_multi;
struct easy_pollset;
#ifdef CURLRES_ASYNCH
@ -67,49 +68,6 @@ int Curl_async_global_init(void);
*/
void Curl_async_global_cleanup(void);
/*
* Curl_async_get_impl()
* Get the resolver implementation instance (c-ares channel) or NULL
* for passing to application callback.
*/
CURLcode Curl_async_get_impl(struct Curl_easy *easy,
struct Curl_resolv_async *async,
void **impl);
/* Curl_async_pollset()
*
* This function is called from the Curl_multi_pollset() function. 'sock' is a
* pointer to an array to hold the file descriptors, with 'numsock' being the
* size of that array (in number of entries). This function is supposed to
* return bitmask indicating what file descriptors (referring to array indexes
* in the 'sock' array) to wait for, read/write.
*/
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps);
/*
* Take the result of an async resolve operation.
* Returns CURLE_OK with `*pdns` != NULL, CURLE_AGAIN while still
* ongoing or an error code for a failed resolve.
*/
CURLcode Curl_async_take_result(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct Curl_dns_entry **pdns);
/*
* Curl_async_await()
*
* Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
*
* On return 'entry' is assigned the resolved dns (CURLE_OK or NULL otherwise.
*
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
CURLcode Curl_async_await(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct Curl_dns_entry **pdns);
/*
* Curl_async_getaddrinfo() - when using this resolver
*
@ -129,6 +87,14 @@ Curl_async_get_ai(struct Curl_easy *data,
struct Curl_resolv_async *async,
int ai_family, unsigned int index);
#ifdef USE_HTTPSRR
const struct Curl_https_rrinfo *
Curl_async_get_https(struct Curl_easy *data,
struct Curl_resolv_async *async);
bool Curl_async_knows_https(struct Curl_easy *data,
struct Curl_resolv_async *async);
#endif /* USE_HTTPSRR */
#ifdef USE_ARES
/* common functions for c-ares and threaded resolver with HTTPSRR */
#include <ares.h>
@ -137,6 +103,10 @@ CURLcode Curl_ares_pollset(struct Curl_easy *data,
ares_channel channel,
struct easy_pollset *ps);
timediff_t Curl_ares_timeout_ms(struct Curl_easy *data,
struct Curl_resolv_async *async,
ares_channel channel);
int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms);
#endif
@ -144,7 +114,6 @@ int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms);
/* async resolving implementation using c-ares alone */
struct async_ares_ctx {
ares_channel channel;
int num_pending; /* number of outstanding c-ares requests */
struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
parts */
int ares_status; /* ARES_SUCCESS, ARES_ENOTFOUND, etc. */
@ -160,18 +129,6 @@ void Curl_async_ares_shutdown(struct Curl_easy *data,
void Curl_async_ares_destroy(struct Curl_easy *data,
struct Curl_resolv_async *async);
/* Set the DNS server to use by ares, from `data` settings. */
CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data);
/* Set the DNS interfacer to use by ares, from `data` settings. */
CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data);
/* Set the local ipv4 address to use by ares, from `data` settings. */
CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data);
/* Set the local ipv6 address to use by ares, from `data` settings. */
CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data);
#endif /* USE_RESOLV_ARES */
#ifdef USE_RESOLV_THREADED
@ -186,12 +143,8 @@ struct async_thrdd_ctx {
struct {
ares_channel channel;
struct Curl_https_rrinfo hinfo;
CURLcode result;
BIT(done);
} rr;
#endif
uint32_t queued;
BIT(done);
};
void Curl_async_thrdd_shutdown(struct Curl_easy *data,
@ -217,15 +170,50 @@ CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi,
struct doh_probes;
#endif
/*
* Curl_async_await()
*
* Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
*
* On return 'entry' is assigned the resolved dns (CURLE_OK or NULL otherwise.
*
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id,
struct Curl_dns_entry **pdns);
/*
* Take the result of an async resolve operation.
* Returns CURLE_OK with `*pdns` != NULL, CURLE_AGAIN while still
* ongoing or an error code for a failed resolve.
*/
CURLcode Curl_async_take_result(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct Curl_dns_entry **pdns);
/* Curl_async_pollset()
*
* This function is called from the Curl_multi_pollset() function. 'sock' is a
* pointer to an array to hold the file descriptors, with 'numsock' being the
* size of that array (in number of entries). This function is supposed to
* return bitmask indicating what file descriptors (referring to array indexes
* in the 'sock' array) to wait for, read/write.
*/
CURLcode Curl_async_pollset(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct easy_pollset *ps);
#else /* CURLRES_ASYNCH */
/* convert these functions if an asynch resolver is not used */
#define Curl_async_get_impl(x, y, z) (*(z) = NULL, CURLE_OK)
#define Curl_async_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST
#define Curl_async_await(x, y, z) CURLE_COULDNT_RESOLVE_HOST
#define Curl_async_global_init() CURLE_OK
#define Curl_async_global_cleanup() Curl_nop_stmt
#define Curl_async_get_ai(a,b,c,d) NULL
#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
#endif /* !CURLRES_ASYNCH */
#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)
@ -233,7 +221,9 @@ struct doh_probes;
#endif
#ifdef USE_CURL_ASYNC
struct Curl_resolv_async {
struct Curl_resolv_async *next;
#ifdef USE_RESOLV_ARES
struct async_ares_ctx ares;
#elif defined(USE_RESOLV_THREADED)
@ -245,31 +235,35 @@ struct Curl_resolv_async {
struct curltime start;
timediff_t interval_end;
timediff_t timeout_ms;
CURLcode result;
uint32_t poll_interval;
uint32_t id; /* unique id per easy handle of the resolve operation */
/* what is being resolved */
uint16_t port;
uint8_t ip_version;
uint8_t dns_queries; /* what queries are being performed */
uint8_t dns_responses; /* what queries had responses so far. */
uint8_t transport;
uint8_t queries_ongoing;
BIT(is_ipaddr);
BIT(is_ipv4addr);
BIT(done);
BIT(shutdown);
char hostname[1];
};
/*
* Curl_async_shutdown().
*
* This shuts down all ongoing operations.
*/
void Curl_async_shutdown(struct Curl_easy *data);
timediff_t Curl_async_timeleft_ms(struct Curl_easy *data,
struct Curl_resolv_async *async);
/* Shut down the given async resolve. */
void Curl_async_shutdown(struct Curl_easy *data,
struct Curl_resolv_async *async);
/* Frees the resources of the given async resolve and the struct itself. */
void Curl_async_destroy(struct Curl_easy *data,
struct Curl_resolv_async *async);
/*
* Curl_async_destroy().
*
* This frees the resources of any async resolve.
*/
void Curl_async_destroy(struct Curl_easy *data);
#else /* !USE_CURL_ASYNC */
#define Curl_async_shutdown(x) Curl_nop_stmt
#define Curl_async_destroy(x) Curl_nop_stmt
#define Curl_async_shutdown(x,y) Curl_nop_stmt
#endif /* USE_CURL_ASYNC */
/********** end of generic resolver interface functions *****************/

View File

@ -37,8 +37,9 @@
struct cf_dns_ctx {
struct Curl_dns_entry *dns;
CURLcode resolv_result;
uint32_t resolv_id;
uint16_t port;
uint8_t ip_version;
uint8_t dns_queries;
uint8_t transport;
BIT(started);
BIT(announced);
@ -47,9 +48,8 @@ struct cf_dns_ctx {
};
static struct cf_dns_ctx *
cf_dns_ctx_create(struct Curl_easy *data,
const char *hostname, uint16_t port,
uint8_t ip_version, uint8_t transport,
cf_dns_ctx_create(struct Curl_easy *data, uint8_t dns_queries,
const char *hostname, uint16_t port, uint8_t transport,
bool abstract_unix_socket,
struct Curl_dns_entry *dns)
{
@ -61,7 +61,7 @@ cf_dns_ctx_create(struct Curl_easy *data,
return NULL;
ctx->port = port;
ctx->ip_version = ip_version;
ctx->dns_queries = dns_queries;
ctx->transport = transport;
ctx->abstract_unix_socket = abstract_unix_socket;
ctx->dns = Curl_dns_entry_link(data, dns);
@ -168,8 +168,9 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf,
/* Resolve target host right on */
CURL_TRC_CF(data, cf, "resolve host %s:%u", ctx->hostname, ctx->port);
result = Curl_resolv(data, ctx->hostname, ctx->port, ctx->ip_version,
ctx->transport, timeout_ms, pdns);
result = Curl_resolv(data, ctx->dns_queries,
ctx->hostname, ctx->port, ctx->transport,
timeout_ms, &ctx->resolv_id, pdns);
DEBUGASSERT(!result || !*pdns);
if(!result) { /* resolved right away, either sync or from dnscache */
DEBUGASSERT(*pdns);
@ -210,9 +211,11 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf,
}
if(!ctx->dns && !ctx->resolv_result) {
ctx->resolv_result = Curl_resolv_take_result(data, &ctx->dns);
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 not complete yet");
CURL_TRC_CF(data, cf, "DNS resolution ongoing for %s:%u",
ctx->hostname, ctx->port);
}
if(ctx->resolv_result) {
@ -242,7 +245,7 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf,
* that one's lock. */
DEBUGASSERT(*done);
cf->connected = TRUE;
Curl_async_shutdown(data);
Curl_resolv_destroy(data, ctx->resolv_id);
Curl_dns_entry_unlink(data, &ctx->dns);
return CURLE_OK;
}
@ -266,8 +269,14 @@ static CURLcode cf_dns_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
{
#ifdef USE_CURL_ASYNC
if(!cf->connected)
return Curl_resolv_pollset(data, ps);
#else
(void)cf;
(void)data;
(void)ps;
#endif
return CURLE_OK;
}
@ -315,9 +324,9 @@ struct Curl_cftype Curl_cft_dns = {
static CURLcode cf_dns_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
bool abstract_unix_socket,
struct Curl_dns_entry *dns)
@ -327,7 +336,7 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf,
CURLcode result = CURLE_OK;
(void)data;
ctx = cf_dns_ctx_create(data, hostname, port, ip_version, transport,
ctx = cf_dns_ctx_create(data, dns_queries, hostname, port, transport,
abstract_unix_socket, dns);
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
@ -347,13 +356,13 @@ out:
* out the hostname/path and port where to connect to. */
static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
uint8_t dns_queries,
uint8_t transport,
struct Curl_dns_entry *dns)
{
struct connectdata *conn = data->conn;
const char *hostname = NULL;
uint16_t port = 0;
uint8_t ip_version = conn->ip_version;
bool abstract_unix_socket = FALSE;
#ifdef USE_UNIX_SOCKETS
@ -391,8 +400,9 @@ static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf,
DEBUGASSERT(0);
return CURLE_FAILED_INIT;
}
return cf_dns_create(pcf, data, hostname, port, ip_version,
transport, abstract_unix_socket, dns);
return cf_dns_create(pcf, data, dns_queries,
hostname, port, transport,
abstract_unix_socket, dns);
}
/* Adds a "resolv" filter at the top of the connection's filter chain.
@ -404,6 +414,7 @@ static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf,
CURLcode Curl_cf_dns_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
uint8_t dns_queries,
uint8_t transport,
struct Curl_dns_entry *dns)
{
@ -412,10 +423,11 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data,
DEBUGASSERT(data);
if(sockindex == FIRSTSOCKET)
result = cf_dns_conn_create(&cf, data, transport, dns);
result = cf_dns_conn_create(&cf, data, dns_queries, transport, dns);
else if(dns) {
result = cf_dns_create(&cf, data, dns->hostname, dns->port,
dns->ip_version, transport, FALSE, dns);
result = cf_dns_create(&cf, data, dns_queries,
dns->hostname, dns->port, transport,
FALSE, dns);
}
else {
DEBUGASSERT(0);
@ -436,16 +448,17 @@ out:
*/
CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport)
{
struct Curl_cfilter *cf;
CURLcode result;
result = cf_dns_create(&cf, data, hostname, port, ip_version,
transport, FALSE, NULL);
result = cf_dns_create(&cf, data, dns_queries,
hostname, port, transport,
FALSE, NULL);
if(result)
return result;
@ -515,7 +528,7 @@ Curl_cf_dns_get_ai(struct Curl_cfilter *cf,
else if(ctx->dns)
return cf_dns_get_nth_ai(ctx->dns->addr, ai_family, index);
else
return Curl_resolv_get_ai(data, ai_family, index);
return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index);
}
}
return NULL;
@ -548,9 +561,28 @@ Curl_conn_dns_get_https(struct Curl_easy *data, int sockindex)
for(; cf; cf = cf->next) {
if(cf->cft == &Curl_cft_dns) {
struct cf_dns_ctx *ctx = cf->ctx;
return ctx->dns ? ctx->dns->hinfo : NULL;
if(ctx->dns)
return ctx->dns->hinfo;
else
return Curl_resolv_get_https(data, ctx->resolv_id);
}
}
return NULL;
}
bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex)
{
struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
for(; cf; cf = cf->next) {
if(cf->cft == &Curl_cft_dns) {
struct cf_dns_ctx *ctx = cf->ctx;
if(ctx->dns)
return TRUE;
else
return Curl_resolv_knows_https(data, ctx->resolv_id);
}
}
return FALSE;
}
#endif /* USE_HTTPSRR */

View File

@ -33,14 +33,15 @@ struct Curl_addrinfo;
CURLcode Curl_cf_dns_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
uint8_t dns_queries,
uint8_t transport,
struct Curl_dns_entry *dns);
CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport);
CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex);
@ -62,6 +63,10 @@ Curl_cf_dns_get_ai(struct Curl_cfilter *cf,
#ifdef USE_HTTPSRR
const struct Curl_https_rrinfo *
Curl_conn_dns_get_https(struct Curl_easy *data, int sockindex);
bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex);
#else
#define Curl_conn_dns_get_https(a,b) NULL
#define Curl_conn_dns_resolved_https(a,b) TRUE
#endif

View File

@ -330,13 +330,6 @@ static CURLcode cf_hc_resolv(struct Curl_cfilter *cf,
* can no longer change that. Any HTTPSRR advice for other hosts and ports
* we need to ignore. */
const struct Curl_https_rrinfo *rr;
bool need_https_rr = FALSE;
if(need_https_rr) {
result = Curl_conn_dns_result(cf->conn, cf->sockindex);
if(result)
return result;
}
/* Do we have HTTPS-RR information? */
rr = Curl_conn_dns_get_https(data, cf->sockindex);
@ -346,7 +339,7 @@ static CURLcode cf_hc_resolv(struct Curl_cfilter *cf,
!rr->target[0] ||
(rr->target[0] == '.' &&
!rr->target[1])) &&
(rr->port < 0 || /* for same port */
(!rr->port_set || /* for same port */
rr->port == cf->conn->remote_port)) {
for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) &&
alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) {

View File

@ -637,14 +637,14 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
* of the connection. The resolve functions should really be changed
* to take a type parameter instead.
*/
uint8_t ip_version = (af == AF_INET) ?
CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_WHATEVER;
uint8_t dns_queries = (af == AF_INET) ?
CURL_DNSQ_A : (CURL_DNSQ_A|CURL_DNSQ_AAAA);
#ifdef USE_IPV6
if(af == AF_INET6)
ip_version = CURL_IPRESOLVE_V6;
dns_queries = CURL_DNSQ_AAAA;
#endif
(void)Curl_resolv_blocking(data, host, 80, ip_version, transport, &h);
(void)Curl_resolv_blocking(data, dns_queries, host, 80, transport, &h);
if(h) {
int h_af = h->addr->ai_family;
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */

View File

@ -557,6 +557,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
int ssl_mode)
{
CURLcode result = CURLE_OK;
uint8_t dns_queries;
DEBUGASSERT(data);
DEBUGASSERT(conn->scheme);
@ -580,7 +581,12 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
goto out;
}
result = Curl_cf_dns_add(data, conn, sockindex,
dns_queries = Curl_resolv_dns_queries(data, conn->ip_version);
#ifdef USE_HTTPSRR
if(sockindex == FIRSTSOCKET)
dns_queries |= CURL_DNSQ_HTTPS;
#endif
result = Curl_cf_dns_add(data, conn, sockindex, dns_queries,
conn->transport_wanted, dns);
DEBUGASSERT(conn->cfilter[sockindex]);
out:

View File

@ -223,6 +223,12 @@ struct curl_trc_feat Curl_trc_feat_timer = {
"TIMER",
CURL_LOG_LVL_NONE,
};
#ifdef USE_THREADS
struct curl_trc_feat Curl_trc_feat_threads = {
"THREADS",
CURL_LOG_LVL_NONE,
};
#endif
#endif
#ifndef CURL_DISABLE_VERBOSE_STRINGS
@ -525,6 +531,9 @@ static struct trc_feat_def trc_feats[] = {
{ &Curl_trc_feat_write, TRC_CT_NONE },
{ &Curl_trc_feat_dns, TRC_CT_NETWORK },
{ &Curl_trc_feat_timer, TRC_CT_NETWORK },
#ifdef USE_THREADS
{ &Curl_trc_feat_threads, TRC_CT_NONE },
#endif
#ifndef CURL_DISABLE_FTP
{ &Curl_trc_feat_ftp, TRC_CT_PROTOCOL },
#endif

View File

@ -308,6 +308,9 @@ 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;
extern struct curl_trc_feat Curl_trc_feat_timer;
#ifdef USE_THREADS
extern struct curl_trc_feat Curl_trc_feat_threads;
#endif
#endif
#ifndef CURL_DISABLE_VERBOSE_STRINGS

View File

@ -46,6 +46,7 @@
#include "curl_trc.h"
#include "dnscache.h"
#include "hash.h"
#include "hostip.h"
#include "httpsrr.h"
#include "progress.h"
#include "rand.h"
@ -59,25 +60,6 @@
#define MAX_DNS_CACHE_SIZE 29999
#ifdef CURLVERBOSE
static const char *dnscache_ipv_str(uint8_t ip_version)
{
switch(ip_version) {
case CURL_IPRESOLVE_WHATEVER:
return "A+AAAA";
case CURL_IPRESOLVE_V4:
return "A";
#ifdef PF_INET6
case CURL_IPRESOLVE_V6:
return "AAAA";
#endif
default:
DEBUGASSERT(0);
return "???";
}
}
#endif
static void dnscache_entry_free(struct Curl_dns_entry *dns)
{
Curl_freeaddrinfo(dns->addr);
@ -229,9 +211,9 @@ void Curl_dnscache_clear(struct Curl_easy *data)
/* lookup address, returns entry if found and not stale */
static CURLcode fetch_addr(struct Curl_easy *data,
struct Curl_dnscache *dnscache,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
struct Curl_dns_entry **pdns)
{
struct Curl_dns_entry *dns = NULL;
@ -273,43 +255,17 @@ static CURLcode fetch_addr(struct Curl_easy *data,
}
}
if(dns && dns->ip_version != ip_version) {
switch(dns->ip_version) {
case CURL_IPRESOLVE_WHATEVER: {
/* Do we have addresses that match the requested ip version? */
int pf = PF_INET;
bool found = FALSE;
struct Curl_addrinfo *addr = dns->addr;
#ifdef PF_INET6
if(ip_version == CURL_IPRESOLVE_V6)
pf = PF_INET6;
#endif
while(addr) {
if(addr->ai_family == pf) {
found = TRUE;
break;
}
addr = addr->ai_next;
}
if(!found) {
/* We assume that CURL_IPRESOLVE_WHATEVER means we tried to
* get addresses for all supported types, but there are none
* for the ip version we need. This is a negative resolve. */
CURL_TRC_DNS(data, "cache entry does not have type=%s addresses",
dnscache_ipv_str(ip_version));
dns = NULL;
result = CURLE_COULDNT_RESOLVE_HOST;
}
break;
}
default:
/* different families, we return NULL + OK, so a new resolve
* attempt may get started. */
if(dns) {
if((dns->dns_queries & dns_queries) != dns_queries) {
/* The entry does not cover all wanted DNS queries, a miss. */
dns = NULL;
break;
}
else if(!(dns->dns_responses & dns_queries)) {
/* The entry has no responses for the wanted DNS queries. */
CURL_TRC_DNS(data, "cache entry does not have type=%s addresses",
Curl_resolv_query_str(dns_queries));
dns = NULL;
result = CURLE_COULDNT_RESOLVE_HOST;
}
}
@ -336,9 +292,9 @@ static CURLcode fetch_addr(struct Curl_easy *data,
* use, or we will leak memory!
*/
CURLcode Curl_dnscache_get(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
struct Curl_dns_entry **pentry)
{
struct Curl_dnscache *dnscache = dnscache_get(data);
@ -346,7 +302,7 @@ CURLcode Curl_dnscache_get(struct Curl_easy *data,
CURLcode result = CURLE_OK;
dnscache_lock(data, dnscache);
result = fetch_addr(data, dnscache, hostname, port, ip_version, &dns);
result = fetch_addr(data, dnscache, dns_queries, hostname, port, &dns);
if(!result && dns)
dns->refcount++; /* we pass out a reference */
else if(result) {
@ -441,14 +397,24 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
}
#endif
static bool dnscache_ai_has_family(struct Curl_addrinfo *ai,
int ai_family)
{
for(; ai; ai = ai->ai_next) {
if(ai->ai_family == ai_family)
return TRUE;
}
return FALSE;
}
static struct Curl_dns_entry *
dnscache_entry_create(struct Curl_easy *data,
uint8_t dns_queries,
struct Curl_addrinfo **paddr1,
struct Curl_addrinfo **paddr2,
const char *hostname,
size_t hostlen,
uint16_t port,
uint8_t ip_version,
bool permanent)
{
struct Curl_dns_entry *dns = NULL;
@ -459,6 +425,11 @@ dnscache_entry_create(struct Curl_easy *data,
goto out;
dns->refcount = 1; /* the cache has the first reference */
dns->dns_queries = dns_queries;
dns->port = port;
if(hostlen)
memcpy(dns->hostname, hostname, hostlen);
if(permanent) {
dns->timestamp.tv_sec = 0; /* an entry that never goes stale */
dns->timestamp.tv_usec = 0; /* an entry that never goes stale */
@ -466,10 +437,6 @@ dnscache_entry_create(struct Curl_easy *data,
else {
dns->timestamp = *Curl_pgrs_now(data);
}
dns->port = port;
dns->ip_version = ip_version;
if(hostlen)
memcpy(dns->hostname, hostname, hostlen);
/* Take the given address lists into the entry */
if(paddr1 && *paddr1) {
@ -484,6 +451,16 @@ dnscache_entry_create(struct Curl_easy *data,
*paddr2 = NULL;
}
if((dns_queries & CURL_DNSQ_A) &&
dnscache_ai_has_family(dns->addr, PF_INET))
dns->dns_responses |= CURL_DNSQ_A;
#ifdef USE_IPV6
if((dns_queries & CURL_DNSQ_AAAA) &&
dnscache_ai_has_family(dns->addr, PF_INET6))
dns->dns_responses |= CURL_DNSQ_AAAA;
#endif /* USE_IPV6 */
#ifndef CURL_DISABLE_SHUFFLE_DNS
/* shuffle addresses if requested */
if(data->set.dns_shuffle_addresses && dns->addr) {
@ -513,36 +490,54 @@ out:
struct Curl_dns_entry *
Curl_dnscache_mk_entry(struct Curl_easy *data,
uint8_t dns_queries,
struct Curl_addrinfo **paddr,
const char *hostname,
uint16_t port,
uint8_t ip_version)
uint16_t port)
{
return dnscache_entry_create(data, paddr, NULL, hostname,
return dnscache_entry_create(data, dns_queries, paddr, NULL, hostname,
hostname ? strlen(hostname) : 0,
port, ip_version, FALSE);
port, FALSE);
}
struct Curl_dns_entry *
Curl_dnscache_mk_entry2(struct Curl_easy *data,
uint8_t dns_queries,
struct Curl_addrinfo **paddr1,
struct Curl_addrinfo **paddr2,
const char *hostname,
uint16_t port, uint8_t ip_version)
uint16_t port)
{
return dnscache_entry_create(data, paddr1, paddr2, hostname,
return dnscache_entry_create(data, dns_queries, paddr1, paddr2, hostname,
hostname ? strlen(hostname) : 0,
port, ip_version, FALSE);
port, FALSE);
}
#ifdef USE_HTTPSRR
void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns,
struct Curl_https_rrinfo *hinfo)
{
/* only do this when this is the only reference */
DEBUGASSERT(dns->refcount == 1);
/* it should have been in the queries */
DEBUGASSERT(dns->dns_queries & CURL_DNSQ_HTTPS);
if(dns->hinfo) {
Curl_httpsrr_cleanup(dns->hinfo);
curlx_free(dns->hinfo);
}
dns->hinfo = hinfo;
dns->dns_responses |= CURL_DNSQ_HTTPS;
}
#endif /* USE_HTTPSRR */
static struct Curl_dns_entry *
dnscache_add_addr(struct Curl_easy *data,
struct Curl_dnscache *dnscache,
uint8_t dns_queries,
struct Curl_addrinfo **paddr,
const char *hostname,
size_t hlen,
uint16_t port,
uint8_t ip_version,
bool permanent)
{
char entry_id[MAX_HOSTCACHE_LEN];
@ -550,8 +545,8 @@ dnscache_add_addr(struct Curl_easy *data,
struct Curl_dns_entry *dns;
struct Curl_dns_entry *dns2;
dns = dnscache_entry_create(data, paddr, NULL, hostname, hlen, port,
ip_version, permanent);
dns = dnscache_entry_create(data, dns_queries, paddr, NULL,
hostname, hlen, port, permanent);
if(!dns)
return NULL;
@ -596,9 +591,9 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data,
}
CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
uint8_t dns_queries,
const char *host,
uint16_t port,
uint8_t ip_version)
uint16_t port)
{
struct Curl_dnscache *dnscache = dnscache_get(data);
struct Curl_dns_entry *dns;
@ -607,14 +602,14 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
return CURLE_FAILED_INIT;
/* put this new host in the cache */
dns = dnscache_add_addr(data, dnscache, NULL, host, strlen(host),
port, ip_version, FALSE);
dns = dnscache_add_addr(data, dnscache, dns_queries, NULL,
host, strlen(host), port, FALSE);
if(dns) {
/* release the returned reference; the cache itself will keep the
* entry alive: */
dns->refcount--;
CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s",
host, port, dnscache_ipv_str(ip_version));
host, port, Curl_resolv_query_str(dns_queries));
return CURLE_OK;
}
return CURLE_OUT_OF_MEMORY;
@ -846,10 +841,10 @@ err:
Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
}
/* put this new host in the cache */
dns = dnscache_add_addr(data, dnscache, &head, curlx_str(&source),
curlx_strlen(&source), port,
CURL_IPRESOLVE_WHATEVER, permanent);
/* put this new host in the cache, an overridy for ALL dns queries */
dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL,
&head, curlx_str(&source),
curlx_strlen(&source), port, permanent);
if(dns)
/* release the returned reference; the cache itself will keep the
* entry alive: */

View File

@ -47,7 +47,8 @@ struct Curl_dns_entry {
uint32_t refcount;
/* hostname port number that resolved to addr. */
uint16_t port;
uint8_t ip_version;
uint8_t dns_queries; /* CURL_DNSQ_* type of queries performed for this */
uint8_t dns_responses; /* CURL_DNSQ_* type this entry has responses for */
/* hostname that resolved to addr. may be NULL (Unix domain sockets). */
char hostname[1];
};
@ -63,17 +64,23 @@ struct Curl_dns_entry {
*/
struct Curl_dns_entry *
Curl_dnscache_mk_entry(struct Curl_easy *data,
uint8_t dns_queries,
struct Curl_addrinfo **paddr,
const char *hostname,
uint16_t port,
uint8_t ip_version);
uint16_t port);
struct Curl_dns_entry *
Curl_dnscache_mk_entry2(struct Curl_easy *data,
uint8_t dns_queries,
struct Curl_addrinfo **paddr1,
struct Curl_addrinfo **paddr2,
const char *hostname,
uint16_t port, uint8_t ip_version);
uint16_t port);
#ifdef USE_HTTPSRR
void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns,
struct Curl_https_rrinfo *hinfo);
#endif /* USE_HTTPSRR */
/* Increase the ref counter and return it for storing in another place.
* May be called with NULL, in which case it returns NULL. */
@ -112,9 +119,9 @@ void Curl_dnscache_clear(struct Curl_easy *data);
* entry was in the cache.
*/
CURLcode Curl_dnscache_get(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
struct Curl_dns_entry **pentry);
/*
@ -127,9 +134,9 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data,
/* Store a "negative" entry for host:port, e.g. remember that
* it could not be resolved. */
CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
uint8_t dns_queries,
const char *host,
uint16_t port,
uint8_t ip_version);
uint16_t port);
/*
* Populate the cache with specified entries from CURLOPT_RESOLVE.

View File

@ -40,6 +40,9 @@
#define DNS_CLASS_IN 0x01
static void doh_close(struct Curl_easy *data,
struct Curl_resolv_async *async);
#ifdef CURLVERBOSE
static const char * const errors[] = {
"",
@ -216,22 +219,24 @@ static void doh_print_buf(struct Curl_easy *data,
static void doh_probe_done(struct Curl_easy *data,
struct Curl_easy *doh, CURLcode result)
{
struct Curl_resolv_async *async = data->state.async;
struct doh_probes *dohp = async ? async->doh : NULL;
struct Curl_resolv_async *async = NULL;
struct doh_probes *dohp = NULL;
struct doh_request *doh_req = NULL;
int i;
if(!dohp) {
doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE);
if(!doh_req) {
DEBUGASSERT(0);
return;
}
doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE);
/* A DoH response may arrive for a resolve operation already cancelled. */
if(doh_req && (doh_req->async_id != async->id)) {
CURL_TRC_DNS(data, "ignoring DoH response from a previous resolve");
async = Curl_async_get(data, doh_req->resolv_id);
if(!async) {
CURL_TRC_DNS(data, "[%u] ignoring outdated DoH response",
doh_req->resolv_id);
return;
}
dohp = async->doh;
for(i = 0; i < DOH_SLOT_COUNT; ++i) {
if(dohp->probe_resp[i].probe_mid == doh->mid)
@ -295,7 +300,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data,
DNStype dnstype,
const char *host,
const char *url, CURLM *multi,
uint32_t async_id,
uint32_t resolv_id,
uint32_t *pmid)
{
struct Curl_easy *doh = NULL;
@ -309,7 +314,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data,
doh_req = curlx_calloc(1, sizeof(*doh_req));
if(!doh_req)
return CURLE_OUT_OF_MEMORY;
doh_req->async_id = async_id;
doh_req->resolv_id = resolv_id;
doh_req->dnstype = dnstype;
curlx_dyn_init(&doh_req->resp_body, DYN_DOH_RESPONSE);
@ -482,16 +487,18 @@ CURLcode Curl_doh(struct Curl_easy *data,
data->sub_xfer_done = doh_probe_done;
/* create IPv4 DoH request */
result = doh_probe_run(data, CURL_DNS_TYPE_A,
async->hostname, data->set.str[STRING_DOH],
data->multi, async->id,
&dohp->probe_resp[DOH_SLOT_IPV4].probe_mid);
if(result)
goto error;
dohp->pending++;
if(async->dns_queries & CURL_DNSQ_A) {
result = doh_probe_run(data, CURL_DNS_TYPE_A,
async->hostname, data->set.str[STRING_DOH],
data->multi, async->id,
&dohp->probe_resp[DOH_SLOT_IPV4].probe_mid);
if(result)
goto error;
dohp->pending++;
}
#ifdef USE_IPV6
if((async->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
if(async->dns_queries & CURL_DNSQ_AAAA) {
/* create IPv6 DoH request */
result = doh_probe_run(data, CURL_DNS_TYPE_AAAA,
async->hostname, data->set.str[STRING_DOH],
@ -504,8 +511,7 @@ CURLcode Curl_doh(struct Curl_easy *data,
#endif
#ifdef USE_HTTPSRR
if(conn->scheme->protocol & PROTO_FAMILY_HTTP) {
/* Only use HTTPS RR for HTTP(S) transfers */
if(async->dns_queries & CURL_DNSQ_HTTPS) {
char *qname = NULL;
if(async->port != PORT_HTTPS) {
qname = curl_maprintf("_%d._https.%s", async->port, async->hostname);
@ -1130,6 +1136,7 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
CURLcode result = CURLE_OUT_OF_MEMORY;
size_t olen;
(void)data;
*hrr = NULL;
if(len <= 2)
return CURLE_BAD_FUNCTION_ARGUMENT;
@ -1147,7 +1154,6 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
result = CURLE_WEIRD_SERVER_REPLY;
goto err;
}
lhrr->port = -1; /* until set */
while(len >= 4) {
pcode = doh_get16bit(cp, 0);
plen = doh_get16bit(cp, 2);
@ -1157,9 +1163,10 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
result = CURLE_WEIRD_SERVER_REPLY;
goto err;
}
result = Curl_httpsrr_set(data, lhrr, pcode, cp, plen);
result = Curl_httpsrr_set(lhrr, pcode, cp, plen);
if(result)
goto err;
Curl_httpsrr_trace(data, lhrr);
cp += plen;
len -= plen;
expected_min_pcode = pcode + 1;
@ -1214,10 +1221,10 @@ UNITTEST void doh_print_httpsrr(struct Curl_easy *data,
#endif
CURLcode Curl_doh_take_result(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct Curl_dns_entry **pdns)
{
struct Curl_resolv_async *async = data->state.async;
struct doh_probes *dohp = async ? async->doh : NULL;
struct doh_probes *dohp = async->doh;
CURLcode result = CURLE_OK;
struct dohentry de;
@ -1237,7 +1244,7 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data,
memset(rc, 0, sizeof(rc));
/* remove DoH handles from multi handle and close them */
Curl_doh_close(data);
doh_close(data, async);
/* parse the responses, create the struct and return it! */
de_init(&de);
for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
@ -1268,8 +1275,8 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data,
goto error;
/* we got a response, create a dns entry. */
dns = Curl_dnscache_mk_entry(data, &ai, dohp->host, dohp->port,
async->ip_version);
dns = Curl_dnscache_mk_entry(data, async->dns_queries,
&ai, dohp->host, dohp->port);
if(!dns) {
result = CURLE_OUT_OF_MEMORY;
goto error;
@ -1290,9 +1297,10 @@ CURLcode Curl_doh_take_result(struct Curl_easy *data,
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
doh_print_httpsrr(data, hrr);
#endif
dns->hinfo = hrr;
Curl_dns_entry_set_https_rr(dns, hrr);
}
#endif /* USE_HTTPSRR */
/* and add the entry to the cache */
result = Curl_dnscache_add(data, dns);
*pdns = dns;
@ -1313,9 +1321,9 @@ error:
return result;
}
void Curl_doh_close(struct Curl_easy *data)
static void doh_close(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct Curl_resolv_async *async = data->state.async;
struct doh_probes *doh = async ? async->doh : NULL;
if(doh && data->multi) {
struct Curl_easy *probe_data;
@ -1348,7 +1356,7 @@ void Curl_doh_cleanup(struct Curl_easy *data,
struct doh_probes *dohp = async->doh;
if(dohp) {
int i;
Curl_doh_close(data);
doh_close(data, async);
for(i = 0; i < DOH_SLOT_COUNT; ++i) {
curlx_dyn_free(&dohp->probe_resp[i].body);
}

View File

@ -93,7 +93,7 @@ struct doh_request {
struct curl_slist *req_hds;
struct dynbuf resp_body;
size_t req_body_len;
uint32_t async_id; /* transfer specific id of the resolve operation */
uint32_t resolv_id; /* id of the resolve operation */
DNStype dnstype;
};
@ -122,6 +122,7 @@ CURLcode Curl_doh(struct Curl_easy *data,
struct Curl_resolv_async *async);
CURLcode Curl_doh_take_result(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct Curl_dns_entry **dns);
#define DOH_MAX_ADDR 24
@ -163,7 +164,6 @@ struct dohentry {
#endif
};
void Curl_doh_close(struct Curl_easy *data);
void Curl_doh_cleanup(struct Curl_easy *data,
struct Curl_resolv_async *async);
#define Curl_doh_wanted(d) (!!(d)->set.doh)
@ -171,7 +171,7 @@ void Curl_doh_cleanup(struct Curl_easy *data,
#else /* CURL_DISABLE_DOH */
#define Curl_doh(a, b) NULL
#define Curl_doh_take_result(x, y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_doh_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST
#define Curl_doh_wanted(d) FALSE
#endif /* !CURL_DISABLE_DOH */

View File

@ -1092,8 +1092,6 @@ void curl_easy_reset(CURL *d)
/* clear all meta data */
Curl_meta_reset(data);
/* clear any resolve data */
Curl_async_shutdown(data);
/* zero out UserDefined data: */
Curl_freeset(data);
memset(&data->set, 0, sizeof(struct UserDefined));

View File

@ -1062,9 +1062,9 @@ static CURLcode ftp_port_resolve_host(struct Curl_easy *data,
CURLcode result;
*resp = NULL;
result = Curl_resolv_blocking(data, host, 0, conn->ip_version,
Curl_conn_get_transport(data, conn),
dns_entryp);
result = Curl_resolv_blocking(
data, Curl_resolv_dns_queries(data, conn->ip_version),
host, 0, Curl_conn_get_transport(data, conn), dns_entryp);
if(result)
failf(data, "failed to resolve the address provided to PORT: %s", host);
else {
@ -2162,10 +2162,10 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
if(result)
goto error;
(void)Curl_resolv_blocking(data, host_name, ipquad.remote_port,
is_ipv6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_V4,
Curl_conn_get_transport(data, conn),
&dns);
(void)Curl_resolv_blocking(
data, is_ipv6 ? CURL_DNSQ_AAAA : CURL_DNSQ_A,
host_name, ipquad.remote_port, Curl_conn_get_transport(data, conn),
&dns);
/* we connect to the proxy's port */
connectport = (unsigned short)ipquad.remote_port;
@ -2189,9 +2189,9 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
goto error;
}
(void)Curl_resolv_blocking(data, newhost, newport, conn->ip_version,
Curl_conn_get_transport(data, conn),
&dns);
(void)Curl_resolv_blocking(
data, Curl_resolv_dns_queries(data, conn->ip_version),
newhost, newport, Curl_conn_get_transport(data, conn), &dns);
connectport = newport; /* we connect to the remote port */
if(!dns) {

View File

@ -118,6 +118,48 @@
*
* If the conversion fails, the target buffer is empty.
*/
uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version)
{
(void)data;
switch(ip_version) {
case CURL_IPRESOLVE_V6:
return CURL_DNSQ_AAAA;
case CURL_IPRESOLVE_V4:
return CURL_DNSQ_A;
default:
if(Curl_ipv6works(data))
return (CURL_DNSQ_A|CURL_DNSQ_AAAA);
else
return CURL_DNSQ_A;
}
}
#ifdef CURLVERBOSE
const char *Curl_resolv_query_str(uint8_t dns_queries)
{
switch(dns_queries) {
case (CURL_DNSQ_A|CURL_DNSQ_AAAA|CURL_DNSQ_HTTPS):
return "A+AAAA+HTTPS";
case (CURL_DNSQ_A|CURL_DNSQ_AAAA):
return "A+AAAA";
case (CURL_DNSQ_AAAA|CURL_DNSQ_HTTPS):
return "AAAA+HTTPS";
case (CURL_DNSQ_AAAA):
return "AAAA";
case (CURL_DNSQ_A|CURL_DNSQ_HTTPS):
return "A+HTTPS";
case (CURL_DNSQ_A):
return "A";
case (CURL_DNSQ_HTTPS):
return "HTTPS";
default:
DEBUGASSERT(0);
return "???";
}
}
#endif
void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
size_t bufsize)
{
@ -287,57 +329,85 @@ static bool tailmatch(const char *full, size_t flen,
return curl_strnequal(part, &full[flen - plen], plen);
}
static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version)
static bool can_resolve_dns_queries(struct Curl_easy *data,
uint8_t dns_queries)
{
#ifdef CURLRES_IPV6
if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
return FALSE;
#elif defined(CURLRES_IPV4)
(void)data;
if(ip_version == CURL_IPRESOLVE_V6)
if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && !Curl_ipv6works(data))
return FALSE;
#else
#error either CURLRES_IPV6 or CURLRES_IPV4 need to be defined
#endif
return TRUE;
}
CURLcode Curl_resolv_announce_start(struct Curl_easy *data,
void *resolver)
{
if(data->set.resolver_start) {
int rc;
CURL_TRC_DNS(data, "announcing resolve to application");
Curl_set_in_callback(data, TRUE);
rc = data->set.resolver_start(resolver, NULL,
data->set.resolver_start_client);
Curl_set_in_callback(data, FALSE);
if(rc) {
CURL_TRC_DNS(data, "application aborted resolve");
return CURLE_ABORTED_BY_CALLBACK;
}
}
return CURLE_OK;
}
#ifdef USE_CURL_ASYNC
static CURLcode hostip_async_new(struct Curl_easy *data,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
timediff_t timeout_ms)
static struct Curl_resolv_async *
hostip_async_new(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t transport,
timediff_t timeout_ms)
{
struct Curl_resolv_async *async;
size_t hostlen = strlen(hostname);
DEBUGASSERT(!data->state.async);
if(!data->multi) {
DEBUGASSERT(0);
return NULL;
}
/* struct size already includes the NUL for hostname */
async = curlx_calloc(1, sizeof(*async) + hostlen);
if(!async)
return CURLE_OUT_OF_MEMORY;
return NULL;
/* Even if this wraps (unlikely), it will be in time so far apart
* that it does not matter for all practical purposes. */
async->id = data->state.next_async_id++;
/* Give every async resolve operation a "unique" id. This may
* wrap around after a long time, making collisions highly unlikely.
* As we keep the async structs at the easy handle, chances of
* easy `mid plus resolv->id` colliding should be astronomical.
* `resolv_id == 0` is never used. */
if(data->multi->last_resolv_id == UINT32_MAX)
data->multi->last_resolv_id = 1; /* wrap around */
else
data->multi->last_resolv_id++;
async->id = data->multi->last_resolv_id;
async->dns_queries = dns_queries;
async->port = port;
async->ip_version = ip_version;
async->transport = transport;
async->start = *Curl_pgrs_now(data);
async->timeout_ms = timeout_ms;
if(hostlen)
if(hostlen) {
memcpy(async->hostname, hostname, hostlen);
async->is_ipaddr = Curl_is_ipaddr(async->hostname);
if(async->is_ipaddr)
async->is_ipv4addr = Curl_is_ipv4addr(async->hostname);
}
data->state.async = async;
return CURLE_OK;
return async;
}
static CURLcode hostip_resolv_take_result(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct Curl_dns_entry **pdns)
{
struct Curl_resolv_async *async = data->state.async;
CURLcode result;
/* If async resolving is ongoing, this must be set */
@ -346,7 +416,7 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data,
#ifndef CURL_DISABLE_DOH
if(data->conn->bits.doh)
result = Curl_doh_take_result(data, pdns);
result = Curl_doh_take_result(data, async, pdns);
else
#endif
result = Curl_async_take_result(data, async, pdns);
@ -362,87 +432,83 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data,
}
const struct Curl_addrinfo *
Curl_resolv_get_ai(struct Curl_easy *data, int ai_family,
unsigned int index)
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 = data->state.async;
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
if(async)
return Curl_async_get_ai(data, async, ai_family, index);
#else
(void)data;
(void)resolv_id;
(void)ai_family;
(void)index;
#endif
return NULL;
}
#endif /* USE_CURL_ASYNC */
static CURLcode hostip_resolv_announce(struct Curl_easy *data,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
timediff_t timeout_ms)
#ifdef USE_HTTPSRR
const struct Curl_https_rrinfo *
Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id)
{
if(data->set.resolver_start) {
void *resolver = NULL;
int st;
#ifdef CURLRES_ASYNCH
CURLcode result;
if(!data->state.async) {
result = hostip_async_new(data, hostname, port, ip_version,
transport, timeout_ms);
if(result)
return result;
}
result = Curl_async_get_impl(data, data->state.async, &resolver);
if(result)
return result;
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
if(async)
return Curl_async_get_https(data, async);
#else
(void)hostname;
(void)port;
(void)ip_version;
(void)timeout_ms;
(void)transport;
(void)data;
(void)resolv_id;
#endif
Curl_set_in_callback(data, TRUE);
st = data->set.resolver_start(resolver, NULL,
data->set.resolver_start_client);
Curl_set_in_callback(data, FALSE);
if(st) {
return CURLE_ABORTED_BY_CALLBACK;
}
}
return CURLE_OK;
return NULL;
}
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;
}
#endif /* USE_HTTPSRR */
#endif /* USE_CURL_ASYNC */
static CURLcode hostip_resolv_start(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
timediff_t timeout_ms,
bool allowDOH,
uint32_t *presolv_id,
struct Curl_dns_entry **pdns)
{
#ifdef USE_CURL_ASYNC
struct Curl_resolv_async *async = NULL;
#endif
struct Curl_addrinfo *addr = NULL;
size_t hostname_len;
CURLcode result = CURLE_OK;
(void)timeout_ms; /* not in all ifdefs */
*presolv_id = 0;
*pdns = NULL;
/* really need to start a resolve operation */
result = hostip_resolv_announce(data, hostname, port, ip_version,
transport, timeout_ms);
if(result)
goto out;
/* Check for "known" things to resolve ourselves. */
#ifndef USE_RESOLVE_ON_IPS
if(Curl_is_ipaddr(hostname)) {
/* test655 verifies that the announce is done, even though there
* is no real resolving. So, keep doing this. */
result = Curl_resolv_announce_start(data, NULL);
if(result)
goto out;
/* shortcut literal IP addresses, if we are not told to resolve them. */
result = Curl_str2addr(hostname, port, &addr);
goto out;
@ -454,6 +520,9 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data,
curl_strequal(hostname, "localhost.") ||
tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
tailmatch(hostname, hostname_len, STRCONST(".localhost."))) {
result = Curl_resolv_announce_start(data, NULL);
if(result)
goto out;
addr = get_localhost(port, hostname);
if(!addr)
result = CURLE_OUT_OF_MEMORY;
@ -462,13 +531,18 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data,
#ifndef CURL_DISABLE_DOH
if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) {
if(!data->state.async) {
result = hostip_async_new(data, hostname, port, ip_version,
transport, timeout_ms);
if(result)
result = Curl_resolv_announce_start(data, NULL);
if(result)
goto out;
if(!async) {
async = hostip_async_new(data, dns_queries, hostname, port,
transport, timeout_ms);
if(!async) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
result = Curl_doh(data, data->state.async);
result = Curl_doh(data, async);
goto out;
}
#else
@ -476,30 +550,35 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data,
#endif
/* Can we provide the requested IP specifics in resolving? */
if(!can_resolve_ip_version(data, ip_version)) {
if(!can_resolve_dns_queries(data, dns_queries)) {
result = CURLE_COULDNT_RESOLVE_HOST;
goto out;
}
#ifdef CURLRES_ASYNCH
(void)addr;
if(!data->state.async) {
result = hostip_async_new(data, hostname, port, ip_version,
transport, timeout_ms);
if(result)
if(!async) {
async = hostip_async_new(data, dns_queries, hostname, port,
transport, timeout_ms);
if(!async) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
result = Curl_async_getaddrinfo(data, data->state.async);
result = Curl_async_getaddrinfo(data, async);
if(result == CURLE_AGAIN) {
/* the answer might be there already. Check. */
CURLcode r2 = hostip_resolv_take_result(data, pdns);
CURLcode r2 = hostip_resolv_take_result(data, async, pdns);
if(r2)
result = r2;
else if(*pdns)
result = CURLE_OK;
}
#else
addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version, transport);
result = Curl_resolv_announce_start(data, NULL);
if(result)
goto out;
addr = Curl_sync_getaddrinfo(data, dns_queries, hostname, port, transport);
if(!addr)
result = CURLE_COULDNT_RESOLVE_HOST;
#endif
@ -509,7 +588,7 @@ out:
if(addr) {
/* we got a response, create a dns entry, add to cache, return */
DEBUGASSERT(!*pdns);
*pdns = Curl_dnscache_mk_entry(data, &addr, hostname, port, ip_version);
*pdns = Curl_dnscache_mk_entry(data, dns_queries, &addr, hostname, port);
if(!*pdns)
result = CURLE_OUT_OF_MEMORY;
}
@ -521,31 +600,39 @@ out:
else if(addr)
Curl_freeaddrinfo(addr);
#ifdef USE_CURL_ASYNC
if(async) {
if(result == CURLE_AGAIN) { /* still need it, link, return id. */
*presolv_id = async->id;
async->next = data->state.async;
data->state.async = async;
}
else {
Curl_async_destroy(data, async);
}
}
#endif
return result;
}
static CURLcode hostip_resolv(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
timediff_t timeout_ms,
bool allowDOH,
uint32_t *presolv_id,
struct Curl_dns_entry **pdns)
{
size_t hostname_len;
CURLcode result = CURLE_COULDNT_RESOLVE_HOST;
bool cache_dns = FALSE;
(void)timeout_ms; /* not used in all ifdefs */
*presolv_id = 0;
*pdns = NULL;
#ifdef USE_CURL_ASYNC
if(data->state.async)
Curl_async_destroy(data);
#else
(void)timeout_ms;
#endif
#ifndef CURL_DISABLE_DOH
data->conn->bits.doh = FALSE; /* default is not */
#else
@ -563,9 +650,9 @@ static CURLcode hostip_resolv(struct Curl_easy *data,
}
#ifdef DEBUGBUILD
CURL_TRC_DNS(data, "hostip_resolv(%s:%u, ip=%x)",
hostname, port, ip_version);
if((ip_version == CURL_IPRESOLVE_V6) &&
CURL_TRC_DNS(data, "hostip_resolv(%s:%u, queries=%s)",
hostname, port, Curl_resolv_query_str(dns_queries));
if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) &&
getenv("CURL_DBG_RESOLV_FAIL_IPV6")) {
infof(data, "DEBUG fail ipv6 resolve");
result = Curl_resolver_error(data, NULL);
@ -573,7 +660,7 @@ static CURLcode hostip_resolv(struct Curl_easy *data,
}
#endif
/* Let's check our DNS cache first */
result = Curl_dnscache_get(data, hostname, port, ip_version, pdns);
result = Curl_dnscache_get(data, dns_queries, hostname, port, pdns);
if(*pdns) {
infof(data, "Hostname %s was found in DNS cache", hostname);
result = CURLE_OK;
@ -585,18 +672,18 @@ static CURLcode hostip_resolv(struct Curl_easy *data,
else {
/* No luck, we need to start resolving. */
cache_dns = TRUE;
result = hostip_resolv_start(data, hostname, port, ip_version,
transport, timeout_ms, allowDOH, pdns);
result = hostip_resolv_start(data, dns_queries, hostname, port,
transport, timeout_ms, allowDOH,
presolv_id, pdns);
}
out:
if(result && (result != CURLE_AGAIN)) {
Curl_dns_entry_unlink(data, pdns);
Curl_async_shutdown(data);
if((result == CURLE_COULDNT_RESOLVE_HOST) ||
(result == CURLE_COULDNT_RESOLVE_PROXY)) {
if(cache_dns)
Curl_dnscache_add_negative(data, hostname, port, ip_version);
Curl_dnscache_add_negative(data, dns_queries, hostname, port);
failf(data, "Could not resolve: %s:%u", hostname, port);
}
else {
@ -609,40 +696,39 @@ out:
Curl_dns_entry_unlink(data, pdns);
}
CURL_TRC_DNS(data, "hostip_resolv(%s:%u, ip=%x, timeout_ms=%" FMT_TIMEDIFF_T
") -> %d, dns %sfound",
hostname, port, ip_version, timeout_ms, result,
*pdns ? "" : "not ");
return result;
}
CURLcode Curl_resolv_blocking(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
struct Curl_dns_entry **pdns)
{
CURLcode result;
uint32_t resolv_id;
DEBUGASSERT(hostname && *hostname);
*pdns = NULL;
/* We cannot do a blocking resolve using DoH currently */
result = hostip_resolv(data, hostname, port, ip_version,
transport, 0, FALSE, pdns);
result = hostip_resolv(data, dns_queries,
hostname, port, transport, 0, FALSE,
&resolv_id, pdns);
switch(result) {
case CURLE_OK:
DEBUGASSERT(*pdns);
return CURLE_OK;
break;
#ifdef USE_CURL_ASYNC
case CURLE_AGAIN:
DEBUGASSERT(!*pdns);
if(!data->state.async)
return CURLE_FAILED_INIT;
return Curl_async_await(data, data->state.async, pdns);
result = Curl_async_await(data, resolv_id, pdns);
Curl_resolv_destroy(data, resolv_id);
break;
#endif
default:
return result;
break;
}
return result;
}
#ifdef USE_ALARM_TIMEOUT
@ -661,11 +747,12 @@ CURL_NORETURN static void alarmfunc(int sig)
#ifdef USE_ALARM_TIMEOUT
static CURLcode resolv_alarm_timeout(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
timediff_t timeout_ms,
uint32_t *presolv_id,
struct Curl_dns_entry **entry)
{
#ifdef HAVE_SIGACTION
@ -742,8 +829,8 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data,
/* Perform the actual name resolution. This might be interrupted by an
* alarm if it takes too long. */
result = hostip_resolv(data, hostname, port, ip_version, transport,
timeout_ms, TRUE, entry);
result = hostip_resolv(data, dns_queries, hostname, port, transport,
timeout_ms, FALSE, presolv_id, entry);
clean_up:
if(!prev_alarm)
@ -816,19 +903,23 @@ clean_up:
* CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
*/
CURLcode Curl_resolv(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
timediff_t timeout_ms,
uint32_t *presolv_id,
struct Curl_dns_entry **entry)
{
DEBUGASSERT(hostname && *hostname);
*presolv_id = 0;
*entry = NULL;
if(timeout_ms < 0)
/* got an already expired timeout */
return CURLE_OPERATION_TIMEDOUT;
else if(!timeout_ms)
timeout_ms = CURL_TIMEOUT_RESOLVE_MS;
#ifdef USE_ALARM_TIMEOUT
if(timeout_ms && data->set.no_signal) {
@ -836,8 +927,8 @@ CURLcode Curl_resolv(struct Curl_easy *data,
timeout_ms = 0;
}
if(timeout_ms && !Curl_doh_wanted(data)) {
return resolv_alarm_timeout(data, hostname, port, ip_version,
transport, timeout_ms, entry);
return resolv_alarm_timeout(data, dns_queries, hostname, port, transport,
timeout_ms, presolv_id, entry);
}
#endif /* !USE_ALARM_TIMEOUT */
@ -846,16 +937,28 @@ CURLcode Curl_resolv(struct Curl_easy *data,
infof(data, "timeout on name lookup is not supported");
#endif
return hostip_resolv(data, hostname, port, ip_version, transport,
timeout_ms, TRUE, entry);
return hostip_resolv(data, dns_queries, hostname, port, transport,
timeout_ms, TRUE, presolv_id, entry);
}
#ifdef USE_CURL_ASYNC
CURLcode Curl_resolv_take_result(struct Curl_easy *data,
struct Curl_dns_entry **pdns)
struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data,
uint32_t resolv_id)
{
struct Curl_resolv_async *async = data->state.async;
for(; async; async = async->next) {
if(async->id == resolv_id)
return async;
}
return NULL;
}
CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id,
struct Curl_dns_entry **pdns)
{
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
CURLcode result;
/* If async resolving is ongoing, this must be set */
@ -863,20 +966,20 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data,
return CURLE_FAILED_INIT;
/* check if we have the name resolved by now (from someone else) */
result = Curl_dnscache_get(data, async->hostname, async->port,
async->ip_version, pdns);
result = Curl_dnscache_get(data, async->dns_queries,
async->hostname, async->port, pdns);
if(*pdns) {
/* Tell a possibly async resolver we no longer need the results. */
infof(data, "Hostname '%s' was found in DNS cache", async->hostname);
Curl_async_shutdown(data);
Curl_async_shutdown(data, async);
return CURLE_OK;
}
else if(result) {
Curl_async_shutdown(data);
Curl_async_shutdown(data, async);
return Curl_resolver_error(data, NULL);
}
result = hostip_resolv_take_result(data, pdns);
result = hostip_resolv_take_result(data, async, pdns);
if(*pdns) {
/* Add to cache */
@ -886,8 +989,8 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data,
}
else if((result == CURLE_COULDNT_RESOLVE_HOST) ||
(result == CURLE_COULDNT_RESOLVE_PROXY)) {
Curl_dnscache_add_negative(data, async->hostname,
async->port, async->ip_version);
Curl_dnscache_add_negative(data, async->dns_queries,
async->hostname, async->port);
failf(data, "Could not resolve: %s:%u", async->hostname, async->port);
}
else if(result) {
@ -897,26 +1000,57 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data,
return result;
}
#endif /* USE_CURL_ASYNC */
CURLcode Curl_resolv_pollset(struct Curl_easy *data,
struct easy_pollset *ps)
{
#ifdef CURLRES_ASYNCH
#ifndef CURL_DISABLE_DOH
if(data->conn->bits.doh)
/* nothing to wait for during DoH resolve, those handles have their own
sockets */
return CURLE_OK;
#endif
return Curl_async_pollset(data, ps);
#else
(void)data;
struct Curl_resolv_async *async = data->state.async;
CURLcode result = CURLE_OK;
(void)ps;
return CURLE_OK;
for(; async && !result; async = async->next) {
#ifndef CURL_DISABLE_DOH
if(async->doh) /* DoH has nothing for the pollset */
continue;
#endif
result = Curl_async_pollset(data, async, ps);
}
return result;
}
void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id)
{
struct Curl_resolv_async **panchor = &data->state.async;
for(; *panchor; panchor = &(*panchor)->next) {
struct Curl_resolv_async *async = *panchor;
if(async->id == resolv_id) {
*panchor = async->next;
Curl_async_destroy(data, async);
break;
}
}
}
void Curl_resolv_shutdown_all(struct Curl_easy *data)
{
struct Curl_resolv_async *async = data->state.async;
for(; async; async = async->next) {
Curl_async_shutdown(data, async);
}
}
void Curl_resolv_destroy_all(struct Curl_easy *data)
{
while(data->state.async) {
struct Curl_resolv_async *async = data->state.async;
data->state.async = async->next;
Curl_async_destroy(data, async);
}
}
#endif /* USE_CURL_ASYNC */
/*
* Curl_resolver_error() calls failf() with the appropriate message after a
* resolve error
@ -964,7 +1098,7 @@ CURLcode Curl_resolv_unix(struct Curl_easy *data,
return result;
}
*pdns = Curl_dnscache_mk_entry(data, &addr, NULL, 0, 0);
*pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0);
return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}
#endif /* USE_UNIX_SOCKETS */

View File

@ -35,8 +35,7 @@
*/
#define CURL_HOSTENT_SIZE 9000
#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this
many seconds for a name resolve */
#define CURL_TIMEOUT_RESOLVE_MS (300 * 1000)
struct addrinfo;
struct hostent;
@ -47,6 +46,21 @@ struct Curl_https_rrinfo;
struct Curl_multi;
struct Curl_dns_entry;
/* DNS query types */
#define CURL_DNSQ_A (1U << 0)
#define CURL_DNSQ_AAAA (1U << 1)
#define CURL_DNSQ_HTTPS (1U << 2)
#define CURL_DNSQ_ALL (CURL_DNSQ_A|CURL_DNSQ_AAAA|CURL_DNSQ_HTTPS)
#define CURL_DNSQ_IP(x) (uint8_t)((x)&(CURL_DNSQ_A|CURL_DNSQ_AAAA))
#ifdef CURLVERBOSE
const char *Curl_resolv_query_str(uint8_t dns_queries);
#endif
/* Return CURL_DNSQ_* bits for the transfer and ip_version. */
uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version);
enum alpnid {
ALPN_none = 0,
ALPN_h1 = CURLALTSVC_H1,
@ -80,50 +94,79 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port);
void Curl_printable_address(const struct Curl_addrinfo *ip,
char *buf, size_t bufsize);
/*
* Curl_resolv() returns an entry with the info for the specified host
* and port.
*
* The returned data *MUST* be "released" with Curl_dns_entry_unlink() after
* use, or we will leak memory!
/* Start DNS resolving for the given parameters. Returns
* - CURLE_OK: `*pdns` is the resolved DNS entry (needs to be unlinked).
* `*presolv_id` is 0.
* - CURLE_AGAIN: resolve is asynchronous and not finished yet.
* `presolv_id` is the identifier for querying results later.
* - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0.
*/
CURLcode Curl_resolv(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
timediff_t timeout_ms,
uint32_t *presolv_id,
struct Curl_dns_entry **pdns);
CURLcode Curl_resolv_blocking(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport,
struct Curl_dns_entry **pdns);
CURLcode Curl_resolv_timeout(struct Curl_easy *data,
const char *hostname, int port,
int ip_version,
struct Curl_dns_entry **entry,
timediff_t timeoutms);
/* Announce start of a resolve operation to application callback,
* passing the resolver implementation (maybe NULL). */
CURLcode Curl_resolv_announce_start(struct Curl_easy *data,
void *resolver);
#ifdef USE_CURL_ASYNC
CURLcode Curl_resolv_take_result(struct Curl_easy *data,
struct Curl_dns_entry **pdns);
const struct Curl_addrinfo *
Curl_resolv_get_ai(struct Curl_easy *data, int ai_family,
unsigned int index);
#else
#define Curl_resolv_take_result(x, y) CURLE_NOT_BUILT_IN
#define Curl_resolv_get_ai(x,y,z) NULL
#endif
CURLcode Curl_resolv_pollset(struct Curl_easy *data,
struct easy_pollset *ps);
/* Get the `async` struct for the given `resolv_id`, if it exists. */
struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data,
uint32_t resolv_id);
/* Shut down all resolves of the given easy handle. */
void Curl_resolv_shutdown_all(struct Curl_easy *data);
/* Destroy all resolve resources of the given easy handle. */
void Curl_resolv_destroy_all(struct Curl_easy *data);
CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id,
struct Curl_dns_entry **pdns);
void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id);
const struct Curl_addrinfo *
Curl_resolv_get_ai(struct Curl_easy *data, uint32_t resolv_id,
int ai_family, unsigned int index);
#ifdef USE_HTTPSRR
const struct Curl_https_rrinfo *
Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id);
bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id);
#endif /* USE_HTTPSRR */
#else /* USE_CURL_ASYNC */
#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_get_ai(x,y,z, a) NULL
#define Curl_resolv_get_https(x,y) NULL
#define Curl_resolv_knows_https(x,y) TRUE
#define Curl_resolv_pollset(x,y) CURLE_OK
#define Curl_resolv_destroy(x,y) Curl_nop_stmt
#endif /* USE_CURL_ASYNC, else */
CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail);
#ifdef CURLRES_SYNCH
/*
* Curl_sync_getaddrinfo() is the non-async low-level name resolve API.
@ -131,9 +174,9 @@ CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail);
* support and platform.
*/
struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport);
#endif

View File

@ -68,14 +68,14 @@
*
*/
struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport)
{
struct Curl_addrinfo *ai = NULL;
(void)ip_version;
(void)dns_queries;
(void)transport;
ai = Curl_ipv4_resolve_r(hostname, port);

View File

@ -63,9 +63,9 @@
* Curl_freeaddrinfo(), nothing else.
*/
struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
uint8_t dns_queries,
const char *hostname,
uint16_t port,
uint8_t ip_version,
uint8_t transport)
{
struct addrinfo hints;
@ -78,8 +78,7 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
#endif
int pf = PF_INET;
if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
/* The stack seems to be IPv6-enabled */
if(dns_queries & CURL_DNSQ_AAAA)
pf = PF_UNSPEC;
memset(&hints, 0, sizeof(hints));

View File

@ -30,6 +30,7 @@
#include "connect.h"
#include "curl_trc.h"
#include "curlx/strdup.h"
#include "curlx/inet_ntop.h"
static CURLcode httpsrr_decode_alpn(const uint8_t *cp, size_t len,
unsigned char *alpns)
@ -68,25 +69,90 @@ static CURLcode httpsrr_decode_alpn(const uint8_t *cp, size_t len,
return CURLE_OK;
}
CURLcode Curl_httpsrr_set(struct Curl_easy *data,
struct Curl_https_rrinfo *hi,
#ifdef CURLVERBOSE
static void httpsrr_report_addr(struct Curl_easy *data, 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;
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 = ", ";
}
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);
}
void Curl_httpsrr_trace(struct Curl_easy *data,
struct Curl_https_rrinfo *hi)
{
if(!hi || !hi->complete) {
CURL_TRC_DNS(data, "[HTTPS-RR] not available");
return;
}
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");
}
#else
#define httpsrr_report_addr(a,b,c,d) Curl_nop_stmt
#endif /* CURLVERBOSE */
CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *hi,
uint16_t rrkey, const uint8_t *val, size_t vlen)
{
CURLcode result = CURLE_OK;
switch(rrkey) {
case HTTPS_RR_CODE_MANDATORY:
CURL_TRC_DNS(data, "HTTPS RR MANDATORY left to implement");
hi->mandatory = TRUE;
break;
case HTTPS_RR_CODE_ALPN: /* str_list */
result = 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:
if(vlen) /* no data */
return CURLE_BAD_FUNCTION_ARGUMENT;
hi->no_def_alpn = TRUE;
CURL_TRC_DNS(data, "HTTPS RR no-def-alpn");
break;
case HTTPS_RR_CODE_IPV4: /* addr4 list */
if(!vlen || (vlen & 3)) /* the size must be 4-byte aligned */
@ -96,7 +162,6 @@ CURLcode Curl_httpsrr_set(struct Curl_easy *data,
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:
if(!vlen)
@ -106,7 +171,6 @@ CURLcode Curl_httpsrr_set(struct Curl_easy *data,
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 */
if(!vlen || (vlen & 15)) /* the size must be 16-byte aligned */
@ -116,16 +180,15 @@ CURLcode Curl_httpsrr_set(struct Curl_easy *data,
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:
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);
hi->port = (uint16_t)((val[0] << 8) | val[1]);
hi->port_set = TRUE;
break;
default:
CURL_TRC_DNS(data, "HTTPS RR unknown code");
/* unknown code */
break;
}
return result;
@ -147,12 +210,12 @@ void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo)
Curl_safefree(rrinfo->ipv4hints);
Curl_safefree(rrinfo->ipv6hints);
Curl_safefree(rrinfo->rrname);
rrinfo->complete = FALSE;
}
#ifdef USE_ARES
static CURLcode httpsrr_opt(struct Curl_easy *data,
const ares_dns_rr_t *rr,
static CURLcode httpsrr_opt(const ares_dns_rr_t *rr,
ares_dns_rr_key_t key, size_t idx,
struct Curl_https_rrinfo *hinfo)
{
@ -161,11 +224,10 @@ static CURLcode httpsrr_opt(struct Curl_easy *data,
size_t len = 0;
code = ares_dns_rr_get_opt(rr, key, idx, &val, &len);
return Curl_httpsrr_set(data, hinfo, code, val, len);
return Curl_httpsrr_set(hinfo, code, val, len);
}
CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data,
const ares_dns_record_t *dnsrec,
CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec,
struct Curl_https_rrinfo *hinfo)
{
CURLcode result = CURLE_OK;
@ -188,18 +250,17 @@ CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
CURL_TRC_DNS(data, "HTTPS RR target: %s", hinfo->target);
}
CURL_TRC_DNS(data, "HTTPS RR priority: %u",
ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY));
hinfo->priority = 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++) {
result = httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt, hinfo);
result = httpsrr_opt(rr, ARES_RR_HTTPS_PARAMS, opt, hinfo);
if(result)
break;
}
}
out:
hinfo->complete = !result;
Curl_safefree(hinfo->rrname);
return result;
}

View File

@ -51,13 +51,15 @@ struct Curl_https_rrinfo {
size_t ipv6hints_len;
unsigned char alpns[MAX_HTTPSRR_ALPNS]; /* keytag = 1 */
/* store parsed alpnid entries in the array, end with ALPN_none */
int port; /* -1 means not set */
uint16_t port; /* -1 means not set */
uint16_t priority;
BIT(no_def_alpn); /* keytag = 2 */
BIT(mandatory); /* keytag = 0 */
BIT(port_set); /* port value has been assigned */
BIT(complete); /* values have been successfully assigned */
};
CURLcode Curl_httpsrr_set(struct Curl_easy *data,
struct Curl_https_rrinfo *hi,
CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *hi,
uint16_t rrkey, const uint8_t *val, size_t vlen);
struct Curl_https_rrinfo *
@ -77,10 +79,17 @@ void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo);
#define HTTPS_RR_CODE_IPV6 0x06
#ifdef USE_ARES
CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data,
const ares_dns_record_t *dnsrec,
CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec,
struct Curl_https_rrinfo *hinfo);
#endif /* USE_ARES */
#ifdef CURLVERBOSE
void Curl_httpsrr_trace(struct Curl_easy *data,
struct Curl_https_rrinfo *hi);
#else
#define Curl_httpsrr_trace(a,b) Curl_nop_stmt
#endif
#endif /* USE_HTTPSRR */
#endif /* HEADER_CURL_HTTPSRR_H */

View File

@ -702,7 +702,7 @@ static CURLcode multi_done(struct Curl_easy *data,
return CURLE_OK;
/* Shut down any ongoing async resolver operation. */
Curl_async_shutdown(data);
Curl_resolv_shutdown_all(data);
/* Cleanup possible redirect junk */
Curl_safefree(data->req.newurl);

View File

@ -175,6 +175,7 @@ struct Curl_multi {
#ifdef DEBUGBUILD
unsigned int now_access_count;
#endif
uint32_t last_resolv_id; /* id of the last DNS resolve operation */
BIT(ipv6_works);
BIT(multiplexing); /* multiplexing wanted */
BIT(recheckstate); /* see Curl_multi_connchanged */

View File

@ -102,9 +102,6 @@ CURLcode Curl_req_done(struct SingleRequest *req,
if(!aborted)
(void)req_flush(data);
Curl_client_reset(data);
#ifndef CURL_DISABLE_DOH
Curl_doh_close(data);
#endif
return CURLE_OK;
}
@ -117,9 +114,8 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
if(req->sendbuf_init)
Curl_bufq_reset(&req->sendbuf);
#ifndef CURL_DISABLE_DOH
Curl_doh_close(data);
#endif
/* clear any resolve data */
Curl_resolv_destroy_all(data);
/* Can no longer memset() this struct as we need to keep some state */
req->size = -1;
req->maxdownload = -1;

View File

@ -2386,28 +2386,16 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
#endif
#ifdef USE_RESOLV_ARES
case CURLOPT_DNS_SERVERS:
result = Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr);
if(result)
break;
return Curl_async_ares_set_dns_servers(data);
return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr);
case CURLOPT_DNS_INTERFACE:
result = Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr);
if(result)
break;
return Curl_async_ares_set_dns_interface(data);
return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr);
case CURLOPT_DNS_LOCAL_IP4:
result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr);
if(result)
break;
return Curl_async_ares_set_dns_local_ip4(data);
return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr);
case CURLOPT_DNS_LOCAL_IP6:
result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr);
if(result)
break;
return Curl_async_ares_set_dns_local_ip6(data);
return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr);
#endif
#ifdef USE_UNIX_SOCKETS

View File

@ -102,6 +102,7 @@ struct socks_state {
const char *proxy_user;
const char *proxy_password;
CURLproxycode presult;
uint32_t resolv_id;
unsigned char version;
BIT(resolve_local);
BIT(start_resolving);
@ -323,10 +324,11 @@ static CURLproxycode socks4_resolving(struct socks_state *sx,
sx->start_resolving = FALSE;
DEBUGASSERT(sx->hostname && *sx->hostname);
result = Curl_resolv(data, sx->hostname, sx->remote_port,
cf->conn->ip_version,
result = Curl_resolv(data,
Curl_resolv_dns_queries(data, cf->conn->ip_version),
sx->hostname, sx->remote_port,
Curl_conn_cf_get_transport(cf, data),
0, &dns);
0, &sx->resolv_id, &dns);
if(result == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "SOCKS4 non-blocking resolve of %s", sx->hostname);
return CURLPX_OK;
@ -336,7 +338,7 @@ static CURLproxycode socks4_resolving(struct socks_state *sx,
}
else {
/* check if we have the name resolved by now */
result = Curl_resolv_take_result(data, &dns);
result = Curl_resolv_take_result(data, sx->resolv_id, &dns);
if(!result && !dns)
return CURLPX_OK;
}
@ -858,10 +860,11 @@ static CURLproxycode socks5_resolving(struct socks_state *sx,
sx->start_resolving = FALSE;
DEBUGASSERT(sx->hostname && *sx->hostname);
result = Curl_resolv(data, sx->hostname, sx->remote_port,
cf->conn->ip_version,
result = Curl_resolv(data,
Curl_resolv_dns_queries(data, cf->conn->ip_version),
sx->hostname, sx->remote_port,
Curl_conn_cf_get_transport(cf, data),
0, &dns);
0, &sx->resolv_id, &dns);
if(result == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "SOCKS5 non-blocking resolve of %s", sx->hostname);
return CURLPX_OK;
@ -871,7 +874,7 @@ static CURLproxycode socks5_resolving(struct socks_state *sx,
}
else {
/* check if we have the name resolved by now */
result = Curl_resolv_take_result(data, &dns);
result = Curl_resolv_take_result(data, sx->resolv_id, &dns);
if(!result && !dns)
return CURLPX_OK;
}

View File

@ -435,38 +435,38 @@ out:
#ifdef CURLVERBOSE
void Curl_thrdpool_trace(struct curl_thrdpool *tpool,
struct Curl_easy *data,
struct curl_trc_feat *feat)
struct Curl_easy *data)
{
struct curl_trc_feat *feat = &Curl_trc_feat_threads;
if(Curl_trc_ft_is_verbose(data, feat)) {
struct Curl_llist_node *e;
struct curltime now = curlx_now();
Curl_mutex_acquire(&tpool->lock);
if(!Curl_llist_count(&tpool->slots)) {
Curl_trc_feat_infof(data, feat, "[%s] [TPOOL] no threads running",
Curl_trc_feat_infof(data, feat, "[TPOOL-%s] no threads running",
tpool->name);
}
for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) {
struct thrdslot *tslot = Curl_node_elem(e);
timediff_t elapsed_ms = curlx_ptimediff_ms(&now, &tslot->starttime);
if(!tslot->running) {
Curl_trc_feat_infof(data, feat, "[%s] [TPOOL] [%u]: not running",
Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: not running",
tpool->name, tslot->id);
}
else if(!tslot->starttime.tv_sec && !tslot->starttime.tv_usec) {
Curl_trc_feat_infof(data, feat, "[%s] [TPOOL] [%u]: starting...",
Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: starting...",
tpool->name, tslot->id);
}
else if(tslot->idle) {
Curl_trc_feat_infof(data, feat, "[%s] [TPOOL] [%u]: idle for %"
Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: idle for %"
FMT_TIMEDIFF_T "ms",
tpool->name, tslot->id, elapsed_ms);
}
else {
timediff_t remain_ms = tslot->work_timeout_ms ?
(tslot->work_timeout_ms - elapsed_ms) : 0;
Curl_trc_feat_infof(data, feat, "[%s] [TPOOL] [%u]: busy %"
Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: busy %"
FMT_TIMEDIFF_T "ms, timeout in %" FMT_TIMEDIFF_T
"ms: %s",
tpool->name, tslot->id, elapsed_ms, remain_ms,

View File

@ -30,7 +30,6 @@
struct curl_thrdpool;
struct Curl_easy;
struct curl_trc_feat;
/* Invoked under thread pool lock to get an "item" to work on. Must
* return NULL if there is nothing to do.
@ -99,8 +98,7 @@ CURLcode Curl_thrdpool_set_props(struct curl_thrdpool *tpool,
#ifdef CURLVERBOSE
void Curl_thrdpool_trace(struct curl_thrdpool *tpool,
struct Curl_easy *data,
struct curl_trc_feat *feat);
struct Curl_easy *data);
#endif
#endif /* USE_THREADS */

View File

@ -380,27 +380,27 @@ CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue,
#ifdef CURLVERBOSE
void Curl_thrdq_trace(struct curl_thrdq *tqueue,
struct Curl_easy *data,
struct curl_trc_feat *feat)
struct Curl_easy *data)
{
struct curl_trc_feat *feat = &Curl_trc_feat_threads;
if(Curl_trc_ft_is_verbose(data, feat)) {
struct Curl_llist_node *e;
struct thrdq_item *qitem;
Curl_thrdpool_trace(tqueue->tpool, data, feat);
Curl_thrdpool_trace(tqueue->tpool, data);
Curl_mutex_acquire(&tqueue->lock);
if(!Curl_llist_count(&tqueue->sendq) &&
!Curl_llist_count(&tqueue->recvq)) {
Curl_trc_feat_infof(data, feat, "[%s] [QUEUE] empty", tqueue->name);
Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] empty", tqueue->name);
}
for(e = Curl_llist_head(&tqueue->sendq); e; e = Curl_node_next(e)) {
qitem = Curl_node_elem(e);
Curl_trc_feat_infof(data, feat, "[%s] [QUEUE] in: %s",
Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] in: %s",
tqueue->name, qitem->description);
}
for(e = Curl_llist_head(&tqueue->recvq); e; e = Curl_node_next(e)) {
qitem = Curl_node_elem(e);
Curl_trc_feat_infof(data, feat, "[%s] [QUEUE] out: %s",
Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] out: %s",
tqueue->name, qitem->description);
}
Curl_mutex_release(&tqueue->lock);

View File

@ -29,7 +29,6 @@
#ifdef USE_THREADS
struct Curl_easy;
struct curl_trc_feat;
struct curl_thrdq;
typedef enum {
@ -111,8 +110,7 @@ CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue,
#ifdef CURLVERBOSE
void Curl_thrdq_trace(struct curl_thrdq *tqueue,
struct Curl_easy *data,
struct curl_trc_feat *feat);
struct Curl_easy *data);
#endif
#endif /* USE_THREADS */

View File

@ -239,7 +239,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
curlx_free(data->state.range);
/* release any resolve information this transfer kept */
Curl_async_destroy(data);
Curl_resolv_destroy_all(data);
data->set.verbose = FALSE; /* no more calls to DEBUGFUNCTION */
data->magic = 0; /* force a clear AFTER the possibly enforced removal from

View File

@ -723,7 +723,6 @@ struct UrlState {
#ifdef USE_CURL_ASYNC
struct Curl_resolv_async *async; /* asynchronous name resolver data */
uint32_t next_async_id; /* id of the next async resolve operation */
#endif
#ifdef USE_OPENSSL

View File

@ -37,6 +37,7 @@
#include "curlx/inet_pton.h"
#include "vtls/openssl.h"
#include "connect.h"
#include "cf-dns.h"
#include "progress.h"
#include "vtls/vtls.h"
#include "vtls/vtls_int.h"
@ -3423,6 +3424,16 @@ ossl_init_session_and_alpns(struct ossl_ctx *octx,
}
#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
static bool ossl_ech_need_httpsrr(struct Curl_easy *data)
{
if(!CURLECH_ENABLED(data))
return FALSE;
if((data->set.tls_ech & CURLECH_GREASE) ||
(data->set.tls_ech & CURLECH_CLA_CFG))
return FALSE;
return TRUE;
}
static CURLcode ossl_init_ech(struct ossl_ctx *octx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
@ -3488,49 +3499,32 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx,
infof(data, "ECH: ECHConfig from command line");
}
else {
struct Curl_dns_entry *dns = NULL;
const struct Curl_https_rrinfo *rinfo =
Curl_conn_dns_get_https(data, cf->sockindex);
if(peer->hostname)
result = Curl_dnscache_get(data, peer->hostname, peer->port,
cf->conn->ip_version, &dns);
if(!dns) {
if(result) {
failf(data, "ECH: could not resolve %s:%d",
peer->hostname, peer->port);
return result;
}
infof(data, "ECH: requested but no DNS info available");
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
else {
struct Curl_https_rrinfo *rinfo = NULL;
if(rinfo && rinfo->echconfiglist) {
const unsigned char *ecl = rinfo->echconfiglist;
size_t elen = rinfo->echconfiglist_len;
rinfo = dns->hinfo;
if(rinfo && rinfo->echconfiglist) {
unsigned char *ecl = rinfo->echconfiglist;
size_t elen = rinfo->echconfiglist_len;
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
infof(data, "ECH: SSL_set1_ech_config_list failed");
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
else {
trying_ech_now = 1;
infof(data, "ECH: imported ECHConfigList of length %zu", elen);
}
}
else {
infof(data, "ECH: requested but no ECHConfig available");
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
infof(data, "ECH: SSL_set1_ech_config_list failed");
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
Curl_dns_entry_unlink(data, &dns);
else {
trying_ech_now = 1;
infof(data, "ECH: imported ECHConfigList of length %zu", elen);
}
}
else {
infof(data, "ECH: requested but no ECHConfig available");
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
}
#ifdef HAVE_BORINGSSL_LIKE
(void)peer;
if(trying_ech_now && outername) {
infof(data, "ECH: setting public_name not supported with BoringSSL");
return CURLE_SSL_CONNECT_ERROR;
@ -4949,6 +4943,15 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf,
connssl->io_need = CURL_SSL_IO_NEED_NONE;
if(ssl_connect_1 == connssl->connecting_state) {
#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
/* if we do ECH and need the HTTPS-RR information for it,
* we delay the connect until it arrives or DNS resolve fails. */
if(ossl_ech_need_httpsrr(data) &&
!Curl_conn_dns_resolved_https(data, cf->sockindex)) {
CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect");
return CURLE_OK;
}
#endif
CURL_TRC_CF(data, cf, "ossl_connect, step1");
result = ossl_connect_step1(cf, data);
if(result)

View File

@ -32,6 +32,7 @@
#include "curlx/fopen.h"
#include "curlx/strerr.h"
#include "urldata.h"
#include "cf-dns.h"
#include "curl_trc.h"
#include "httpsrr.h"
#include "vtls/vtls.h"
@ -909,16 +910,26 @@ cleanup:
}
#ifdef USE_ECH
static bool cr_ech_need_httpsrr(struct Curl_easy *data)
{
if(!CURLECH_ENABLED(data))
return FALSE;
if((data->set.tls_ech & CURLECH_GREASE) ||
(data->set.tls_ech & CURLECH_CLA_CFG))
return FALSE;
return TRUE;
}
static CURLcode
init_config_builder_ech(struct Curl_easy *data,
const struct ssl_connect_data *connssl,
struct Curl_cfilter *cf,
struct rustls_client_config_builder *builder)
{
const rustls_hpke *hpke = rustls_supported_hpke();
unsigned char *ech_config = NULL;
size_t ech_config_len = 0;
struct Curl_dns_entry *dns = NULL;
struct Curl_https_rrinfo *rinfo = NULL;
CURLcode result = CURLE_OK;
rustls_result rr;
@ -963,22 +974,9 @@ init_config_builder_ech(struct Curl_easy *data,
}
}
else {
if(connssl->peer.hostname) {
result = Curl_dnscache_get(data, connssl->peer.hostname,
connssl->peer.port, data->conn->ip_version,
&dns);
}
if(!dns) {
if(result) {
failf(data, "ECH: could not resolve %s:%d",
connssl->peer.hostname, connssl->peer.port);
return result;
}
failf(data, "rustls: ECH requested but no DNS info available");
result = CURLE_SSL_CONNECT_ERROR;
goto cleanup;
}
rinfo = dns->hinfo;
const struct Curl_https_rrinfo *rinfo =
Curl_conn_dns_get_https(data, cf->sockindex);
if(!rinfo || !rinfo->echconfiglist) {
failf(data, "rustls: ECH requested but no ECHConfig available");
result = CURLE_SSL_CONNECT_ERROR;
@ -1075,7 +1073,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf,
#ifdef USE_ECH
if(CURLECH_ENABLED(data)) {
result = init_config_builder_ech(data, connssl, config_builder);
result = init_config_builder_ech(data, cf, config_builder);
if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) {
rustls_client_config_builder_free(config_builder);
return result;
@ -1146,6 +1144,16 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state);
*done = FALSE;
#ifdef USE_ECH
/* if we do ECH and need the HTTPS-RR information for it,
* we delay the connect until it arrives or DNS resolve fails. */
if(cr_ech_need_httpsrr(data) &&
!Curl_conn_dns_resolved_https(data, cf->sockindex)) {
CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect");
return CURLE_OK;
}
#endif /* USE_ECH */
if(!backend->conn) {
result = cr_init_backend(cf, data,
(struct rustls_ssl_backend_data *)connssl->backend);

View File

@ -56,6 +56,7 @@
#include "urldata.h"
#include "curl_trc.h"
#include "httpsrr.h"
#include "cf-dns.h"
#include "vtls/vtls.h"
#include "vtls/vtls_int.h"
#include "vtls/vtls_scache.h"
@ -1289,55 +1290,31 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx,
}
}
else {
struct ssl_connect_data *connssl = cf->ctx;
struct Curl_dns_entry *dns = NULL;
CURLcode result;
const struct Curl_https_rrinfo *rinfo =
Curl_conn_dns_get_https(data, cf->sockindex);
result = Curl_dnscache_get(data, connssl->peer.hostname,
connssl->peer.port, cf->conn->ip_version,
&dns);
if(!dns) {
if(result) {
failf(data, "ECH: could not resolve %s:%d",
connssl->peer.hostname,
connssl->peer.port);
return result;
}
infof(data, "ECH: requested but no DNS info"
" available");
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
else {
struct Curl_https_rrinfo *rinfo = NULL;
if(rinfo && rinfo->echconfiglist) {
const unsigned char *ecl = rinfo->echconfiglist;
size_t elen = rinfo->echconfiglist_len;
rinfo = dns->hinfo;
if(rinfo && rinfo->echconfiglist) {
unsigned char *ecl = rinfo->echconfiglist;
size_t elen = rinfo->echconfiglist_len;
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) !=
WOLFSSL_SUCCESS) {
infof(data, "ECH: wolfSSL_SetEchConfigs failed");
if(data->set.tls_ech & CURLECH_HARD) {
Curl_dns_entry_unlink(data, &dns);
return CURLE_SSL_CONNECT_ERROR;
}
}
else {
trying_ech_now = 1;
infof(data, "ECH: imported ECHConfigList of length %zu", elen);
}
}
else {
infof(data, "ECH: requested but no ECHConfig available");
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) !=
WOLFSSL_SUCCESS) {
infof(data, "ECH: wolfSSL_SetEchConfigs failed");
if(data->set.tls_ech & CURLECH_HARD) {
Curl_dns_entry_unlink(data, &dns);
return CURLE_SSL_CONNECT_ERROR;
}
}
Curl_dns_entry_unlink(data, &dns);
else {
trying_ech_now = 1;
infof(data, "ECH: imported ECHConfigList of length %zu", elen);
}
}
else {
infof(data, "ECH: requested but no ECHConfig available");
if(data->set.tls_ech & CURLECH_HARD) {
return CURLE_SSL_CONNECT_ERROR;
}
}
}
@ -1510,6 +1487,18 @@ out:
return result;
}
#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
static bool wssl_ech_need_httpsrr(struct Curl_easy *data)
{
if(!CURLECH_ENABLED(data))
return FALSE;
if((data->set.tls_ech & CURLECH_GREASE) ||
(data->set.tls_ech & CURLECH_CLA_CFG))
return FALSE;
return TRUE;
}
#endif
/*
* This function loads all the client/CA certificates and CRLs. Setup the TLS
* layer and do all necessary magic.
@ -2179,6 +2168,15 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf,
connssl->io_need = CURL_SSL_IO_NEED_NONE;
if(ssl_connect_1 == connssl->connecting_state) {
#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
/* if we do ECH and need the HTTPS-RR information for it,
* we delay the connect until it arrives or DNS resolve fails. */
if(wssl_ech_need_httpsrr(data) &&
!Curl_conn_dns_resolved_https(data, cf->sockindex)) {
CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect");
return CURLE_OK;
}
#endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */
result = wssl_connect_step1(cf, data);
if(result)
return result;

View File

@ -23,6 +23,8 @@
***************************************************************************/
#include "first.h"
#include "testtrace.h"
static const char *TEST_DATA_STRING = "Test data";
static int cb_count = 0;
@ -61,6 +63,9 @@ static CURLcode test_lib655(const char *URL)
CURL *curl;
CURLcode result = CURLE_OK;
debug_config.nohex = TRUE;
debug_config.tracetime = TRUE;
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
curl_mfprintf(stderr, "curl_global_init() failed\n");
return TEST_ERR_MAJOR_BAD;
@ -74,6 +79,9 @@ static CURLcode test_lib655(const char *URL)
/* Set the URL that is about to receive our first request. */
test_setopt(curl, CURLOPT_URL, URL);
test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config);
test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb);
test_setopt(curl, CURLOPT_VERBOSE, 1L);
test_setopt(curl, CURLOPT_RESOLVER_START_DATA, TEST_DATA_STRING);
test_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_fail);
@ -91,6 +99,9 @@ static CURLcode test_lib655(const char *URL)
/* Set the URL that receives our second request. */
test_setopt(curl, CURLOPT_URL, libtest_arg2);
test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config);
test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb);
test_setopt(curl, CURLOPT_VERBOSE, 1L);
test_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_pass);

View File

@ -66,7 +66,7 @@ static void rrresults(struct Curl_https_rrinfo *rr, CURLcode res)
curl_msnprintf(p, pend - p, "no-def-alpn|");
p += strlen(p);
}
if(rr->port >= 0) {
if(rr->port_set) {
curl_msnprintf(p, pend - p, "port:%d|", rr->port);
p += strlen(p);
}