mirror of
https://github.com/curl/curl.git
synced 2026-04-11 12:01:42 +08:00
lib: make resolving HTTPS DNS records reliable:
- allow to specify when they are wanted on starting a resolve - match dns cache entries accordingly. An entry which never tried to get HTTPS-RRs is no answer for a resolve that wants it. - fix late arrivals of resolve answers to match the "async" records that started them - if it still exists. - provide for multiple "async" resolves in a transfer at the same time. We may need to resolve an IP interface while the main connection resolve has not finished yet. - allow lookup of HTTPS-RR information as soon as it is available, even if A/AAAA queries are still ongoing. For this, the "async" infrastructure is changed: - Defined bits for DNS queries `CURL_DNSQ_A`, `CURL_DNSQ_AAAA` and `CURL_DNSQ_HTTPS`. These replace `ip_version` which says nothing about HTTPS. Use them in dns cache entries for matching. - enhance the `async->id` to be a unique `uint32_t` for resolves inside one multi. This is weak, as the id may wrap around. However it is combined with the `mid` of the easy handle, making collisions highly unlikely. `data->state.async` is only accessed in few places where the mid/async-id match is performed. - vtls: for ECH supporting TLS backends (openssl, rustls, wolfssl), retrieve the HTTPS-RR information from the dns connection filter. Delay the connect if the HTTPS-RR is needed, but has not been resolved yet. The implementation of all this is complete for the threaded resolver. c-ares resolver and DoH do not take advantage of all new async features yet. To be done in separate PRs. Details: c-ares: cleanup settings and initialisation. Any ares channel is only being created on starting a resolve and propagating operations in setopt.c to the channel are not helpful. Changed threaded+ares pollset handling so that they do not overwrite each others `ASYNC_NAME` timeouts. Add trace name 'threads' for tracing thread queue and pool used by threaded resolver. Closes #21175
This commit is contained in:
parent
03a792b186
commit
2b3dfb4ad4
@ -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.
|
||||
|
||||
221
lib/asyn-ares.c
221
lib/asyn-ares.c
@ -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
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
314
lib/asyn-thrdd.c
314
lib/asyn-thrdd.c
@ -99,16 +99,6 @@ void Curl_async_global_cleanup(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
CURLcode Curl_async_get_impl(struct Curl_easy *data,
|
||||
struct Curl_resolv_async *async,
|
||||
void **impl)
|
||||
{
|
||||
(void)data;
|
||||
(void)async;
|
||||
*impl = NULL;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
#define CURL_ASYN_ITEM_DESC_LEN 64
|
||||
#define async_item_description(x) (x)->description
|
||||
@ -123,10 +113,10 @@ struct async_thrdd_item {
|
||||
#endif
|
||||
int sock_error;
|
||||
uint32_t mid;
|
||||
uint32_t async_id;
|
||||
uint32_t resolv_id;
|
||||
uint16_t port;
|
||||
uint8_t ip_version;
|
||||
uint8_t transport;
|
||||
uint8_t dns_queries;
|
||||
#ifdef DEBUGBUILD
|
||||
uint32_t delay_ms;
|
||||
uint32_t delay_fail_ms;
|
||||
@ -147,13 +137,12 @@ static void async_thrdd_item_destroy(struct async_thrdd_item *item)
|
||||
/* Initialize context for threaded resolver */
|
||||
static struct async_thrdd_item *
|
||||
async_thrdd_item_create(struct Curl_easy *data,
|
||||
uint32_t resolv_id, uint8_t dns_queries,
|
||||
const char *hostname, uint16_t port,
|
||||
uint8_t ip_version, uint8_t transport,
|
||||
uint32_t async_id)
|
||||
uint8_t transport)
|
||||
{
|
||||
size_t hostlen = strlen(hostname);
|
||||
struct async_thrdd_item *item;
|
||||
VERBOSE(const char *qtype);
|
||||
|
||||
item = curlx_calloc(1, sizeof(*item) + hostlen);
|
||||
if(!item)
|
||||
@ -161,18 +150,18 @@ async_thrdd_item_create(struct Curl_easy *data,
|
||||
|
||||
if(hostlen) /* NUL byte of name already in struct size */
|
||||
memcpy(item->hostname, hostname, hostlen);
|
||||
item->port = port;
|
||||
item->ip_version = ip_version;
|
||||
item->transport = transport;
|
||||
item->mid = data->mid;
|
||||
item->async_id = async_id;
|
||||
item->resolv_id = resolv_id;
|
||||
item->dns_queries = dns_queries;
|
||||
item->port = port;
|
||||
item->transport = transport;
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
qtype = (ip_version == CURL_IPRESOLVE_WHATEVER) ? "A+AAAA":
|
||||
((ip_version == CURL_IPRESOLVE_V6) ? "AAAA" : "A");
|
||||
curl_msnprintf(item->description, sizeof(item->description),
|
||||
"[%" FMT_OFF_T "/%d] %s %s:%u",
|
||||
data->id, item->async_id, qtype, item->hostname, item->port);
|
||||
"[%" FMT_OFF_T "/%u] %s %s:%u",
|
||||
data->id, item->resolv_id,
|
||||
Curl_resolv_query_str(dns_queries),
|
||||
item->hostname, item->port);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUGBUILD
|
||||
@ -205,14 +194,18 @@ static void async_thrdd_rr_done(void *user_data, ares_status_t status,
|
||||
size_t timeouts,
|
||||
const ares_dns_record_t *dnsrec)
|
||||
{
|
||||
struct Curl_easy *data = user_data;
|
||||
struct async_thrdd_ctx *thrdd = &data->state.async->thrdd;
|
||||
struct Curl_resolv_async *async = user_data;
|
||||
struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
|
||||
|
||||
(void)timeouts;
|
||||
thrdd->rr.done = TRUE;
|
||||
if((ARES_SUCCESS != status) || !dnsrec)
|
||||
if(!thrdd)
|
||||
return;
|
||||
thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo);
|
||||
|
||||
async->dns_responses |= CURL_DNSQ_HTTPS;
|
||||
async->queries_ongoing--;
|
||||
async->done = !async->queries_ongoing;
|
||||
if((ARES_SUCCESS == status) && dnsrec)
|
||||
async->result = Curl_httpsrr_from_ares(dnsrec, &thrdd->rr.hinfo);
|
||||
}
|
||||
|
||||
static CURLcode async_rr_start(struct Curl_easy *data,
|
||||
@ -246,13 +239,13 @@ static CURLcode async_rr_start(struct Curl_easy *data,
|
||||
#endif
|
||||
|
||||
memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
|
||||
thrdd->rr.hinfo.port = -1;
|
||||
thrdd->rr.hinfo.rrname = rrname;
|
||||
ares_query_dnsrec(thrdd->rr.channel,
|
||||
rrname ? rrname : async->hostname, ARES_CLASS_IN,
|
||||
ARES_REC_TYPE_HTTPS,
|
||||
async_thrdd_rr_done, data, NULL);
|
||||
CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s",
|
||||
async_thrdd_rr_done, async, NULL);
|
||||
async->queries_ongoing++;
|
||||
CURL_TRC_DNS(data, "[HTTPS-RR] initiated request for %s",
|
||||
rrname ? rrname : async->hostname);
|
||||
return CURLE_OK;
|
||||
}
|
||||
@ -275,7 +268,7 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data,
|
||||
struct Curl_resolv_async *async)
|
||||
{
|
||||
(void)data;
|
||||
if(async->thrdd.queued && !async->thrdd.done &&
|
||||
if(async->queries_ongoing && !async->done &&
|
||||
data->multi && data->multi->resolv_thrdq) {
|
||||
/* Remove any resolve items still queued */
|
||||
Curl_thrdq_clear(data->multi->resolv_thrdq,
|
||||
@ -298,17 +291,19 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data,
|
||||
* Waits for a resolve to finish. This function should be avoided since using
|
||||
* this risk getting the multi interface to "hang".
|
||||
*/
|
||||
CURLcode Curl_async_await(struct Curl_easy *data,
|
||||
struct Curl_resolv_async *async,
|
||||
CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id,
|
||||
struct Curl_dns_entry **pdns)
|
||||
{
|
||||
struct async_thrdd_ctx *thrdd = &async->thrdd;
|
||||
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
|
||||
struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
|
||||
timediff_t milli, ms;
|
||||
|
||||
CURL_TRC_DNS(data, "[async] await %s", async->hostname);
|
||||
while(thrdd->queued && !thrdd->done) {
|
||||
if(!thrdd)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
while(async->queries_ongoing && !async->done) {
|
||||
Curl_async_thrdd_multi_process(data->multi);
|
||||
if(thrdd->done)
|
||||
if(async->done)
|
||||
break;
|
||||
|
||||
ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
|
||||
@ -320,8 +315,7 @@ CURLcode Curl_async_await(struct Curl_easy *data,
|
||||
milli = 50;
|
||||
else
|
||||
milli = 200;
|
||||
CURL_TRC_DNS(data, "[async] await, waiting %" FMT_TIMEDIFF_T "ms",
|
||||
milli);
|
||||
CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli);
|
||||
curlx_wait_ms(milli);
|
||||
}
|
||||
return Curl_async_take_result(data, async, pdns);
|
||||
@ -350,19 +344,23 @@ static void async_thrdd_item_process(void *arg)
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
#ifdef CURLRES_IPV6
|
||||
if(item->ip_version != CURL_IPRESOLVE_V4) {
|
||||
pf = (item->ip_version == CURL_IPRESOLVE_V6) ? PF_INET6 : PF_UNSPEC;
|
||||
if(item->dns_queries & CURL_DNSQ_AAAA) {
|
||||
pf = (item->dns_queries & CURL_DNSQ_A) ? PF_UNSPEC : PF_INET6;
|
||||
}
|
||||
#endif
|
||||
hints.ai_family = pf;
|
||||
hints.ai_socktype = Curl_socktype_for_transport(item->transport);
|
||||
hints.ai_protocol = Curl_protocol_for_transport(item->transport);
|
||||
#ifdef __APPLE__
|
||||
/* If we leave `ai_flags == 0` then macOS is looking for IPV4MAPPED
|
||||
* when doing AAAA queries. We do not want this "help". */
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
#endif
|
||||
|
||||
curl_msnprintf(service, sizeof(service), "%u", item->port);
|
||||
#ifdef AI_NUMERICSERV
|
||||
/* Without service and flags, resolvers might lookup up in more
|
||||
* places than we want them to, causing a delay. */
|
||||
hints.ai_flags |= AI_NUMERICSERV;
|
||||
#endif
|
||||
curl_msnprintf(service, sizeof(service), "%u", item->port);
|
||||
|
||||
rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res);
|
||||
if(rc) {
|
||||
@ -433,7 +431,7 @@ CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi,
|
||||
{
|
||||
CURLcode result;
|
||||
DEBUGASSERT(!multi->resolv_thrdq);
|
||||
result = Curl_thrdq_create(&multi->resolv_thrdq, "async", 0,
|
||||
result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0,
|
||||
min_threads, max_threads, idle_time_ms,
|
||||
async_thrdd_item_free,
|
||||
async_thrdd_item_process,
|
||||
@ -471,9 +469,12 @@ static void async_thrdd_report_item(struct Curl_easy *data,
|
||||
struct dynbuf tmp;
|
||||
const char *sep = "";
|
||||
const struct Curl_addrinfo *ai = item->res;
|
||||
int ai_family = (item->ip_version == CURL_IPRESOLVE_V6) ? AF_INET6 : AF_INET;
|
||||
int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET;
|
||||
CURLcode result;
|
||||
|
||||
if(!Curl_trc_is_verbose(data))
|
||||
return;
|
||||
|
||||
curlx_dyn_init(&tmp, 1024);
|
||||
for(; ai; ai = ai->ai_next) {
|
||||
if(ai->ai_family == ai_family) {
|
||||
@ -489,7 +490,7 @@ static void async_thrdd_report_item(struct Curl_easy *data,
|
||||
|
||||
infof(data, "Host %s:%u resolved IPv%c: %s",
|
||||
item->hostname, item->port,
|
||||
(item->ip_version == CURL_IPRESOLVE_V6) ? '6' : '4',
|
||||
(item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4',
|
||||
(curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)"));
|
||||
out:
|
||||
curlx_dyn_free(&tmp);
|
||||
@ -507,20 +508,20 @@ void Curl_async_thrdd_multi_process(struct Curl_multi *multi)
|
||||
while(!Curl_thrdq_recv(multi->resolv_thrdq, &qitem)) {
|
||||
/* dispatch resolve result */
|
||||
struct async_thrdd_item *item = qitem;
|
||||
struct Curl_resolv_async *async = NULL;
|
||||
|
||||
data = Curl_multi_get_easy(multi, item->mid);
|
||||
/* there is a chance that the original resolve was discarded and
|
||||
* either no new, or a new resolve with a different id is ongoing. */
|
||||
if(data && data->conn && data->state.async &&
|
||||
(data->state.async->id == item->async_id)) {
|
||||
struct Curl_resolv_async *async = data->state.async;
|
||||
if(data)
|
||||
async = Curl_async_get(data, item->resolv_id);
|
||||
if(async) {
|
||||
struct async_thrdd_item **pdest = &async->thrdd.res_A;
|
||||
|
||||
--async->thrdd.queued;
|
||||
async->thrdd.done = !async->thrdd.queued;
|
||||
async->dns_responses |= item->dns_queries;
|
||||
--async->queries_ongoing;
|
||||
async->done = !async->queries_ongoing;
|
||||
|
||||
#ifdef CURLRES_IPV6
|
||||
if(item->ip_version == CURL_IPRESOLVE_V6)
|
||||
if(item->dns_queries & CURL_DNSQ_AAAA)
|
||||
pdest = &async->thrdd.res_AAAA;
|
||||
#endif
|
||||
if(!*pdest) {
|
||||
@ -535,7 +536,7 @@ void Curl_async_thrdd_multi_process(struct Curl_multi *multi)
|
||||
async_thrdd_item_free(item);
|
||||
}
|
||||
#ifdef CURLVERBOSE
|
||||
Curl_thrdq_trace(multi->resolv_thrdq, multi->admin, &Curl_trc_feat_dns);
|
||||
Curl_thrdq_trace(multi->resolv_thrdq, multi->admin);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -550,25 +551,25 @@ CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi,
|
||||
|
||||
static CURLcode async_thrdd_query(struct Curl_easy *data,
|
||||
struct Curl_resolv_async *async,
|
||||
uint8_t query_version)
|
||||
uint8_t dns_queries)
|
||||
{
|
||||
struct async_thrdd_item *item;
|
||||
CURLcode result;
|
||||
|
||||
item = async_thrdd_item_create(data, async->hostname, async->port,
|
||||
query_version, async->transport,
|
||||
async->id);
|
||||
item = async_thrdd_item_create(data, async->id, dns_queries,
|
||||
async->hostname, async->port,
|
||||
async->transport);
|
||||
if(!item) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
CURL_TRC_DNS(data, "[async] queueing %s", item->description);
|
||||
CURL_TRC_DNS(data, "queueing query %s", item->description);
|
||||
result = Curl_thrdq_send(data->multi->resolv_thrdq, item,
|
||||
async_item_description(item), async->timeout_ms);
|
||||
if(result)
|
||||
goto out;
|
||||
item = NULL;
|
||||
async->thrdd.queued++;
|
||||
async->queries_ongoing++;
|
||||
|
||||
out:
|
||||
if(item)
|
||||
@ -580,89 +581,87 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
|
||||
struct Curl_resolv_async *async)
|
||||
{
|
||||
CURLcode result = CURLE_FAILED_INIT;
|
||||
void *resolver = NULL;
|
||||
|
||||
if(async->thrdd.queued || async->thrdd.done)
|
||||
if(async->queries_ongoing || async->done)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
switch(async->ip_version) {
|
||||
#ifdef CURL_IPRESOLVE_V6
|
||||
case CURL_IPRESOLVE_V6:
|
||||
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V6);
|
||||
break;
|
||||
#ifdef USE_HTTPSRR_ARES
|
||||
DEBUGASSERT(!async->thrdd.rr.channel);
|
||||
if((async->dns_queries & CURL_DNSQ_HTTPS) && !async->is_ipaddr) {
|
||||
result = async_rr_start(data, async);
|
||||
if(result)
|
||||
goto out;
|
||||
resolver = async->thrdd.rr.channel;
|
||||
}
|
||||
#endif
|
||||
case CURL_IPRESOLVE_V4:
|
||||
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V4);
|
||||
break;
|
||||
default:
|
||||
|
||||
result = Curl_resolv_announce_start(data, resolver);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
#ifdef CURL_IPRESOLVE_V6
|
||||
/* When resolving IP addresses, some resolvers (e.g. macOS) will
|
||||
* happily "embed" an IPv4 address into the IPv6 equivalent.
|
||||
* This will then confuse FTP that has been told an IPv4 for
|
||||
* DATA, but suddenly sees IPv6. */
|
||||
if(Curl_ipv6works(data) && !Curl_is_ipv4addr(async->hostname)) {
|
||||
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V6);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
/* Do not start an AAAA query for an ipv4 address when
|
||||
* we will start an A query for it. */
|
||||
if((async->dns_queries & CURL_DNSQ_AAAA) &&
|
||||
!(async->is_ipv4addr && (async->dns_queries & CURL_DNSQ_A))) {
|
||||
result = async_thrdd_query(data, async, CURL_DNSQ_AAAA);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
result = async_thrdd_query(data, async, CURL_IPRESOLVE_V4);
|
||||
break;
|
||||
if(async->dns_queries & CURL_DNSQ_A) {
|
||||
result = async_thrdd_query(data, async, CURL_DNSQ_A);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
Curl_thrdq_trace(data->multi->resolv_thrdq, data, &Curl_trc_feat_dns);
|
||||
#endif
|
||||
|
||||
#ifdef USE_HTTPSRR_ARES
|
||||
DEBUGASSERT(!async->thrdd.rr.channel);
|
||||
if(async_rr_start(data, async))
|
||||
infof(data, "Failed HTTPS RR operation");
|
||||
Curl_thrdq_trace(data->multi->resolv_thrdq, data);
|
||||
#endif
|
||||
|
||||
out:
|
||||
if(result)
|
||||
CURL_TRC_DNS(data, "[async] error queueing %s:%d -> %d",
|
||||
CURL_TRC_DNS(data, "error queueing query %s:%d -> %d",
|
||||
async->hostname, async->port, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
|
||||
CURLcode Curl_async_pollset(struct Curl_easy *data,
|
||||
struct Curl_resolv_async *async,
|
||||
struct easy_pollset *ps)
|
||||
{
|
||||
struct Curl_resolv_async *async = data->state.async;
|
||||
struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
|
||||
timediff_t timeout_ms;
|
||||
|
||||
if(!thrdd)
|
||||
return CURLE_OK;
|
||||
timeout_ms = Curl_async_timeleft_ms(data, async);
|
||||
#ifdef USE_HTTPSRR_ARES
|
||||
if(thrdd->rr.channel) {
|
||||
CURLcode result = Curl_ares_pollset(data, thrdd->rr.channel, ps);
|
||||
if(async->thrdd.rr.channel) {
|
||||
CURLcode result = Curl_ares_pollset(data, async->thrdd.rr.channel, ps);
|
||||
if(result)
|
||||
return result;
|
||||
timeout_ms = Curl_ares_timeout_ms(data, async, async->thrdd.rr.channel);
|
||||
}
|
||||
#else
|
||||
(void)ps;
|
||||
#endif
|
||||
|
||||
if(!thrdd->done) {
|
||||
#ifdef ENABLE_WAKEUP
|
||||
/* The multi "wakeup" socket pair triggers result processing,
|
||||
* no need for an extra timer. */
|
||||
(void)data;
|
||||
#else
|
||||
timediff_t milli;
|
||||
timediff_t ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
|
||||
if(ms < 3)
|
||||
milli = 1;
|
||||
else if(ms <= 50)
|
||||
milli = ms / 3;
|
||||
else if(ms <= 250)
|
||||
milli = 50;
|
||||
if(!async->done) {
|
||||
#ifndef ENABLE_WAKEUP
|
||||
timediff_t stutter_ms, elapsed_ms;
|
||||
elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
|
||||
if(elapsed_ms < 3)
|
||||
stutter_ms = 1;
|
||||
else if(elapsed_ms <= 50)
|
||||
stutter_ms = elapsed_ms / 3;
|
||||
else if(elapsed_ms <= 250)
|
||||
stutter_ms = 50;
|
||||
else
|
||||
milli = 200;
|
||||
Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
|
||||
stutter_ms = 200;
|
||||
timeout_ms = CURLMIN(stutter_ms, timeout_ms);
|
||||
#endif
|
||||
Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
@ -677,11 +676,12 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
|
||||
struct Curl_dns_entry **pdns)
|
||||
{
|
||||
struct async_thrdd_ctx *thrdd = &async->thrdd;
|
||||
struct Curl_dns_entry *dns = NULL;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
DEBUGASSERT(pdns);
|
||||
*pdns = NULL;
|
||||
if(!thrdd->queued && !thrdd->done) {
|
||||
if(!async->queries_ongoing && !async->done) {
|
||||
DEBUGASSERT(0);
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
@ -692,52 +692,60 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
|
||||
(void)Curl_ares_perform(thrdd->rr.channel, 0);
|
||||
#endif
|
||||
|
||||
if(!thrdd->done)
|
||||
if(!async->done)
|
||||
return CURLE_AGAIN;
|
||||
|
||||
Curl_expire_done(data, EXPIRE_ASYNC_NAME);
|
||||
if(async->result)
|
||||
goto out;
|
||||
|
||||
if((thrdd->res_A && thrdd->res_A->res) ||
|
||||
(thrdd->res_AAAA && thrdd->res_AAAA->res)) {
|
||||
struct Curl_dns_entry *dns =
|
||||
Curl_dnscache_mk_entry2(data,
|
||||
thrdd->res_A ? &thrdd->res_A->res : NULL,
|
||||
thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL,
|
||||
async->hostname, async->port, async->ip_version);
|
||||
if(!dns)
|
||||
dns = Curl_dnscache_mk_entry2(
|
||||
data, async->dns_queries,
|
||||
thrdd->res_A ? &thrdd->res_A->res : NULL,
|
||||
thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL,
|
||||
async->hostname, async->port);
|
||||
if(!dns) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef USE_HTTPSRR_ARES
|
||||
if(!result && thrdd->rr.channel) {
|
||||
result = thrdd->rr.result;
|
||||
if(!result) {
|
||||
struct Curl_https_rrinfo *lhrr;
|
||||
if(thrdd->rr.channel) {
|
||||
struct Curl_https_rrinfo *lhrr = NULL;
|
||||
if(thrdd->rr.hinfo.complete) {
|
||||
lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
|
||||
if(!lhrr)
|
||||
if(!lhrr) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
else
|
||||
dns->hinfo = lhrr;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
Curl_httpsrr_trace(data, lhrr);
|
||||
Curl_dns_entry_set_https_rr(dns, lhrr);
|
||||
}
|
||||
#endif
|
||||
if(!result && dns) {
|
||||
CURL_TRC_DNS(data, "[async] resolving complete");
|
||||
*pdns = dns;
|
||||
dns = NULL;
|
||||
}
|
||||
Curl_dns_entry_unlink(data, &dns);
|
||||
}
|
||||
|
||||
if(dns) {
|
||||
CURL_TRC_DNS(data, "resolving complete");
|
||||
*pdns = dns;
|
||||
dns = NULL;
|
||||
}
|
||||
#ifdef CURLVERBOSE
|
||||
Curl_thrdq_trace(data->multi->resolv_thrdq, data, &Curl_trc_feat_dns);
|
||||
Curl_thrdq_trace(data->multi->resolv_thrdq, data);
|
||||
#endif
|
||||
|
||||
out:
|
||||
Curl_dns_entry_unlink(data, &dns);
|
||||
Curl_async_thrdd_shutdown(data, async);
|
||||
if(!result && !*pdns)
|
||||
result = Curl_resolver_error(data, NULL);
|
||||
Curl_async_thrdd_shutdown(data, async);
|
||||
if(result &&
|
||||
(result != CURLE_COULDNT_RESOLVE_HOST) &&
|
||||
(result != CURLE_COULDNT_RESOLVE_PROXY)) {
|
||||
CURL_TRC_DNS(data, "[async] %s:%d: error %d",
|
||||
async->hostname, async->port, result);
|
||||
CURL_TRC_DNS(data, "Error %d resolving %s:%d",
|
||||
result, async->hostname, async->port);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -780,4 +788,30 @@ Curl_async_get_ai(struct Curl_easy *data,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef USE_HTTPSRR
|
||||
const struct Curl_https_rrinfo *
|
||||
Curl_async_get_https(struct Curl_easy *data,
|
||||
struct Curl_resolv_async *async)
|
||||
{
|
||||
#ifdef USE_HTTPSRR_ARES
|
||||
if(Curl_async_knows_https(data, async))
|
||||
return &async->thrdd.rr.hinfo;
|
||||
#else
|
||||
(void)data;
|
||||
(void)async;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Curl_async_knows_https(struct Curl_easy *data,
|
||||
struct Curl_resolv_async *async)
|
||||
{
|
||||
(void)data;
|
||||
if(async->dns_queries & CURL_DNSQ_HTTPS)
|
||||
return ((async->dns_responses & CURL_DNSQ_HTTPS) || async->done);
|
||||
return TRUE; /* we know it will never come */
|
||||
}
|
||||
|
||||
#endif /* USE_HTTPSRR */
|
||||
|
||||
#endif /* USE_RESOLV_THREADED */
|
||||
|
||||
150
lib/asyn.h
150
lib/asyn.h
@ -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 *****************/
|
||||
|
||||
78
lib/cf-dns.c
78
lib/cf-dns.c
@ -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 */
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
159
lib/dnscache.c
159
lib/dnscache.c
@ -46,6 +46,7 @@
|
||||
#include "curl_trc.h"
|
||||
#include "dnscache.h"
|
||||
#include "hash.h"
|
||||
#include "hostip.h"
|
||||
#include "httpsrr.h"
|
||||
#include "progress.h"
|
||||
#include "rand.h"
|
||||
@ -59,25 +60,6 @@
|
||||
|
||||
#define MAX_DNS_CACHE_SIZE 29999
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
static const char *dnscache_ipv_str(uint8_t ip_version)
|
||||
{
|
||||
switch(ip_version) {
|
||||
case CURL_IPRESOLVE_WHATEVER:
|
||||
return "A+AAAA";
|
||||
case CURL_IPRESOLVE_V4:
|
||||
return "A";
|
||||
#ifdef PF_INET6
|
||||
case CURL_IPRESOLVE_V6:
|
||||
return "AAAA";
|
||||
#endif
|
||||
default:
|
||||
DEBUGASSERT(0);
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void dnscache_entry_free(struct Curl_dns_entry *dns)
|
||||
{
|
||||
Curl_freeaddrinfo(dns->addr);
|
||||
@ -229,9 +211,9 @@ void Curl_dnscache_clear(struct Curl_easy *data)
|
||||
/* lookup address, returns entry if found and not stale */
|
||||
static CURLcode fetch_addr(struct Curl_easy *data,
|
||||
struct Curl_dnscache *dnscache,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t ip_version,
|
||||
struct Curl_dns_entry **pdns)
|
||||
{
|
||||
struct Curl_dns_entry *dns = NULL;
|
||||
@ -273,43 +255,17 @@ static CURLcode fetch_addr(struct Curl_easy *data,
|
||||
}
|
||||
}
|
||||
|
||||
if(dns && dns->ip_version != ip_version) {
|
||||
switch(dns->ip_version) {
|
||||
case CURL_IPRESOLVE_WHATEVER: {
|
||||
/* Do we have addresses that match the requested ip version? */
|
||||
int pf = PF_INET;
|
||||
bool found = FALSE;
|
||||
struct Curl_addrinfo *addr = dns->addr;
|
||||
|
||||
#ifdef PF_INET6
|
||||
if(ip_version == CURL_IPRESOLVE_V6)
|
||||
pf = PF_INET6;
|
||||
#endif
|
||||
|
||||
while(addr) {
|
||||
if(addr->ai_family == pf) {
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
addr = addr->ai_next;
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
/* We assume that CURL_IPRESOLVE_WHATEVER means we tried to
|
||||
* get addresses for all supported types, but there are none
|
||||
* for the ip version we need. This is a negative resolve. */
|
||||
CURL_TRC_DNS(data, "cache entry does not have type=%s addresses",
|
||||
dnscache_ipv_str(ip_version));
|
||||
dns = NULL;
|
||||
result = CURLE_COULDNT_RESOLVE_HOST;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* different families, we return NULL + OK, so a new resolve
|
||||
* attempt may get started. */
|
||||
if(dns) {
|
||||
if((dns->dns_queries & dns_queries) != dns_queries) {
|
||||
/* The entry does not cover all wanted DNS queries, a miss. */
|
||||
dns = NULL;
|
||||
break;
|
||||
}
|
||||
else if(!(dns->dns_responses & dns_queries)) {
|
||||
/* The entry has no responses for the wanted DNS queries. */
|
||||
CURL_TRC_DNS(data, "cache entry does not have type=%s addresses",
|
||||
Curl_resolv_query_str(dns_queries));
|
||||
dns = NULL;
|
||||
result = CURLE_COULDNT_RESOLVE_HOST;
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,9 +292,9 @@ static CURLcode fetch_addr(struct Curl_easy *data,
|
||||
* use, or we will leak memory!
|
||||
*/
|
||||
CURLcode Curl_dnscache_get(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t ip_version,
|
||||
struct Curl_dns_entry **pentry)
|
||||
{
|
||||
struct Curl_dnscache *dnscache = dnscache_get(data);
|
||||
@ -346,7 +302,7 @@ CURLcode Curl_dnscache_get(struct Curl_easy *data,
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
dnscache_lock(data, dnscache);
|
||||
result = fetch_addr(data, dnscache, hostname, port, ip_version, &dns);
|
||||
result = fetch_addr(data, dnscache, dns_queries, hostname, port, &dns);
|
||||
if(!result && dns)
|
||||
dns->refcount++; /* we pass out a reference */
|
||||
else if(result) {
|
||||
@ -441,14 +397,24 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool dnscache_ai_has_family(struct Curl_addrinfo *ai,
|
||||
int ai_family)
|
||||
{
|
||||
for(; ai; ai = ai->ai_next) {
|
||||
if(ai->ai_family == ai_family)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static struct Curl_dns_entry *
|
||||
dnscache_entry_create(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
struct Curl_addrinfo **paddr1,
|
||||
struct Curl_addrinfo **paddr2,
|
||||
const char *hostname,
|
||||
size_t hostlen,
|
||||
uint16_t port,
|
||||
uint8_t ip_version,
|
||||
bool permanent)
|
||||
{
|
||||
struct Curl_dns_entry *dns = NULL;
|
||||
@ -459,6 +425,11 @@ dnscache_entry_create(struct Curl_easy *data,
|
||||
goto out;
|
||||
|
||||
dns->refcount = 1; /* the cache has the first reference */
|
||||
dns->dns_queries = dns_queries;
|
||||
dns->port = port;
|
||||
if(hostlen)
|
||||
memcpy(dns->hostname, hostname, hostlen);
|
||||
|
||||
if(permanent) {
|
||||
dns->timestamp.tv_sec = 0; /* an entry that never goes stale */
|
||||
dns->timestamp.tv_usec = 0; /* an entry that never goes stale */
|
||||
@ -466,10 +437,6 @@ dnscache_entry_create(struct Curl_easy *data,
|
||||
else {
|
||||
dns->timestamp = *Curl_pgrs_now(data);
|
||||
}
|
||||
dns->port = port;
|
||||
dns->ip_version = ip_version;
|
||||
if(hostlen)
|
||||
memcpy(dns->hostname, hostname, hostlen);
|
||||
|
||||
/* Take the given address lists into the entry */
|
||||
if(paddr1 && *paddr1) {
|
||||
@ -484,6 +451,16 @@ dnscache_entry_create(struct Curl_easy *data,
|
||||
*paddr2 = NULL;
|
||||
}
|
||||
|
||||
if((dns_queries & CURL_DNSQ_A) &&
|
||||
dnscache_ai_has_family(dns->addr, PF_INET))
|
||||
dns->dns_responses |= CURL_DNSQ_A;
|
||||
|
||||
#ifdef USE_IPV6
|
||||
if((dns_queries & CURL_DNSQ_AAAA) &&
|
||||
dnscache_ai_has_family(dns->addr, PF_INET6))
|
||||
dns->dns_responses |= CURL_DNSQ_AAAA;
|
||||
#endif /* USE_IPV6 */
|
||||
|
||||
#ifndef CURL_DISABLE_SHUFFLE_DNS
|
||||
/* shuffle addresses if requested */
|
||||
if(data->set.dns_shuffle_addresses && dns->addr) {
|
||||
@ -513,36 +490,54 @@ out:
|
||||
|
||||
struct Curl_dns_entry *
|
||||
Curl_dnscache_mk_entry(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
struct Curl_addrinfo **paddr,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t ip_version)
|
||||
uint16_t port)
|
||||
{
|
||||
return dnscache_entry_create(data, paddr, NULL, hostname,
|
||||
return dnscache_entry_create(data, dns_queries, paddr, NULL, hostname,
|
||||
hostname ? strlen(hostname) : 0,
|
||||
port, ip_version, FALSE);
|
||||
port, FALSE);
|
||||
}
|
||||
|
||||
struct Curl_dns_entry *
|
||||
Curl_dnscache_mk_entry2(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
struct Curl_addrinfo **paddr1,
|
||||
struct Curl_addrinfo **paddr2,
|
||||
const char *hostname,
|
||||
uint16_t port, uint8_t ip_version)
|
||||
uint16_t port)
|
||||
{
|
||||
return dnscache_entry_create(data, paddr1, paddr2, hostname,
|
||||
return dnscache_entry_create(data, dns_queries, paddr1, paddr2, hostname,
|
||||
hostname ? strlen(hostname) : 0,
|
||||
port, ip_version, FALSE);
|
||||
port, FALSE);
|
||||
}
|
||||
|
||||
#ifdef USE_HTTPSRR
|
||||
void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns,
|
||||
struct Curl_https_rrinfo *hinfo)
|
||||
{
|
||||
/* only do this when this is the only reference */
|
||||
DEBUGASSERT(dns->refcount == 1);
|
||||
/* it should have been in the queries */
|
||||
DEBUGASSERT(dns->dns_queries & CURL_DNSQ_HTTPS);
|
||||
if(dns->hinfo) {
|
||||
Curl_httpsrr_cleanup(dns->hinfo);
|
||||
curlx_free(dns->hinfo);
|
||||
}
|
||||
dns->hinfo = hinfo;
|
||||
dns->dns_responses |= CURL_DNSQ_HTTPS;
|
||||
}
|
||||
#endif /* USE_HTTPSRR */
|
||||
|
||||
static struct Curl_dns_entry *
|
||||
dnscache_add_addr(struct Curl_easy *data,
|
||||
struct Curl_dnscache *dnscache,
|
||||
uint8_t dns_queries,
|
||||
struct Curl_addrinfo **paddr,
|
||||
const char *hostname,
|
||||
size_t hlen,
|
||||
uint16_t port,
|
||||
uint8_t ip_version,
|
||||
bool permanent)
|
||||
{
|
||||
char entry_id[MAX_HOSTCACHE_LEN];
|
||||
@ -550,8 +545,8 @@ dnscache_add_addr(struct Curl_easy *data,
|
||||
struct Curl_dns_entry *dns;
|
||||
struct Curl_dns_entry *dns2;
|
||||
|
||||
dns = dnscache_entry_create(data, paddr, NULL, hostname, hlen, port,
|
||||
ip_version, permanent);
|
||||
dns = dnscache_entry_create(data, dns_queries, paddr, NULL,
|
||||
hostname, hlen, port, permanent);
|
||||
if(!dns)
|
||||
return NULL;
|
||||
|
||||
@ -596,9 +591,9 @@ CURLcode Curl_dnscache_add(struct Curl_easy *data,
|
||||
}
|
||||
|
||||
CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
const char *host,
|
||||
uint16_t port,
|
||||
uint8_t ip_version)
|
||||
uint16_t port)
|
||||
{
|
||||
struct Curl_dnscache *dnscache = dnscache_get(data);
|
||||
struct Curl_dns_entry *dns;
|
||||
@ -607,14 +602,14 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
/* put this new host in the cache */
|
||||
dns = dnscache_add_addr(data, dnscache, NULL, host, strlen(host),
|
||||
port, ip_version, FALSE);
|
||||
dns = dnscache_add_addr(data, dnscache, dns_queries, NULL,
|
||||
host, strlen(host), port, FALSE);
|
||||
if(dns) {
|
||||
/* release the returned reference; the cache itself will keep the
|
||||
* entry alive: */
|
||||
dns->refcount--;
|
||||
CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s",
|
||||
host, port, dnscache_ipv_str(ip_version));
|
||||
host, port, Curl_resolv_query_str(dns_queries));
|
||||
return CURLE_OK;
|
||||
}
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
@ -846,10 +841,10 @@ err:
|
||||
Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
|
||||
}
|
||||
|
||||
/* put this new host in the cache */
|
||||
dns = dnscache_add_addr(data, dnscache, &head, curlx_str(&source),
|
||||
curlx_strlen(&source), port,
|
||||
CURL_IPRESOLVE_WHATEVER, permanent);
|
||||
/* put this new host in the cache, an overridy for ALL dns queries */
|
||||
dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL,
|
||||
&head, curlx_str(&source),
|
||||
curlx_strlen(&source), port, permanent);
|
||||
if(dns)
|
||||
/* release the returned reference; the cache itself will keep the
|
||||
* entry alive: */
|
||||
|
||||
@ -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.
|
||||
|
||||
68
lib/doh.c
68
lib/doh.c
@ -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);
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
20
lib/ftp.c
20
lib/ftp.c
@ -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) {
|
||||
|
||||
424
lib/hostip.c
424
lib/hostip.c
@ -118,6 +118,48 @@
|
||||
*
|
||||
* If the conversion fails, the target buffer is empty.
|
||||
*/
|
||||
|
||||
uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version)
|
||||
{
|
||||
(void)data;
|
||||
switch(ip_version) {
|
||||
case CURL_IPRESOLVE_V6:
|
||||
return CURL_DNSQ_AAAA;
|
||||
case CURL_IPRESOLVE_V4:
|
||||
return CURL_DNSQ_A;
|
||||
default:
|
||||
if(Curl_ipv6works(data))
|
||||
return (CURL_DNSQ_A|CURL_DNSQ_AAAA);
|
||||
else
|
||||
return CURL_DNSQ_A;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
const char *Curl_resolv_query_str(uint8_t dns_queries)
|
||||
{
|
||||
switch(dns_queries) {
|
||||
case (CURL_DNSQ_A|CURL_DNSQ_AAAA|CURL_DNSQ_HTTPS):
|
||||
return "A+AAAA+HTTPS";
|
||||
case (CURL_DNSQ_A|CURL_DNSQ_AAAA):
|
||||
return "A+AAAA";
|
||||
case (CURL_DNSQ_AAAA|CURL_DNSQ_HTTPS):
|
||||
return "AAAA+HTTPS";
|
||||
case (CURL_DNSQ_AAAA):
|
||||
return "AAAA";
|
||||
case (CURL_DNSQ_A|CURL_DNSQ_HTTPS):
|
||||
return "A+HTTPS";
|
||||
case (CURL_DNSQ_A):
|
||||
return "A";
|
||||
case (CURL_DNSQ_HTTPS):
|
||||
return "HTTPS";
|
||||
default:
|
||||
DEBUGASSERT(0);
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
|
||||
size_t bufsize)
|
||||
{
|
||||
@ -287,57 +329,85 @@ static bool tailmatch(const char *full, size_t flen,
|
||||
return curl_strnequal(part, &full[flen - plen], plen);
|
||||
}
|
||||
|
||||
static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version)
|
||||
static bool can_resolve_dns_queries(struct Curl_easy *data,
|
||||
uint8_t dns_queries)
|
||||
{
|
||||
#ifdef CURLRES_IPV6
|
||||
if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
|
||||
return FALSE;
|
||||
#elif defined(CURLRES_IPV4)
|
||||
(void)data;
|
||||
if(ip_version == CURL_IPRESOLVE_V6)
|
||||
if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && !Curl_ipv6works(data))
|
||||
return FALSE;
|
||||
#else
|
||||
#error either CURLRES_IPV6 or CURLRES_IPV4 need to be defined
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
CURLcode Curl_resolv_announce_start(struct Curl_easy *data,
|
||||
void *resolver)
|
||||
{
|
||||
if(data->set.resolver_start) {
|
||||
int rc;
|
||||
|
||||
CURL_TRC_DNS(data, "announcing resolve to application");
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
rc = data->set.resolver_start(resolver, NULL,
|
||||
data->set.resolver_start_client);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
if(rc) {
|
||||
CURL_TRC_DNS(data, "application aborted resolve");
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#ifdef USE_CURL_ASYNC
|
||||
static CURLcode hostip_async_new(struct Curl_easy *data,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t ip_version,
|
||||
uint8_t transport,
|
||||
timediff_t timeout_ms)
|
||||
static struct Curl_resolv_async *
|
||||
hostip_async_new(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t transport,
|
||||
timediff_t timeout_ms)
|
||||
{
|
||||
struct Curl_resolv_async *async;
|
||||
size_t hostlen = strlen(hostname);
|
||||
|
||||
DEBUGASSERT(!data->state.async);
|
||||
if(!data->multi) {
|
||||
DEBUGASSERT(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* struct size already includes the NUL for hostname */
|
||||
async = curlx_calloc(1, sizeof(*async) + hostlen);
|
||||
if(!async)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
|
||||
/* Even if this wraps (unlikely), it will be in time so far apart
|
||||
* that it does not matter for all practical purposes. */
|
||||
async->id = data->state.next_async_id++;
|
||||
/* Give every async resolve operation a "unique" id. This may
|
||||
* wrap around after a long time, making collisions highly unlikely.
|
||||
* As we keep the async structs at the easy handle, chances of
|
||||
* easy `mid plus resolv->id` colliding should be astronomical.
|
||||
* `resolv_id == 0` is never used. */
|
||||
if(data->multi->last_resolv_id == UINT32_MAX)
|
||||
data->multi->last_resolv_id = 1; /* wrap around */
|
||||
else
|
||||
data->multi->last_resolv_id++;
|
||||
async->id = data->multi->last_resolv_id;
|
||||
async->dns_queries = dns_queries;
|
||||
async->port = port;
|
||||
async->ip_version = ip_version;
|
||||
async->transport = transport;
|
||||
async->start = *Curl_pgrs_now(data);
|
||||
async->timeout_ms = timeout_ms;
|
||||
if(hostlen)
|
||||
if(hostlen) {
|
||||
memcpy(async->hostname, hostname, hostlen);
|
||||
async->is_ipaddr = Curl_is_ipaddr(async->hostname);
|
||||
if(async->is_ipaddr)
|
||||
async->is_ipv4addr = Curl_is_ipv4addr(async->hostname);
|
||||
}
|
||||
|
||||
data->state.async = async;
|
||||
return CURLE_OK;
|
||||
return async;
|
||||
}
|
||||
|
||||
static CURLcode hostip_resolv_take_result(struct Curl_easy *data,
|
||||
struct Curl_resolv_async *async,
|
||||
struct Curl_dns_entry **pdns)
|
||||
{
|
||||
struct Curl_resolv_async *async = data->state.async;
|
||||
CURLcode result;
|
||||
|
||||
/* If async resolving is ongoing, this must be set */
|
||||
@ -346,7 +416,7 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data,
|
||||
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
if(data->conn->bits.doh)
|
||||
result = Curl_doh_take_result(data, pdns);
|
||||
result = Curl_doh_take_result(data, async, pdns);
|
||||
else
|
||||
#endif
|
||||
result = Curl_async_take_result(data, async, pdns);
|
||||
@ -362,87 +432,83 @@ static CURLcode hostip_resolv_take_result(struct Curl_easy *data,
|
||||
}
|
||||
|
||||
const struct Curl_addrinfo *
|
||||
Curl_resolv_get_ai(struct Curl_easy *data, int ai_family,
|
||||
unsigned int index)
|
||||
Curl_resolv_get_ai(struct Curl_easy *data, uint32_t resolv_id,
|
||||
int ai_family, unsigned int index)
|
||||
{
|
||||
#ifdef CURLRES_ASYNCH
|
||||
struct Curl_resolv_async *async = data->state.async;
|
||||
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
|
||||
if(async)
|
||||
return Curl_async_get_ai(data, async, ai_family, index);
|
||||
#else
|
||||
(void)data;
|
||||
(void)resolv_id;
|
||||
(void)ai_family;
|
||||
(void)index;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* USE_CURL_ASYNC */
|
||||
|
||||
static CURLcode hostip_resolv_announce(struct Curl_easy *data,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t ip_version,
|
||||
uint8_t transport,
|
||||
timediff_t timeout_ms)
|
||||
#ifdef USE_HTTPSRR
|
||||
const struct Curl_https_rrinfo *
|
||||
Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id)
|
||||
{
|
||||
if(data->set.resolver_start) {
|
||||
void *resolver = NULL;
|
||||
int st;
|
||||
#ifdef CURLRES_ASYNCH
|
||||
CURLcode result;
|
||||
if(!data->state.async) {
|
||||
result = hostip_async_new(data, hostname, port, ip_version,
|
||||
transport, timeout_ms);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = Curl_async_get_impl(data, data->state.async, &resolver);
|
||||
if(result)
|
||||
return result;
|
||||
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
|
||||
if(async)
|
||||
return Curl_async_get_https(data, async);
|
||||
#else
|
||||
(void)hostname;
|
||||
(void)port;
|
||||
(void)ip_version;
|
||||
(void)timeout_ms;
|
||||
(void)transport;
|
||||
(void)data;
|
||||
(void)resolv_id;
|
||||
#endif
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
st = data->set.resolver_start(resolver, NULL,
|
||||
data->set.resolver_start_client);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
if(st) {
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
}
|
||||
return CURLE_OK;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id)
|
||||
{
|
||||
#ifdef CURLRES_ASYNCH
|
||||
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
|
||||
if(async)
|
||||
return Curl_async_knows_https(data, async);
|
||||
#else
|
||||
(void)data;
|
||||
(void)resolv_id;
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif /* USE_HTTPSRR */
|
||||
|
||||
#endif /* USE_CURL_ASYNC */
|
||||
|
||||
static CURLcode hostip_resolv_start(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t ip_version,
|
||||
uint8_t transport,
|
||||
timediff_t timeout_ms,
|
||||
bool allowDOH,
|
||||
uint32_t *presolv_id,
|
||||
struct Curl_dns_entry **pdns)
|
||||
{
|
||||
#ifdef USE_CURL_ASYNC
|
||||
struct Curl_resolv_async *async = NULL;
|
||||
#endif
|
||||
struct Curl_addrinfo *addr = NULL;
|
||||
size_t hostname_len;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
(void)timeout_ms; /* not in all ifdefs */
|
||||
*presolv_id = 0;
|
||||
*pdns = NULL;
|
||||
|
||||
/* really need to start a resolve operation */
|
||||
result = hostip_resolv_announce(data, hostname, port, ip_version,
|
||||
transport, timeout_ms);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
/* Check for "known" things to resolve ourselves. */
|
||||
#ifndef USE_RESOLVE_ON_IPS
|
||||
if(Curl_is_ipaddr(hostname)) {
|
||||
/* test655 verifies that the announce is done, even though there
|
||||
* is no real resolving. So, keep doing this. */
|
||||
result = Curl_resolv_announce_start(data, NULL);
|
||||
if(result)
|
||||
goto out;
|
||||
/* shortcut literal IP addresses, if we are not told to resolve them. */
|
||||
result = Curl_str2addr(hostname, port, &addr);
|
||||
goto out;
|
||||
@ -454,6 +520,9 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data,
|
||||
curl_strequal(hostname, "localhost.") ||
|
||||
tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
|
||||
tailmatch(hostname, hostname_len, STRCONST(".localhost."))) {
|
||||
result = Curl_resolv_announce_start(data, NULL);
|
||||
if(result)
|
||||
goto out;
|
||||
addr = get_localhost(port, hostname);
|
||||
if(!addr)
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
@ -462,13 +531,18 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data,
|
||||
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) {
|
||||
if(!data->state.async) {
|
||||
result = hostip_async_new(data, hostname, port, ip_version,
|
||||
transport, timeout_ms);
|
||||
if(result)
|
||||
result = Curl_resolv_announce_start(data, NULL);
|
||||
if(result)
|
||||
goto out;
|
||||
if(!async) {
|
||||
async = hostip_async_new(data, dns_queries, hostname, port,
|
||||
transport, timeout_ms);
|
||||
if(!async) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
result = Curl_doh(data, data->state.async);
|
||||
result = Curl_doh(data, async);
|
||||
goto out;
|
||||
}
|
||||
#else
|
||||
@ -476,30 +550,35 @@ static CURLcode hostip_resolv_start(struct Curl_easy *data,
|
||||
#endif
|
||||
|
||||
/* Can we provide the requested IP specifics in resolving? */
|
||||
if(!can_resolve_ip_version(data, ip_version)) {
|
||||
if(!can_resolve_dns_queries(data, dns_queries)) {
|
||||
result = CURLE_COULDNT_RESOLVE_HOST;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef CURLRES_ASYNCH
|
||||
(void)addr;
|
||||
if(!data->state.async) {
|
||||
result = hostip_async_new(data, hostname, port, ip_version,
|
||||
transport, timeout_ms);
|
||||
if(result)
|
||||
if(!async) {
|
||||
async = hostip_async_new(data, dns_queries, hostname, port,
|
||||
transport, timeout_ms);
|
||||
if(!async) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
result = Curl_async_getaddrinfo(data, data->state.async);
|
||||
result = Curl_async_getaddrinfo(data, async);
|
||||
if(result == CURLE_AGAIN) {
|
||||
/* the answer might be there already. Check. */
|
||||
CURLcode r2 = hostip_resolv_take_result(data, pdns);
|
||||
CURLcode r2 = hostip_resolv_take_result(data, async, pdns);
|
||||
if(r2)
|
||||
result = r2;
|
||||
else if(*pdns)
|
||||
result = CURLE_OK;
|
||||
}
|
||||
#else
|
||||
addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version, transport);
|
||||
result = Curl_resolv_announce_start(data, NULL);
|
||||
if(result)
|
||||
goto out;
|
||||
addr = Curl_sync_getaddrinfo(data, dns_queries, hostname, port, transport);
|
||||
if(!addr)
|
||||
result = CURLE_COULDNT_RESOLVE_HOST;
|
||||
#endif
|
||||
@ -509,7 +588,7 @@ out:
|
||||
if(addr) {
|
||||
/* we got a response, create a dns entry, add to cache, return */
|
||||
DEBUGASSERT(!*pdns);
|
||||
*pdns = Curl_dnscache_mk_entry(data, &addr, hostname, port, ip_version);
|
||||
*pdns = Curl_dnscache_mk_entry(data, dns_queries, &addr, hostname, port);
|
||||
if(!*pdns)
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
@ -521,31 +600,39 @@ out:
|
||||
else if(addr)
|
||||
Curl_freeaddrinfo(addr);
|
||||
|
||||
#ifdef USE_CURL_ASYNC
|
||||
if(async) {
|
||||
if(result == CURLE_AGAIN) { /* still need it, link, return id. */
|
||||
*presolv_id = async->id;
|
||||
async->next = data->state.async;
|
||||
data->state.async = async;
|
||||
}
|
||||
else {
|
||||
Curl_async_destroy(data, async);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode hostip_resolv(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t ip_version,
|
||||
uint8_t transport,
|
||||
timediff_t timeout_ms,
|
||||
bool allowDOH,
|
||||
uint32_t *presolv_id,
|
||||
struct Curl_dns_entry **pdns)
|
||||
{
|
||||
size_t hostname_len;
|
||||
CURLcode result = CURLE_COULDNT_RESOLVE_HOST;
|
||||
bool cache_dns = FALSE;
|
||||
|
||||
(void)timeout_ms; /* not used in all ifdefs */
|
||||
*presolv_id = 0;
|
||||
*pdns = NULL;
|
||||
|
||||
#ifdef USE_CURL_ASYNC
|
||||
if(data->state.async)
|
||||
Curl_async_destroy(data);
|
||||
#else
|
||||
(void)timeout_ms;
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
data->conn->bits.doh = FALSE; /* default is not */
|
||||
#else
|
||||
@ -563,9 +650,9 @@ static CURLcode hostip_resolv(struct Curl_easy *data,
|
||||
}
|
||||
|
||||
#ifdef DEBUGBUILD
|
||||
CURL_TRC_DNS(data, "hostip_resolv(%s:%u, ip=%x)",
|
||||
hostname, port, ip_version);
|
||||
if((ip_version == CURL_IPRESOLVE_V6) &&
|
||||
CURL_TRC_DNS(data, "hostip_resolv(%s:%u, queries=%s)",
|
||||
hostname, port, Curl_resolv_query_str(dns_queries));
|
||||
if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) &&
|
||||
getenv("CURL_DBG_RESOLV_FAIL_IPV6")) {
|
||||
infof(data, "DEBUG fail ipv6 resolve");
|
||||
result = Curl_resolver_error(data, NULL);
|
||||
@ -573,7 +660,7 @@ static CURLcode hostip_resolv(struct Curl_easy *data,
|
||||
}
|
||||
#endif
|
||||
/* Let's check our DNS cache first */
|
||||
result = Curl_dnscache_get(data, hostname, port, ip_version, pdns);
|
||||
result = Curl_dnscache_get(data, dns_queries, hostname, port, pdns);
|
||||
if(*pdns) {
|
||||
infof(data, "Hostname %s was found in DNS cache", hostname);
|
||||
result = CURLE_OK;
|
||||
@ -585,18 +672,18 @@ static CURLcode hostip_resolv(struct Curl_easy *data,
|
||||
else {
|
||||
/* No luck, we need to start resolving. */
|
||||
cache_dns = TRUE;
|
||||
result = hostip_resolv_start(data, hostname, port, ip_version,
|
||||
transport, timeout_ms, allowDOH, pdns);
|
||||
result = hostip_resolv_start(data, dns_queries, hostname, port,
|
||||
transport, timeout_ms, allowDOH,
|
||||
presolv_id, pdns);
|
||||
}
|
||||
|
||||
out:
|
||||
if(result && (result != CURLE_AGAIN)) {
|
||||
Curl_dns_entry_unlink(data, pdns);
|
||||
Curl_async_shutdown(data);
|
||||
if((result == CURLE_COULDNT_RESOLVE_HOST) ||
|
||||
(result == CURLE_COULDNT_RESOLVE_PROXY)) {
|
||||
if(cache_dns)
|
||||
Curl_dnscache_add_negative(data, hostname, port, ip_version);
|
||||
Curl_dnscache_add_negative(data, dns_queries, hostname, port);
|
||||
failf(data, "Could not resolve: %s:%u", hostname, port);
|
||||
}
|
||||
else {
|
||||
@ -609,40 +696,39 @@ out:
|
||||
Curl_dns_entry_unlink(data, pdns);
|
||||
}
|
||||
|
||||
CURL_TRC_DNS(data, "hostip_resolv(%s:%u, ip=%x, timeout_ms=%" FMT_TIMEDIFF_T
|
||||
") -> %d, dns %sfound",
|
||||
hostname, port, ip_version, timeout_ms, result,
|
||||
*pdns ? "" : "not ");
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_resolv_blocking(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t ip_version,
|
||||
uint8_t transport,
|
||||
struct Curl_dns_entry **pdns)
|
||||
{
|
||||
CURLcode result;
|
||||
uint32_t resolv_id;
|
||||
DEBUGASSERT(hostname && *hostname);
|
||||
*pdns = NULL;
|
||||
/* We cannot do a blocking resolve using DoH currently */
|
||||
result = hostip_resolv(data, hostname, port, ip_version,
|
||||
transport, 0, FALSE, pdns);
|
||||
result = hostip_resolv(data, dns_queries,
|
||||
hostname, port, transport, 0, FALSE,
|
||||
&resolv_id, pdns);
|
||||
switch(result) {
|
||||
case CURLE_OK:
|
||||
DEBUGASSERT(*pdns);
|
||||
return CURLE_OK;
|
||||
break;
|
||||
#ifdef USE_CURL_ASYNC
|
||||
case CURLE_AGAIN:
|
||||
DEBUGASSERT(!*pdns);
|
||||
if(!data->state.async)
|
||||
return CURLE_FAILED_INIT;
|
||||
return Curl_async_await(data, data->state.async, pdns);
|
||||
result = Curl_async_await(data, resolv_id, pdns);
|
||||
Curl_resolv_destroy(data, resolv_id);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return result;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef USE_ALARM_TIMEOUT
|
||||
@ -661,11 +747,12 @@ CURL_NORETURN static void alarmfunc(int sig)
|
||||
#ifdef USE_ALARM_TIMEOUT
|
||||
|
||||
static CURLcode resolv_alarm_timeout(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t ip_version,
|
||||
uint8_t transport,
|
||||
timediff_t timeout_ms,
|
||||
uint32_t *presolv_id,
|
||||
struct Curl_dns_entry **entry)
|
||||
{
|
||||
#ifdef HAVE_SIGACTION
|
||||
@ -742,8 +829,8 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data,
|
||||
|
||||
/* Perform the actual name resolution. This might be interrupted by an
|
||||
* alarm if it takes too long. */
|
||||
result = hostip_resolv(data, hostname, port, ip_version, transport,
|
||||
timeout_ms, TRUE, entry);
|
||||
result = hostip_resolv(data, dns_queries, hostname, port, transport,
|
||||
timeout_ms, FALSE, presolv_id, entry);
|
||||
|
||||
clean_up:
|
||||
if(!prev_alarm)
|
||||
@ -816,19 +903,23 @@ clean_up:
|
||||
* CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
|
||||
*/
|
||||
CURLcode Curl_resolv(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t ip_version,
|
||||
uint8_t transport,
|
||||
timediff_t timeout_ms,
|
||||
uint32_t *presolv_id,
|
||||
struct Curl_dns_entry **entry)
|
||||
{
|
||||
DEBUGASSERT(hostname && *hostname);
|
||||
*presolv_id = 0;
|
||||
*entry = NULL;
|
||||
|
||||
if(timeout_ms < 0)
|
||||
/* got an already expired timeout */
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
else if(!timeout_ms)
|
||||
timeout_ms = CURL_TIMEOUT_RESOLVE_MS;
|
||||
|
||||
#ifdef USE_ALARM_TIMEOUT
|
||||
if(timeout_ms && data->set.no_signal) {
|
||||
@ -836,8 +927,8 @@ CURLcode Curl_resolv(struct Curl_easy *data,
|
||||
timeout_ms = 0;
|
||||
}
|
||||
if(timeout_ms && !Curl_doh_wanted(data)) {
|
||||
return resolv_alarm_timeout(data, hostname, port, ip_version,
|
||||
transport, timeout_ms, entry);
|
||||
return resolv_alarm_timeout(data, dns_queries, hostname, port, transport,
|
||||
timeout_ms, presolv_id, entry);
|
||||
}
|
||||
#endif /* !USE_ALARM_TIMEOUT */
|
||||
|
||||
@ -846,16 +937,28 @@ CURLcode Curl_resolv(struct Curl_easy *data,
|
||||
infof(data, "timeout on name lookup is not supported");
|
||||
#endif
|
||||
|
||||
return hostip_resolv(data, hostname, port, ip_version, transport,
|
||||
timeout_ms, TRUE, entry);
|
||||
return hostip_resolv(data, dns_queries, hostname, port, transport,
|
||||
timeout_ms, TRUE, presolv_id, entry);
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_CURL_ASYNC
|
||||
CURLcode Curl_resolv_take_result(struct Curl_easy *data,
|
||||
struct Curl_dns_entry **pdns)
|
||||
|
||||
struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data,
|
||||
uint32_t resolv_id)
|
||||
{
|
||||
struct Curl_resolv_async *async = data->state.async;
|
||||
for(; async; async = async->next) {
|
||||
if(async->id == resolv_id)
|
||||
return async;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id,
|
||||
struct Curl_dns_entry **pdns)
|
||||
{
|
||||
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
|
||||
CURLcode result;
|
||||
|
||||
/* If async resolving is ongoing, this must be set */
|
||||
@ -863,20 +966,20 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data,
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
/* check if we have the name resolved by now (from someone else) */
|
||||
result = Curl_dnscache_get(data, async->hostname, async->port,
|
||||
async->ip_version, pdns);
|
||||
result = Curl_dnscache_get(data, async->dns_queries,
|
||||
async->hostname, async->port, pdns);
|
||||
if(*pdns) {
|
||||
/* Tell a possibly async resolver we no longer need the results. */
|
||||
infof(data, "Hostname '%s' was found in DNS cache", async->hostname);
|
||||
Curl_async_shutdown(data);
|
||||
Curl_async_shutdown(data, async);
|
||||
return CURLE_OK;
|
||||
}
|
||||
else if(result) {
|
||||
Curl_async_shutdown(data);
|
||||
Curl_async_shutdown(data, async);
|
||||
return Curl_resolver_error(data, NULL);
|
||||
}
|
||||
|
||||
result = hostip_resolv_take_result(data, pdns);
|
||||
result = hostip_resolv_take_result(data, async, pdns);
|
||||
|
||||
if(*pdns) {
|
||||
/* Add to cache */
|
||||
@ -886,8 +989,8 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data,
|
||||
}
|
||||
else if((result == CURLE_COULDNT_RESOLVE_HOST) ||
|
||||
(result == CURLE_COULDNT_RESOLVE_PROXY)) {
|
||||
Curl_dnscache_add_negative(data, async->hostname,
|
||||
async->port, async->ip_version);
|
||||
Curl_dnscache_add_negative(data, async->dns_queries,
|
||||
async->hostname, async->port);
|
||||
failf(data, "Could not resolve: %s:%u", async->hostname, async->port);
|
||||
}
|
||||
else if(result) {
|
||||
@ -897,26 +1000,57 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data,
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* USE_CURL_ASYNC */
|
||||
|
||||
CURLcode Curl_resolv_pollset(struct Curl_easy *data,
|
||||
struct easy_pollset *ps)
|
||||
{
|
||||
#ifdef CURLRES_ASYNCH
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
if(data->conn->bits.doh)
|
||||
/* nothing to wait for during DoH resolve, those handles have their own
|
||||
sockets */
|
||||
return CURLE_OK;
|
||||
#endif
|
||||
return Curl_async_pollset(data, ps);
|
||||
#else
|
||||
(void)data;
|
||||
struct Curl_resolv_async *async = data->state.async;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
(void)ps;
|
||||
return CURLE_OK;
|
||||
for(; async && !result; async = async->next) {
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
if(async->doh) /* DoH has nothing for the pollset */
|
||||
continue;
|
||||
#endif
|
||||
result = Curl_async_pollset(data, async, ps);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id)
|
||||
{
|
||||
struct Curl_resolv_async **panchor = &data->state.async;
|
||||
|
||||
for(; *panchor; panchor = &(*panchor)->next) {
|
||||
struct Curl_resolv_async *async = *panchor;
|
||||
if(async->id == resolv_id) {
|
||||
*panchor = async->next;
|
||||
Curl_async_destroy(data, async);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Curl_resolv_shutdown_all(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_resolv_async *async = data->state.async;
|
||||
for(; async; async = async->next) {
|
||||
Curl_async_shutdown(data, async);
|
||||
}
|
||||
}
|
||||
|
||||
void Curl_resolv_destroy_all(struct Curl_easy *data)
|
||||
{
|
||||
while(data->state.async) {
|
||||
struct Curl_resolv_async *async = data->state.async;
|
||||
data->state.async = async->next;
|
||||
Curl_async_destroy(data, async);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* USE_CURL_ASYNC */
|
||||
|
||||
|
||||
/*
|
||||
* Curl_resolver_error() calls failf() with the appropriate message after a
|
||||
* resolve error
|
||||
@ -964,7 +1098,7 @@ CURLcode Curl_resolv_unix(struct Curl_easy *data,
|
||||
return result;
|
||||
}
|
||||
|
||||
*pdns = Curl_dnscache_mk_entry(data, &addr, NULL, 0, 0);
|
||||
*pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0);
|
||||
return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
#endif /* USE_UNIX_SOCKETS */
|
||||
|
||||
93
lib/hostip.h
93
lib/hostip.h
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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));
|
||||
|
||||
103
lib/httpsrr.c
103
lib/httpsrr.c
@ -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;
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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;
|
||||
|
||||
20
lib/setopt.c
20
lib/setopt.c
@ -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
|
||||
|
||||
19
lib/socks.c
19
lib/socks.c
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
#include "curlx/inet_pton.h"
|
||||
#include "vtls/openssl.h"
|
||||
#include "connect.h"
|
||||
#include "cf-dns.h"
|
||||
#include "progress.h"
|
||||
#include "vtls/vtls.h"
|
||||
#include "vtls/vtls_int.h"
|
||||
@ -3423,6 +3424,16 @@ ossl_init_session_and_alpns(struct ossl_ctx *octx,
|
||||
}
|
||||
|
||||
#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
|
||||
static bool ossl_ech_need_httpsrr(struct Curl_easy *data)
|
||||
{
|
||||
if(!CURLECH_ENABLED(data))
|
||||
return FALSE;
|
||||
if((data->set.tls_ech & CURLECH_GREASE) ||
|
||||
(data->set.tls_ech & CURLECH_CLA_CFG))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static CURLcode ossl_init_ech(struct ossl_ctx *octx,
|
||||
struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
@ -3488,49 +3499,32 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx,
|
||||
infof(data, "ECH: ECHConfig from command line");
|
||||
}
|
||||
else {
|
||||
struct Curl_dns_entry *dns = NULL;
|
||||
const struct Curl_https_rrinfo *rinfo =
|
||||
Curl_conn_dns_get_https(data, cf->sockindex);
|
||||
|
||||
if(peer->hostname)
|
||||
result = Curl_dnscache_get(data, peer->hostname, peer->port,
|
||||
cf->conn->ip_version, &dns);
|
||||
if(!dns) {
|
||||
if(result) {
|
||||
failf(data, "ECH: could not resolve %s:%d",
|
||||
peer->hostname, peer->port);
|
||||
return result;
|
||||
}
|
||||
infof(data, "ECH: requested but no DNS info available");
|
||||
if(data->set.tls_ech & CURLECH_HARD)
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
else {
|
||||
struct Curl_https_rrinfo *rinfo = NULL;
|
||||
if(rinfo && rinfo->echconfiglist) {
|
||||
const unsigned char *ecl = rinfo->echconfiglist;
|
||||
size_t elen = rinfo->echconfiglist_len;
|
||||
|
||||
rinfo = dns->hinfo;
|
||||
if(rinfo && rinfo->echconfiglist) {
|
||||
unsigned char *ecl = rinfo->echconfiglist;
|
||||
size_t elen = rinfo->echconfiglist_len;
|
||||
|
||||
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
|
||||
if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
|
||||
infof(data, "ECH: SSL_set1_ech_config_list failed");
|
||||
if(data->set.tls_ech & CURLECH_HARD)
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
else {
|
||||
trying_ech_now = 1;
|
||||
infof(data, "ECH: imported ECHConfigList of length %zu", elen);
|
||||
}
|
||||
}
|
||||
else {
|
||||
infof(data, "ECH: requested but no ECHConfig available");
|
||||
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
|
||||
if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
|
||||
infof(data, "ECH: SSL_set1_ech_config_list failed");
|
||||
if(data->set.tls_ech & CURLECH_HARD)
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
Curl_dns_entry_unlink(data, &dns);
|
||||
else {
|
||||
trying_ech_now = 1;
|
||||
infof(data, "ECH: imported ECHConfigList of length %zu", elen);
|
||||
}
|
||||
}
|
||||
else {
|
||||
infof(data, "ECH: requested but no ECHConfig available");
|
||||
if(data->set.tls_ech & CURLECH_HARD)
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_BORINGSSL_LIKE
|
||||
(void)peer;
|
||||
if(trying_ech_now && outername) {
|
||||
infof(data, "ECH: setting public_name not supported with BoringSSL");
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
@ -4949,6 +4943,15 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf,
|
||||
connssl->io_need = CURL_SSL_IO_NEED_NONE;
|
||||
|
||||
if(ssl_connect_1 == connssl->connecting_state) {
|
||||
#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
|
||||
/* if we do ECH and need the HTTPS-RR information for it,
|
||||
* we delay the connect until it arrives or DNS resolve fails. */
|
||||
if(ossl_ech_need_httpsrr(data) &&
|
||||
!Curl_conn_dns_resolved_https(data, cf->sockindex)) {
|
||||
CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect");
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif
|
||||
CURL_TRC_CF(data, cf, "ossl_connect, step1");
|
||||
result = ossl_connect_step1(cf, data);
|
||||
if(result)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -56,6 +56,7 @@
|
||||
#include "urldata.h"
|
||||
#include "curl_trc.h"
|
||||
#include "httpsrr.h"
|
||||
#include "cf-dns.h"
|
||||
#include "vtls/vtls.h"
|
||||
#include "vtls/vtls_int.h"
|
||||
#include "vtls/vtls_scache.h"
|
||||
@ -1289,55 +1290,31 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx,
|
||||
}
|
||||
}
|
||||
else {
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
struct Curl_dns_entry *dns = NULL;
|
||||
CURLcode result;
|
||||
const struct Curl_https_rrinfo *rinfo =
|
||||
Curl_conn_dns_get_https(data, cf->sockindex);
|
||||
|
||||
result = Curl_dnscache_get(data, connssl->peer.hostname,
|
||||
connssl->peer.port, cf->conn->ip_version,
|
||||
&dns);
|
||||
if(!dns) {
|
||||
if(result) {
|
||||
failf(data, "ECH: could not resolve %s:%d",
|
||||
connssl->peer.hostname,
|
||||
connssl->peer.port);
|
||||
return result;
|
||||
}
|
||||
infof(data, "ECH: requested but no DNS info"
|
||||
" available");
|
||||
if(data->set.tls_ech & CURLECH_HARD)
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
else {
|
||||
struct Curl_https_rrinfo *rinfo = NULL;
|
||||
if(rinfo && rinfo->echconfiglist) {
|
||||
const unsigned char *ecl = rinfo->echconfiglist;
|
||||
size_t elen = rinfo->echconfiglist_len;
|
||||
|
||||
rinfo = dns->hinfo;
|
||||
if(rinfo && rinfo->echconfiglist) {
|
||||
unsigned char *ecl = rinfo->echconfiglist;
|
||||
size_t elen = rinfo->echconfiglist_len;
|
||||
|
||||
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
|
||||
if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) !=
|
||||
WOLFSSL_SUCCESS) {
|
||||
infof(data, "ECH: wolfSSL_SetEchConfigs failed");
|
||||
if(data->set.tls_ech & CURLECH_HARD) {
|
||||
Curl_dns_entry_unlink(data, &dns);
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
trying_ech_now = 1;
|
||||
infof(data, "ECH: imported ECHConfigList of length %zu", elen);
|
||||
}
|
||||
}
|
||||
else {
|
||||
infof(data, "ECH: requested but no ECHConfig available");
|
||||
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
|
||||
if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) !=
|
||||
WOLFSSL_SUCCESS) {
|
||||
infof(data, "ECH: wolfSSL_SetEchConfigs failed");
|
||||
if(data->set.tls_ech & CURLECH_HARD) {
|
||||
Curl_dns_entry_unlink(data, &dns);
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
}
|
||||
Curl_dns_entry_unlink(data, &dns);
|
||||
else {
|
||||
trying_ech_now = 1;
|
||||
infof(data, "ECH: imported ECHConfigList of length %zu", elen);
|
||||
}
|
||||
}
|
||||
else {
|
||||
infof(data, "ECH: requested but no ECHConfig available");
|
||||
if(data->set.tls_ech & CURLECH_HARD) {
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1510,6 +1487,18 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
|
||||
static bool wssl_ech_need_httpsrr(struct Curl_easy *data)
|
||||
{
|
||||
if(!CURLECH_ENABLED(data))
|
||||
return FALSE;
|
||||
if((data->set.tls_ech & CURLECH_GREASE) ||
|
||||
(data->set.tls_ech & CURLECH_CLA_CFG))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function loads all the client/CA certificates and CRLs. Setup the TLS
|
||||
* layer and do all necessary magic.
|
||||
@ -2179,6 +2168,15 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf,
|
||||
connssl->io_need = CURL_SSL_IO_NEED_NONE;
|
||||
|
||||
if(ssl_connect_1 == connssl->connecting_state) {
|
||||
#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
|
||||
/* if we do ECH and need the HTTPS-RR information for it,
|
||||
* we delay the connect until it arrives or DNS resolve fails. */
|
||||
if(wssl_ech_need_httpsrr(data) &&
|
||||
!Curl_conn_dns_resolved_https(data, cf->sockindex)) {
|
||||
CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect");
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */
|
||||
result = wssl_connect_step1(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user