← Commit list

2b3dfb4ad4

Stefan Eissing
Stefan Eissing
2026-04-01
SUCCESS Duration: 351s

Subject

lib: make resolving HTTPS DNS records reliable:

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

docs/libcurl/curl_global_trace.md+4 -0
@@ -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.
lib/asyn-ares.c+111 -110
@@ -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
}
lib/asyn-base.c+54 -23
@@ -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);
}
}
lib/asyn-thrdd.c+173 -139
@@ -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->mid = data->mid;
+ item->resolv_id = resolv_id;
+ item->dns_queries = dns_queries;
item->port = port;
- item->ip_version = ip_version;
item->transport = transport;
- item->mid = data->mid;
- item->async_id = async_id;
#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 */
lib/asyn.h+72 -78
@@ -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 *****************/
lib/cf-dns.c+55 -23
@@ -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 */
lib/cf-dns.h+6 -1
@@ -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
lib/cf-https-connect.c+1 -8
@@ -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) {
lib/cf-socket.c+4 -4
@@ -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 */
lib/connect.c+7 -1
@@ -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:
lib/curl_trc.c+9 -0
@@ -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
lib/curl_trc.h+3 -0
@@ -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
lib/dnscache.c+76 -81
@@ -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;
+ if(dns) {
+ if((dns->dns_queries & dns_queries) != dns_queries) {
+ /* The entry does not cover all wanted DNS queries, a miss. */
+ dns = NULL;
}
- default:
- /* different families, we return NULL + OK, so a new resolve
- * attempt may get started. */
+ 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;
- break;
+ 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: */
lib/dnscache.h+14 -7
@@ -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.
lib/doh.c+38 -30
@@ -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);
}
lib/doh.h+3 -3
@@ -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 */
lib/easy.c+0 -2
@@ -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));
lib/ftp.c+10 -10
@@ -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) {
lib/hostip.c+278 -144
@@ -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;
- }
+ struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
+ if(async)
+ return Curl_async_get_https(data, async);
+#else
+ (void)data;
+ (void)resolv_id;
+#endif
+ return NULL;
+}
- result = Curl_async_get_impl(data, data->state.async, &resolver);
- if(result)
- return result;
+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)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 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 */
lib/hostip.h+68 -25
@@ -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
lib/hostip4.c+2 -2
@@ -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);
lib/hostip6.c+2 -3
@@ -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));
lib/httpsrr.c+82 -21
@@ -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;
}
lib/httpsrr.h+14 -5
@@ -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 */
lib/multi.c+1 -1
@@ -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);
lib/multihandle.h+1 -0
@@ -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 */
lib/request.c+2 -6
@@ -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;
lib/setopt.c+4 -16
@@ -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
lib/socks.c+11 -8
@@ -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;
}
lib/thrdpool.c+7 -7
@@ -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,
lib/thrdpool.h+1 -3
@@ -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 */
lib/thrdqueue.c+6 -6
@@ -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);
lib/thrdqueue.h+1 -3
@@ -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 */
lib/url.c+1 -1
@@ -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
lib/urldata.h+0 -1
@@ -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
lib/vtls/openssl.c+40 -37
@@ -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;
-
- 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;
-
- 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");
+ const struct Curl_https_rrinfo *rinfo =
+ Curl_conn_dns_get_https(data, cf->sockindex);
+
+ if(rinfo && rinfo->echconfiglist) {
+ const 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;
}
- 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)
lib/vtls/rustls.c+27 -19
@@ -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);
lib/vtls/wolfssl.c+43 -45
@@ -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;
-
- 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;
-
- 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");
+ const struct Curl_https_rrinfo *rinfo =
+ Curl_conn_dns_get_https(data, cf->sockindex);
+
+ if(rinfo && rinfo->echconfiglist) {
+ const 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;
}
}
- 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;
tests/libtest/lib655.c+11 -0
@@ -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);
tests/unit/unit1658.c+1 -1
@@ -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);
}