]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
hostip: do DNS cache pruning in milliseconds
authorDaniel Stenberg <daniel@haxx.se>
Mon, 4 Aug 2025 12:15:03 +0000 (14:15 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 4 Aug 2025 14:20:50 +0000 (16:20 +0200)
Instead of using integer seconds. Also: if the cache contains over
30,000 entries after first pruning, it makes anoter round and removes
all entries that are older than half the age of the oldest entry until
it goes below 30,000.

Closes #18160

lib/hostip.c
lib/hostip.h
lib/setopt.c
lib/url.c
lib/urldata.h
tests/unit/unit1607.c

index 95c2c6224259e31be67e0228f9198e72ebb1d3bb..e01f111a64b534329bb5f538c99a68fd45a7f723 100644 (file)
@@ -180,9 +180,9 @@ create_dnscache_id(const char *name,
 }
 
 struct dnscache_prune_data {
-  time_t now;
-  time_t oldest; /* oldest time in cache not pruned. */
-  int max_age_sec;
+  struct curltime now;
+  timediff_t oldest_ms; /* oldest time in cache not pruned. */
+  timediff_t max_age_ms;
 };
 
 /*
@@ -199,36 +199,36 @@ dnscache_entry_is_stale(void *datap, void *hc)
     (struct dnscache_prune_data *) datap;
   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) hc;
 
-  if(dns->timestamp) {
-    /* age in seconds */
-    time_t age = prune->now - dns->timestamp;
-    if(age >= (time_t)prune->max_age_sec)
+  if(dns->timestamp.tv_sec || dns->timestamp.tv_usec) {
+    /* get age in milliseconds */
+    timediff_t age = curlx_timediff(prune->now, dns->timestamp);
+    if(age >= prune->max_age_ms)
       return TRUE;
-    if(age > prune->oldest)
-      prune->oldest = age;
+    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.
+ * Returns the 'age' of the oldest still kept entry - in milliseconds.
  */
-static time_t
-dnscache_prune(struct Curl_hash *hostcache, int cache_timeout,
-               time_t now)
+static timediff_t
+dnscache_prune(struct Curl_hash *hostcache, int cache_timeout_ms,
+               struct curltime now)
 {
   struct dnscache_prune_data user;
 
-  user.max_age_sec = cache_timeout;
+  user.max_age_ms = cache_timeout_ms;
   user.now = now;
-  user.oldest = 0;
+  user.oldest_ms = 0;
 
   Curl_hash_clean_with_criterium(hostcache,
                                  (void *) &user,
                                  dnscache_entry_is_stale);
 
-  return user.oldest;
+  return user.oldest_ms;
 }
 
 static struct Curl_dnscache *dnscache_get(struct Curl_easy *data)
@@ -261,31 +261,35 @@ static void dnscache_unlock(struct Curl_easy *data,
 void Curl_dnscache_prune(struct Curl_easy *data)
 {
   struct Curl_dnscache *dnscache = dnscache_get(data);
-  time_t now;
+  struct curltime now;
   /* the timeout may be set -1 (forever) */
-  int timeout = data->set.dns_cache_timeout;
+  int timeout_ms = data->set.dns_cache_timeout_ms;
 
-  if(!dnscache)
+  if(!dnscache || (timeout_ms == -1))
     /* NULL hostcache means we cannot do it */
     return;
 
   dnscache_lock(data, dnscache);
 
-  now = time(NULL);
+  now = curlx_now();
 
   do {
     /* Remove outdated and unused entries from the hostcache */
-    time_t oldest = dnscache_prune(&dnscache->entries, timeout, now);
+    timediff_t oldest_ms = dnscache_prune(&dnscache->entries, timeout_ms, now);
 
-    if(oldest < INT_MAX)
-      timeout = (int)oldest; /* we know it fits */
+    if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE) {
+      if(oldest_ms < INT_MAX)
+        /* prune the ones over half this age */
+        timeout_ms = (int)oldest_ms / 2;
+      else
+        timeout_ms = INT_MAX/2;
+    }
     else
-      timeout = INT_MAX - 1;
+      break;
 
-    /* if the cache size is still too big, use the oldest age as new
-       prune limit */
-  } while(timeout &&
-          (Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE));
+    /* if the cache size is still too big, use the oldest age as new prune
+       limit */
+  } while(timeout_ms);
 
   dnscache_unlock(data, dnscache);
 }
@@ -337,13 +341,13 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
     dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
   }
 
