dnscache: own source file, improvements

- Rename `Curl_resolv_unlink()` to `Curl_dns_entry_unlink()`.
- Change `Curl_dnscache_get()` to return CURLcode result. Returns
  now `CURLE_COULDNT_RESOLVE_HOST` for "negative" cache entries.
- Add `Curl_dnscache_add_negative()` to put a "negative" entry
  into the cache.

Closes #20864
This commit is contained in:
Stefan Eissing 2026-03-06 09:22:26 +01:00 committed by Daniel Stenberg
parent b0f6e9a3d7
commit 96d5b5c688
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
35 changed files with 1648 additions and 1413 deletions

View File

@ -235,7 +235,7 @@ function in a new field in the `dohentry` structure.
The qname for the DoH query is modified if the port number is not 443, as
defined in the SVCB specification.
When the DoH process has worked, `Curl_doh_is_resolved()` now also returns
When the DoH process has worked, `Curl_doh_take_result()` now also returns
the relevant HTTPS RR value data in the `Curl_dns_entry` structure.
That is later accessed when the TLS session is being established, if ECH is
enabled (from `lib/vtls/openssl.c` as described above).

View File

@ -181,6 +181,7 @@ LIB_CFILES = \
cw-out.c \
cw-pause.c \
dict.c \
dnscache.c \
doh.c \
dynhds.c \
easy.c \
@ -316,6 +317,7 @@ LIB_HFILES = \
cw-out.h \
cw-pause.h \
dict.h \
dnscache.h \
doh.h \
dynhds.h \
easy_lock.h \

View File

@ -118,7 +118,7 @@ void Curl_amiga_cleanup(void)
* allocates memory also.
*/
struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port)
struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port)
{
struct Curl_addrinfo *ai = NULL;
struct hostent *h;

View File

@ -142,9 +142,10 @@ static void sock_state_cb(void *data, ares_socket_t socket_fd,
}
}
static CURLcode async_ares_init(struct Curl_easy *data)
static CURLcode async_ares_init(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct async_ares_ctx *ares = &data->state.async.ares;
struct async_ares_ctx *ares = &async->ares;
int status;
struct ares_options options;
int optmask = ARES_OPT_SOCK_STATE_CB;
@ -202,20 +203,23 @@ out:
return rc;
}
static CURLcode async_ares_init_lazy(struct Curl_easy *data)
static CURLcode async_ares_init_lazy(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct async_ares_ctx *ares = &data->state.async.ares;
struct async_ares_ctx *ares = &async->ares;
if(!ares->channel)
return async_ares_init(data);
return async_ares_init(data, async);
return CURLE_OK;
}
CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
CURLcode Curl_async_get_impl(struct Curl_easy *data,
struct Curl_resolv_async *async,
void **impl)
{
struct async_ares_ctx *ares = &data->state.async.ares;
struct async_ares_ctx *ares = &async->ares;
CURLcode result = CURLE_OK;
if(!ares->channel) {
result = async_ares_init(data);
result = async_ares_init(data, async);
}
*impl = ares->channel;
return result;
@ -224,9 +228,9 @@ CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
/*
* async_ares_cleanup() cleans up async resolver data.
*/
static void async_ares_cleanup(struct Curl_easy *data)
static void async_ares_cleanup(struct Curl_resolv_async *async)
{
struct async_ares_ctx *ares = &data->state.async.ares;
struct async_ares_ctx *ares = &async->ares;
if(ares->temp_ai) {
Curl_freeaddrinfo(ares->temp_ai);
ares->temp_ai = NULL;
@ -236,24 +240,27 @@ static void async_ares_cleanup(struct Curl_easy *data)
#endif
}
void Curl_async_ares_shutdown(struct Curl_easy *data)
void Curl_async_ares_shutdown(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
/* c-ares has a method to "cancel" operations on a channel, but
* as reported in #18216, this does not totally reset the channel
* and ares may get stuck.
* We need to destroy the channel and on demand create a new
* one to avoid that. */
Curl_async_ares_destroy(data);
Curl_async_ares_destroy(data, async);
}
void Curl_async_ares_destroy(struct Curl_easy *data)
void Curl_async_ares_destroy(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct async_ares_ctx *ares = &data->state.async.ares;
struct async_ares_ctx *ares = &async->ares;
(void)data;
if(ares->channel) {
ares_destroy(ares->channel);
ares->channel = NULL;
}
async_ares_cleanup(data);
async_ares_cleanup(async);
}
/*
@ -263,32 +270,31 @@ void Curl_async_ares_destroy(struct Curl_easy *data)
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
{
struct async_ares_ctx *ares = &data->state.async.ares;
if(ares->channel)
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;
}
/*
* Curl_async_is_resolved() is called repeatedly to check if a previous
* 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.
*
* Returns normal CURLcode errors.
*/
CURLcode Curl_async_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dns)
CURLcode Curl_async_take_result(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct Curl_dns_entry **pdns)
{
struct async_ares_ctx *ares = &data->state.async.ares;
struct async_ares_ctx *ares = &async->ares;
CURLcode result = CURLE_OK;
DEBUGASSERT(dns);
*dns = NULL;
if(data->state.async.done) {
*dns = data->state.async.dns;
return ares->result;
}
DEBUGASSERT(pdns);
*pdns = NULL;
if(!ares)
return CURLE_FAILED_INIT;
if(Curl_ares_perform(ares->channel, 0) < 0) {
result = CURLE_UNRECOVERABLE_POLL;
@ -322,15 +328,13 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
if(!ares->num_pending) {
/* all c-ares operations done, what is the result to report? */
Curl_resolv_unlink(data, &data->state.async.dns);
data->state.async.done = TRUE;
result = ares->result;
if(ares->ares_status == ARES_SUCCESS && !result) {
data->state.async.dns =
struct Curl_dns_entry *dns =
Curl_dnscache_mk_entry(data, &ares->temp_ai,
data->state.async.hostname, 0,
data->state.async.port, FALSE);
if(!data->state.async.dns) {
async->hostname, async->port,
async->ip_version);
if(!dns) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
@ -340,28 +344,29 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
if(!lhrr)
result = CURLE_OUT_OF_MEMORY;
else
data->state.async.dns->hinfo = lhrr;
dns->hinfo = lhrr;
}
#endif
if(!result)
result = Curl_dnscache_add(data, data->state.async.dns);
if(!result) {
result = Curl_dnscache_add(data, dns);
*pdns = dns;
}
}
/* if we have not found anything, report the proper
* CURLE_COULDNT_RESOLVE_* code */
if(!result && !data->state.async.dns) {
if(!result && !*pdns) {
const char *msg = NULL;
if(ares->ares_status != ARES_SUCCESS)
msg = ares_strerror(ares->ares_status);
result = Curl_resolver_error(data, msg);
}
if(result)
Curl_resolv_unlink(data, &data->state.async.dns);
*dns = data->state.async.dns;
CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
result, *dns ? "" : "not ");
async_ares_cleanup(data);
result, *pdns ? "" : "not ");
async_ares_cleanup(async);
}
else
return CURLE_AGAIN;
out:
ares->result = result;
@ -406,14 +411,15 @@ static timediff_t async_ares_poll_timeout(struct async_ares_ctx *ares,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
CURLcode Curl_async_await(struct Curl_easy *data,
struct Curl_dns_entry **dns)
struct Curl_resolv_async *async,
struct Curl_dns_entry **pdns)
{
struct async_ares_ctx *ares = &data->state.async.ares;
struct async_ares_ctx *ares = &async->ares;
struct curltime start = *Curl_pgrs_now(data);
CURLcode result = CURLE_OK;
DEBUGASSERT(dns);
*dns = NULL; /* clear on entry */
DEBUGASSERT(pdns);
*pdns = NULL; /* clear on entry */
/* Wait for the name resolve query to complete or time out. */
while(!result) {
@ -439,8 +445,10 @@ CURLcode Curl_async_await(struct Curl_easy *data,
break;
}
result = Curl_async_is_resolved(data, dns);
if(result || data->state.async.done)
result = Curl_async_take_result(data, async, pdns);
if(result == CURLE_AGAIN)
result = CURLE_OK;
else if(result || *pdns)
break;
if(Curl_pgrsUpdate(data)) {
@ -449,11 +457,6 @@ CURLcode Curl_async_await(struct Curl_easy *data,
}
}
/* Operation complete, if the lookup was successful we now have the entry
in the cache. */
data->state.async.done = TRUE;
*dns = data->state.async.dns;
if(result)
ares_cancel(ares->channel);
return result;
@ -502,17 +505,19 @@ static void async_ares_hostbyname_cb(void *user_data,
struct hostent *hostent)
{
struct Curl_easy *data = (struct Curl_easy *)user_data;
struct async_ares_ctx *ares = &data->state.async.ares;
struct Curl_resolv_async *async = data->state.async;
struct async_ares_ctx *ares = async ? &async->ares : NULL;
(void)timeouts; /* ignored */
if(!ares)
return;
if(ARES_EDESTRUCTION == status)
return;
if(ARES_SUCCESS == status) {
ares->ares_status = status; /* one success overrules any error */
async_addr_concat(&ares->temp_ai,
Curl_he2ai(hostent, data->state.async.port));
async_addr_concat(&ares->temp_ai, Curl_he2ai(hostent, async->port));
}
else if(ares->ares_status != ARES_SUCCESS) {
/* no success so far, remember last error */
@ -664,8 +669,12 @@ 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 async_ares_ctx *ares = &data->state.async.ares;
struct Curl_resolv_async *async = data->state.async;
struct async_ares_ctx *ares = async ? &async->ares : NULL;
(void)timeouts;
if(!ares)
return;
if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */
ares->ares_status = status;
if(status == ARES_SUCCESS) {
@ -687,8 +696,11 @@ static void async_ares_rr_done(void *user_data, ares_status_t status,
const ares_dns_record_t *dnsrec)
{
struct Curl_easy *data = user_data;
struct async_ares_ctx *ares = &data->state.async.ares;
struct Curl_resolv_async *async = data->state.async;
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, "
@ -708,27 +720,20 @@ static void async_ares_rr_done(void *user_data, ares_status_t status,
*
* Starts a name resolve for the given hostname and port number.
*/
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
int port, int ip_version)
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct async_ares_ctx *ares = &data->state.async.ares;
struct async_ares_ctx *ares = &async->ares;
#ifdef USE_HTTPSRR
char *rrname = NULL;
#endif
if(async_ares_init_lazy(data))
if(async_ares_init_lazy(data, async))
return CURLE_FAILED_INIT;
data->state.async.done = FALSE; /* not done */
data->state.async.dns = NULL; /* clear */
data->state.async.port = port;
data->state.async.ip_version = ip_version;
data->state.async.hostname = curlx_strdup(hostname);
if(!data->state.async.hostname)
return CURLE_OUT_OF_MEMORY;
#ifdef USE_HTTPSRR
if(port != 443) {
rrname = curl_maprintf("_%d._https.%s", port, hostname);
if(async->port != 443) {
rrname = curl_maprintf("_%d._https.%s", async->port, async->hostname);
if(!rrname)
return CURLE_OUT_OF_MEMORY;
}
@ -753,9 +758,9 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
int pf = PF_INET;
memset(&hints, 0, sizeof(hints));
#ifdef CURLRES_IPV6
if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
if((async->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* The stack seems to be IPv6-enabled */
if(ip_version == CURL_IPRESOLVE_V6)
if(async->ip_version == CURL_IPRESOLVE_V6)
pf = PF_INET6;
else
pf = PF_UNSPEC;
@ -772,23 +777,23 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
* accordingly to save a call to getservbyname in inside C-Ares
*/
hints.ai_flags = ARES_AI_NUMERICSERV;
curl_msnprintf(service, sizeof(service), "%d", port);
curl_msnprintf(service, sizeof(service), "%d", async->port);
ares->num_pending = 1;
ares_getaddrinfo(ares->channel, data->state.async.hostname,
ares_getaddrinfo(ares->channel, async->hostname,
service, &hints, async_ares_addrinfo_cb, data);
}
#else
#if ARES_VERSION >= 0x010601 /* IPv6 supported since 1.6.1 */
if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
if((async->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* The stack seems to be IPv6-enabled */
/* areschannel is already setup in the Curl_open() function */
CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET,
ares_gethostbyname(ares->channel, async->hostname, PF_INET,
async_ares_hostbyname_cb, data);
CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA");
ares->num_pending = 2;
ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET6,
ares_gethostbyname(ares->channel, async->hostname, PF_INET6,
async_ares_hostbyname_cb, data);
}
else
@ -797,20 +802,20 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
/* areschannel is already setup in the Curl_open() function */
CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
ares->num_pending = 1;
ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET,
ares_gethostbyname(ares->channel, async->hostname, PF_INET,
async_ares_hostbyname_cb, data);
}
#endif
#ifdef USE_HTTPSRR
{
CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR: %s",
rrname ? rrname : data->state.async.hostname);
rrname ? rrname : async->hostname);
memset(&ares->hinfo, 0, sizeof(ares->hinfo));
ares->hinfo.port = -1;
ares->hinfo.rrname = rrname;
ares->num_pending++; /* one more */
ares_query_dnsrec(ares->channel,
rrname ? rrname : data->state.async.hostname,
rrname ? rrname : async->hostname,
ARES_CLASS_IN, ARES_REC_TYPE_HTTPS,
async_ares_rr_done, data, NULL);
}
@ -828,7 +833,8 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
bool reset_on_null)
{
struct async_ares_ctx *ares = &data->state.async.ares;
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];
int ares_result = ARES_SUCCESS;
@ -847,7 +853,7 @@ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
#ifdef HAVE_CARES_SERVERS_CSV
/* if channel is not there, this is a parameter check */
if(ares->channel)
if(ares && ares->channel)
#ifdef HAVE_CARES_PORTS_CSV
ares_result = ares_set_servers_ports_csv(ares->channel, servers);
#else
@ -883,14 +889,15 @@ CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data)
CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data)
{
#ifdef HAVE_CARES_LOCAL_DEV
struct async_ares_ctx *ares = &data->state.async.ares;
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];
if(!interf)
interf = "";
/* if channel is not there, this is a parameter check */
if(ares->channel)
if(ares && ares->channel)
ares_set_local_dev(ares->channel, interf);
return CURLE_OK;
@ -903,7 +910,8 @@ CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data)
CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
{
#ifdef HAVE_CARES_SET_LOCAL
struct async_ares_ctx *ares = &data->state.async.ares;
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];
@ -918,7 +926,7 @@ CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
}
/* if channel is not there yet, this is a parameter check */
if(ares->channel)
if(ares && ares->channel)
ares_set_local_ip4(ares->channel, ntohl(a4.s_addr));
return CURLE_OK;
@ -931,7 +939,8 @@ CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data)
{
#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6)
struct async_ares_ctx *ares = &data->state.async.ares;
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];
@ -947,7 +956,7 @@ CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data)
}
/* if channel is not there, this is a parameter check */
if(ares->channel)
if(ares && ares->channel)
ares_set_local_ip6(ares->channel, a6);
return CURLE_OK;

