mirror of
https://github.com/curl/curl.git
synced 2026-04-11 12:01:42 +08:00
Now that we support multiple async operations at the same transfer, fix the thread queue cleanup to match not only the mid but also the resolv_id. Found by Codex Security Closes #21202
826 lines
22 KiB
C
826 lines
22 KiB
C
/***************************************************************************
|
|
* _ _ ____ _
|
|
* Project ___| | | | _ \| |
|
|
* / __| | | | |_) | |
|
|
* | (__| |_| | _ <| |___
|
|
* \___|\___/|_| \_\_____|
|
|
*
|
|
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
|
*
|
|
* This software is licensed as described in the file COPYING, which
|
|
* you should have received as part of this distribution. The terms
|
|
* are also available at https://curl.se/docs/copyright.html.
|
|
*
|
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
|
* copies of the Software, and permit persons to whom the Software is
|
|
* furnished to do so, under the terms of the COPYING file.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
* SPDX-License-Identifier: curl
|
|
*
|
|
***************************************************************************/
|
|
#include "curl_setup.h"
|
|
|
|
/***********************************************************************
|
|
* Only for threaded name resolves builds
|
|
**********************************************************************/
|
|
#ifdef USE_RESOLV_THREADED
|
|
|
|
#ifdef HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#ifdef HAVE_NETDB_H
|
|
#include <netdb.h>
|
|
#endif
|
|
#ifdef HAVE_ARPA_INET_H
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
#ifdef __VMS
|
|
#include <in.h>
|
|
#include <inet.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_GETADDRINFO
|
|
#define RESOLVER_ENOMEM EAI_MEMORY /* = WSA_NOT_ENOUGH_MEMORY on Windows */
|
|
#else
|
|
#define RESOLVER_ENOMEM SOCKENOMEM
|
|
#endif
|
|
|
|
#include "urldata.h"
|
|
#include "cfilters.h"
|
|
#include "curl_addrinfo.h"
|
|
#include "curl_trc.h"
|
|
#include "hostip.h"
|
|
#include "httpsrr.h"
|
|
#include "url.h"
|
|
#include "multiif.h"
|
|
#include "curl_threads.h"
|
|
#include "progress.h"
|
|
#include "rand.h"
|
|
#include "select.h"
|
|
#include "thrdqueue.h"
|
|
#include "curlx/strparse.h"
|
|
#include "curlx/wait.h"
|
|
|
|
#ifdef USE_ARES
|
|
#include <ares.h>
|
|
#ifdef USE_HTTPSRR
|
|
#define USE_HTTPSRR_ARES /* the combo */
|
|
#endif
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Curl_async_global_init()
|
|
* Called from curl_global_init() to initialize global resolver environment.
|
|
* Does nothing here.
|
|
*/
|
|
int Curl_async_global_init(void)
|
|
{
|
|
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
|
|
if(ares_library_init(ARES_LIB_INIT_ALL)) {
|
|
return CURLE_FAILED_INIT;
|
|
}
|
|
#endif
|
|
return CURLE_OK;
|
|
}
|
|
|
|
/*
|
|
* Curl_async_global_cleanup()
|
|
* Called from curl_global_cleanup() to destroy global resolver environment.
|
|
* Does nothing here.
|
|
*/
|
|
void Curl_async_global_cleanup(void)
|
|
{
|
|
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
|
|
ares_library_cleanup();
|
|
#endif
|
|
}
|
|
|
|
#ifdef CURLVERBOSE
|
|
#define CURL_ASYN_ITEM_DESC_LEN 64
|
|
#define async_item_description(x) (x)->description
|
|
#else
|
|
#define async_item_description(x) NULL
|
|
#endif
|
|
|
|
struct async_thrdd_item {
|
|
struct Curl_addrinfo *res;
|
|
#ifdef CURLVERBOSE
|
|
char description[CURL_ASYN_ITEM_DESC_LEN];
|
|
#endif
|
|
int sock_error;
|
|
uint32_t mid;
|
|
uint32_t resolv_id;
|
|
uint16_t port;
|
|
uint8_t transport;
|
|
uint8_t dns_queries;
|
|
#ifdef DEBUGBUILD
|
|
uint32_t delay_ms;
|
|
uint32_t delay_fail_ms;
|
|
#endif
|
|
char hostname[1];
|
|
};
|
|
|
|
/* Give up reference to add_ctx */
|
|
static void async_thrdd_item_destroy(struct async_thrdd_item *item)
|
|
{
|
|
if(item) {
|
|
if(item->res)
|
|
Curl_freeaddrinfo(item->res);
|
|
curlx_free(item);
|
|
}
|
|
}
|
|
|
|
/* Initialize context for threaded resolver */
|
|
static struct async_thrdd_item *
|
|
async_thrdd_item_create(struct Curl_easy *data,
|
|
uint32_t resolv_id, uint8_t dns_queries,
|
|
const char *hostname, uint16_t port,
|
|
uint8_t transport)
|
|
{
|
|
size_t hostlen = strlen(hostname);
|
|
struct async_thrdd_item *item;
|
|
|
|
item = curlx_calloc(1, sizeof(*item) + hostlen);
|
|
if(!item)
|
|
return NULL;
|
|
|
|
if(hostlen) /* NUL byte of name already in struct size */
|
|
memcpy(item->hostname, hostname, hostlen);
|
|
item->mid = data->mid;
|
|
item->resolv_id = resolv_id;
|
|
item->dns_queries = dns_queries;
|
|
item->port = port;
|
|
item->transport = transport;
|
|
|
|
#ifdef CURLVERBOSE
|
|
curl_msnprintf(item->description, sizeof(item->description),
|
|
"[%" FMT_OFF_T "/%u] %s %s:%u",
|
|
data->id, item->resolv_id,
|
|
Curl_resolv_query_str(dns_queries),
|
|
item->hostname, item->port);
|
|
#endif
|
|
|
|
#ifdef DEBUGBUILD
|
|
{
|
|
const char *p = getenv("CURL_DBG_RESOLV_DELAY");
|
|
if(p) {
|
|
curl_off_t l;
|
|
if(!curlx_str_number(&p, &l, UINT32_MAX)) {
|
|
item->delay_ms = (uint32_t)l;
|
|
}
|
|
}
|
|
p = getenv("CURL_DBG_RESOLV_FAIL_DELAY");
|
|
if(p) {
|
|
curl_off_t l;
|
|
if(!curlx_str_number(&p, &l, UINT32_MAX)) {
|
|
unsigned char c = 0;
|
|
Curl_rand_bytes(data, FALSE, &c, 1);
|
|
item->delay_fail_ms = (uint32_t)l + c;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return item;
|
|
}
|
|
|
|
#ifdef USE_HTTPSRR_ARES
|
|
|
|
static void async_thrdd_rr_done(void *user_data, ares_status_t status,
|
|
size_t timeouts,
|
|
const ares_dns_record_t *dnsrec)
|
|
{
|
|
struct Curl_resolv_async *async = user_data;
|
|
struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
|
|
|
|
(void)timeouts;
|
|
if(!thrdd)
|
|
return;
|
|
|
|
async->dns_responses |= CURL_DNSQ_HTTPS;
|
|
async->queries_ongoing--;
|
|
async->done = !async->queries_ongoing;
|
|
if((ARES_SUCCESS == status) && dnsrec)
|
|
async->result = Curl_httpsrr_from_ares(dnsrec, &thrdd->rr.hinfo);
|
|
}
|
|
|
|
static CURLcode async_rr_start(struct Curl_easy *data,
|
|
struct Curl_resolv_async *async)
|
|
{
|
|
struct async_thrdd_ctx *thrdd = &async->thrdd;
|
|
int status;
|
|
char *rrname = NULL;
|
|
|
|
DEBUGASSERT(!thrdd->rr.channel);
|
|
if(async->port != 443) {
|
|
rrname = curl_maprintf("_%d_.https.%s", async->port, async->hostname);
|
|
if(!rrname)
|
|
return CURLE_OUT_OF_MEMORY;
|
|
}
|
|
status = ares_init_options(&thrdd->rr.channel, NULL, 0);
|
|
if(status != ARES_SUCCESS) {
|
|
thrdd->rr.channel = NULL;
|
|
curlx_free(rrname);
|
|
return CURLE_FAILED_INIT;
|
|
}
|
|
#ifdef DEBUGBUILD
|
|
if(getenv("CURL_DNS_SERVER")) {
|
|
const char *servers = getenv("CURL_DNS_SERVER");
|
|
status = ares_set_servers_ports_csv(thrdd->rr.channel, servers);
|
|
if(status) {
|
|
curlx_free(rrname);
|
|
return CURLE_FAILED_INIT;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
|
|
thrdd->rr.hinfo.rrname = rrname;
|
|
ares_query_dnsrec(thrdd->rr.channel,
|
|
rrname ? rrname : async->hostname, ARES_CLASS_IN,
|
|
ARES_REC_TYPE_HTTPS,
|
|
async_thrdd_rr_done, async, NULL);
|
|
async->queries_ongoing++;
|
|
CURL_TRC_DNS(data, "[HTTPS-RR] initiated request for %s",
|
|
rrname ? rrname : async->hostname);
|
|
return CURLE_OK;
|
|
}
|
|
#endif
|
|
|
|
void Curl_async_thrdd_shutdown(struct Curl_easy *data,
|
|
struct Curl_resolv_async *async)
|
|
{
|
|
Curl_async_thrdd_destroy(data, async);
|
|
}
|
|
|
|
struct async_thrdd_match_ctx {
|
|
uint32_t mid;
|
|
uint32_t resolv_id;
|
|
};
|
|
|
|
static bool async_thrdd_match_item(void *qitem, void *match_data)
|
|
{
|
|
const struct async_thrdd_match_ctx *ctx = match_data;
|
|
struct async_thrdd_item *item = qitem;
|
|
return (item->mid == ctx->mid) && (item->resolv_id == ctx->resolv_id);
|
|
}
|
|
|
|
void Curl_async_thrdd_destroy(struct Curl_easy *data,
|
|
struct Curl_resolv_async *async)
|
|
{
|
|
(void)data;
|
|
if(async->queries_ongoing && !async->done &&
|
|
data->multi && data->multi->resolv_thrdq) {
|
|
/* Remove any resolve items still queued */
|
|
struct async_thrdd_match_ctx mctx;
|
|
mctx.mid = data->mid;
|
|
mctx.resolv_id = async->id;
|
|
Curl_thrdq_clear(data->multi->resolv_thrdq,
|
|
async_thrdd_match_item, &mctx);
|
|
}
|
|
#ifdef USE_HTTPSRR_ARES
|
|
if(async->thrdd.rr.channel) {
|
|
ares_destroy(async->thrdd.rr.channel);
|
|
async->thrdd.rr.channel = NULL;
|
|
}
|
|
Curl_httpsrr_cleanup(&async->thrdd.rr.hinfo);
|
|
#endif
|
|
async_thrdd_item_destroy(async->thrdd.res_A);
|
|
async->thrdd.res_A = NULL;
|
|
async_thrdd_item_destroy(async->thrdd.res_AAAA);
|
|
async->thrdd.res_AAAA = NULL;
|
|
}
|
|
|
|
/*
|
|
* Waits for a resolve to finish. This function should be avoided since using
|
|
* this risk getting the multi interface to "hang".
|
|
*/
|
|
CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id,
|
|
struct Curl_dns_entry **pdns)
|
|
{
|
|
struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
|
|
struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
|
|
timediff_t milli, ms;
|
|
|
|
if(!thrdd)
|
|
return CURLE_FAILED_INIT;
|
|
|
|
while(async->queries_ongoing && !async->done) {
|
|
Curl_async_thrdd_multi_process(data->multi);
|
|
if(async->done)
|
|
break;
|
|
|
|
ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
|
|
if(ms < 3)
|
|
milli = 0;
|
|
else if(ms <= 50)
|
|
milli = ms / 3;
|
|
else if(ms <= 250)
|
|
milli = 50;
|
|
else
|
|
milli = 200;
|
|
CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli);
|
|
curlx_wait_ms(milli);
|
|
}
|
|
return Curl_async_take_result(data, async, pdns);
|
|
}
|
|
|
|
#ifdef HAVE_GETADDRINFO
|
|
|
|
/* Process the item, using Curl_getaddrinfo_ex() */
|
|
static void async_thrdd_item_process(void *arg)
|
|
{
|
|
struct async_thrdd_item *item = arg;
|
|
struct addrinfo hints;
|
|
char service[12];
|
|
int pf = PF_INET;
|
|
int rc;
|
|
|
|
#ifdef DEBUGBUILD
|
|
if(item->delay_ms) {
|
|
curlx_wait_ms(item->delay_ms);
|
|
}
|
|
if(item->delay_fail_ms) {
|
|
curlx_wait_ms(item->delay_fail_ms);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
#ifdef CURLRES_IPV6
|
|
if(item->dns_queries & CURL_DNSQ_AAAA) {
|
|
pf = (item->dns_queries & CURL_DNSQ_A) ? PF_UNSPEC : PF_INET6;
|
|
}
|
|
#endif
|
|
hints.ai_family = pf;
|
|
hints.ai_socktype = Curl_socktype_for_transport(item->transport);
|
|
hints.ai_protocol = Curl_protocol_for_transport(item->transport);
|
|
#ifdef __APPLE__
|
|
/* If we leave `ai_flags == 0` then macOS is looking for IPV4MAPPED
|
|
* when doing AAAA queries. We do not want this "help". */
|
|
hints.ai_flags = AI_ADDRCONFIG;
|
|
#endif
|
|
|
|
curl_msnprintf(service, sizeof(service), "%u", item->port);
|
|
#ifdef AI_NUMERICSERV
|
|
hints.ai_flags |= AI_NUMERICSERV;
|
|
#endif
|
|
|
|
rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res);
|
|
if(rc) {
|
|
item->sock_error = SOCKERRNO ? SOCKERRNO : rc;
|
|
if(item->sock_error == 0)
|
|
item->sock_error = RESOLVER_ENOMEM;
|
|
}
|
|
else {
|
|
Curl_addrinfo_set_port(item->res, item->port);
|
|
}
|
|
}
|
|
|
|
#else /* HAVE_GETADDRINFO */
|
|
|
|
/* Process the item, using Curl_ipv4_resolve_r() */
|
|
static void async_thrdd_item_process(void *item)
|
|
{
|
|
struct async_thrdd_item *item = arg;
|
|
|
|
#ifdef DEBUGBUILD
|
|
if(item->delay_ms) {
|
|
curlx_wait_ms(item->delay_ms);
|
|
}
|
|
if(item->delay_fail_ms) {
|
|
curlx_wait_ms(item->delay_fail_ms);
|
|
return;
|
|
}
|
|
#endif
|
|
item->res = Curl_ipv4_resolve_r(item->hostname, item->port);
|
|
if(!item->res) {
|
|
item->sock_error = SOCKERRNO;
|
|
if(item->sock_error == 0)
|
|
item->sock_error = RESOLVER_ENOMEM;
|
|
}
|
|
}
|
|
|
|
#endif /* HAVE_GETADDRINFO */
|
|
|
|
#ifdef ENABLE_WAKEUP
|
|
static void async_thrdd_event(const struct curl_thrdq *tqueue,
|
|
Curl_thrdq_event ev,
|
|
void *user_data)
|
|
{
|
|
struct Curl_multi *multi = user_data;
|
|
(void)tqueue;
|
|
switch(ev) {
|
|
case CURL_THRDQ_EV_ITEM_DONE:
|
|
(void)curl_multi_wakeup(multi);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
#define async_thrdd_event NULL
|
|
#endif
|
|
|
|
static void async_thrdd_item_free(void *item)
|
|
{
|
|
async_thrdd_item_destroy(item);
|
|
}
|
|
|
|
/* Create a thread queue for processing resolv items */
|
|
CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi,
|
|
uint32_t min_threads,
|
|
uint32_t max_threads,
|
|
uint32_t idle_time_ms)
|
|
{
|
|
CURLcode result;
|
|
DEBUGASSERT(!multi->resolv_thrdq);
|
|
result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0,
|
|
min_threads, max_threads, idle_time_ms,
|
|
async_thrdd_item_free,
|
|
async_thrdd_item_process,
|
|
async_thrdd_event,
|
|
multi);
|
|
#ifdef DEBUGBUILD
|
|
if(!result) {
|
|
const char *p = getenv("CURL_DBG_RESOLV_MAX_THREADS");
|
|
if(p) {
|
|
curl_off_t l;
|
|
if(!curlx_str_number(&p, &l, UINT32_MAX)) {
|
|
result = Curl_async_thrdd_multi_set_props(
|
|
multi, min_threads, (uint32_t)l, idle_time_ms);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
/* Tear down the thread queue, joining active threads or detaching them */
|
|
void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join)
|
|
{
|
|
if(multi->resolv_thrdq) {
|
|
Curl_thrdq_destroy(multi->resolv_thrdq, join);
|
|
multi->resolv_thrdq = NULL;
|
|
}
|
|
}
|
|
|
|
#ifdef CURLVERBOSE
|
|
static void async_thrdd_report_item(struct Curl_easy *data,
|
|
struct async_thrdd_item *item)
|
|
{
|
|
char buf[MAX_IPADR_LEN];
|
|
struct dynbuf tmp;
|
|
const char *sep = "";
|
|
const struct Curl_addrinfo *ai = item->res;
|
|
int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET;
|
|
CURLcode result;
|
|
|
|
if(!Curl_trc_is_verbose(data))
|
|
return;
|
|
|
|
curlx_dyn_init(&tmp, 1024);
|
|
for(; ai; ai = ai->ai_next) {
|
|
if(ai->ai_family == ai_family) {
|
|
Curl_printable_address(ai, buf, sizeof(buf));
|
|
result = curlx_dyn_addf(&tmp, "%s%s", sep, buf);
|
|
if(result) {
|
|
CURL_TRC_DNS(data, "too many IP, cannot show");
|
|
goto out;
|
|
}
|
|
sep = ", ";
|
|
}
|
|
}
|
|
|
|
infof(data, "Host %s:%u resolved IPv%c: %s",
|
|
item->hostname, item->port,
|
|
(item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4',
|
|
(curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)"));
|
|
out:
|
|
curlx_dyn_free(&tmp);
|
|
}
|
|
#endif /* CURLVERBOSE */
|
|
|
|
/* Process the receiving end of the thread queue, dispatching
|
|
* processed items to their transfer when it can still be found
|
|
* and has an `async` state present. Otherwise, destroy the item. */
|
|
void Curl_async_thrdd_multi_process(struct Curl_multi *multi)
|
|
{
|
|
struct Curl_easy *data;
|
|
void *qitem;
|
|
|
|
while(!Curl_thrdq_recv(multi->resolv_thrdq, &qitem)) {
|
|
/* dispatch resolve result */
|
|
struct async_thrdd_item *item = qitem;
|
|
struct Curl_resolv_async *async = NULL;
|
|
|
|
data = Curl_multi_get_easy(multi, item->mid);
|
|
if(data)
|
|
async = Curl_async_get(data, item->resolv_id);
|
|
if(async) {
|
|
struct async_thrdd_item **pdest = &async->thrdd.res_A;
|
|
|
|
async->dns_responses |= item->dns_queries;
|
|
--async->queries_ongoing;
|
|
async->done = !async->queries_ongoing;
|
|
|
|
#ifdef CURLRES_IPV6
|
|
if(item->dns_queries & CURL_DNSQ_AAAA)
|
|
pdest = &async->thrdd.res_AAAA;
|
|
#endif
|
|
if(!*pdest) {
|
|
VERBOSE(async_thrdd_report_item(data, item));
|
|
*pdest = item;
|
|
item = NULL;
|
|
}
|
|
else
|
|
DEBUGASSERT(0); /* should not receive duplicates here */
|
|
Curl_multi_mark_dirty(data);
|
|
}
|
|
async_thrdd_item_free(item);
|
|
}
|
|
#ifdef CURLVERBOSE
|
|
Curl_thrdq_trace(multi->resolv_thrdq, multi->admin);
|
|
#endif
|
|
}
|
|
|
|
CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi,
|
|
uint32_t min_threads,
|
|
uint32_t max_threads,
|
|
uint32_t idle_time_ms)
|
|
{
|
|
return Curl_thrdq_set_props(multi->resolv_thrdq, 0,
|
|
min_threads, max_threads, idle_time_ms);
|
|
}
|
|
|
|
static CURLcode async_thrdd_query(struct Curl_easy *data,
|
|
struct Curl_resolv_async *async,
|
|
uint8_t dns_queries)
|
|
{
|
|
struct async_thrdd_item *item;
|
|
CURLcode result;
|
|
|
|
item = async_thrdd_item_create(data, async->id, dns_queries,
|
|
async->hostname, async->port,
|
|
async->transport);
|
|
if(!item) {
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
goto out;
|
|
}
|
|
CURL_TRC_DNS(data, "queueing query %s", item->description);
|
|
result = Curl_thrdq_send(data->multi->resolv_thrdq, item,
|
|
async_item_description(item), async->timeout_ms);
|
|
if(result)
|
|
goto out;
|
|
item = NULL;
|
|
async->queries_ongoing++;
|
|
|
|
out:
|
|
if(item)
|
|
async_thrdd_item_free(item);
|
|
return result;
|
|
}
|
|
|
|
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
|
|
struct Curl_resolv_async *async)
|
|
{
|
|
CURLcode result = CURLE_FAILED_INIT;
|
|
void *resolver = NULL;
|
|
|
|
if(async->queries_ongoing || async->done)
|
|
return CURLE_FAILED_INIT;
|
|
|
|
#ifdef USE_HTTPSRR_ARES
|
|
DEBUGASSERT(!async->thrdd.rr.channel);
|
|
if((async->dns_queries & CURL_DNSQ_HTTPS) && !async->is_ipaddr) {
|
|
result = async_rr_start(data, async);
|
|
if(result)
|
|
goto out;
|
|
resolver = async->thrdd.rr.channel;
|
|
}
|
|
#endif
|
|
|
|
result = Curl_resolv_announce_start(data, resolver);
|
|
if(result)
|
|
return result;
|
|
|
|
#ifdef CURL_IPRESOLVE_V6
|
|
/* Do not start an AAAA query for an ipv4 address when
|
|
* we will start an A query for it. */
|
|
if((async->dns_queries & CURL_DNSQ_AAAA) &&
|
|
!(async->is_ipv4addr && (async->dns_queries & CURL_DNSQ_A))) {
|
|
result = async_thrdd_query(data, async, CURL_DNSQ_AAAA);
|
|
if(result)
|
|
goto out;
|
|
}
|
|
#endif
|
|
if(async->dns_queries & CURL_DNSQ_A) {
|
|
result = async_thrdd_query(data, async, CURL_DNSQ_A);
|
|
if(result)
|
|
goto out;
|
|
}
|
|
if(result)
|
|
goto out;
|
|
|
|
#ifdef CURLVERBOSE
|
|
Curl_thrdq_trace(data->multi->resolv_thrdq, data);
|
|
#endif
|
|
|
|
out:
|
|
if(result)
|
|
CURL_TRC_DNS(data, "error queueing query %s:%d -> %d",
|
|
async->hostname, async->port, result);
|
|
return result;
|
|
}
|
|
|
|
CURLcode Curl_async_pollset(struct Curl_easy *data,
|
|
struct Curl_resolv_async *async,
|
|
struct easy_pollset *ps)
|
|
{
|
|
timediff_t timeout_ms;
|
|
|
|
timeout_ms = Curl_async_timeleft_ms(data, async);
|
|
#ifdef USE_HTTPSRR_ARES
|
|
if(async->thrdd.rr.channel) {
|
|
CURLcode result = Curl_ares_pollset(data, async->thrdd.rr.channel, ps);
|
|
if(result)
|
|
return result;
|
|
timeout_ms = Curl_ares_timeout_ms(data, async, async->thrdd.rr.channel);
|
|
}
|
|
#else
|
|
(void)ps;
|
|
#endif
|
|
|
|
if(!async->done) {
|
|
#ifndef ENABLE_WAKEUP
|
|
timediff_t stutter_ms, elapsed_ms;
|
|
elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
|
|
if(elapsed_ms < 3)
|
|
stutter_ms = 1;
|
|
else if(elapsed_ms <= 50)
|
|
stutter_ms = elapsed_ms / 3;
|
|
else if(elapsed_ms <= 250)
|
|
stutter_ms = 50;
|
|
else
|
|
stutter_ms = 200;
|
|
timeout_ms = CURLMIN(stutter_ms, timeout_ms);
|
|
#endif
|
|
Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME);
|
|
}
|
|
return CURLE_OK;
|
|
}
|
|
|
|
/*
|
|
* Curl_async_take_result() is called repeatedly to check if a previous
|
|
* name resolve request has completed. It should also make sure to time-out if
|
|
* the operation seems to take too long.
|
|
*/
|
|
CURLcode Curl_async_take_result(struct Curl_easy *data,
|
|
struct Curl_resolv_async *async,
|
|
struct Curl_dns_entry **pdns)
|
|
{
|
|
struct async_thrdd_ctx *thrdd = &async->thrdd;
|
|
struct Curl_dns_entry *dns = NULL;
|
|
CURLcode result = CURLE_OK;
|
|
|
|
DEBUGASSERT(pdns);
|
|
*pdns = NULL;
|
|
if(!async->queries_ongoing && !async->done) {
|
|
DEBUGASSERT(0);
|
|
return CURLE_FAILED_INIT;
|
|
}
|
|
|
|
#ifdef USE_HTTPSRR_ARES
|
|
/* best effort, ignore errors */
|
|
if(thrdd->rr.channel)
|
|
(void)Curl_ares_perform(thrdd->rr.channel, 0);
|
|
#endif
|
|
|
|
if(!async->done)
|
|
return CURLE_AGAIN;
|
|
|
|
Curl_expire_done(data, EXPIRE_ASYNC_NAME);
|
|
if(async->result)
|
|
goto out;
|
|
|
|
if((thrdd->res_A && thrdd->res_A->res) ||
|
|
(thrdd->res_AAAA && thrdd->res_AAAA->res)) {
|
|
dns = Curl_dnscache_mk_entry2(
|
|
data, async->dns_queries,
|
|
thrdd->res_A ? &thrdd->res_A->res : NULL,
|
|
thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL,
|
|
async->hostname, async->port);
|
|
if(!dns) {
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
#ifdef USE_HTTPSRR_ARES
|
|
if(thrdd->rr.channel) {
|
|
struct Curl_https_rrinfo *lhrr = NULL;
|
|
if(thrdd->rr.hinfo.complete) {
|
|
lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
|
|
if(!lhrr) {
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
goto out;
|
|
}
|
|
}
|
|
Curl_httpsrr_trace(data, lhrr);
|
|
Curl_dns_entry_set_https_rr(dns, lhrr);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if(dns) {
|
|
CURL_TRC_DNS(data, "resolving complete");
|
|
*pdns = dns;
|
|
dns = NULL;
|
|
}
|
|
#ifdef CURLVERBOSE
|
|
Curl_thrdq_trace(data->multi->resolv_thrdq, data);
|
|
#endif
|
|
|
|
out:
|
|
Curl_dns_entry_unlink(data, &dns);
|
|
Curl_async_thrdd_shutdown(data, async);
|
|
if(!result && !*pdns)
|
|
result = Curl_resolver_error(data, NULL);
|
|
if(result &&
|
|
(result != CURLE_COULDNT_RESOLVE_HOST) &&
|
|
(result != CURLE_COULDNT_RESOLVE_PROXY)) {
|
|
CURL_TRC_DNS(data, "Error %d resolving %s:%d",
|
|
result, async->hostname, async->port);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static const struct Curl_addrinfo *
|
|
async_thrdd_get_ai(const struct Curl_addrinfo *ai,
|
|
int ai_family, unsigned int index)
|
|
{
|
|
unsigned int i = 0;
|
|
for(i = 0; ai; ai = ai->ai_next) {
|
|
if(ai->ai_family == ai_family) {
|
|
if(i == index)
|
|
return ai;
|
|
++i;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const struct Curl_addrinfo *
|
|
Curl_async_get_ai(struct Curl_easy *data,
|
|
struct Curl_resolv_async *async,
|
|
int ai_family, unsigned int index)
|
|
{
|
|
struct async_thrdd_ctx *thrdd = &async->thrdd;
|
|
|
|
(void)data;
|
|
switch(ai_family) {
|
|
case AF_INET:
|
|
if(thrdd->res_A)
|
|
return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index);
|
|
break;
|
|
case AF_INET6:
|
|
if(thrdd->res_AAAA)
|
|
return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef USE_HTTPSRR
|
|
const struct Curl_https_rrinfo *
|
|
Curl_async_get_https(struct Curl_easy *data,
|
|
struct Curl_resolv_async *async)
|
|
{
|
|
#ifdef USE_HTTPSRR_ARES
|
|
if(Curl_async_knows_https(data, async))
|
|
return &async->thrdd.rr.hinfo;
|
|
#else
|
|
(void)data;
|
|
(void)async;
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
bool Curl_async_knows_https(struct Curl_easy *data,
|
|
struct Curl_resolv_async *async)
|
|
{
|
|
(void)data;
|
|
if(async->dns_queries & CURL_DNSQ_HTTPS)
|
|
return ((async->dns_responses & CURL_DNSQ_HTTPS) || async->done);
|
|
return TRUE; /* we know it will never come */
|
|
}
|
|
|
|
#endif /* USE_HTTPSRR */
|
|
|
|
#endif /* USE_RESOLV_THREADED */
|