-  if(dns && (data->set.dns_cache_timeout != -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 = time(NULL);
-    user.max_age_sec = data->set.dns_cache_timeout;
-    user.oldest = 0;
+    user.now = curlx_now();
+    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");
@@ -530,12 +534,12 @@ Curl_dnscache_mk_entry(struct Curl_easy *data,
 
   dns->refcount = 1; /* the cache has the first reference */
   dns->addr = addr; /* this is the address(es) */
-  if(permanent)
-    dns->timestamp = 0; /* an entry that never goes stale */
+  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 = time(NULL);
-    if(dns->timestamp == 0)
-      dns->timestamp = 1;
+    dns->timestamp = curlx_now();
   }
   dns->hostport = port;
   if(hostlen)
index 56c5e9cad126153f1353dd97f8db8dd0ea95b024..5548a6f125d3e3f5694e096425b9846636c0ba8c 100644 (file)
@@ -67,7 +67,7 @@ struct Curl_dns_entry {
   struct Curl_https_rrinfo *hinfo;
 #endif
   /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */
-  time_t timestamp;
+  struct curltime timestamp;
   /* reference counter, entry is freed on reaching 0 */
   size_t refcount;
   /* hostname port number that resolved to addr. */
index 93f26d285fb689d303bd588f417cc4479b3046a1..ac1e8177ae5a20e698a68d7602b8e013bc35ccfc 100644 (file)
@@ -868,10 +868,12 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
   case CURLOPT_DNS_CACHE_TIMEOUT:
     if(arg < -1)
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    else if(arg > INT_MAX)
-      arg = INT_MAX;
+    else if(arg > INT_MAX/1000)
+      arg = INT_MAX/1000;
 
-    s->dns_cache_timeout = (int)arg;
+    if(arg > 0)
+      arg *= 1000;
+    s->dns_cache_timeout_ms = (int)arg;
     break;
   case CURLOPT_CA_CACHE_TIMEOUT:
     if(Curl_ssl_supports(data, SSLSUPP_CA_CACHE)) {
index 79d4c52716d94ab847d5fa0d79ddfa2fe2f42b24..9cf920ed3a6a4030f9be9352bd5c582b50d5481c 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -388,7 +388,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
   set->ftp_filemethod = FTPFILE_MULTICWD;
   set->ftp_skip_ip = TRUE;    /* skip PASV IP by default */
 #endif
-  set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */
+  set->dns_cache_timeout_ms = 60000; /* Timeout every 60 seconds by default */
 
   /* Timeout every 24 hours by default */
   set->general_ssl.ca_cache_timeout = 24 * 60 * 60;
index 2ae52840f50ec8ca42d98364c3a12e91c58ac86b..83d7b50e448d2382dc6c36820fe22db94fd0801e 100644 (file)
@@ -1431,7 +1431,7 @@ struct UserDefined {
   unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */
 #endif
   struct ssl_general_config general_ssl; /* general user defined SSL stuff */
-  int dns_cache_timeout; /* DNS cache timeout (seconds) */
+  int dns_cache_timeout_ms; /* DNS cache timeout (milliseconds) */
   unsigned int buffer_size;      /* size of receive buffer to use */
   unsigned int upload_buffer_size; /* size of upload buffer to use,
                                       keep it >= CURL_MAX_WRITE_SIZE */
index 26d1457df6b5f0f842e5b4a1397981b994377447..ad0df33be8d62a0a14a3df1f0fba4e487a5e09d4 100644 (file)
@@ -193,7 +193,7 @@ static CURLcode test_unit1607(const char *arg)
         break;
       }
 
-      if(dns->timestamp && tests[i].permanent) {
+      if(dns->timestamp.tv_sec && tests[i].permanent) {
         curl_mfprintf(stderr,
                       "%s:%d tests[%d] failed. the timestamp is not zero "
                       "but tests[%d].permanent is TRUE\n",
@@ -202,7 +202,7 @@ static CURLcode test_unit1607(const char *arg)
         break;
       }
 
-      if(dns->timestamp == 0 && !tests[i].permanent) {
+      if(dns->timestamp.tv_sec == 0 && !tests[i].permanent) {
         curl_mfprintf(stderr, "%s:%d tests[%d] failed. the timestamp is zero "
                       "but tests[%d].permanent is FALSE\n",
                       __FILE__, __LINE__, i, i);