View File

@ -43,6 +43,7 @@
#endif
#include "urldata.h"
#include "curl_trc.h"
#include "hostip.h"
#include "multiif.h"
#include "select.h"
@ -180,30 +181,35 @@ int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms)
void Curl_async_shutdown(struct Curl_easy *data)
{
if(data->state.async) {
CURL_TRC_DNS(data, "shutdown async");
#ifdef CURLRES_ARES
Curl_async_ares_shutdown(data);
Curl_async_ares_shutdown(data, data->state.async);
#endif
#ifdef CURLRES_THREADED
Curl_async_thrdd_shutdown(data);
Curl_async_thrdd_shutdown(data, data->state.async);
#endif
#ifndef CURL_DISABLE_DOH
Curl_doh_cleanup(data);
Curl_doh_cleanup(data, data->state.async);
#endif
Curl_safefree(data->state.async.hostname);
}
}
void Curl_async_destroy(struct Curl_easy *data)
{
if(data->state.async) {
CURL_TRC_DNS(data, "destroy async");
#ifdef CURLRES_ARES
Curl_async_ares_destroy(data);
Curl_async_ares_destroy(data, data->state.async);
#endif
#ifdef CURLRES_THREADED
Curl_async_thrdd_destroy(data);
Curl_async_thrdd_destroy(data, data->state.async);
#endif
#ifndef CURL_DISABLE_DOH
Curl_doh_cleanup(data);
Curl_doh_cleanup(data, data->state.async);
#endif
Curl_safefree(data->state.async.hostname);
Curl_safefree(data->state.async);
}
}
#endif /* USE_CURL_ASYNC */

View File

