mirror of
https://github.com/curl/curl.git
synced 2026-04-11 12:01:42 +08:00
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:
parent
b0f6e9a3d7
commit
96d5b5c688
@ -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).
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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;
|
||||
|
||||
189
lib/asyn-ares.c
189
lib/asyn-ares.c
@ -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;
|
||||
|
||||
@ -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 */
|
||||
|
||||
176
lib/asyn-thrdd.c
176
lib/asyn-thrdd.c
@ -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");
|
||||
|
||||
61
lib/asyn.h
61
lib/asyn.h
@ -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];
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
832
lib/dnscache.c
Normal 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
126
lib/dnscache.h
Normal 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
217
lib/doh.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
23
lib/doh.h
23
lib/doh.h
@ -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 */
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
1063
lib/hostip.c
1063
lib/hostip.c
File diff suppressed because it is too large
Load Diff
137
lib/hostip.h
137
lib/hostip.h
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
37
lib/multi.c
37
lib/multi.c
@ -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;
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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--;
|
||||
|
||||
32
lib/socks.c
32
lib/socks.c
@ -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,
|
||||
|
||||
@ -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"
|
||||
|
||||
46
lib/url.c
46
lib/url.c
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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*() */
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user