* CURLRES_* defines based on the config*.h and curl_setup.h defines.
*/
-static void hostcache_unlink_entry(void *entry);
+static void dnscache_entry_free(struct Curl_dns_entry *dns);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static void show_resolve_info(struct Curl_easy *data,
* the DNS caching. Without alloc. Return length of the id string.
*/
static size_t
-create_hostcache_id(const char *name,
- size_t nlen, /* 0 or actual name length */
- int port, char *ptr, size_t buflen)
+create_dnscache_id(const char *name,
+ size_t nlen, /* 0 or actual name length */
+ int port, char *ptr, size_t buflen)
{
size_t len = nlen ? nlen : strlen(name);
DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
return msnprintf(&ptr[len], 7, ":%u", port) + len;
}
-struct hostcache_prune_data {
+struct dnscache_prune_data {
time_t now;
time_t oldest; /* oldest time in cache not pruned. */
int max_age_sec;
* cache.
*/
static int
-hostcache_entry_is_stale(void *datap, void *hc)
+dnscache_entry_is_stale(void *datap, void *hc)
{
- struct hostcache_prune_data *prune =
- (struct hostcache_prune_data *) datap;
+ struct dnscache_prune_data *prune =
+ (struct dnscache_prune_data *) datap;
struct Curl_dns_entry *dns = (struct Curl_dns_entry *) hc;
if(dns->timestamp) {
* Returns the 'age' of the oldest still kept entry.
*/
static time_t
-hostcache_prune(struct Curl_hash *hostcache, int cache_timeout,
- time_t now)
+dnscache_prune(struct Curl_hash *hostcache, int cache_timeout,
+ time_t now)
{
- struct hostcache_prune_data user;
+ struct dnscache_prune_data user;
user.max_age_sec = cache_timeout;
user.now = now;
Curl_hash_clean_with_criterium(hostcache,
(void *) &user,
- hostcache_entry_is_stale);
+ dnscache_entry_is_stale);
return user.oldest;
}
+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_hostcache_prune(struct Curl_easy *data)
+void Curl_dnscache_prune(struct Curl_easy *data)
{
+ struct Curl_dnscache *dnscache = dnscache_get(data);
time_t now;
/* the timeout may be set -1 (forever) */
int timeout = data->set.dns_cache_timeout;
- if(!data->dns.hostcache)
+ if(!dnscache)
/* NULL hostcache means we cannot do it */
return;
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+ dnscache_lock(data, dnscache);
now = time(NULL);
do {
/* Remove outdated and unused entries from the hostcache */
- time_t oldest = hostcache_prune(data->dns.hostcache, timeout, now);
+ time_t oldest = dnscache_prune(&dnscache->entries, timeout, now);
if(oldest < INT_MAX)
timeout = (int)oldest; /* we know it fits */
/* if the cache size is still too big, use the oldest age as new
prune limit */
} while(timeout &&
- (Curl_hash_count(data->dns.hostcache) > MAX_DNS_CACHE_SIZE));
+ (Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE));
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ dnscache_unlock(data, dnscache);
}
#ifdef USE_ALARM_TIMEOUT
/* lookup address, returns entry if found and not stale */
static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
+ struct Curl_dnscache *dnscache,
const char *hostname,
int port)
{
struct Curl_dns_entry *dns = NULL;
char entry_id[MAX_HOSTCACHE_LEN];
+ size_t entry_len;
+
+ if(!dnscache)
+ return NULL;
/* Create an entry id, based upon the hostname and port */
- size_t entry_len = create_hostcache_id(hostname, 0, port,
- entry_id, sizeof(entry_id));
+ 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(data->dns.hostcache, entry_id, entry_len + 1);
+ 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_hostcache_id("*", 1, port, entry_id, sizeof(entry_id));
+ 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(data->dns.hostcache, entry_id, entry_len + 1);
+ dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
}
if(dns && (data->set.dns_cache_timeout != -1)) {
/* See whether the returned entry is stale. Done before we release lock */
- struct hostcache_prune_data user;
+ struct dnscache_prune_data user;
user.now = time(NULL);
user.max_age_sec = data->set.dns_cache_timeout;
user.oldest = 0;
- if(hostcache_entry_is_stale(&user, dns)) {
+ 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(data->dns.hostcache, entry_id, entry_len + 1);
+ Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
}
}
if(!found) {
infof(data, "Hostname in DNS cache does not have needed family, zapped");
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(&dnscache->entries, entry_id, entry_len + 1);
}
}
return dns;
const char *hostname,
int port)
{
+ struct Curl_dnscache *dnscache = dnscache_get(data);
struct Curl_dns_entry *dns = NULL;
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
- dns = fetch_addr(data, hostname, port);
+ dnscache_lock(data, dnscache);
+ dns = fetch_addr(data, dnscache, hostname, port);
if(dns)
dns->refcount++; /* we use it! */
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ dnscache_unlock(data, dnscache);
return dns;
}
}
#endif
-/*
- * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
- *
- * When calling Curl_resolv() has resulted in a response with a returned
- * address, we call this function to store the information in the dns
- * cache etc
- *
- * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
- */
-struct Curl_dns_entry *
-Curl_cache_addr(struct Curl_easy *data,
- struct Curl_addrinfo *addr,
- const char *hostname,
- size_t hostlen, /* length or zero */
- int port,
- bool permanent)
+static struct Curl_dns_entry *
+dnscache_add_addr(struct Curl_easy *data,
+ struct Curl_dnscache *dnscache,
+ struct Curl_addrinfo *addr,
+ const char *hostname,
+ size_t hostlen, /* length or zero */
+ int port,
+ bool permanent)
{
char entry_id[MAX_HOSTCACHE_LEN];
size_t entry_len;
/* shuffle addresses if requested */
if(data->set.dns_shuffle_addresses) {
CURLcode result = Curl_shuffle_addr(data, &addr);
- if(result)
+ if(result) {
+ Curl_freeaddrinfo(addr);
return NULL;
+ }
}
#endif
if(!hostlen)
/* Create a new cache entry */
dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
if(!dns) {
+ Curl_freeaddrinfo(addr);
return NULL;
}
/* Create an entry id, based upon the hostname and port */
- entry_len = create_hostcache_id(hostname, hostlen, port,
- entry_id, sizeof(entry_id));
+ entry_len = create_dnscache_id(hostname, hostlen, port,
+ entry_id, sizeof(entry_id));
dns->refcount = 1; /* the cache has the first reference */
dns->addr = addr; /* this is the address(es) */
memcpy(dns->hostname, hostname, hostlen);
/* Store the resolved data in our DNS cache. */
- dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
+ dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1,
(void *)dns);
if(!dns2) {
- free(dns);
+ dnscache_entry_free(dns);
return NULL;
}
return dns;
}
+/*
+ * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
+ *
+ * When calling Curl_resolv() has resulted in a response with a returned
+ * address, we call this function to store the information in the dns
+ * cache etc
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
+ */
+struct Curl_dns_entry *
+Curl_cache_addr(struct Curl_easy *data,
+ struct Curl_addrinfo *addr,
+ const char *hostname,
+ size_t hostlen, /* length or zero */
+ int port,
+ bool permanent)
+{
+ struct Curl_dnscache *dnscache = dnscache_get(data);
+ struct Curl_dns_entry *dns;
+
+ if(!dnscache) {
+ Curl_freeaddrinfo(addr);
+ return NULL;
+ }
+
+ dnscache_lock(data, dnscache);
+ dns = dnscache_add_addr(data, dnscache, addr,
+ hostname, hostlen, port, permanent);
+ dnscache_unlock(data, dnscache);
+ return dns;
+}
+
#ifdef USE_IPV6
/* return a static IPv6 ::1 for the name */
static struct Curl_addrinfo *get_localhost6(int port, const char *name)
bool allowDOH,
struct Curl_dns_entry **entry)
{
+ struct Curl_dnscache *dnscache = dnscache_get(data);
struct Curl_dns_entry *dns = NULL;
CURLcode result;
enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
(void)allowDOH;
#endif
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+ if(!dnscache)
+ return CURLRESOLV_ERROR;
+
+ dnscache_lock(data, dnscache);
- dns = fetch_addr(data, hostname, port);
+ dns = fetch_addr(data, dnscache, hostname, port);
if(dns) {
infof(data, "Hostname %s was found in DNS cache", hostname);
rc = CURLRESOLV_RESOLVED;
}
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ dnscache_unlock(data, dnscache);
if(!dns) {
/* The entry was not in the cache. Resolve it to IP address */
}
}
else {
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
/* we got a response, store it in the cache */
dns = Curl_cache_addr(data, addr, hostname, 0, port, FALSE);
-
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
- if(!dns)
- /* returned failure, bail out nicely */
- Curl_freeaddrinfo(addr);
- else {
+ if(dns) {
rc = CURLRESOLV_RESOLVED;
show_resolve_info(data, dns);
}
return rc;
}
+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);
+ free(dns->hinfo);
+ }
+#endif
+ free(dns);
+}
+
/*
* Curl_resolv_unlink() releases a reference to the given cached DNS entry.
* When the reference count reaches 0, the entry is destroyed. It is important
*/
void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns)
{
+ struct Curl_dnscache *dnscache = dnscache_get(data);
struct Curl_dns_entry *dns = *pdns;
*pdns = NULL;
- if(data && data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
- hostcache_unlink_entry(dns);
-
- if(data && data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ dnscache_lock(data, dnscache);
+ dns->refcount--;
+ if(dns->refcount == 0)
+ dnscache_entry_free(dns);
+ dnscache_unlock(data, dnscache);
}
-/*
- * File-internal: release cache dns entry reference, free if inuse drops to 0
- */
-static void hostcache_unlink_entry(void *entry)
+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) {
- Curl_freeaddrinfo(dns->addr);
-#ifdef USE_HTTPSRR
- if(dns->hinfo) {
- Curl_httpsrr_cleanup(dns->hinfo);
- free(dns->hinfo);
- }
-#endif
- free(dns);
- }
+ if(dns->refcount == 0)
+ dnscache_entry_free(dns);
}
/*
- * Curl_init_dnscache() inits a new DNS cache.
+ * Curl_dnscache_init() inits a new DNS cache.
*/
-void Curl_init_dnscache(struct Curl_hash *hash, size_t size)
+void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size)
{
- Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
- hostcache_unlink_entry);
+ Curl_hash_init(&dns->entries, size, Curl_hash_str, Curl_str_key_compare,
+ dnscache_entry_dtor);
}
-/*
- * Curl_hostcache_clean()
- *
- * This _can_ be called with 'data' == NULL but then of course no locking
- * can be done!
- */
-
-void Curl_hostcache_clean(struct Curl_easy *data,
- struct Curl_hash *hash)
+void Curl_dnscache_destroy(struct Curl_dnscache *dns)
{
- if(data && data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
- Curl_hash_clean(hash);
-
- if(data && data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_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;
if(!Curl_str_number(&host, &num, 0xffff)) {
/* Create an entry id, based upon the hostname and port */
- entry_len = create_hostcache_id(Curl_str(&source),
- Curl_strlen(&source), (int)num,
- entry_id, sizeof(entry_id));
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
+ entry_len = create_dnscache_id(Curl_str(&source),
+ Curl_strlen(&source), (int)num,
+ entry_id, sizeof(entry_id));
+ dnscache_lock(data, dnscache);
/* delete entry, ignore if it did not exist */
- Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
-
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
+ dnscache_unlock(data, dnscache);
}
}
else {
}
/* Create an entry id, based upon the hostname and port */
- entry_len = create_hostcache_id(Curl_str(&source), Curl_strlen(&source),
- (int)port,
- entry_id, sizeof(entry_id));
+ entry_len = create_dnscache_id(Curl_str(&source), Curl_strlen(&source),
+ (int)port,
+ entry_id, sizeof(entry_id));
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+ dnscache_lock(data, dnscache);
/* See if it is already in our dns cache */
- dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+ dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
if(dns) {
infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T
4. when adding a non-permanent entry, we want it to get a "fresh"
timeout that starts _now_. */
- Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+ Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
}
/* put this new host in the cache */
- dns = Curl_cache_addr(data, head, Curl_str(&source),
- Curl_strlen(&source), (int)port, permanent);
+ dns = dnscache_add_addr(data, dnscache, head, Curl_str(&source),
+ Curl_strlen(&source), (int)port, permanent);
if(dns) {
/* release the returned reference; the cache itself will keep the
* entry alive: */
dns->refcount--;
}
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ dnscache_unlock(data, dnscache);
- if(!dns) {
- Curl_freeaddrinfo(head);
+ if(!dns)
return CURLE_OUT_OF_MEMORY;
- }
+
#ifndef CURL_DISABLE_VERBOSE_STRINGS
infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s",
(int)Curl_strlen(&source), Curl_str(&source), port, addresses,