@ -101,12 +101,12 @@ void Curl_async_global_cleanup(void)
#endif
}
static void async_thrdd_destroy(struct Curl_easy *data);
static void async_thrdd_shutdown(struct Curl_easy *data);
CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
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;
}
@ -144,7 +144,7 @@ static void addr_ctx_unlink(struct async_thrdd_addr_ctx **paddr_ctx,
/* Initialize context for threaded resolver */
static struct async_thrdd_addr_ctx *
addr_ctx_create(struct Curl_easy *data,
const char *hostname, int port,
struct Curl_resolv_async *async,
const struct addrinfo *hints)
{
struct async_thrdd_addr_ctx *addr_ctx = curlx_calloc(1, sizeof(*addr_ctx));
@ -152,7 +152,7 @@ addr_ctx_create(struct Curl_easy *data,
return NULL;
addr_ctx->thread_hnd = curl_thread_t_null;
addr_ctx->port = port;
addr_ctx->port = async->port;
addr_ctx->ref_count = 1;
#ifdef HAVE_GETADDRINFO
@ -177,7 +177,7 @@ addr_ctx_create(struct Curl_easy *data,
/* Copying hostname string because original can be destroyed by parent
* thread during gethostbyname execution.
*/
addr_ctx->hostname = curlx_strdup(hostname);
addr_ctx->hostname = curlx_strdup(async->hostname);
if(!addr_ctx->hostname)
goto err_exit;
@ -287,9 +287,10 @@ static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg)
/*
* async_thrdd_destroy() cleans up async resolver data and thread handle.
*/
static void async_thrdd_destroy(struct Curl_easy *data)
static void async_thrdd_destroy(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
struct async_thrdd_ctx *thrdd = &async->thrdd;
struct async_thrdd_addr_ctx *addr = thrdd->addr;
#ifdef USE_HTTPSRR_ARES
@ -333,7 +334,7 @@ static void async_thrdd_rr_done(void *user_data, ares_status_t status,
const ares_dns_record_t *dnsrec)
{
struct Curl_easy *data = user_data;
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
struct async_thrdd_ctx *thrdd = &data->state.async->thrdd;
(void)timeouts;
thrdd->rr.done = TRUE;
@ -344,7 +345,7 @@ static void async_thrdd_rr_done(void *user_data, ares_status_t status,
static CURLcode async_rr_start(struct Curl_easy *data, int port)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
struct async_thrdd_ctx *thrdd = &data->state.async->thrdd;
int status;
char *rrname = NULL;
@ -390,38 +391,21 @@ static CURLcode async_rr_start(struct Curl_easy *data, int port)
* Returns FALSE in case of failure, otherwise TRUE.
*/
static bool async_thrdd_init(struct Curl_easy *data,
const char *hostname, int port, int ip_version,
struct Curl_resolv_async *async,
const struct addrinfo *hints)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
struct async_thrdd_ctx *thrdd = &async->thrdd;
struct async_thrdd_addr_ctx *addr_ctx;
/* !checksrc! disable ERRNOVAR 1 */
int err = ENOMEM;
if(thrdd->addr
DEBUGASSERT(!thrdd->addr);
#ifdef USE_HTTPSRR_ARES
|| thrdd->rr.channel
DEBUGASSERT(!thrdd->rr.channel);
#endif
) {
CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up");
async_thrdd_destroy(data);
DEBUGASSERT(!thrdd->addr);
#ifdef USE_HTTPSRR_ARES
DEBUGASSERT(!thrdd->rr.channel);
#endif
}
data->state.async.dns = NULL;
data->state.async.done = FALSE;
data->state.async.port = port;
data->state.async.ip_version = ip_version;
curlx_free(data->state.async.hostname);
data->state.async.hostname = curlx_strdup(hostname);
if(!data->state.async.hostname)
goto err_exit;
addr_ctx = addr_ctx_create(data, hostname, port, hints);
addr_ctx = addr_ctx_create(data, async, hints);
if(!addr_ctx)
goto err_exit;
thrdd->addr = addr_ctx;
@ -445,22 +429,24 @@ static bool async_thrdd_init(struct Curl_easy *data,
}
#ifdef USE_HTTPSRR_ARES
if(async_rr_start(data, port))
if(async_rr_start(data, async->port))
infof(data, "Failed HTTPS RR operation");
#endif
CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port);
CURL_TRC_DNS(data, "resolve thread started for of %s:%d",
async->hostname, async->port);
return TRUE;
err_exit:
CURL_TRC_DNS(data, "resolve thread failed init: %d", err);
async_thrdd_destroy(data);
async_thrdd_destroy(data, async);
errno = err;
return FALSE;
}
static void async_thrdd_shutdown(struct Curl_easy *data)
static void async_thrdd_shutdown(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
struct async_thrdd_ctx *thrdd = &async->thrdd;
struct async_thrdd_addr_ctx *addr_ctx = thrdd->addr;
curl_bit done;
@ -491,15 +477,16 @@ static void async_thrdd_shutdown(struct Curl_easy *data)
* 'entry' may be NULL and then no data is returned
*/
static CURLcode asyn_thrdd_await(struct Curl_easy *data,
struct async_thrdd_addr_ctx *addr_ctx,
struct Curl_resolv_async *async,
struct Curl_dns_entry **entry)
{
struct async_thrdd_addr_ctx *addr_ctx = async->thrdd.addr;
CURLcode result = CURLE_OK;
if(addr_ctx->thread_hnd != curl_thread_t_null) {
if(addr_ctx && (addr_ctx->thread_hnd != curl_thread_t_null)) {
/* not interested in result? cancel, if still running... */
if(!entry)
async_thrdd_shutdown(data);
async_thrdd_shutdown(data, async);
if(addr_ctx->thread_hnd != curl_thread_t_null) {
CURL_TRC_DNS(data, "resolve, wait for thread to finish");
@ -508,14 +495,13 @@ static CURLcode asyn_thrdd_await(struct Curl_easy *data,
}
}
if(entry)
result = Curl_async_is_resolved(data, entry);
if(entry) {
result = Curl_async_take_result(data, async, entry);
if(result == CURLE_AGAIN)
result = CURLE_OK;
}
}
data->state.async.done = TRUE;
if(entry)
*entry = data->state.async.dns;
return result;
}
@ -523,19 +509,19 @@ static CURLcode asyn_thrdd_await(struct Curl_easy *data,
* Until we gain a way to signal the resolver threads to stop early, we must
* wait for them and ignore their results.
*/
void Curl_async_thrdd_shutdown(struct Curl_easy *data)
void Curl_async_thrdd_shutdown(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
async_thrdd_shutdown(data);
async_thrdd_shutdown(data, async);
}
void Curl_async_thrdd_destroy(struct Curl_easy *data)
void Curl_async_thrdd_destroy(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
if(thrdd->addr && !data->set.quick_exit) {
(void)asyn_thrdd_await(data, thrdd->addr, NULL);
if(!data->set.quick_exit) {
(void)asyn_thrdd_await(data, async, NULL);
}
async_thrdd_destroy(data);
async_thrdd_destroy(data, async);
}
/*
@ -552,34 +538,26 @@ void Curl_async_thrdd_destroy(struct Curl_easy *data)
* This is the version for resolves-in-a-thread.
*/
CURLcode Curl_async_await(struct Curl_easy *data,
struct Curl_dns_entry **dns)
struct Curl_resolv_async *async,
struct Curl_dns_entry **entry)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
if(thrdd->addr)
return asyn_thrdd_await(data, thrdd->addr, dns);
return CURLE_FAILED_INIT;
return asyn_thrdd_await(data, async, entry);
}
/*
* Curl_async_is_resolved() is called repeatedly to check if a previous
* 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_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dns)
CURLcode Curl_async_take_result(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct Curl_dns_entry **pdns)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
struct async_thrdd_ctx *thrdd = &async->thrdd;
curl_bit done = FALSE;
DEBUGASSERT(dns);
*dns = NULL;
if(data->state.async.done) {
*dns = data->state.async.dns;
CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound",
*dns ? "" : "not ");
return CURLE_OK;
}
DEBUGASSERT(pdns);
*pdns = NULL;
#ifdef USE_HTTPSRR_ARES
/* best effort, ignore errors */
@ -598,16 +576,14 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
if(done) {
CURLcode result = CURLE_OK;
data->state.async.done = TRUE;
Curl_resolv_unlink(data, &data->state.async.dns);
Curl_expire_done(data, EXPIRE_ASYNC_NAME);
if(thrdd->addr->res) {
data->state.async.dns =
struct Curl_dns_entry *dns =
Curl_dnscache_mk_entry(data, &thrdd->addr->res,
data->state.async.hostname, 0,
data->state.async.port, FALSE);
if(!data->state.async.dns)
async->hostname, async->port,
async->ip_version);
if(!dns)
result = CURLE_OUT_OF_MEMORY;
#ifdef USE_HTTPSRR_ARES
@ -619,22 +595,21 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
if(!lhrr)
result = CURLE_OUT_OF_MEMORY;
else
data->state.async.dns->hinfo = lhrr;
dns->hinfo = lhrr;
}
}
#endif
if(!result && data->state.async.dns)
result = Curl_dnscache_add(data, data->state.async.dns);
if(!result && dns) {
result = Curl_dnscache_add(data, dns);
*pdns = dns;
}
}
if(!result && !data->state.async.dns)
if(!result && !*pdns)
result = Curl_resolver_error(data, NULL);
if(result)
Curl_resolv_unlink(data, &data->state.async.dns);
*dns = data->state.async.dns;
CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
result, *dns ? "" : "not ");
async_thrdd_shutdown(data);
result, *pdns ? "" : "not ");
async_thrdd_shutdown(data, async);
return result;
}
else {
@ -657,13 +632,13 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval;
Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME);
return CURLE_OK;
return CURLE_AGAIN;
}
}
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
struct async_thrdd_ctx *thrdd = &data->state.async->thrdd;
CURLcode result = CURLE_OK;
curl_bit thrd_done;
@ -711,13 +686,13 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
/*
* Curl_async_getaddrinfo() - for platforms without getaddrinfo
*/
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
int port, int ip_version)
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
(void)ip_version;
/* fire up a new resolver thread! */
if(async_thrdd_init(data, hostname, port, ip_version, NULL)) {
if(async_thrdd_init(data, async, NULL)) {
return CURLE_OK;
}
@ -730,23 +705,22 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
/*
* Curl_async_getaddrinfo() - for getaddrinfo
*/
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
int port, int ip_version)
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct addrinfo hints;
int pf = PF_INET;
CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port);
CURL_TRC_DNS(data, "init threaded resolve of %s:%d",
async->hostname, async->port);
#ifdef CURLRES_IPV6
if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
if((async->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* The stack seems to be IPv6-enabled */
if(ip_version == CURL_IPRESOLVE_V6)
if(async->ip_version == CURL_IPRESOLVE_V6)
pf = PF_INET6;
else
pf = PF_UNSPEC;
}
#else
(void)ip_version;
#endif /* CURLRES_IPV6 */
memset(&hints, 0, sizeof(hints));
@ -756,7 +730,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
SOCK_STREAM : SOCK_DGRAM;
/* fire up a new resolver thread! */
if(async_thrdd_init(data, hostname, port, ip_version, &hints))
if(async_thrdd_init(data, async, &hints))
return CURLE_OK;
failf(data, "getaddrinfo() thread failed to start");

View File

@ -31,6 +31,7 @@
struct Curl_easy;
struct Curl_dns_entry;
struct Curl_resolv_async;
#ifdef CURLRES_ASYNCH
@ -70,7 +71,9 @@ void Curl_async_global_cleanup(void);
* Get the resolver implementation instance (c-ares channel) or NULL
* for passing to application callback.
*/
CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl);
CURLcode Curl_async_get_impl(struct Curl_easy *easy,
struct Curl_resolv_async *async,
void **impl);
/* Curl_async_pollset()
*
@ -83,16 +86,13 @@ CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl);
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps);
/*
* Curl_async_is_resolved()
*
* 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.
*
* Returns normal CURLcode errors.
* 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_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dns);
CURLcode Curl_async_take_result(struct Curl_easy *data,
struct Curl_resolv_async *async,
struct Curl_dns_entry **pdns);
/*
* Curl_async_await()
@ -106,7 +106,8 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
CURLcode Curl_async_await(struct Curl_easy *data,
struct Curl_dns_entry **dns);
struct Curl_resolv_async *async,
struct Curl_dns_entry **pdns);
/*
* Curl_async_getaddrinfo() - when using this resolver
@ -119,8 +120,8 @@ CURLcode Curl_async_await(struct Curl_easy *data,
* Each resolver backend must of course make sure to return data in the
* correct format to comply with this.
*/
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
int port, int ip_version);
CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
struct Curl_resolv_async *async);
#ifdef USE_ARES
/* common functions for c-ares and threaded resolver with HTTPSRR */
@ -150,8 +151,10 @@ struct async_ares_ctx {
#endif
};
void Curl_async_ares_shutdown(struct Curl_easy *data);
void Curl_async_ares_destroy(struct Curl_easy *data);
void Curl_async_ares_shutdown(struct Curl_easy *data,
struct Curl_resolv_async *async);
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);
@ -211,8 +214,10 @@ struct async_thrdd_ctx {
#endif
};
void Curl_async_thrdd_shutdown(struct Curl_easy *data);
void Curl_async_thrdd_destroy(struct Curl_easy *data);
void Curl_async_thrdd_shutdown(struct Curl_easy *data,
struct Curl_resolv_async *async);
void Curl_async_thrdd_destroy(struct Curl_easy *data,
struct Curl_resolv_async *async);
#endif /* CURLRES_THREADED */
@ -223,11 +228,11 @@ struct doh_probes;
#else /* CURLRES_ASYNCH */
/* convert these functions if an asynch resolver is not used */
#define Curl_async_get_impl(x, y) (*(y) = NULL, CURLE_OK)
#define Curl_async_is_resolved(x, y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_async_await(x, y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_async_global_init() CURLE_OK
#define Curl_async_global_cleanup() Curl_nop_stmt
#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
#endif /* !CURLRES_ASYNCH */
@ -236,7 +241,7 @@ struct doh_probes;
#endif
#ifdef USE_CURL_ASYNC
struct Curl_async {
struct Curl_resolv_async {
#ifdef CURLRES_ARES
struct async_ares_ctx ares;
#elif defined(CURLRES_THREADED)
@ -245,11 +250,11 @@ struct Curl_async {
#ifndef CURL_DISABLE_DOH
struct doh_probes *doh; /* DoH specific data for this request */
#endif
struct Curl_dns_entry *dns; /* result of resolving on success */
char *hostname; /* copy of the params resolv started with */
int port;
int ip_version;
BIT(done);
uint32_t id; /* unique id per easy handle of the resolve operation */
/* what is being resolved */
uint16_t port;
uint8_t ip_version;
char hostname[1];
};
/*

View File

@ -641,8 +641,8 @@ 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.
*/
int ip_version = (af == AF_INET) ?
CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_WHATEVER;
uint8_t ip_version = (af == AF_INET) ?
CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_WHATEVER;
#ifdef USE_IPV6
if(af == AF_INET6)
ip_version = CURL_IPRESOLVE_V6;
@ -655,7 +655,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
Curl_printable_address(h->addr, myhost, sizeof(myhost));
infof(data, "Name '%s' family %i resolved to '%s' family %i",
host, af, myhost, h_af);
Curl_resolv_unlink(data, &h); /* this will NULL, potential free h */
Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */
if(af != h_af) {
/* bad IP version combo, signal the caller to try another address
family if available */

View File

@ -564,7 +564,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
DEBUGASSERT(conn->scheme);
DEBUGASSERT(dns);
Curl_resolv_unlink(data, &data->state.dns[sockindex]);
Curl_dns_entry_unlink(data, &data->state.dns[sockindex]);
data->state.dns[sockindex] = dns;
#ifndef CURL_DISABLE_HTTP
@ -588,7 +588,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
DEBUGASSERT(conn->cfilter[sockindex]);
out:
if(result)
Curl_resolv_unlink(data, &data->state.dns[sockindex]);
Curl_dns_entry_unlink(data, &data->state.dns[sockindex]);
return result;
}

View File

@ -404,7 +404,7 @@ static CURLcode ip2addr(struct Curl_addrinfo **addrp, int af,
* Given an IPv4 or IPv6 dotted string address, this converts it to a proper
* allocated Curl_addrinfo struct and returns it.
*/
CURLcode Curl_str2addr(const char *dotted, int port,
CURLcode Curl_str2addr(const char *dotted, uint16_t port,
struct Curl_addrinfo **addrp)
{
struct in_addr in;

View File

@ -72,7 +72,7 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port);
#endif
bool Curl_is_ipaddr(const char *address);
CURLcode Curl_str2addr(const char *dotted, int port,
CURLcode Curl_str2addr(const char *dotted, uint16_t port,
struct Curl_addrinfo **addrp);
#ifdef USE_UNIX_SOCKETS

832
lib/dnscache.c Normal file
View File

@ -0,0 +1,832 @@
/***************************************************************************
* _ _ ____ _
* 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"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_IN6_H
#include <netinet/in6.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
#include "urldata.h"
#include "curl_addrinfo.h"
#include "curl_share.h"
#include "curl_trc.h"
#include "dnscache.h"
#include "hash.h"
#include "httpsrr.h"
#include "progress.h"
#include "rand.h"
#include "strcase.h"
#include "curlx/inet_ntop.h"
#include "curlx/inet_pton.h"
#include "curlx/strcopy.h"
#include "curlx/strparse.h"
#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
#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);
#ifdef USE_HTTPSRR
if(dns->hinfo) {
Curl_httpsrr_cleanup(dns->hinfo);
curlx_free(dns->hinfo);
}
#endif
curlx_free(dns);
}
/*
* Create a hostcache id string for the provided host + port, to be used by
* the DNS caching. Without alloc. Return length of the id string.
*/
static size_t create_dnscache_id(const char *name,
size_t nlen, /* 0 or actual name length */
uint16_t port, char *ptr, size_t buflen)
{
size_t len = nlen ? nlen : strlen(name);
DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
if(len > (buflen - 7))
len = buflen - 7;
/* store and lower case the name */
Curl_strntolower(ptr, name, len);
return curl_msnprintf(&ptr[len], 7, ":%u", port) + len;
}
struct dnscache_prune_data {
struct curltime now;
timediff_t oldest_ms; /* oldest time in cache not pruned. */
timediff_t max_age_ms;
};
/*
* This function is set as a callback to be called for every entry in the DNS
* cache when we want to prune old unused entries.
*
* Returning non-zero means remove the entry, return 0 to keep it in the
* cache.
*/
static int dnscache_entry_is_stale(void *datap, void *hc)
{
struct dnscache_prune_data *prune = (struct dnscache_prune_data *)datap;
struct Curl_dns_entry *dns = (struct Curl_dns_entry *)hc;
if(dns->timestamp.tv_sec || dns->timestamp.tv_usec) {
/* get age in milliseconds */
timediff_t age = curlx_ptimediff_ms(&prune->now, &dns->timestamp);
if(!dns->addr)
age *= 2; /* negative entries age twice as fast */
if(age >= prune->max_age_ms)
return TRUE;
if(age > prune->oldest_ms)
prune->oldest_ms = age;
}
return FALSE;
}
/*
* Prune the DNS cache. This assumes that a lock has already been taken.
* Returns the 'age' of the oldest still kept entry - in milliseconds.
*/
static timediff_t dnscache_prune(struct Curl_hash *hostcache,
timediff_t cache_timeout_ms,
struct curltime now)
{
struct dnscache_prune_data user;
user.max_age_ms = cache_timeout_ms;
user.now = now;
user.oldest_ms = 0;
Curl_hash_clean_with_criterium(hostcache,
(void *)&user,
dnscache_entry_is_stale);
return user.oldest_ms;
}
static struct Curl_dnscache *dnscache_get(struct Curl_easy *data)
{
if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS))
return &data->share->dnscache;
if(data->multi)
return &data->multi->dnscache;
return NULL;
}
static void dnscache_lock(struct Curl_easy *data,
struct Curl_dnscache *dnscache)
{
if(data->share && dnscache == &data->share->dnscache)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
}
static void dnscache_unlock(struct Curl_easy *data,
struct Curl_dnscache *dnscache)
{
if(data->share && dnscache == &data->share->dnscache)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
}
/*
* Library-wide function for pruning the DNS cache. This function takes and
* returns the appropriate locks.
*/
void Curl_dnscache_prune(struct Curl_easy *data)
{
struct Curl_dnscache *dnscache = dnscache_get(data);
/* the timeout may be set -1 (forever) */
timediff_t timeout_ms = data->set.dns_cache_timeout_ms;
if(!dnscache || (timeout_ms == -1))
/* NULL hostcache means we cannot do it */
return;
dnscache_lock(data, dnscache);
do {
/* Remove outdated and unused entries from the hostcache */
timediff_t oldest_ms =
dnscache_prune(&dnscache->entries, timeout_ms, *Curl_pgrs_now(data));
if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE)
/* prune the ones over half this age */
timeout_ms = oldest_ms / 2;
else
break;
/* if the cache size is still too big, use the oldest age as new prune
limit */
} while(timeout_ms);
dnscache_unlock(data, dnscache);
}
void Curl_dnscache_clear(struct Curl_easy *data)
{
struct Curl_dnscache *dnscache = dnscache_get(data);
if(dnscache) {
dnscache_lock(data, dnscache);
Curl_hash_clean(&dnscache->entries);
dnscache_unlock(data, dnscache);
}
}
/* lookup address, returns entry if found and not stale */
static CURLcode fetch_addr(struct Curl_easy *data,
struct Curl_dnscache *dnscache,
const char *hostname,
uint16_t port,
uint8_t ip_version,
struct Curl_dns_entry **pdns)
{
struct Curl_dns_entry *dns = NULL;
char entry_id[MAX_HOSTCACHE_LEN];
size_t entry_len;
CURLcode result = CURLE_OK;
*pdns = NULL;
if(!dnscache)
return CURLE_OK;
/* Create an entry id, based upon the hostname and port */
entry_len = create_dnscache_id(hostname, 0, port,
entry_id, sizeof(entry_id));
/* See if it is already in our dns cache */
dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
/* No entry found in cache, check if we might have a wildcard entry */
if(!dns && data->state.wildcard_resolve) {
entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id));
/* See if it is already in our dns cache */
dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
}
if(dns && (data->set.dns_cache_timeout_ms != -1)) {
/* See whether the returned entry is stale. Done before we release lock */
struct dnscache_prune_data user;
user.now = *Curl_pgrs_now(data);
user.max_age_ms = data->set.dns_cache_timeout_ms;
user.oldest_ms = 0;
if(dnscache_entry_is_stale(&user, dns)) {
infof(data, "Hostname in DNS cache was stale, zapped");
dns = NULL; /* the memory deallocation is being handled by the hash */
Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
}
}
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. */
dns = NULL;
break;
}
}
if(dns && !dns->addr) { /* negative entry */
dns = NULL;
result = CURLE_COULDNT_RESOLVE_HOST;
}
*pdns = dns;
return result;
}
/*
* Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
*
* Curl_resolv() checks initially and multi_runsingle() checks each time
* it discovers the handle in the state WAITRESOLVE whether the hostname
* has already been resolved and the address has already been stored in
* the DNS cache. This short circuits waiting for a lot of pending
* lookups for the same hostname requested by different handles.
*
* Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
*
* The returned data *MUST* be "released" with Curl_dns_entry_unlink() after
* use, or we will leak memory!
*/
CURLcode Curl_dnscache_get(struct Curl_easy *data,
const char *hostname,
uint16_t port,
uint8_t ip_version,
struct Curl_dns_entry **pentry)
{
struct Curl_dnscache *dnscache = dnscache_get(data);
struct Curl_dns_entry *dns = NULL;
CURLcode result = CURLE_OK;
dnscache_lock(data, dnscache);
result = fetch_addr(data, dnscache, hostname, port, ip_version, &dns);
if(!result && dns)
dns->refcount++; /* we pass out a reference */
else if(result) {
DEBUGASSERT(!dns);
dns = NULL;
}
dnscache_unlock(data, dnscache);
*pentry = dns;
return result;
}
#ifndef CURL_DISABLE_SHUFFLE_DNS
/*
* Return # of addresses in a Curl_addrinfo struct
*/
static int num_addresses(const struct Curl_addrinfo *addr)
{
int i = 0;
while(addr) {
addr = addr->ai_next;
i++;
}
return i;
}
UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
struct Curl_addrinfo **addr);
/*
* Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
* struct by re-linking its linked list.
*
* The addr argument should be the address of a pointer to the head node of a
* `Curl_addrinfo` list and it will be modified to point to the new head after
* shuffling.
*
* Not declared static only to make it easy to use in a unit test!
*
* @unittest: 1608
*/
UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
struct Curl_addrinfo **addr)
{
CURLcode result = CURLE_OK;
const int num_addrs = num_addresses(*addr);
if(num_addrs > 1) {
struct Curl_addrinfo **nodes;
CURL_TRC_DNS(data, "Shuffling %i addresses", num_addrs);
nodes = curlx_malloc(num_addrs * sizeof(*nodes));
if(nodes) {
int i;
unsigned int *rnd;
const size_t rnd_size = num_addrs * sizeof(*rnd);
/* build a plain array of Curl_addrinfo pointers */
nodes[0] = *addr;
for(i = 1; i < num_addrs; i++) {
nodes[i] = nodes[i - 1]->ai_next;
}
rnd = curlx_malloc(rnd_size);
if(rnd) {
/* Fisher-Yates shuffle */
if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
struct Curl_addrinfo *swap_tmp;
for(i = num_addrs - 1; i > 0; i--) {
swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)];
nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i];
nodes[i] = swap_tmp;
}
/* relink list in the new order */
for(i = 1; i < num_addrs; i++) {
nodes[i - 1]->ai_next = nodes[i];
}
nodes[num_addrs - 1]->ai_next = NULL;
*addr = nodes[0];
}
curlx_free(rnd);
}
else
result = CURLE_OUT_OF_MEMORY;
curlx_free(nodes);
}
else
result = CURLE_OUT_OF_MEMORY;
}
return result;
}
#endif
static struct Curl_dns_entry *
dnscache_entry_create(struct Curl_easy *data,
struct Curl_addrinfo **paddr,
const char *hostname,
size_t hostlen, /* length or zero */
uint16_t port,
uint8_t ip_version,
bool permanent)
{
struct Curl_dns_entry *dns = NULL;
#ifndef CURL_DISABLE_SHUFFLE_DNS
/* shuffle addresses if requested */
if(data->set.dns_shuffle_addresses && paddr) {
CURLcode result = Curl_shuffle_addr(data, paddr);
if(result)
goto out;
}
#else
(void)data;
#endif
if(!hostlen)
hostlen = strlen(hostname);
/* Create a new cache entry */
dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
if(!dns)
goto out;
dns->refcount = 1; /* the cache has the first reference */
dns->addr = paddr ? *paddr : NULL; /* this is the address(es) */
if(permanent) {
dns->timestamp.tv_sec = 0; /* an entry that never goes stale */
dns->timestamp.tv_usec = 0; /* an entry that never goes stale */
}
else {
dns->timestamp = *Curl_pgrs_now(data);
}
dns->port = port;
dns->ip_version = ip_version;
if(hostlen)
memcpy(dns->hostname, hostname, hostlen);
out:
if(paddr) {
if(!dns)
Curl_freeaddrinfo(*paddr);
*paddr = NULL;
}
return dns;
}
struct Curl_dns_entry *
Curl_dnscache_mk_entry(struct Curl_easy *data,
struct Curl_addrinfo **paddr,
const char *hostname,
uint16_t port,
uint8_t ip_version)
{
return dnscache_entry_create(data, paddr, hostname, 0,
port, ip_version, FALSE);
}
static struct Curl_dns_entry *
dnscache_add_addr(struct Curl_easy *data,
struct Curl_dnscache *dnscache,
struct Curl_addrinfo **paddr,
const char *hostname,
size_t hlen, /* length or zero */
uint16_t port,
uint8_t ip_version,
bool permanent)
{
char entry_id[MAX_HOSTCACHE_LEN];
size_t entry_len;
struct Curl_dns_entry *dns;
struct Curl_dns_entry *dns2;
dns = dnscache_entry_create(data, paddr, hostname, hlen, port,
ip_version, permanent);
if(!dns)
return NULL;
/* Create an entry id, based upon the hostname and port */
entry_len = create_dnscache_id(hostname, hlen, port,
entry_id, sizeof(entry_id));
/* Store the resolved data in our DNS cache. */
dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1,
(void *)dns);
if(!dns2) {
dnscache_entry_free(dns);
return NULL;
}
dns = dns2;
dns->refcount++; /* mark entry as in-use */
return dns;
}
CURLcode Curl_dnscache_add(struct Curl_easy *data,
struct Curl_dns_entry *entry)
{
struct Curl_dnscache *dnscache = dnscache_get(data);
char id[MAX_HOSTCACHE_LEN];
size_t idlen;
if(!dnscache)
return CURLE_FAILED_INIT;
/* Create an entry id, based upon the hostname and port */
idlen = create_dnscache_id(entry->hostname, 0, entry->port, id, sizeof(id));
/* Store the resolved data in our DNS cache and up ref count */
dnscache_lock(data, dnscache);
if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) {
dnscache_unlock(data, dnscache);
return CURLE_OUT_OF_MEMORY;
}
entry->refcount++;
dnscache_unlock(data, dnscache);
return CURLE_OK;
}
CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
const char *host,
uint16_t port,
uint8_t ip_version)
{
struct Curl_dnscache *dnscache = dnscache_get(data);
struct Curl_dns_entry *dns;
DEBUGASSERT(dnscache);
if(!dnscache)
return CURLE_FAILED_INIT;
/* put this new host in the cache */
dns = dnscache_add_addr(data, dnscache, NULL, host, 0,
port, ip_version, 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));
return CURLE_OK;
}
return CURLE_OUT_OF_MEMORY;
}
/*
* Curl_dns_entry_unlink() releases a reference to the given cached DNS entry.
* When the reference count reaches 0, the entry is destroyed. It is important
* that only one unlink is made for each Curl_resolv() call.
*
* May be called with 'data' == NULL for global cache.
*/
void Curl_dns_entry_unlink(struct Curl_easy *data,
struct Curl_dns_entry **pdns)
{
if(*pdns) {
struct Curl_dnscache *dnscache = dnscache_get(data);
struct Curl_dns_entry *dns = *pdns;
*pdns = NULL;
dnscache_lock(data, dnscache);
dns->refcount--;
if(dns->refcount == 0)
dnscache_entry_free(dns);
dnscache_unlock(data, dnscache);
}
}
static void dnscache_entry_dtor(void *entry)
{
struct Curl_dns_entry *dns = (struct Curl_dns_entry *)entry;
DEBUGASSERT(dns && (dns->refcount > 0));
dns->refcount--;
if(dns->refcount == 0)
dnscache_entry_free(dns);
}
/*
* Curl_dnscache_init() inits a new DNS cache.
*/
void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size)
{
Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare,
dnscache_entry_dtor);
}
void Curl_dnscache_destroy(struct Curl_dnscache *dns)
{
Curl_hash_destroy(&dns->entries);
}
CURLcode Curl_loadhostpairs(struct Curl_easy *data)
{
struct Curl_dnscache *dnscache = dnscache_get(data);
struct curl_slist *hostp;
if(!dnscache)
return CURLE_FAILED_INIT;
/* Default is no wildcard found */
data->state.wildcard_resolve = FALSE;
for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
char entry_id[MAX_HOSTCACHE_LEN];
const char *host = hostp->data;
struct Curl_str source;
if(!host)
continue;
if(*host == '-') {
curl_off_t num = 0;
size_t entry_len;
host++;
if(!curlx_str_single(&host, '[')) {
if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
curlx_str_single(&host, ']') ||
curlx_str_single(&host, ':'))
continue;
}
else {
if(curlx_str_until(&host, &source, 4096, ':') ||
curlx_str_single(&host, ':')) {
continue;
}
}
if(!curlx_str_number(&host, &num, 0xffff)) {
/* Create an entry id, based upon the hostname and port */
entry_len = create_dnscache_id(curlx_str(&source),
curlx_strlen(&source), (uint16_t)num,
entry_id, sizeof(entry_id));
dnscache_lock(data, dnscache);
/* delete entry, ignore if it did not exist */
Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
dnscache_unlock(data, dnscache);
}
}
else {
struct Curl_dns_entry *dns;
struct Curl_addrinfo *head = NULL, *tail = NULL;
size_t entry_len;
char address[64];
curl_off_t tmpofft = 0;
uint16_t port = 0;
bool permanent = TRUE;
bool error = TRUE;
VERBOSE(const char *addresses = NULL);
if(*host == '+') {
host++;
permanent = FALSE;
}
if(!curlx_str_single(&host, '[')) {
if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
curlx_str_single(&host, ']'))
continue;
}
else {
if(curlx_str_until(&host, &source, 4096, ':'))
continue;
}
if(curlx_str_single(&host, ':') ||
curlx_str_number(&host, &tmpofft, 0xffff) ||
curlx_str_single(&host, ':'))
goto err;
port = (uint16_t)tmpofft;
VERBOSE(addresses = host);
/* start the address section */
while(*host) {
struct Curl_str target;
struct Curl_addrinfo *ai;
CURLcode result;
if(!curlx_str_single(&host, '[')) {
if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') ||
curlx_str_single(&host, ']'))
goto err;
}
else {
if(curlx_str_until(&host, &target, 4096, ',')) {
if(curlx_str_single(&host, ','))
goto err;
/* survive nothing but a comma */
continue;
}
}
#ifndef USE_IPV6
if(memchr(curlx_str(&target), ':', curlx_strlen(&target))) {
infof(data, "Ignoring resolve address '%.*s', missing IPv6 support.",
(int)curlx_strlen(&target), curlx_str(&target));
if(curlx_str_single(&host, ','))
goto err;
continue;
}
#endif
if(curlx_strlen(&target) >= sizeof(address))
goto err;
memcpy(address, curlx_str(&target), curlx_strlen(&target));
address[curlx_strlen(&target)] = '\0';
result = Curl_str2addr(address, port, &ai);
if(result) {
infof(data, "Resolve address '%s' found illegal", address);
goto err;
}
if(tail) {
tail->ai_next = ai;
tail = tail->ai_next;
}
else {
head = tail = ai;
}
if(curlx_str_single(&host, ','))
break;
}
if(!head)
goto err;
error = FALSE;
err:
if(error) {
failf(data, "Could not parse CURLOPT_RESOLVE entry '%s'", hostp->data);
Curl_freeaddrinfo(head);
return CURLE_SETOPT_OPTION_SYNTAX;
}
/* Create an entry id, based upon the hostname and port */
entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source),
port, entry_id, sizeof(entry_id));
dnscache_lock(data, dnscache);
/* See if it is already in our dns cache */
dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
if(dns) {
infof(data, "RESOLVE %.*s:%u - old addresses discarded",
(int)curlx_strlen(&source),
curlx_str(&source), port);
/* delete old entry, there are two reasons for this
1. old entry may have different addresses.
2. even if entry with correct addresses is already in the cache,
but if it is close to expire, then by the time next http
request is made, it can get expired and pruned because old
entry is not necessarily marked as permanent.
3. when adding a non-permanent entry, we want it to remove and
replace an existing permanent entry.
4. when adding a non-permanent entry, we want it to get a "fresh"
timeout that starts _now_. */
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);
if(dns)
/* release the returned reference; the cache itself will keep the
* entry alive: */
dns->refcount--;
dnscache_unlock(data, dnscache);
if(!dns)
return CURLE_OUT_OF_MEMORY;
infof(data, "Added %.*s:%u:%s to DNS cache%s",
(int)curlx_strlen(&source), curlx_str(&source), port, addresses,
permanent ? "" : " (non-permanent)");
/* Wildcard hostname */
if(curlx_str_casecompare(&source, "*")) {
infof(data, "RESOLVE *:%u using wildcard", port);
data->state.wildcard_resolve = TRUE;
}
}
}
data->state.resolve = NULL; /* dealt with now */
return CURLE_OK;
}

