mirror of
https://github.com/curl/curl.git
synced 2025-09-12 15:12:42 +03:00
url: dns_entry related improvements
Replace Curl_resolv_unlock() with Curl_resolv_unlink(): -replace inuse member with refcount in Curl_dns_entry - pass Curl_dns_entry ** to unlink, so it gets always cleared - solve potential (but unlikley) UAF in FTP's handling of looked up Curl_dns_entry. Esp. do not use addr information after unlinking an entry. In reality, the unlink will not free memory, as the dns entry is still referenced by the hostcache. But this is not safe and relying on no other code pruning the cache in the meantime. - pass permanent flag when adding a dns entry instead of fixing timestamp afterwards. url.c: fold several static *resolve_* functions into one. Closes #14195
This commit is contained in:
parent
2372a5915c
commit
5a9262a333
|
@ -679,12 +679,13 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
||||||
conn->ip_version = ipver;
|
conn->ip_version = ipver;
|
||||||
|
|
||||||
if(h) {
|
if(h) {
|
||||||
|
int h_af = h->addr->ai_family;
|
||||||
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
|
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
|
||||||
Curl_printable_address(h->addr, myhost, sizeof(myhost));
|
Curl_printable_address(h->addr, myhost, sizeof(myhost));
|
||||||
infof(data, "Name '%s' family %i resolved to '%s' family %i",
|
infof(data, "Name '%s' family %i resolved to '%s' family %i",
|
||||||
host, af, myhost, h->addr->ai_family);
|
host, af, myhost, h_af);
|
||||||
Curl_resolv_unlock(data, h);
|
Curl_resolv_unlink(data, &h); /* this will NULL, potential free h */
|
||||||
if(af != h->addr->ai_family) {
|
if(af != h_af) {
|
||||||
/* bad IP version combo, signal the caller to try another address
|
/* bad IP version combo, signal the caller to try another address
|
||||||
family if available */
|
family if available */
|
||||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||||
|
|
|
@ -772,10 +772,8 @@ static void connc_run_conn_shutdown_handler(struct Curl_easy *data,
|
||||||
struct connectdata *conn)
|
struct connectdata *conn)
|
||||||
{
|
{
|
||||||
if(!conn->bits.shutdown_handler) {
|
if(!conn->bits.shutdown_handler) {
|
||||||
if(conn->dns_entry) {
|
if(conn->dns_entry)
|
||||||
Curl_resolv_unlock(data, conn->dns_entry);
|
Curl_resolv_unlink(data, &conn->dns_entry);
|
||||||
conn->dns_entry = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleanup NTLM connection-related data */
|
/* Cleanup NTLM connection-related data */
|
||||||
Curl_http_auth_cleanup_ntlm(conn);
|
Curl_http_auth_cleanup_ntlm(conn);
|
||||||
|
@ -1178,7 +1176,7 @@ void Curl_conncache_print(struct conncache *connc)
|
||||||
while(curr) {
|
while(curr) {
|
||||||
conn = curr->ptr;
|
conn = curr->ptr;
|
||||||
|
|
||||||
fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
|
fprintf(stderr, " [%p %d]", (void *)conn, conn->refcount);
|
||||||
curr = curr->next;
|
curr = curr->next;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
|
|
|
@ -1360,7 +1360,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
|
||||||
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
|
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
|
||||||
|
|
||||||
/* we got a response, store it in the cache */
|
/* we got a response, store it in the cache */
|
||||||
dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port);
|
dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port, FALSE);
|
||||||
|
|
||||||
if(data->share)
|
if(data->share)
|
||||||
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
||||||
|
|
22
lib/ftp.c
22
lib/ftp.c
|
@ -1042,7 +1042,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
|
||||||
int error;
|
int error;
|
||||||
char *host = NULL;
|
char *host = NULL;
|
||||||
char *string_ftpport = data->set.str[STRING_FTPPORT];
|
char *string_ftpport = data->set.str[STRING_FTPPORT];
|
||||||
struct Curl_dns_entry *h = NULL;
|
struct Curl_dns_entry *dns_entry = NULL;
|
||||||
unsigned short port_min = 0;
|
unsigned short port_min = 0;
|
||||||
unsigned short port_max = 0;
|
unsigned short port_max = 0;
|
||||||
unsigned short port;
|
unsigned short port;
|
||||||
|
@ -1178,15 +1178,12 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* resolv ip/host to ip */
|
/* resolv ip/host to ip */
|
||||||
rc = Curl_resolv(data, host, 0, FALSE, &h);
|
rc = Curl_resolv(data, host, 0, FALSE, &dns_entry);
|
||||||
if(rc == CURLRESOLV_PENDING)
|
if(rc == CURLRESOLV_PENDING)
|
||||||
(void)Curl_resolver_wait_resolv(data, &h);
|
(void)Curl_resolver_wait_resolv(data, &dns_entry);
|
||||||
if(h) {
|
if(dns_entry) {
|
||||||
res = h->addr;
|
res = dns_entry->addr;
|
||||||
/* when we return from this function, we can forget about this entry
|
}
|
||||||
to we can unlock it now already */
|
|
||||||
Curl_resolv_unlock(data, h);
|
|
||||||
} /* (h) */
|
|
||||||
else
|
else
|
||||||
res = NULL; /* failure! */
|
res = NULL; /* failure! */
|
||||||
|
|
||||||
|
@ -1381,6 +1378,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
|
||||||
ftp_state(data, FTP_PORT);
|
ftp_state(data, FTP_PORT);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
/* If we looked up a dns_entry, now is the time to safely release it */
|
||||||
|
if(dns_entry)
|
||||||
|
Curl_resolv_unlink(data, &dns_entry);
|
||||||
if(result) {
|
if(result) {
|
||||||
ftp_state(data, FTP_STOP);
|
ftp_state(data, FTP_STOP);
|
||||||
}
|
}
|
||||||
|
@ -2098,7 +2098,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
|
||||||
CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
|
CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
|
||||||
|
|
||||||
if(result) {
|
if(result) {
|
||||||
Curl_resolv_unlock(data, addr); /* we are done using this address */
|
Curl_resolv_unlink(data, &addr); /* we are done using this address */
|
||||||
if(ftpc->count1 == 0 && ftpcode == 229)
|
if(ftpc->count1 == 0 && ftpcode == 229)
|
||||||
return ftp_epsv_disable(data, conn);
|
return ftp_epsv_disable(data, conn);
|
||||||
|
|
||||||
|
@ -2116,7 +2116,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
|
||||||
/* this just dumps information about this second connection */
|
/* this just dumps information about this second connection */
|
||||||
ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport);
|
ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport);
|
||||||
|
|
||||||
Curl_resolv_unlock(data, addr); /* we are done using this address */
|
Curl_resolv_unlink(data, &addr); /* we are done using this address */
|
||||||
|
|
||||||
Curl_safefree(conn->secondaryhostname);
|
Curl_safefree(conn->secondaryhostname);
|
||||||
conn->secondary_port = ftpc->newport;
|
conn->secondary_port = ftpc->newport;
|
||||||
|
|
|
@ -79,7 +79,7 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
|
||||||
|
|
||||||
dns = Curl_cache_addr(data, ai,
|
dns = Curl_cache_addr(data, ai,
|
||||||
data->state.async.hostname, 0,
|
data->state.async.hostname, 0,
|
||||||
data->state.async.port);
|
data->state.async.port, FALSE);
|
||||||
if(data->share)
|
if(data->share)
|
||||||
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
||||||
|
|
||||||
|
|
85
lib/hostip.c
85
lib/hostip.c
|
@ -115,7 +115,7 @@
|
||||||
* CURLRES_* defines based on the config*.h and curl_setup.h defines.
|
* CURLRES_* defines based on the config*.h and curl_setup.h defines.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void freednsentry(void *freethis);
|
static void hostcache_unlink_entry(void *entry);
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||||
static void show_resolve_info(struct Curl_easy *data,
|
static void show_resolve_info(struct Curl_easy *data,
|
||||||
|
@ -178,7 +178,7 @@ create_hostcache_id(const char *name,
|
||||||
struct hostcache_prune_data {
|
struct hostcache_prune_data {
|
||||||
time_t now;
|
time_t now;
|
||||||
time_t oldest; /* oldest time in cache not pruned. */
|
time_t oldest; /* oldest time in cache not pruned. */
|
||||||
int cache_timeout;
|
int max_age_sec;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -189,16 +189,16 @@ struct hostcache_prune_data {
|
||||||
* cache.
|
* cache.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
hostcache_timestamp_remove(void *datap, void *hc)
|
hostcache_entry_is_stale(void *datap, void *hc)
|
||||||
{
|
{
|
||||||
struct hostcache_prune_data *prune =
|
struct hostcache_prune_data *prune =
|
||||||
(struct hostcache_prune_data *) datap;
|
(struct hostcache_prune_data *) datap;
|
||||||
struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
|
struct Curl_dns_entry *dns = (struct Curl_dns_entry *) hc;
|
||||||
|
|
||||||
if(c->timestamp) {
|
if(dns->timestamp) {
|
||||||
/* age in seconds */
|
/* age in seconds */
|
||||||
time_t age = prune->now - c->timestamp;
|
time_t age = prune->now - dns->timestamp;
|
||||||
if(age >= prune->cache_timeout)
|
if(age >= prune->max_age_sec)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
if(age > prune->oldest)
|
if(age > prune->oldest)
|
||||||
prune->oldest = age;
|
prune->oldest = age;
|
||||||
|
@ -216,13 +216,13 @@ hostcache_prune(struct Curl_hash *hostcache, int cache_timeout,
|
||||||
{
|
{
|
||||||
struct hostcache_prune_data user;
|
struct hostcache_prune_data user;
|
||||||
|
|
||||||
user.cache_timeout = cache_timeout;
|
user.max_age_sec = cache_timeout;
|
||||||
user.now = now;
|
user.now = now;
|
||||||
user.oldest = 0;
|
user.oldest = 0;
|
||||||
|
|
||||||
Curl_hash_clean_with_criterium(hostcache,
|
Curl_hash_clean_with_criterium(hostcache,
|
||||||
(void *) &user,
|
(void *) &user,
|
||||||
hostcache_timestamp_remove);
|
hostcache_entry_is_stale);
|
||||||
|
|
||||||
return user.oldest;
|
return user.oldest;
|
||||||
}
|
}
|
||||||
|
@ -299,10 +299,10 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
|
||||||
struct hostcache_prune_data user;
|
struct hostcache_prune_data user;
|
||||||
|
|
||||||
user.now = time(NULL);
|
user.now = time(NULL);
|
||||||
user.cache_timeout = data->set.dns_cache_timeout;
|
user.max_age_sec = data->set.dns_cache_timeout;
|
||||||
user.oldest = 0;
|
user.oldest = 0;
|
||||||
|
|
||||||
if(hostcache_timestamp_remove(&user, dns)) {
|
if(hostcache_entry_is_stale(&user, dns)) {
|
||||||
infof(data, "Hostname in DNS cache was stale, zapped");
|
infof(data, "Hostname in DNS cache was stale, zapped");
|
||||||
dns = NULL; /* the memory deallocation is being handled by the hash */
|
dns = NULL; /* the memory deallocation is being handled by the hash */
|
||||||
Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
|
Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
|
||||||
|
@ -348,7 +348,7 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
|
||||||
*
|
*
|
||||||
* Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
|
* Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
|
||||||
*
|
*
|
||||||
* The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
|
* The returned data *MUST* be "released" with Curl_resolv_unlink() after
|
||||||
* use, or we will leak memory!
|
* use, or we will leak memory!
|
||||||
*/
|
*/
|
||||||
struct Curl_dns_entry *
|
struct Curl_dns_entry *
|
||||||
|
@ -364,7 +364,7 @@ Curl_fetch_addr(struct Curl_easy *data,
|
||||||
dns = fetch_addr(data, hostname, port);
|
dns = fetch_addr(data, hostname, port);
|
||||||
|
|
||||||
if(dns)
|
if(dns)
|
||||||
dns->inuse++; /* we use it! */
|
dns->refcount++; /* we use it! */
|
||||||
|
|
||||||
if(data->share)
|
if(data->share)
|
||||||
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
||||||
|
@ -468,7 +468,8 @@ Curl_cache_addr(struct Curl_easy *data,
|
||||||
struct Curl_addrinfo *addr,
|
struct Curl_addrinfo *addr,
|
||||||
const char *hostname,
|
const char *hostname,
|
||||||
size_t hostlen, /* length or zero */
|
size_t hostlen, /* length or zero */
|
||||||
int port)
|
int port,
|
||||||
|
bool permanent)
|
||||||
{
|
{
|
||||||
char entry_id[MAX_HOSTCACHE_LEN];
|
char entry_id[MAX_HOSTCACHE_LEN];
|
||||||
size_t entry_len;
|
size_t entry_len;
|
||||||
|
@ -496,11 +497,15 @@ Curl_cache_addr(struct Curl_easy *data,
|
||||||
entry_len = create_hostcache_id(hostname, hostlen, port,
|
entry_len = create_hostcache_id(hostname, hostlen, port,
|
||||||
entry_id, sizeof(entry_id));
|
entry_id, sizeof(entry_id));
|
||||||
|
|
||||||
dns->inuse = 1; /* the cache has the first reference */
|
dns->refcount = 1; /* the cache has the first reference */
|
||||||
dns->addr = addr; /* this is the address(es) */
|
dns->addr = addr; /* this is the address(es) */
|
||||||
time(&dns->timestamp);
|
if(permanent)
|
||||||
if(dns->timestamp == 0)
|
dns->timestamp = 0; /* an entry that never goes stale */
|
||||||
dns->timestamp = 1; /* zero indicates permanent CURLOPT_RESOLVE entry */
|
else {
|
||||||
|
dns->timestamp = time(NULL);
|
||||||
|
if(dns->timestamp == 0)
|
||||||
|
dns->timestamp = 1;
|
||||||
|
}
|
||||||
dns->hostport = port;
|
dns->hostport = port;
|
||||||
if(hostlen)
|
if(hostlen)
|
||||||
memcpy(dns->hostname, hostname, hostlen);
|
memcpy(dns->hostname, hostname, hostlen);
|
||||||
|
@ -514,7 +519,7 @@ Curl_cache_addr(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
|
|
||||||
dns = dns2;
|
dns = dns2;
|
||||||
dns->inuse++; /* mark entry as in-use */
|
dns->refcount++; /* mark entry as in-use */
|
||||||
return dns;
|
return dns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -666,8 +671,8 @@ static bool tailmatch(const char *full, const char *part)
|
||||||
* resolves. See the return codes.
|
* resolves. See the return codes.
|
||||||
*
|
*
|
||||||
* The cache entry we return will get its 'inuse' counter increased when this
|
* The cache entry we return will get its 'inuse' counter increased when this
|
||||||
* function is used. You MUST call Curl_resolv_unlock() later (when you are
|
* function is used. You MUST call Curl_resolv_unlink() later (when you are
|
||||||
* done using this struct) to decrease the counter again.
|
* done using this struct) to decrease the reference counter again.
|
||||||
*
|
*
|
||||||
* Return codes:
|
* Return codes:
|
||||||
*
|
*
|
||||||
|
@ -708,7 +713,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
|
||||||
|
|
||||||
if(dns) {
|
if(dns) {
|
||||||
infof(data, "Hostname %s was found in DNS cache", hostname);
|
infof(data, "Hostname %s was found in DNS cache", hostname);
|
||||||
dns->inuse++; /* we use it! */
|
dns->refcount++; /* we use it! */
|
||||||
rc = CURLRESOLV_RESOLVED;
|
rc = CURLRESOLV_RESOLVED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -828,7 +833,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
|
||||||
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
|
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
|
||||||
|
|
||||||
/* we got a response, store it in the cache */
|
/* we got a response, store it in the cache */
|
||||||
dns = Curl_cache_addr(data, addr, hostname, 0, port);
|
dns = Curl_cache_addr(data, addr, hostname, 0, port, FALSE);
|
||||||
|
|
||||||
if(data->share)
|
if(data->share)
|
||||||
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
||||||
|
@ -868,8 +873,8 @@ void alarmfunc(int sig)
|
||||||
* resolves. See the return codes.
|
* resolves. See the return codes.
|
||||||
*
|
*
|
||||||
* The cache entry we return will get its 'inuse' counter increased when this
|
* The cache entry we return will get its 'inuse' counter increased when this
|
||||||
* function is used. You MUST call Curl_resolv_unlock() later (when you are
|
* function is used. You MUST call Curl_resolv_unlink() later (when you are
|
||||||
* done using this struct) to decrease the counter again.
|
* done using this struct) to decrease the reference counter again.
|
||||||
*
|
*
|
||||||
* If built with a synchronous resolver and use of signals is not
|
* If built with a synchronous resolver and use of signals is not
|
||||||
* disabled by the application, then a nonzero timeout will cause a
|
* disabled by the application, then a nonzero timeout will cause a
|
||||||
|
@ -1037,18 +1042,20 @@ clean_up:
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
|
* Curl_resolv_unlink() releases a reference to the given cached DNS entry.
|
||||||
* made, the struct may be destroyed due to pruning. It is important that only
|
* When the reference count reaches 0, the entry is destroyed. It is important
|
||||||
* one unlock is made for each Curl_resolv() call.
|
* that only one unlink is made for each Curl_resolv() call.
|
||||||
*
|
*
|
||||||
* May be called with 'data' == NULL for global cache.
|
* May be called with 'data' == NULL for global cache.
|
||||||
*/
|
*/
|
||||||
void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
|
void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns)
|
||||||
{
|
{
|
||||||
|
struct Curl_dns_entry *dns = *pdns;
|
||||||
|
*pdns = NULL;
|
||||||
if(data && data->share)
|
if(data && data->share)
|
||||||
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
|
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
|
||||||
|
|
||||||
freednsentry(dns);
|
hostcache_unlink_entry(dns);
|
||||||
|
|
||||||
if(data && data->share)
|
if(data && data->share)
|
||||||
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
|
||||||
|
@ -1057,13 +1064,13 @@ void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
|
||||||
/*
|
/*
|
||||||
* File-internal: release cache dns entry reference, free if inuse drops to 0
|
* File-internal: release cache dns entry reference, free if inuse drops to 0
|
||||||
*/
|
*/
|
||||||
static void freednsentry(void *freethis)
|
static void hostcache_unlink_entry(void *entry)
|
||||||
{
|
{
|
||||||
struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
|
struct Curl_dns_entry *dns = (struct Curl_dns_entry *) entry;
|
||||||
DEBUGASSERT(dns && (dns->inuse>0));
|
DEBUGASSERT(dns && (dns->refcount>0));
|
||||||
|
|
||||||
dns->inuse--;
|
dns->refcount--;
|
||||||
if(dns->inuse == 0) {
|
if(dns->refcount == 0) {
|
||||||
Curl_freeaddrinfo(dns->addr);
|
Curl_freeaddrinfo(dns->addr);
|
||||||
#ifdef USE_HTTPSRR
|
#ifdef USE_HTTPSRR
|
||||||
if(dns->hinfo) {
|
if(dns->hinfo) {
|
||||||
|
@ -1092,7 +1099,7 @@ static void freednsentry(void *freethis)
|
||||||
void Curl_init_dnscache(struct Curl_hash *hash, size_t size)
|
void Curl_init_dnscache(struct Curl_hash *hash, size_t size)
|
||||||
{
|
{
|
||||||
Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
|
Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
|
||||||
freednsentry);
|
hostcache_unlink_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1285,13 +1292,11 @@ err:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* put this new host in the cache */
|
/* put this new host in the cache */
|
||||||
dns = Curl_cache_addr(data, head, host_begin, hlen, port);
|
dns = Curl_cache_addr(data, head, host_begin, hlen, port, permanent);
|
||||||
if(dns) {
|
if(dns) {
|
||||||
if(permanent)
|
|
||||||
dns->timestamp = 0; /* mark as permanent */
|
|
||||||
/* release the returned reference; the cache itself will keep the
|
/* release the returned reference; the cache itself will keep the
|
||||||
* entry alive: */
|
* entry alive: */
|
||||||
dns->inuse--;
|
dns->refcount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data->share)
|
if(data->share)
|
||||||
|
|
19
lib/hostip.h
19
lib/hostip.h
|
@ -99,8 +99,8 @@ struct Curl_dns_entry {
|
||||||
#endif
|
#endif
|
||||||
/* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */
|
/* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */
|
||||||
time_t timestamp;
|
time_t timestamp;
|
||||||
/* use-counter, use Curl_resolv_unlock to release reference */
|
/* reference counter, entry is freed on reaching 0 */
|
||||||
long inuse;
|
size_t refcount;
|
||||||
/* hostname port number that resolved to addr. */
|
/* hostname port number that resolved to addr. */
|
||||||
int hostport;
|
int hostport;
|
||||||
/* hostname that resolved to addr. may be NULL (unix domain sockets). */
|
/* hostname that resolved to addr. may be NULL (unix domain sockets). */
|
||||||
|
@ -113,7 +113,7 @@ bool Curl_host_is_ipnum(const char *hostname);
|
||||||
* Curl_resolv() returns an entry with the info for the specified host
|
* Curl_resolv() returns an entry with the info for the specified host
|
||||||
* and port.
|
* and port.
|
||||||
*
|
*
|
||||||
* The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
|
* The returned data *MUST* be "released" with Curl_resolv_unlink() after
|
||||||
* use, or we will leak memory!
|
* use, or we will leak memory!
|
||||||
*/
|
*/
|
||||||
/* return codes */
|
/* return codes */
|
||||||
|
@ -161,9 +161,9 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
|
||||||
int *waitp);
|
int *waitp);
|
||||||
|
|
||||||
|
|
||||||
/* unlock a previously resolved dns entry */
|
/* unlink a dns entry, potentially shared with a cache */
|
||||||
void Curl_resolv_unlock(struct Curl_easy *data,
|
void Curl_resolv_unlink(struct Curl_easy *data,
|
||||||
struct Curl_dns_entry *dns);
|
struct Curl_dns_entry **pdns);
|
||||||
|
|
||||||
/* init a new dns cache */
|
/* init a new dns cache */
|
||||||
void Curl_init_dnscache(struct Curl_hash *hash, size_t hashsize);
|
void Curl_init_dnscache(struct Curl_hash *hash, size_t hashsize);
|
||||||
|
@ -199,7 +199,7 @@ void Curl_printable_address(const struct Curl_addrinfo *ip,
|
||||||
*
|
*
|
||||||
* Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
|
* Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
|
||||||
*
|
*
|
||||||
* The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
|
* The returned data *MUST* be "released" with Curl_resolv_unlink() after
|
||||||
* use, or we will leak memory!
|
* use, or we will leak memory!
|
||||||
*/
|
*/
|
||||||
struct Curl_dns_entry *
|
struct Curl_dns_entry *
|
||||||
|
@ -209,12 +209,13 @@ Curl_fetch_addr(struct Curl_easy *data,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
|
* Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
|
||||||
*
|
* @param permanent iff TRUE, entry will never become stale
|
||||||
* Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
|
* Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
|
||||||
*/
|
*/
|
||||||
struct Curl_dns_entry *
|
struct Curl_dns_entry *
|
||||||
Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr,
|
Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr,
|
||||||
const char *hostname, size_t hostlen, int port);
|
const char *hostname, size_t hostlen, int port,
|
||||||
|
bool permanent);
|
||||||
|
|
||||||
#ifndef INADDR_NONE
|
#ifndef INADDR_NONE
|
||||||
#define CURL_INADDR_NONE (in_addr_t) ~0
|
#define CURL_INADDR_NONE (in_addr_t) ~0
|
||||||
|
|
|
@ -742,10 +742,8 @@ static CURLcode multi_done(struct Curl_easy *data,
|
||||||
|
|
||||||
data->state.done = TRUE; /* called just now! */
|
data->state.done = TRUE; /* called just now! */
|
||||||
|
|
||||||
if(conn->dns_entry) {
|
if(conn->dns_entry)
|
||||||
Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
|
Curl_resolv_unlink(data, &conn->dns_entry); /* done with this */
|
||||||
conn->dns_entry = NULL;
|
|
||||||
}
|
|
||||||
Curl_hostcache_prune(data);
|
Curl_hostcache_prune(data);
|
||||||
|
|
||||||
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
|
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
|
||||||
|
|
|
@ -388,7 +388,7 @@ CONNECT_RESOLVED:
|
||||||
|
|
||||||
infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
|
infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
|
||||||
|
|
||||||
Curl_resolv_unlock(data, dns); /* not used anymore from now on */
|
Curl_resolv_unlink(data, &dns); /* not used anymore from now on */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
failf(data, "SOCKS4 connection to %s not supported", sx->hostname);
|
failf(data, "SOCKS4 connection to %s not supported", sx->hostname);
|
||||||
|
@ -893,7 +893,7 @@ CONNECT_RESOLVED:
|
||||||
failf(data, "SOCKS5 connection to %s not supported", dest);
|
failf(data, "SOCKS5 connection to %s not supported", dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
Curl_resolv_unlock(data, dns); /* not used anymore from now on */
|
Curl_resolv_unlink(data, &dns); /* not used anymore from now on */
|
||||||
goto CONNECT_REQ_SEND;
|
goto CONNECT_REQ_SEND;
|
||||||
}
|
}
|
||||||
CONNECT_RESOLVE_REMOTE:
|
CONNECT_RESOLVE_REMOTE:
|
||||||
|
|
202
lib/url.c
202
lib/url.c
|
@ -637,10 +637,8 @@ void Curl_disconnect(struct Curl_easy *data,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(conn->dns_entry) {
|
if(conn->dns_entry)
|
||||||
Curl_resolv_unlock(data, conn->dns_entry);
|
Curl_resolv_unlink(data, &conn->dns_entry);
|
||||||
conn->dns_entry = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleanup NTLM connection-related data */
|
/* Cleanup NTLM connection-related data */
|
||||||
Curl_http_auth_cleanup_ntlm(conn);
|
Curl_http_auth_cleanup_ntlm(conn);
|
||||||
|
@ -3101,118 +3099,12 @@ static CURLcode resolve_unix(struct Curl_easy *data,
|
||||||
return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY;
|
return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
hostaddr->inuse++;
|
hostaddr->refcount = 1; /* connection is the only one holding this */
|
||||||
conn->dns_entry = hostaddr;
|
conn->dns_entry = hostaddr;
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_PROXY
|
|
||||||
static CURLcode resolve_proxy(struct Curl_easy *data,
|
|
||||||
struct connectdata *conn,
|
|
||||||
bool *async)
|
|
||||||
{
|
|
||||||
struct Curl_dns_entry *hostaddr = NULL;
|
|
||||||
struct hostname *host;
|
|
||||||
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
DEBUGASSERT(conn->dns_entry == NULL);
|
|
||||||
|
|
||||||
host = conn->bits.socksproxy ? &conn->socks_proxy.host :
|
|
||||||
&conn->http_proxy.host;
|
|
||||||
|
|
||||||
conn->hostname_resolve = strdup(host->name);
|
|
||||||
if(!conn->hostname_resolve)
|
|
||||||
return CURLE_OUT_OF_MEMORY;
|
|
||||||
|
|
||||||
rc = Curl_resolv_timeout(data, conn->hostname_resolve,
|
|
||||||
conn->primary.remote_port, &hostaddr, timeout_ms);
|
|
||||||
conn->dns_entry = hostaddr;
|
|
||||||
if(rc == CURLRESOLV_PENDING)
|
|
||||||
*async = TRUE;
|
|
||||||
else if(rc == CURLRESOLV_TIMEDOUT)
|
|
||||||
return CURLE_OPERATION_TIMEDOUT;
|
|
||||||
else if(!hostaddr) {
|
|
||||||
failf(data, "Couldn't resolve proxy '%s'", host->dispname);
|
|
||||||
return CURLE_COULDNT_RESOLVE_PROXY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CURLE_OK;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static CURLcode resolve_host(struct Curl_easy *data,
|
|
||||||
struct connectdata *conn,
|
|
||||||
bool *async)
|
|
||||||
{
|
|
||||||
struct Curl_dns_entry *hostaddr = NULL;
|
|
||||||
struct hostname *connhost;
|
|
||||||
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
DEBUGASSERT(conn->dns_entry == NULL);
|
|
||||||
|
|
||||||
connhost = 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. */
|
|
||||||
conn->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port :
|
|
||||||
conn->remote_port;
|
|
||||||
|
|
||||||
/* Resolve target host right on */
|
|
||||||
conn->hostname_resolve = strdup(connhost->name);
|
|
||||||
if(!conn->hostname_resolve)
|
|
||||||
return CURLE_OUT_OF_MEMORY;
|
|
||||||
|
|
||||||
rc = Curl_resolv_timeout(data, conn->hostname_resolve,
|
|
||||||
conn->primary.remote_port, &hostaddr, timeout_ms);
|
|
||||||
conn->dns_entry = hostaddr;
|
|
||||||
if(rc == CURLRESOLV_PENDING)
|
|
||||||
*async = TRUE;
|
|
||||||
else if(rc == CURLRESOLV_TIMEDOUT) {
|
|
||||||
failf(data, "Failed to resolve host '%s' with timeout after %"
|
|
||||||
CURL_FORMAT_TIMEDIFF_T " ms", connhost->dispname,
|
|
||||||
Curl_timediff(Curl_now(), data->progress.t_startsingle));
|
|
||||||
return CURLE_OPERATION_TIMEDOUT;
|
|
||||||
}
|
|
||||||
else if(!hostaddr) {
|
|
||||||
failf(data, "Could not resolve host: %s", connhost->dispname);
|
|
||||||
return CURLE_COULDNT_RESOLVE_HOST;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CURLE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform a fresh resolve */
|
|
||||||
static CURLcode resolve_fresh(struct Curl_easy *data,
|
|
||||||
struct connectdata *conn,
|
|
||||||
bool *async)
|
|
||||||
{
|
|
||||||
#ifdef USE_UNIX_SOCKETS
|
|
||||||
char *unix_path = conn->unix_domain_socket;
|
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_PROXY
|
|
||||||
if(!unix_path && conn->socks_proxy.host.name &&
|
|
||||||
!strncmp(UNIX_SOCKET_PREFIX"/",
|
|
||||||
conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
|
|
||||||
unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(unix_path) {
|
|
||||||
conn->transport = TRNSPRT_UNIX;
|
|
||||||
return resolve_unix(data, conn, unix_path);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_PROXY
|
|
||||||
if(CONN_IS_PROXIED(conn))
|
|
||||||
return resolve_proxy(data, conn, async);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return resolve_host(data, conn, async);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*************************************************************
|
/*************************************************************
|
||||||
* Resolve the address of the server or proxy
|
* Resolve the address of the server or proxy
|
||||||
*************************************************************/
|
*************************************************************/
|
||||||
|
@ -3220,19 +3112,67 @@ static CURLcode resolve_server(struct Curl_easy *data,
|
||||||
struct connectdata *conn,
|
struct connectdata *conn,
|
||||||
bool *async)
|
bool *async)
|
||||||
{
|
{
|
||||||
DEBUGASSERT(conn);
|
struct hostname *ehost;
|
||||||
DEBUGASSERT(data);
|
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
|
||||||
|
const char *peertype = "host";
|
||||||
|
int rc;
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
char *unix_path = conn->unix_domain_socket;
|
||||||
|
|
||||||
/* Resolve the name of the server or proxy */
|
#ifndef CURL_DISABLE_PROXY
|
||||||
if(conn->bits.reuse) {
|
if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
|
||||||
/* We are reusing the connection - no need to resolve anything, and
|
!strncmp(UNIX_SOCKET_PREFIX"/",
|
||||||
idnconvert_hostname() was called already in create_conn() for the reuse
|
conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
|
||||||
case. */
|
unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
|
||||||
*async = FALSE;
|
#endif
|
||||||
return CURLE_OK;
|
|
||||||
|
if(unix_path) {
|
||||||
|
/* TODO, this only works if previous transport is TRNSPRT_TCP. Check it? */
|
||||||
|
conn->transport = TRNSPRT_UNIX;
|
||||||
|
return resolve_unix(data, conn, unix_path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DEBUGASSERT(conn->dns_entry == NULL);
|
||||||
|
|
||||||
|
#ifndef CURL_DISABLE_PROXY
|
||||||
|
if(CONN_IS_PROXIED(conn)) {
|
||||||
|
ehost = conn->bits.socksproxy ? &conn->socks_proxy.host :
|
||||||
|
&conn->http_proxy.host;
|
||||||
|
peertype = "proxy";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
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. */
|
||||||
|
conn->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port :
|
||||||
|
conn->remote_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolve_fresh(data, conn, async);
|
/* Resolve target host right on */
|
||||||
|
conn->hostname_resolve = strdup(ehost->name);
|
||||||
|
if(!conn->hostname_resolve)
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
rc = Curl_resolv_timeout(data, conn->hostname_resolve,
|
||||||
|
conn->primary.remote_port,
|
||||||
|
&conn->dns_entry, timeout_ms);
|
||||||
|
if(rc == CURLRESOLV_PENDING)
|
||||||
|
*async = TRUE;
|
||||||
|
else if(rc == CURLRESOLV_TIMEDOUT) {
|
||||||
|
failf(data, "Failed to resolve %s '%s' with timeout after %"
|
||||||
|
CURL_FORMAT_TIMEDIFF_T " ms", peertype, ehost->dispname,
|
||||||
|
Curl_timediff(Curl_now(), data->progress.t_startsingle));
|
||||||
|
return CURLE_OPERATION_TIMEDOUT;
|
||||||
|
}
|
||||||
|
else if(!conn->dns_entry) {
|
||||||
|
failf(data, "Could not resolve %s: %s", peertype, ehost->dispname);
|
||||||
|
return CURLE_COULDNT_RESOLVE_HOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3708,12 +3648,20 @@ static CURLcode create_conn(struct Curl_easy *data,
|
||||||
|
|
||||||
/* Continue connectdata initialization here. */
|
/* Continue connectdata initialization here. */
|
||||||
|
|
||||||
/*************************************************************
|
if(conn->bits.reuse) {
|
||||||
* Resolve the address of the server or proxy
|
/* We are reusing the connection - no need to resolve anything, and
|
||||||
*************************************************************/
|
idnconvert_hostname() was called already in create_conn() for the reuse
|
||||||
result = resolve_server(data, conn, async);
|
case. */
|
||||||
if(result)
|
*async = FALSE;
|
||||||
goto out;
|
}
|
||||||
|
else {
|
||||||
|
/*************************************************************
|
||||||
|
* Resolve the address of the server or proxy
|
||||||
|
*************************************************************/
|
||||||
|
result = resolve_server(data, conn, async);
|
||||||
|
if(result)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* Everything general done, inform filters that they need
|
/* Everything general done, inform filters that they need
|
||||||
* to prepare for a data transfer.
|
* to prepare for a data transfer.
|
||||||
|
|
|
@ -831,7 +831,8 @@ struct connectdata {
|
||||||
struct proxy_info http_proxy;
|
struct proxy_info http_proxy;
|
||||||
#endif
|
#endif
|
||||||
/* 'primary' and 'secondary' get filled with IP quadruple
|
/* 'primary' and 'secondary' get filled with IP quadruple
|
||||||
(local/remote numerical ip address and port) whenever a is *attempted*.
|
(local/remote numerical ip address and port) whenever a connect is
|
||||||
|
*attempted*.
|
||||||
When more than one address is tried for a connection these will hold data
|
When more than one address is tried for a connection these will hold data
|
||||||
for the last attempt. When the connection is actually established
|
for the last attempt. When the connection is actually established
|
||||||
these are updated with data which comes directly from the socket. */
|
these are updated with data which comes directly from the socket. */
|
||||||
|
|
|
@ -3897,7 +3897,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||||
if(data->set.tls_ech & CURLECH_HARD)
|
if(data->set.tls_ech & CURLECH_HARD)
|
||||||
return CURLE_SSL_CONNECT_ERROR;
|
return CURLE_SSL_CONNECT_ERROR;
|
||||||
}
|
}
|
||||||
Curl_resolv_unlock(data, dns);
|
Curl_resolv_unlink(data, &dns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# ifdef OPENSSL_IS_BORINGSSL
|
# ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
|
|
@ -988,7 +988,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||||
if(data->set.tls_ech & CURLECH_HARD)
|
if(data->set.tls_ech & CURLECH_HARD)
|
||||||
return CURLE_SSL_CONNECT_ERROR;
|
return CURLE_SSL_CONNECT_ERROR;
|
||||||
}
|
}
|
||||||
Curl_resolv_unlock(data, dns);
|
Curl_resolv_unlink(data, &dns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ UNITTEST_START
|
||||||
abort_unless(rc == CURLE_OK, "data node creation failed");
|
abort_unless(rc == CURLE_OK, "data node creation failed");
|
||||||
key_len = strlen(data_key);
|
key_len = strlen(data_key);
|
||||||
|
|
||||||
data_node->inuse = 1; /* hash will hold the reference */
|
data_node->refcount = 1; /* hash will hold the reference */
|
||||||
nodep = Curl_hash_add(&hp, data_key, key_len + 1, data_node);
|
nodep = Curl_hash_add(&hp, data_key, key_len + 1, data_node);
|
||||||
abort_unless(nodep, "insertion into hash failed");
|
abort_unless(nodep, "insertion into hash failed");
|
||||||
/* Freeing will now be done by Curl_hash_destroy */
|
/* Freeing will now be done by Curl_hash_destroy */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user