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

@ -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");