126
lib/dnscache.h Normal file
View File

@ -0,0 +1,126 @@
#ifndef HEADER_CURL_DNSCACHE_H
#define HEADER_CURL_DNSCACHE_H
/***************************************************************************
* _ _ ____ _
* 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"
#include "hash.h"
struct addrinfo;
struct hostent;
struct Curl_easy;
struct connectdata;
struct easy_pollset;
struct Curl_https_rrinfo;
struct Curl_multi;
struct Curl_dns_entry {
struct Curl_addrinfo *addr;
#ifdef USE_HTTPSRR
struct Curl_https_rrinfo *hinfo;
#endif
/* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */
struct curltime timestamp;
/* reference counter, entry is freed on reaching 0 */
uint32_t refcount;
/* hostname port number that resolved to addr. */
uint16_t port;
uint8_t ip_version;
/* hostname that resolved to addr. may be NULL (Unix domain sockets). */
char hostname[1];
};
/*
* Create a `Curl_dns_entry` with a reference count of 1.
* Use `Curl_dns_entry_unlink()` to release your hold on it.
*
* The call takes ownership of `addr`, even in case of failure, and always
* clears `*paddr`. It makes a copy of `hostname`.
*
* Returns entry or NULL on OOM.
*/
struct Curl_dns_entry *
Curl_dnscache_mk_entry(struct Curl_easy *data,
struct Curl_addrinfo **paddr,
const char *hostname,
uint16_t port,
uint8_t ip_version);
/* unlink a dns entry, frees all resources if it was the last reference.
* Always clears `*pdns`` */
void Curl_dns_entry_unlink(struct Curl_easy *data,
struct Curl_dns_entry **pdns);
struct Curl_dnscache {
struct Curl_hash entries;
};
/* init a new dns cache */
void Curl_dnscache_init(struct Curl_dnscache *dns, size_t hashsize);
void Curl_dnscache_destroy(struct Curl_dnscache *dns);
/* prune old entries from the DNS cache */
void Curl_dnscache_prune(struct Curl_easy *data);
/* clear the DNS cache */
void Curl_dnscache_clear(struct Curl_easy *data);
/*
* Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
*
* Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
*
* The returned data *MUST* be "released" with Curl_dns_entry_unlink() after
* use, or we will leak memory!
* Returns CURLE_OK or CURLE_COULDNT_RESOLVE_HOST when a negative
* entry was in the cache.
*/
CURLcode Curl_dnscache_get(struct Curl_easy *data,
const char *hostname,
uint16_t port,
uint8_t ip_version,
struct Curl_dns_entry **pentry);
/*
* Curl_dnscache_addr() adds `entry` to the cache, increasing its
* reference count on success.
*/
CURLcode Curl_dnscache_add(struct Curl_easy *data,
struct Curl_dns_entry *entry);
/* 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,
const char *host,
uint16_t port,
uint8_t ip_version);
/*
* Populate the cache with specified entries from CURLOPT_RESOLVE.
*/
CURLcode Curl_loadhostpairs(struct Curl_easy *data);
#endif /* HEADER_CURL_DNSCACHE_H */

