From 22ac08e06db32cb2a7872316a669eb81ec3ea204 Mon Sep 17 00:00:00 2001
From: Sterling Hughes <sterling@bumblebury.com>
Date: Tue, 8 Jan 2002 04:26:47 +0000
Subject: [PATCH] Add support for DNS cache timeouts via the
 CURLOPT_DNS_CACHE_TIMEOUT option.

The default cache timeout for this is 60 seconds, which is arbitrary and
completely subject to change :)
---
 include/curl/curl.h |  3 +++
 lib/hostip.c        | 61 +++++++++++++++++++++++++++++++++++++--------
 lib/url.c           | 10 ++++++--
 lib/urldata.h       |  1 +
 4 files changed, 62 insertions(+), 13 deletions(-)

diff --git a/include/curl/curl.h b/include/curl/curl.h
index 3d91c85ffe..9059d14921 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -491,6 +491,9 @@ typedef enum {
   /* Non-zero value means to use the global dns cache */
   CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91),
 
+  /* DNS cache timeout */
+  CINIT(DNS_CACHE_TIMEOUT, LONG, 92),
+  
   CURLOPT_LASTENTRY /* the last unusued */
 } CURLoption;
 
diff --git a/lib/hostip.c b/lib/hostip.c
index 9d0013ffe7..fc76242f77 100644
--- a/lib/hostip.c
+++ b/lib/hostip.c
@@ -93,24 +93,59 @@ void Curl_global_host_cache_dtor(void)
   }
 }
 
+struct curl_dns_cache_entry {
+  Curl_addrinfo *addr;
+  int            timestamp;
+};
+
 Curl_addrinfo *Curl_resolv(struct SessionHandle *data,
                            char *hostname,
                            int port,
                            char **bufp)
 {
-  Curl_addrinfo *addr = NULL;
-  size_t hostname_len = strlen(hostname)+1;
+  struct curl_dns_cache_entry *p = NULL;
+  size_t hostname_len;
+  time_t now;
+
+  /* If the host cache timeout is 0, we don't do DNS cach'ing
+     so fall through */
+  if (data->set.dns_cache_timeout == 0) {
+    return Curl_getaddrinfo(data, hostname, port, bufp);
+  }
+
+  hostname_len = strlen(hostname)+1;
+
+  time(&now);
+  /* See if its already in our dns cache */
+  if (curl_hash_find(data->hostcache, hostname, hostname_len, (void **) &p)) {
+    /* Do we need to check for a cache timeout? */
+    if (data->set.dns_cache_timeout != -1) {
+      /* Return if the entry has not timed out */
+      if ((now - p->timestamp) < data->set.dns_cache_timeout) {
+        return p->addr;
+      }
+    }
+    else {
+      return p->addr;
+    }
+  }
 
-  if (curl_hash_find(data->hostcache, hostname, hostname_len, (void **) &addr)) {
-    return addr;
+  /* Create a new cache entry */
+  p = (struct curl_dns_cache_entry *) malloc(sizeof(struct curl_dns_cache_entry));
+  if (!p) {
+    return NULL;
   }
-  
-  addr = Curl_getaddrinfo(data, hostname, port, bufp);
-  if (!addr)
+
+  p->addr = Curl_getaddrinfo(data, hostname, port, bufp);
+  if (!p->addr) {
     return NULL;
+  }
+  p->timestamp = now;
 
-  curl_hash_add(data->hostcache, hostname, hostname_len, (const void *) addr);
-  return addr;
+  /* Save it in our host cache */
+  curl_hash_update(data->hostcache, hostname, hostname_len, (const void *) p);
+
+  return p->addr;
 }
 
 /*
@@ -120,11 +155,15 @@ Curl_addrinfo *Curl_resolv(struct SessionHandle *data,
  */
 void Curl_freeaddrinfo(void *freethis)
 {
+  struct curl_dns_cache_entry *p = (struct curl_dns_cache_entry *) freethis;
+
 #ifdef ENABLE_IPV6
-  freeaddrinfo(freethis);
+  freeaddrinfo(p->addr);
 #else
-  free(freethis);
+  free(p->addr);
 #endif
+
+  free(p);
 }
 
 /* --- resolve name or IP-number --- */
diff --git a/lib/url.c b/lib/url.c
index 56e7fa73fb..63d9ae56a8 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -251,13 +251,15 @@ CURLcode Curl_open(struct SessionHandle **curl)
   data->set.httpreq = HTTPREQ_GET; /* Default HTTP request */
   data->set.ftp_use_epsv = TRUE;   /* FTP defaults to EPSV operations */
 
+  data->set.dns_cache_timeout = 60; /* Timeout every 60 seconds by default */
+  
   /* make libcurl quiet by default: */
   data->set.hide_progress = TRUE;  /* CURLOPT_NOPROGRESS changes these */
   data->progress.flags |= PGRS_HIDE;
 
   /* Set the default size of the SSL session ID cache */
   data->set.ssl.numsessions = 5;
-
+  
   /* create an array with connection data struct pointers */
   data->state.numconnects = 5; /* hard-coded right now */
   data->state.connects = (struct connectdata **)
@@ -284,7 +286,11 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
   va_start(param, option);
 
   switch(option) {
-    case CURLOPT_DNS_USE_GLOBAL_CACHE: {
+  case CURLOPT_DNS_CACHE_TIMEOUT:
+    data->set.dns_cache_timeout = va_arg(param, int);
+    break;
+  case CURLOPT_DNS_USE_GLOBAL_CACHE: 
+    {
       int use_cache = va_arg(param, int);
       if (use_cache) {
         Curl_global_host_cache_init();
diff --git a/lib/urldata.h b/lib/urldata.h
index 36358d60f0..64ee821ca6 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -647,6 +647,7 @@ struct UserDefined {
   bool ftp_use_epsv;     /* if EPSV is to be attempted or not */
 
   bool global_dns_cache;
+  bool dns_cache_timeout;
 };
 
 /*
-- 
GitLab