217
lib/doh.c
View File

@ -216,45 +216,57 @@ 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 doh_probes *dohp = data->state.async.doh;
DEBUGASSERT(dohp);
if(dohp) {
struct doh_request *doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE);
int i;
struct Curl_resolv_async *async = data->state.async;
struct doh_probes *dohp = async ? async->doh : NULL;
struct doh_request *doh_req = NULL;
int i;
for(i = 0; i < DOH_SLOT_COUNT; ++i) {
if(dohp->probe_resp[i].probe_mid == doh->mid)
break;
}
if(i >= DOH_SLOT_COUNT) {
failf(data, "unknown sub request done");
return;
}
if(!dohp) {
DEBUGASSERT(0);
return;
}
dohp->pending--;
infof(doh, "a DoH request is completed, %u to go", dohp->pending);
dohp->probe_resp[i].result = result;
/* We expect either the meta data still to exist or the sub request
* to have already failed. */
DEBUGASSERT(doh_req || result);
if(doh_req) {
if(!result) {
dohp->probe_resp[i].dnstype = doh_req->dnstype;
result = curlx_dyn_addn(&dohp->probe_resp[i].body,
curlx_dyn_ptr(&doh_req->resp_body),
curlx_dyn_len(&doh_req->resp_body));
curlx_dyn_free(&doh_req->resp_body);
}
Curl_meta_remove(doh, CURL_EZM_DOH_PROBE);
}
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");
return;
}
if(result)
infof(doh, "DoH request %s", curl_easy_strerror(result));
for(i = 0; i < DOH_SLOT_COUNT; ++i) {
if(dohp->probe_resp[i].probe_mid == doh->mid)
break;
}
/* We really should have found the slot where to store the response */
if(i >= DOH_SLOT_COUNT) {
DEBUGASSERT(0);
failf(data, "DoH: unknown sub request done");
return;
}
if(!dohp->pending) {
/* DoH completed, run the transfer picking up the results */
Curl_multi_mark_dirty(data);
dohp->pending--;
infof(doh, "a DoH request is completed, %u to go", dohp->pending);
dohp->probe_resp[i].result = result;
/* We expect either the meta data still to exist or the sub request
* to have already failed. */
DEBUGASSERT(doh_req || result);
if(doh_req) {
if(!result) {
dohp->probe_resp[i].dnstype = doh_req->dnstype;
result = curlx_dyn_addn(&dohp->probe_resp[i].body,
curlx_dyn_ptr(&doh_req->resp_body),
curlx_dyn_len(&doh_req->resp_body));
curlx_dyn_free(&doh_req->resp_body);
}
Curl_meta_remove(doh, CURL_EZM_DOH_PROBE);
}
if(result)
infof(doh, "DoH request %s", curl_easy_strerror(result));
if(!dohp->pending) {
/* DoH completed, run the transfer picking up the results */
Curl_multi_mark_dirty(data);
}
}
@ -283,6 +295,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 *pmid)
{
struct Curl_easy *doh = NULL;
@ -296,6 +309,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->dnstype = dnstype;
curlx_dyn_init(&doh_req->resp_body, DYN_DOH_RESPONSE);
@ -436,8 +450,8 @@ error:
* a 'Curl_addrinfo *' with the address information.
*/
CURLcode Curl_doh(struct Curl_easy *data, const char *hostname,
int port, int ip_version)
CURLcode Curl_doh(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
CURLcode result = CURLE_OK;
struct doh_probes *dohp = NULL;
@ -445,21 +459,13 @@ CURLcode Curl_doh(struct Curl_easy *data, const char *hostname,
size_t i;
DEBUGASSERT(conn);
DEBUGASSERT(!data->state.async.doh);
DEBUGASSERT(hostname && hostname[0]);
if(data->state.async.doh)
Curl_doh_cleanup(data);
data->state.async.done = FALSE;
data->state.async.port = port;
data->state.async.ip_version = ip_version;
curlx_free(data->state.async.hostname);
data->state.async.hostname = curlx_strdup(hostname);
if(!data->state.async.hostname)
return CURLE_OUT_OF_MEMORY;
DEBUGASSERT(!async->doh);
DEBUGASSERT(async->hostname[0]);
if(async->doh)
Curl_doh_cleanup(data, async);
/* start clean, consider allocating this struct on demand */
data->state.async.doh = dohp = curlx_calloc(1, sizeof(struct doh_probes));
async->doh = dohp = curlx_calloc(1, sizeof(struct doh_probes));
if(!dohp)
return CURLE_OUT_OF_MEMORY;
@ -469,27 +475,27 @@ CURLcode Curl_doh(struct Curl_easy *data, const char *hostname,
}
conn->bits.doh = TRUE;
dohp->host = data->state.async.hostname;
dohp->port = data->state.async.port;
dohp->host = async->hostname;
dohp->port = async->port;
/* We are making sub easy handles and want to be called back when
* one is done. */
data->sub_xfer_done = doh_probe_done;
/* create IPv4 DoH request */
result = doh_probe_run(data, CURL_DNS_TYPE_A,
hostname, data->set.str[STRING_DOH],
data->multi,
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((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
if((async->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* create IPv6 DoH request */
result = doh_probe_run(data, CURL_DNS_TYPE_AAAA,
hostname, data->set.str[STRING_DOH],
data->multi,
async->hostname, data->set.str[STRING_DOH],
data->multi, async->id,
&dohp->probe_resp[DOH_SLOT_IPV6].probe_mid);
if(result)
goto error;
@ -501,14 +507,15 @@ CURLcode Curl_doh(struct Curl_easy *data, const char *hostname,
if(conn->scheme->protocol & PROTO_FAMILY_HTTP) {
/* Only use HTTPS RR for HTTP(S) transfers */
char *qname = NULL;
if(port != PORT_HTTPS) {
qname = curl_maprintf("_%d._https.%s", port, hostname);
if(async->port != PORT_HTTPS) {
qname = curl_maprintf("_%d._https.%s", async->port, async->hostname);
if(!qname)
goto error;
}
result = doh_probe_run(data, CURL_DNS_TYPE_HTTPS,
qname ? qname : hostname, data->set.str[STRING_DOH],
data->multi,
qname ? qname : async->hostname,
data->set.str[STRING_DOH], data->multi,
async->id,
&dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid);
curlx_free(qname);
if(result)
@ -519,7 +526,7 @@ CURLcode Curl_doh(struct Curl_easy *data, const char *hostname,
return CURLE_OK;
error:
Curl_doh_cleanup(data);
Curl_doh_cleanup(data, async);
return result;
}
@ -1206,13 +1213,15 @@ UNITTEST void doh_print_httpsrr(struct Curl_easy *data,
# endif
#endif
CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dnsp)
CURLcode Curl_doh_take_result(struct Curl_easy *data,
struct Curl_dns_entry **pdns)
{
struct Curl_resolv_async *async = data->state.async;
struct doh_probes *dohp = async ? async->doh : NULL;
CURLcode result = CURLE_OK;
struct doh_probes *dohp = data->state.async.doh;
struct dohentry de;
*dnsp = NULL; /* defaults to no response */
*pdns = NULL; /* defaults to no response */
if(!dohp)
return CURLE_OUT_OF_MEMORY;
@ -1226,9 +1235,6 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
DOHcode rc[DOH_SLOT_COUNT];
int slot;
/* Clear any result the might still be there */
Curl_resolv_unlink(data, &data->state.async.dns);
memset(rc, 0, sizeof(rc));
/* remove DoH handles from multi handle and close them */
Curl_doh_close(data);
@ -1247,7 +1253,6 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
}
} /* next slot */
result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) {
/* we have an address, of one kind or other */
struct Curl_dns_entry *dns;
@ -1263,50 +1268,55 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
goto error;
/* we got a response, create a dns entry. */
dns = Curl_dnscache_mk_entry(data, &ai, dohp->host, 0,
dohp->port, FALSE);
if(dns) {
/* Now add and HTTPSRR information if we have */
#ifdef USE_HTTPSRR
if(de.numhttps_rrs > 0 && result == CURLE_OK) {
struct Curl_https_rrinfo *hrr = NULL;
result = doh_resp_decode_httpsrr(data, de.https_rrs->val,
de.https_rrs->len, &hrr);
if(result) {
infof(data, "Failed to decode HTTPS RR");
Curl_resolv_unlink(data, &dns);
goto error;
}
infof(data, "Some HTTPS RR to process");
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
doh_print_httpsrr(data, hrr);
#endif
dns->hinfo = hrr;
}
#endif /* USE_HTTPSRR */
/* and add the entry to the cache */
data->state.async.dns = dns;
result = Curl_dnscache_add(data, dns);
*dnsp = data->state.async.dns;
dns = Curl_dnscache_mk_entry(data, &ai, dohp->host, dohp->port,
async->ip_version);
if(!dns) {
result = CURLE_OUT_OF_MEMORY;
goto error;
}
} /* address processing done */
/* All done */
data->state.async.done = TRUE;
/* Now add and HTTPSRR information if we have */
#ifdef USE_HTTPSRR
if(de.numhttps_rrs > 0 && result == CURLE_OK) {
struct Curl_https_rrinfo *hrr = NULL;
result = doh_resp_decode_httpsrr(data, de.https_rrs->val,
de.https_rrs->len, &hrr);
if(result) {
infof(data, "Failed to decode HTTPS RR");
Curl_dns_entry_unlink(data, &dns);
goto error;
}
infof(data, "Some HTTPS RR to process");
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
doh_print_httpsrr(data, hrr);
#endif
dns->hinfo = hrr;
}
#endif /* USE_HTTPSRR */
/* and add the entry to the cache */
result = Curl_dnscache_add(data, dns);
*pdns = dns;
} /* address processing done */
else {
result = CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY :
CURLE_COULDNT_RESOLVE_HOST;
}
} /* !dohp->pending */
else
/* wait for pending DoH transactions to complete */
return CURLE_OK;
return CURLE_AGAIN;
error:
de_cleanup(&de);
Curl_doh_cleanup(data);
Curl_doh_cleanup(data, async);
return result;
}
void Curl_doh_close(struct Curl_easy *data)
{
struct doh_probes *doh = data->state.async.doh;
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;
uint32_t mid;
@ -1332,16 +1342,17 @@ void Curl_doh_close(struct Curl_easy *data)
}
}
void Curl_doh_cleanup(struct Curl_easy *data)
void Curl_doh_cleanup(struct Curl_easy *data,
struct Curl_resolv_async *async)
{
struct doh_probes *dohp = data->state.async.doh;
struct doh_probes *dohp = async->doh;
if(dohp) {
int i;
Curl_doh_close(data);
for(i = 0; i < DOH_SLOT_COUNT; ++i) {
curlx_dyn_free(&dohp->probe_resp[i].body);
}
Curl_safefree(data->state.async.doh);
Curl_safefree(async->doh);
}
}

View File

@ -28,6 +28,8 @@
/* enums outside of the #ifdef to make the types work in unitprotos.h even on
builds without DoH support */
struct Curl_resolv_async;
typedef enum {
DOH_OK,
DOH_DNS_BAD_LABEL, /* 1 */
@ -91,6 +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 */
DNStype dnstype;
};
@ -106,7 +109,7 @@ struct doh_response {
struct doh_probes {
struct doh_response probe_resp[DOH_SLOT_COUNT];
unsigned int pending; /* still outstanding probes */
int port;
uint16_t port;
const char *host;
};
@ -115,11 +118,11 @@ struct doh_probes {
* name and returns a 'Curl_addrinfo *' with the address information.
*/
CURLcode Curl_doh(struct Curl_easy *data, const char *hostname,
int port, int ip_version);
CURLcode Curl_doh(struct Curl_easy *data,
struct Curl_resolv_async *async);
CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dnsp);
CURLcode Curl_doh_take_result(struct Curl_easy *data,
struct Curl_dns_entry **dns);
#define DOH_MAX_ADDR 24
#define DOH_MAX_CNAME 4
@ -161,11 +164,15 @@ struct dohentry {
};
void Curl_doh_close(struct Curl_easy *data);
void Curl_doh_cleanup(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)
#else /* CURL_DISABLE_DOH */
#define Curl_doh(a, b, c, d, e) NULL
#define Curl_doh_is_resolved(x, y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_doh(a, b) NULL
#define Curl_doh_take_result(x, y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_doh_wanted(d) FALSE
#endif /* !CURL_DISABLE_DOH */
#endif /* HEADER_CURL_DOH_H */

View File

@ -1093,8 +1093,8 @@ void curl_easy_reset(CURL *d)
Curl_meta_reset(data);
/* clear any resolve data */
Curl_async_shutdown(data);
Curl_resolv_unlink(data, &data->state.dns[0]);
Curl_resolv_unlink(data, &data->state.dns[1]);
Curl_dns_entry_unlink(data, &data->state.dns[0]);
Curl_dns_entry_unlink(data, &data->state.dns[1]);
/* zero out UserDefined data: */
Curl_freeset(data);
memset(&data->set, 0, sizeof(struct UserDefined));

View File

@ -1387,7 +1387,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
/* cleanup */
if(dns_entry)
Curl_resolv_unlink(data, &dns_entry);
Curl_dns_entry_unlink(data, &dns_entry);
if(result) {
ftp_state(data, ftpc, FTP_STOP);
}

File diff suppressed because it is too large Load Diff

View File

@ -45,6 +45,7 @@ struct connectdata;
struct easy_pollset;
struct Curl_https_rrinfo;
struct Curl_multi;
struct Curl_dns_entry;
enum alpnid {
ALPN_none = 0,
@ -53,53 +54,8 @@ enum alpnid {
ALPN_h3 = CURLALTSVC_H3
};
struct Curl_dns_entry {
struct Curl_addrinfo *addr;
#ifdef USE_HTTPSRR
struct Curl_https_rrinfo *hinfo;
#endif
/* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */
struct curltime timestamp;
/* reference counter, entry is freed on reaching 0 */
size_t refcount;
/* hostname port number that resolved to addr. */
int hostport;
/* hostname that resolved to addr. may be NULL (Unix domain sockets). */
char hostname[1];
};
struct Curl_dnscache {
struct Curl_hash entries;
};
bool Curl_host_is_ipnum(const char *hostname);
/*
* Curl_resolv() returns an entry with the info for the specified host
* and port.
*
* The returned data *MUST* be "released" with Curl_resolv_unlink() after
* use, or we will leak memory!
*/
CURLcode Curl_resolv(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version,
bool allowDOH,
struct Curl_dns_entry **entry);
CURLcode Curl_resolv_blocking(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version,
struct Curl_dns_entry **entry);
CURLcode Curl_resolv_timeout(struct Curl_easy *data,
const char *hostname, int port,
int ip_version,
struct Curl_dns_entry **entry,
timediff_t timeoutms);
#ifdef USE_IPV6
/* probe if it seems to work */
@ -113,87 +69,50 @@ bool Curl_ipv6works(struct Curl_easy *data);
#define Curl_ipv6works(x) FALSE
#endif
/* unlink a dns entry, potentially shared with a cache */
void Curl_resolv_unlink(struct Curl_easy *data,
struct Curl_dns_entry **pdns);
/* init a new dns cache */
void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size);
void Curl_dnscache_destroy(struct Curl_dnscache *dns);
/* prune old entries from the DNS cache */
void Curl_dnscache_prune(struct Curl_easy *data);
/* clear the DNS cache */
void Curl_dnscache_clear(struct Curl_easy *data);
/* IPv4 thread-safe resolve function used for synch and asynch builds */
struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port);
CURLcode Curl_once_resolved(struct Curl_easy *data,
struct Curl_dns_entry *dns,
bool *protocol_done);
struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port);
/*
* Curl_printable_address() returns a printable version of the 1st address
* given in the 'ip' argument. The result will be stored in the buf that is
* bufsize bytes big.
*/
void Curl_printable_address(const struct Curl_addrinfo *ai,
void Curl_printable_address(const struct Curl_addrinfo *ip,
char *buf, size_t bufsize);
/*
* Make a `Curl_dns_entry`.
* Creates a dnscache entry *without* adding it to a dnscache. This allows
* further modifications of the entry *before* then adding it to a cache.
* Curl_resolv() returns an entry with the info for the specified host
* and port.
*
* The entry is created with a reference count of 1.
* Use `Curl_resolv_unlink()` to release your hold on it.
*
* The call takes ownership of `addr`, even in case of failure, and always
* clears `*paddr`. It makes a copy of `hostname`.
*
* Returns entry or NULL on OOM.
*/
struct Curl_dns_entry *
Curl_dnscache_mk_entry(struct Curl_easy *data,
struct Curl_addrinfo **paddr,
const char *hostname,
size_t hostlen, /* length or zero */
int port,
bool permanent);
/*
* Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
*
* Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
*
* The returned data *MUST* be "released" with Curl_resolv_unlink() after
* The returned data *MUST* be "released" with Curl_dns_entry_unlink() after
* use, or we will leak memory!
*/
struct Curl_dns_entry *Curl_dnscache_get(struct Curl_easy *data,
const char *hostname, int port,
int ip_version);
CURLcode Curl_resolv(struct Curl_easy *data,
const char *hostname,
uint16_t port,
uint8_t ip_version,
timediff_t timeoutms,
struct Curl_dns_entry **pdns);
/*
* Curl_dnscache_addr() adds `entry` to the cache, increasing its
* reference count on success.
*/
CURLcode Curl_dnscache_add(struct Curl_easy *data,
struct Curl_dns_entry *entry);
CURLcode Curl_resolv_blocking(struct Curl_easy *data,
const char *hostname,
uint16_t port,
uint8_t ip_version,
struct Curl_dns_entry **pdns);
/*
* Populate the cache with specified entries from CURLOPT_RESOLVE.
*/
CURLcode Curl_loadhostpairs(struct Curl_easy *data);
CURLcode Curl_resolv_timeout(struct Curl_easy *data,
const char *hostname, int port,
int ip_version,
struct Curl_dns_entry **entry,
timediff_t timeoutms);
#ifdef USE_CURL_ASYNC
CURLcode Curl_resolv_check(struct Curl_easy *data,
struct Curl_dns_entry **dns);
CURLcode Curl_resolv_take_result(struct Curl_easy *data,
struct Curl_dns_entry **pdns);
#else
#define Curl_resolv_check(x, y) CURLE_NOT_BUILT_IN
#define Curl_resolv_take_result(x, y) CURLE_NOT_BUILT_IN
#endif
CURLcode Curl_resolv_pollset(struct Curl_easy *data,
struct easy_pollset *ps);
@ -207,8 +126,8 @@ CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail);
*/
struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version);
uint16_t port,
uint8_t ip_version);
#endif

View File

@ -69,8 +69,8 @@
*/
struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version)
uint16_t port,
uint8_t ip_version)
{
struct Curl_addrinfo *ai = NULL;
@ -95,7 +95,7 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
*
*/
struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
int port)
uint16_t port)
{
#if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) && \
defined(HAVE_GETHOSTBYNAME_R_3)

View File

@ -64,8 +64,8 @@
*/
struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version)
uint16_t port,
uint8_t ip_version)
{
struct addrinfo hints;
struct Curl_addrinfo *res;

View File

@ -633,8 +633,8 @@ static void multi_done_locked(struct connectdata *conn,
data->state.done = TRUE; /* called now! */
data->state.recent_conn_id = conn->connection_id;
Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */
Curl_resolv_unlink(data, &data->state.dns[1]);
Curl_dns_entry_unlink(data, &data->state.dns[0]); /* done with this */
Curl_dns_entry_unlink(data, &data->state.dns[1]);
Curl_dnscache_prune(data);
if(multi_conn_should_close(conn, data, (bool)mdctx->premature)) {
@ -2295,12 +2295,13 @@ static CURLMcode state_resolving(struct Curl_multi *multi,
CURLcode *resultp)
{
struct Curl_dns_entry *dns = NULL;
CURLcode result;
CURLMcode mresult = CURLM_OK;
CURLcode result;
result = Curl_resolv_check(data, &dns);
CURL_TRC_DNS(data, "Curl_resolv_check() -> %d, %s",
result = Curl_resolv_take_result(data, &dns);
CURL_TRC_DNS(data, "Curl_resolv_take_result() -> %d, %s",
result, dns ? "found" : "missing");
/* Update sockets here, because the socket(s) may have been closed and the
application thus needs to be told, even if it is likely that the same
socket(s) will again be used further down. If the name has not yet been
@ -2314,23 +2315,21 @@ static CURLMcode state_resolving(struct Curl_multi *multi,
bool connected;
/* Perform the next step in the connection phase, and then move on to the
WAITCONNECT state */
result = Curl_once_resolved(data, dns, &connected);
if(result)
/* if Curl_once_resolved() returns failure, the connection struct is
already freed and gone */
data->conn = NULL; /* no more connection */
else {
/* call again please so that we get the next socket setup */
mresult = CURLM_CALL_MULTI_PERFORM;
if(connected)
multistate(data, MSTATE_PROTOCONNECT);
else {
multistate(data, MSTATE_CONNECTING);
}
result = Curl_setup_conn(data, dns, &connected);
if(result) {
/* setup failed, terminate connection */
struct connectdata *conn = data->conn;
Curl_detach_connection(data);
Curl_conn_terminate(data, conn, TRUE);
goto out;
}
/* call again please so that we get the next socket setup */
mresult = CURLM_CALL_MULTI_PERFORM;
multistate(data, connected ? MSTATE_PROTOCONNECT : MSTATE_CONNECTING);
}
out:
if(result)
/* failure detected */
*stream_errorp = TRUE;

View File

@ -27,7 +27,7 @@
#include "hash.h"
#include "conncache.h"
#include "cshutdn.h"
#include "hostip.h"
#include "dnscache.h"
#include "multi_ev.h"
#include "multi_ntfy.h"
#include "psl.h"

View File

@ -1508,8 +1508,8 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
data->psl = data->multi ? &data->multi->psl : NULL;
#endif
if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) {
Curl_resolv_unlink(data, &data->state.dns[0]);
Curl_resolv_unlink(data, &data->state.dns[1]);
Curl_dns_entry_unlink(data, &data->state.dns[0]);
Curl_dns_entry_unlink(data, &data->state.dns[1]);
}
data->share->dirty--;

View File

@ -98,7 +98,7 @@ struct socks_state {
enum socks_state_t state;
struct bufq iobuf;
const char *hostname;
int remote_port;
uint16_t remote_port;
const char *proxy_user;
const char *proxy_password;
CURLproxycode presult;
@ -324,7 +324,7 @@ static CURLproxycode socks4_resolving(struct socks_state *sx,
DEBUGASSERT(sx->hostname && *sx->hostname);
result = Curl_resolv(data, sx->hostname, sx->remote_port,
cf->conn->ip_version, TRUE, &dns);
cf->conn->ip_version, 0, &dns);
if(result == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "SOCKS4 non-blocking resolve of %s", sx->hostname);
return CURLPX_OK;
@ -334,7 +334,7 @@ static CURLproxycode socks4_resolving(struct socks_state *sx,
}
else {
/* check if we have the name resolved by now */
result = Curl_resolv_check(data, &dns);
result = Curl_resolv_take_result(data, &dns);
if(!result && !dns)
return CURLPX_OK;
}
@ -342,7 +342,7 @@ static CURLproxycode socks4_resolving(struct socks_state *sx,
if(result || !dns) {
failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", sx->hostname);
if(dns)
Curl_resolv_unlink(data, &dns);
Curl_dns_entry_unlink(data, &dns);
return CURLPX_RESOLVE_HOST;
}
@ -368,12 +368,12 @@ static CURLproxycode socks4_resolving(struct socks_state *sx,
(unsigned char *)&saddr_in->sin_addr.s_addr, 4,
&nwritten);
Curl_resolv_unlink(data, &dns); /* not used anymore from now on */
Curl_dns_entry_unlink(data, &dns); /* not used anymore from now on */
if(result || (nwritten != 4))
return CURLPX_SEND_REQUEST;
}
else {
Curl_resolv_unlink(data, &dns);
Curl_dns_entry_unlink(data, &dns);
failf(data, "SOCKS4 connection to %s not supported", sx->hostname);
return CURLPX_RESOLVE_HOST;
}
@ -495,7 +495,7 @@ process_state:
/* SOCKS4 can only do IPv4, insist! */
cf->conn->ip_version = CURL_IPRESOLVE_V4;
CURL_TRC_CF(data, cf, "SOCKS4%s communication to%s %s:%d",
CURL_TRC_CF(data, cf, "SOCKS4%s communication to%s %s:%u",
sx->socks4a ? "a" : "",
cf->conn->bits.httpproxy ? " HTTP proxy" : "",
sx->hostname, sx->remote_port);
@ -826,7 +826,7 @@ static CURLproxycode socks5_req1_init(struct socks_state *sx,
result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten);
if(result || (nwritten != 2))
return CURLPX_SEND_REQUEST;
CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (remotely resolved)",
CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (remotely resolved)",
sx->hostname, sx->remote_port);
return CURLPX_OK;
}
@ -853,7 +853,7 @@ static CURLproxycode socks5_resolving(struct socks_state *sx,
DEBUGASSERT(sx->hostname && *sx->hostname);
result = Curl_resolv(data, sx->hostname, sx->remote_port,
cf->conn->ip_version, TRUE, &dns);
cf->conn->ip_version, 0, &dns);
if(result == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "SOCKS5 non-blocking resolve of %s", sx->hostname);
return CURLPX_OK;
@ -863,7 +863,7 @@ static CURLproxycode socks5_resolving(struct socks_state *sx,
}
else {
/* check if we have the name resolved by now */
result = Curl_resolv_check(data, &dns);
result = Curl_resolv_take_result(data, &dns);
if(!result && !dns)
return CURLPX_OK;
}
@ -899,7 +899,7 @@ static CURLproxycode socks5_resolving(struct socks_state *sx,
destlen = 4;
saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
destination = (const unsigned char *)&saddr_in->sin_addr.s_addr;
CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (locally resolved)",
CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (locally resolved)",
dest, sx->remote_port);
}
#ifdef USE_IPV6
@ -909,7 +909,7 @@ static CURLproxycode socks5_resolving(struct socks_state *sx,
destlen = 16;
saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
destination = (const unsigned char *)&saddr_in6->sin6_addr.s6_addr;
CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%d (locally resolved)",
CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%u (locally resolved)",
dest, sx->remote_port);
}
#endif
@ -942,7 +942,7 @@ static CURLproxycode socks5_resolving(struct socks_state *sx,
out:
if(dns)
Curl_resolv_unlink(data, &dns);
Curl_dns_entry_unlink(data, &dns);
*done = (presult == CURLPX_OK);
return presult;
}
@ -1062,7 +1062,7 @@ process_state:
case SOCKS5_ST_START:
if(cf->conn->bits.httpproxy)
CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %d",
CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %u",
sx->hostname, sx->remote_port);
presult = socks5_req0_init(cf, sx, data);
if(presult)
@ -1252,10 +1252,10 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
sockindex == SECONDARYSOCKET ?
conn->secondaryhostname : conn->host.name;
sx->remote_port =
conn->bits.httpproxy ? (int)conn->http_proxy.port :
conn->bits.httpproxy ? conn->http_proxy.port :
sockindex == SECONDARYSOCKET ? conn->secondary_port :
conn->bits.conn_to_port ? conn->conn_to_port :
conn->remote_port;
(uint16_t)conn->remote_port;
sx->proxy_user = conn->socks_proxy.user;
sx->proxy_password = conn->socks_proxy.passwd;
Curl_bufq_init2(&sx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS,

View File

@ -59,6 +59,7 @@
#include "hostip.h"
#include "cfilters.h"
#include "cw-out.h"
#include "dnscache.h"
#include "transfer.h"
#include "sendf.h"
#include "curl_trc.h"

View File

@ -245,8 +245,8 @@ CURLcode Curl_close(struct Curl_easy **datap)
/* release any resolve information this transfer kept */
Curl_async_destroy(data);
Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */
Curl_resolv_unlink(data, &data->state.dns[1]);
Curl_dns_entry_unlink(data, &data->state.dns[0]); /* done with this */
Curl_dns_entry_unlink(data, &data->state.dns[1]);
data->set.verbose = FALSE; /* no more calls to DEBUGFUNCTION */
data->magic = 0; /* force a clear AFTER the possibly enforced removal from
@ -2821,7 +2821,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
if(port >= 0) {
conn->conn_to_port = (uint16_t)port;
conn->bits.conn_to_port = TRUE;
infof(data, "Connecting to port: %d", port);
infof(data, "Connecting to port: %u", conn->conn_to_port);
}
else {
/* no "connect to port" */
@ -2991,7 +2991,6 @@ static CURLcode resolve_unix(struct Curl_easy *data,
*************************************************************/
static CURLcode resolve_server(struct Curl_easy *data,
struct connectdata *conn,
bool *async,
struct Curl_dns_entry **pdns)
{
struct hostname *ehost;
@ -3035,30 +3034,32 @@ static CURLcode resolve_server(struct Curl_easy *data,
ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
/* If not connecting via a proxy, extract the port from the URL, if it is
* there, thus overriding any defaults that might have been set above. */
eport = conn->bits.conn_to_port ? conn->conn_to_port : conn->remote_port;
eport = conn->bits.conn_to_port ?
conn->conn_to_port : (uint16_t)conn->remote_port;
}
result = Curl_resolv_timeout(data, ehost->name,
eport, conn->ip_version,
pdns, timeout_ms);
result = Curl_resolv(data, ehost->name, eport,
conn->ip_version, timeout_ms, pdns);
DEBUGASSERT(!result || !*pdns);
if(result == CURLE_AGAIN) {
*async = TRUE;
if(!result) { /* resolved right away, either sync or from dnscache */
DEBUGASSERT(*pdns);
return CURLE_OK;
}
else if(result == CURLE_OPERATION_TIMEDOUT) {
else if(result == CURLE_AGAIN) { /* async resolv in progress */
return CURLE_AGAIN;
}
else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */
failf(data, "Failed to resolve %s '%s' with timeout after %"
FMT_TIMEDIFF_T " ms", peertype, ehost->dispname,
curlx_ptimediff_ms(Curl_pgrs_now(data),
&data->progress.t_startsingle));
return CURLE_OPERATION_TIMEDOUT;
}
else if(result) {
else {
DEBUGASSERT(result);
failf(data, "Could not resolve %s: %s", peertype, ehost->dispname);
return result;
}
DEBUGASSERT(*pdns);
return CURLE_OK;
}
static void url_move_hostname(struct hostname *dest, struct hostname *src)
@ -3594,14 +3595,17 @@ CURLcode Curl_connect(struct Curl_easy *data,
* Resolve the address of the server or proxy
*************************************************************/
struct Curl_dns_entry *dns;
result = resolve_server(data, conn, asyncp, &dns);
result = resolve_server(data, conn, &dns);
if(!result) {
*asyncp = !dns;
if(dns)
/* DNS resolution is done: that is either because this is a reused
connection, in which case DNS was unnecessary, or because DNS
really did finish already (synch resolver/fast async resolve) */
result = Curl_setup_conn(data, dns, protocol_done);
DEBUGASSERT(dns);
/* DNS resolution is done: that is either because this is a reused
connection, in which case DNS was unnecessary, or because DNS
really did finish already (synch resolver/fast async resolve) */
result = Curl_setup_conn(data, dns, protocol_done);
}
else if(result == CURLE_AGAIN) {
*asyncp = TRUE;
result = CURLE_OK;
}
}
}

View File

@ -719,7 +719,8 @@ struct UrlState {
struct Curl_dns_entry *dns[2]; /* DNS to connect FIRST/SECONDARY */
#ifdef USE_CURL_ASYNC
struct Curl_async async; /* asynchronous name resolver data */
struct Curl_resolv_async *async; /* asynchronous name resolver data */
uint32_t next_async_id; /* id of the next async resolve operation */
#endif
#ifdef USE_OPENSSL

View File

@ -3469,7 +3469,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx,
size_t ech_config_len = 0;
char *outername = data->set.str[STRING_ECH_PUBLIC];
int trying_ech_now = 0;
CURLcode result;
CURLcode result = CURLE_OK;
if(!CURLECH_ENABLED(data))
return CURLE_OK;
@ -3528,9 +3528,14 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx,
struct Curl_dns_entry *dns = NULL;
if(peer->hostname)
dns = Curl_dnscache_get(data, peer->hostname, peer->port,
cf->conn->ip_version);
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;
@ -3559,7 +3564,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx,
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
Curl_resolv_unlink(data, &dns);
Curl_dns_entry_unlink(data, &dns);
}
}
#ifdef HAVE_BORINGSSL_LIKE

View File

@ -964,10 +964,16 @@ init_config_builder_ech(struct Curl_easy *data,
}
else {
if(connssl->peer.hostname) {
dns = Curl_dnscache_get(data, connssl->peer.hostname,
connssl->peer.port, data->conn->ip_version);
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;
@ -997,7 +1003,7 @@ cleanup:
curlx_free(ech_config);
}
if(dns) {
Curl_resolv_unlink(data, &dns);
Curl_dns_entry_unlink(data, &dns);
}
return result;
}

View File

@ -1248,7 +1248,7 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
{
ehostname = cf->conn->host.name;
edispname = cf->conn->host.dispname;
peer->port = cf->conn->remote_port;
peer->port = (uint16_t)cf->conn->remote_port;
}
/* hostname MUST exist and not be empty */

View File

@ -1382,9 +1382,15 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
struct ssl_connect_data *connssl = cf->ctx;
struct Curl_dns_entry *dns = NULL;
dns = Curl_dnscache_get(data, connssl->peer.hostname, connssl->peer.port,
cf->conn->ip_version);
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);
goto out;
}
infof(data, "ECH: requested but no DNS info available");
if(data->set.tls_ech & CURLECH_HARD) {
result = CURLE_SSL_CONNECT_ERROR;
@ -1420,7 +1426,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
goto out;
}
}
Curl_resolv_unlink(data, &dns);
Curl_dns_entry_unlink(data, &dns);
}
}

View File

@ -288,6 +288,7 @@ static void usage_hx_download(const char *msg)
" -r <host>:<port>:<addr> resolve information\n"
" -T number max concurrent connections total\n"
" -V http_version (http/1.1, h2, h3) http version to use\n"
" -6 use ipv6 for resolving the FIRST url\n"
);
}
@ -315,11 +316,12 @@ static CURLcode test_cli_hx_download(const char *URL)
size_t max_total_conns = 0;
int fresh_connect = 0;
char *cafile = NULL;
bool first_ipv6 = FALSE;
CURLcode result = CURLE_OK;
(void)URL;
while((ch = cgetopt(test_argc, test_argv, "aefhm:n:xA:C:F:M:P:r:T:V:"))
while((ch = cgetopt(test_argc, test_argv, "aefhm:n:xA:C:F:M:P:r:T:V:6"))
!= -1) {
const char *opt = coptarg;
curl_off_t num;
@ -390,6 +392,9 @@ static CURLcode test_cli_hx_download(const char *URL)
}
break;
}
case '6':
first_ipv6 = TRUE;
break;
default:
usage_hx_download("invalid option");
result = (CURLcode)1;
@ -466,6 +471,8 @@ static CURLcode test_cli_hx_download(const char *URL)
result = (CURLcode)1;
goto cleanup;
}
if(!i && first_ipv6)
curl_easy_setopt(t->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
curl_multi_add_handle(multi, t->curl);
t->started = 1;
++active_transfers;

View File

@ -43,6 +43,10 @@ extern const struct entry_s s_entries[];
extern int unitfail; /* for unittests */
#ifdef UNITTESTS
#include "unitprotos.h"
#endif
#include "curlx/base64.h" /* for curlx_base64* */
#include "curlx/dynbuf.h" /* for curlx_dyn_*() */
#include "curlx/fopen.h" /* for curlx_f*() */

View File

@ -35,7 +35,7 @@
#include "curl_addrinfo.h"
#include "hash.h"
#include "hostip.h"
#include "dnscache.h"
static struct Curl_dnscache hp;
static char *data_key;