From ca015f1a45c68aa1d641678cfc13ce0df0c58fe0 Mon Sep 17 00:00:00 2001
From: Vsevolod Novikov <novikov@doroga.tv>
Date: Sat, 29 Jan 2011 20:12:10 +0100
Subject: [PATCH] asynch resolvers: unified

Introducing an internal API for handling of different async resolver
backends.
---
 lib/easy.c     |  19 ++---
 lib/hostares.c | 227 +++++++++++++++++++++++++++++++++++++++++++------
 lib/hostasyn.c |  75 ----------------
 lib/hostip.h   |  79 ++++++++++-------
 lib/hostip4.c  |   1 +
 lib/hostsyn.c  |  55 ++++++++++++
 lib/hostthre.c |  75 ++++++++++++++--
 lib/url.c      |  27 +++---
 lib/urldata.h  |  14 +--
 lib/version.c  |   2 +-
 10 files changed, 393 insertions(+), 181 deletions(-)

diff --git a/lib/easy.c b/lib/easy.c
index 05d8ded9d0..9ce80d0867 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -269,12 +269,10 @@ CURLcode curl_global_init(long flags)
   idna_init();
 #endif
 
-#ifdef CARES_HAVE_ARES_LIBRARY_INIT
-  if(ares_library_init(ARES_LIB_INIT_ALL)) {
-    DEBUGF(fprintf(stderr, "Error: ares_library_init failed\n"));
+  if( Curl_resolver_global_init() != CURLE_OK ) {
+    DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
     return CURLE_FAILED_INIT;
   }
-#endif
 
 #if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_INIT)
   if(libssh2_init(0)) {
@@ -340,9 +338,7 @@ void curl_global_cleanup(void)
   if(init_flags & CURL_GLOBAL_SSL)
     Curl_ssl_cleanup();
 
-#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
-  ares_library_cleanup();
-#endif
+  Curl_resolver_global_cleanup();
 
   if(init_flags & CURL_GLOBAL_WIN32)
     win32_cleanup();
@@ -676,12 +672,9 @@ CURL *curl_easy_duphandle(CURL *incurl)
     outcurl->change.referer_alloc = TRUE;
   }
 
-#ifdef USE_ARES
-  /* If we use ares, we clone the ares channel for the new handle */
-  if(ARES_SUCCESS != ares_dup(&outcurl->state.areschannel,
-                              data->state.areschannel))
-    goto fail;
-#endif
+  /* Clone the resolver handle, if present, for the new handle */
+  if( Curl_resolver_duphandle(&outcurl->state.resolver, data->state.resolver) != CURLE_OK )
+   goto fail;
 
   Curl_convert_setup(outcurl);
 
diff --git a/lib/hostares.c b/lib/hostares.c
index a165cb91f5..1b6978d517 100644
--- a/lib/hostares.c
+++ b/lib/hostares.c
@@ -60,6 +60,12 @@
 #define in_addr_t unsigned long
 #endif
 
+/***********************************************************************
+ * Only for ares-enabled builds
+ **********************************************************************/
+
+#ifdef CURLRES_ARES
+
 #include "urldata.h"
 #include "sendf.h"
 #include "hostip.h"
@@ -76,15 +82,132 @@
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
 
+#  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
+     (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
+#    define CARES_STATICLIB
+#  endif
+#  include <ares.h>
+
+#if ARES_VERSION >= 0x010500
+/* c-ares 1.5.0 or later, the callback proto is modified */
+#define HAVE_CARES_CALLBACK_TIMEOUTS 1
+#endif
+
 #include "curl_memory.h"
 /* The last #include file should be: */
 #include "memdebug.h"
 
-/***********************************************************************
- * Only for ares-enabled builds
- **********************************************************************/
+struct ResolverResults {
+  int num_pending; /* number of ares_gethostbyname() requests */
+  Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
+  int last_status;
+};
 
-#ifdef CURLRES_ARES
+/*
+ * Curl_resolver_global_init() - the generic low-level asynchronous name resolve API.
+ * Called from curl_global_init() to initialize global resolver environment.
+ * Initializes ares library.
+ */
+int Curl_resolver_global_init()
+{
+#ifdef CARES_HAVE_ARES_LIBRARY_INIT
+  if(ares_library_init(ARES_LIB_INIT_ALL)) {
+    return CURLE_FAILED_INIT;
+  }
+#endif
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_global_cleanup() - the generic low-level asynchronous name resolve API.
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ * Deinitializes ares library.
+ */
+void Curl_resolver_global_cleanup()
+{
+#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
+  ares_library_cleanup();
+#endif
+}
+
+/*
+ * Curl_resolver_init() - the generic low-level name resolve API.
+ * Called from curl_easy_init() -> Curl_open() to initialize resolver URL-state specific environment
+ * ('resolver' member of the UrlState structure).
+ * Fills the passed pointer by the initialized ares_channel.
+ */
+int Curl_resolver_init(void **resolver)
+{
+  int status = ares_init((ares_channel*)resolver);
+  if(status != ARES_SUCCESS) {
+    if(status == ARES_ENOMEM)
+      return CURLE_OUT_OF_MEMORY;
+    else
+      return CURLE_FAILED_INIT;
+  }
+  return CURLE_OK;
+  /* make sure that all other returns from this function should destroy the
+     ares channel before returning error! */
+}
+
+/*
+ * Curl_resolver_cleanup() - the generic low-level name resolve API.
+ * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver URL-state specific environment
+ * ('resolver' member of the UrlState structure).
+ * Destroys the ares channel.
+ */
+void Curl_resolver_cleanup(void *resolver)
+{
+  ares_destroy((ares_channel)resolver);
+}
+
+/*
+ * Curl_resolver_duphandle() - the generic low-level name resolve API.
+ * Called from curl_easy_duphandle() to duplicate resolver URL-state specific environment
+ * ('resolver' member of the UrlState structure).
+ * Duplicates the 'from' ares channel and passes the resulting channel to the 'to' pointer.
+ */
+int Curl_resolver_duphandle(void **to, void *from)
+{
+  /* Clone the ares channel for the new handle */
+  if(ARES_SUCCESS != ares_dup((ares_channel*)to,(ares_channel)from))
+    return CURLE_FAILED_INIT;
+  return CURLE_OK;
+}
+
+static void destroy_async_data (struct Curl_async *async);
+/*
+ * Cancel all possibly still on-going resolves for this connection.
+ */
+void Curl_async_cancel(struct connectdata *conn)
+{
+  if( conn && conn->data && conn->data->state.resolver )
+    ares_cancel((ares_channel)conn->data->state.resolver);
+  destroy_async_data(&conn->async);
+}
+
+/*
+ * destroy_async_data() cleans up async resolver data.
+ */
+static void destroy_async_data (struct Curl_async *async)
+{
+  if(async->hostname)
+    free(async->hostname);
+
+  if(async->os_specific) {
+    struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
+    if( res ) {
+        if( res->temp_ai ) {
+            Curl_freeaddrinfo(res->temp_ai);
+            res->temp_ai = NULL;
+        }
+        free(res);
+    }
+    async->os_specific = NULL;
+  }
+
+  async->hostname = NULL;
+}
 
 /*
  * Curl_resolv_fdset() is called when someone from the outside world (using
@@ -103,14 +226,14 @@ int Curl_resolv_getsock(struct connectdata *conn,
   struct timeval maxtime;
   struct timeval timebuf;
   struct timeval *timeout;
-  int max = ares_getsock(conn->data->state.areschannel,
+  int max = ares_getsock((ares_channel)conn->data->state.resolver,
                          (ares_socket_t *)socks, numsocks);
 
 
   maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
   maxtime.tv_usec = 0;
 
-  timeout = ares_timeout(conn->data->state.areschannel, &maxtime, &timebuf);
+  timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime, &timebuf);
 
   Curl_expire(conn->data,
               (timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
@@ -138,7 +261,7 @@ static int waitperform(struct connectdata *conn, int timeout_ms)
   int i;
   int num = 0;
 
-  bitmask = ares_getsock(data->state.areschannel, socks, ARES_GETSOCK_MAXNUM);
+  bitmask = ares_getsock((ares_channel)data->state.resolver, socks, ARES_GETSOCK_MAXNUM);
 
   for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
     pfd[i].events = 0;
@@ -165,11 +288,11 @@ static int waitperform(struct connectdata *conn, int timeout_ms)
   if(!nfds)
     /* Call ares_process() unconditonally here, even if we simply timed out
        above, as otherwise the ares name resolve won't timeout! */
-    ares_process_fd(data->state.areschannel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
+    ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
   else {
     /* move through the descriptors and ask for processing on them */
     for(i=0; i < num; i++)
-      ares_process_fd(data->state.areschannel,
+      ares_process_fd((ares_channel)data->state.resolver,
                       pfd[i].revents & (POLLRDNORM|POLLIN)?
                       pfd[i].fd:ARES_SOCKET_BAD,
                       pfd[i].revents & (POLLWRNORM|POLLOUT)?
@@ -189,13 +312,17 @@ CURLcode Curl_is_resolved(struct connectdata *conn,
                           struct Curl_dns_entry **dns)
 {
   struct SessionHandle *data = conn->data;
+  struct ResolverResults *res = (struct ResolverResults *)conn->async.os_specific;
 
   *dns = NULL;
 
   waitperform(conn, 0);
 
-  if(conn->async.done) {
-    /* we're done, kill the ares handle */
+  if( res && !res->num_pending ) {
+    (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
+    /* temp_ai ownership is moved to the connection, so we need not free-up them */
+    res->temp_ai = NULL;
+    destroy_async_data(&conn->async);
     if(!conn->async.dns) {
       failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
             ares_strerror(conn->async.status));
@@ -223,6 +350,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
   struct SessionHandle *data = conn->data;
   long timeout;
   struct timeval now = Curl_tvnow();
+  struct Curl_dns_entry *temp_entry;
 
   timeout = Curl_timeleft(data, &now, TRUE);
   if(!timeout)
@@ -240,7 +368,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
     store.tv_sec = itimeout/1000;
     store.tv_usec = (itimeout%1000)*1000;
 
-    tvp = ares_timeout(data->state.areschannel, &store, &tv);
+    tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
 
     /* use the timeout period ares returned to us above if less than one
        second is left, otherwise just use 1000ms to make sure the progress
@@ -251,6 +379,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
       timeout_ms = 1000;
 
     waitperform(conn, timeout_ms);
+    Curl_is_resolved(conn,&temp_entry);
 
     if(conn->async.done)
       break;
@@ -267,7 +396,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
     }
     if(timeout < 0) {
       /* our timeout, so we cancel the ares operation */
-      ares_cancel(data->state.areschannel);
+      ares_cancel((ares_channel)data->state.resolver);
       break;
     }
   }
@@ -313,6 +442,22 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
   return rc;
 }
 
+/* Connects results to the list */
+static void ares_compound_results(struct ResolverResults *res, Curl_addrinfo *ai)
+{
+      Curl_addrinfo *ai_tail;
+      if( !ai )
+        return;
+      ai_tail = ai;
+
+      while (ai_tail->ai_next)
+        ai_tail = ai_tail->ai_next;
+
+      /* Add the new results to the list of old results. */
+      ai_tail->ai_next = res->temp_ai;
+      res->temp_ai = ai;
+}
+
 /*
  * ares_query_completed_cb() is the callback that ares will call when
  * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
@@ -326,26 +471,44 @@ static void ares_query_completed_cb(void *arg,  /* (struct connectdata *) */
                                     struct hostent *hostent)
 {
   struct connectdata *conn = (struct connectdata *)arg;
-  struct Curl_addrinfo * ai = NULL;
+  struct ResolverResults *res = (struct ResolverResults *)conn->async.os_specific;
+
+  if( !conn->data ) {
+    /* Immediately return just because the handle is destroying */
+    return;
+  }
+
+  if( !conn->data->magic ) {
+    /* Immediately return just because the handle is destroying */
+    return;
+  }
+
+  if( !res ) {
+    /* Immediately return just because the results are destroyed for some reason */
+    return;
+  }
 
 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
   (void)timeouts; /* ignored */
 #endif
 
+  res->num_pending--;
+
   switch(status) {
   case CURL_ASYNC_SUCCESS:
-    ai = Curl_he2ai(hostent, conn->async.port);
+    ares_compound_results(res,Curl_he2ai(hostent, conn->async.port));
     break;
-  case ARES_EDESTRUCTION:
-    /* this ares handle is getting destroyed, the 'arg' pointer may not be
+  /* this ares handle is getting destroyed, the 'arg' pointer may not be
        valid! */
-    return;
+  /* conn->magic check instead
+  case ARES_EDESTRUCTION:
+    return; */
   default:
-    /* do nothing */
     break;
   }
-
-  (void)Curl_addrinfo_callback(arg, status, ai);
+  /* The successfull result empties any error */
+  if( res->last_status != ARES_SUCCESS )
+    res->last_status = status;
 }
 
 /*
@@ -402,33 +565,41 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
 #endif /* CURLRES_IPV6 */
 
   bufp = strdup(hostname);
-
   if(bufp) {
+    struct ResolverResults *res = NULL;
     Curl_safefree(conn->async.hostname);
     conn->async.hostname = bufp;
     conn->async.port = port;
     conn->async.done = FALSE;   /* not done */
     conn->async.status = 0;     /* clear */
     conn->async.dns = NULL;     /* clear */
-    conn->async.temp_ai = NULL; /* clear */
+    res = (struct ResolverResults *)calloc(sizeof(struct ResolverResults),1);
+    if( !res ) {
+      Curl_safefree(conn->async.hostname);
+      conn->async.hostname = NULL;
+      return NULL;
+    }
+    conn->async.os_specific = res;
 
+    /* initial status - failed */
+    res->last_status = ARES_ENOTFOUND;
 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
     if(family == PF_UNSPEC) {
-      conn->async.num_pending = 2;
+      res->num_pending = 2;
 
       /* areschannel is already setup in the Curl_open() function */
-      ares_gethostbyname(data->state.areschannel, hostname, PF_INET,
+      ares_gethostbyname((ares_channel)data->state.resolver, hostname, PF_INET,
                          ares_query_completed_cb, conn);
-      ares_gethostbyname(data->state.areschannel, hostname, PF_INET6,
+      ares_gethostbyname((ares_channel)data->state.resolver, hostname, PF_INET6,
                          ares_query_completed_cb, conn);
     }
     else
 #endif /* CURLRES_IPV6 */
     {
-      conn->async.num_pending = 1;
+      res->num_pending = 1;
 
       /* areschannel is already setup in the Curl_open() function */
-      ares_gethostbyname(data->state.areschannel, hostname, family,
+      ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
                          ares_query_completed_cb, conn);
     }
 
diff --git a/lib/hostasyn.c b/lib/hostasyn.c
index 710ff50b72..5269c08dc7 100644
--- a/lib/hostasyn.c
+++ b/lib/hostasyn.c
@@ -72,20 +72,6 @@
  **********************************************************************/
 #ifdef CURLRES_ASYNCH
 
-/*
- * Cancel all possibly still on-going resolves for this connection.
- */
-void Curl_async_cancel(struct connectdata *conn)
-{
-  /* If we have a "half" response already received, we first clear that off
-     so that nothing is tempted to use it */
-  if(conn->async.temp_ai) {
-    Curl_freeaddrinfo(conn->async.temp_ai);
-    conn->async.temp_ai = NULL;
-  }
-}
-
-
 /*
  * Curl_addrinfo_callback() gets called by ares, gethostbyname_thread()
  * or getaddrinfo_thread() when we got the name resolved (or not!).
@@ -109,24 +95,6 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn,
     if(ai) {
       struct SessionHandle *data = conn->data;
 
-#if defined(ENABLE_IPV6) && defined(CURLRES_ARES) /* CURLRES_IPV6 */
-      Curl_addrinfo *ai_tail = ai;
-
-      while (ai_tail->ai_next)
-        ai_tail = ai_tail->ai_next;
-
-      /* Add the new results to the list of old results. */
-      ai_tail->ai_next = conn->async.temp_ai;
-      conn->async.temp_ai = ai;
-
-      if(--conn->async.num_pending > 0)
-        /* We are not done yet. Just return. */
-        return CURLE_OK;
-
-      /* make sure the temp pointer is cleared and isn't pointing to something
-         we take care of below */
-      conn->async.temp_ai = NULL;
-#endif
       if(data->share)
         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
 
@@ -143,52 +111,9 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn,
         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
     }
     else {
-#if defined(ENABLE_IPV6) && defined(CURLRES_ARES) /* CURLRES_IPV6 */
-      if(--conn->async.num_pending > 0) {
-        /* We are not done yet. Clean up and return.
-	   This function will be called again. */
-        if(conn->async.temp_ai) {
-          Curl_freeaddrinfo(conn->async.temp_ai);
-          conn->async.temp_ai = NULL;
-        }
-        return CURLE_OUT_OF_MEMORY;
-      }
-#endif
       rc = CURLE_OUT_OF_MEMORY;
     }
   }
-#if defined(ENABLE_IPV6) && defined(CURLRES_ARES) /* CURLRES_IPV6 */
-  else
-  {
-      if(--conn->async.num_pending > 0)
-        /* We are not done yet. Just return. */
-        return CURLE_OK;
-
-      if(conn->async.temp_ai) {
-        /* We are done, and while this latest request
-           failed, some previous results exist. */
-        struct SessionHandle *data = conn->data;
-
-        if(data->share)
-          Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-        dns = Curl_cache_addr(data, conn->async.temp_ai,
-                              conn->async.hostname,
-                              conn->async.port);
-        if(!dns) {
-          /* failed to store, cleanup and return error */
-          Curl_freeaddrinfo(conn->async.temp_ai);
-          rc = CURLE_OUT_OF_MEMORY;
-        }
-        if(data->share)
-          Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
-        /* make sure the temp pointer is cleared and isn't pointing to
-           something we've taken care of already */
-        conn->async.temp_ai = NULL;
-      }
-  }
-#endif
 
   conn->async.dns = dns;
 
diff --git a/lib/hostip.h b/lib/hostip.h
index a7c3345689..70ce7cd743 100644
--- a/lib/hostip.h
+++ b/lib/hostip.h
@@ -35,14 +35,6 @@
 #define in_addr_t unsigned long
 #endif
 
-/*
- * Comfortable CURLRES_* definitions are included from setup.h
- */
-
-#ifdef USE_ARES
-#include <ares_version.h>
-#endif
-
 /* Allocate enough memory to hold the full name information structs and
  * everything. OSF1 is known to require at least 8872 bytes. The buffer
  * required for storing all possible aliases and IP numbers is according to
@@ -53,29 +45,13 @@
 #define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this
                                     many seconds for a name resolve */
 
-#ifdef CURLRES_ARES
-#define CURL_ASYNC_SUCCESS ARES_SUCCESS
-#if ARES_VERSION >= 0x010500
-/* c-ares 1.5.0 or later, the callback proto is modified */
-#define HAVE_CARES_CALLBACK_TIMEOUTS 1
-#endif
-#else
 #define CURL_ASYNC_SUCCESS CURLE_OK
-#define ares_cancel(x) do {} while(0)
-#define ares_destroy(x) do {} while(0)
-#endif
 
 struct addrinfo;
 struct hostent;
 struct SessionHandle;
 struct connectdata;
 
-#ifdef CURLRES_ASYNCH
-void Curl_async_cancel(struct connectdata *conn);
-#else
-#define Curl_async_cancel(x) do {} while(0)
-#endif
-
 /*
  * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
  * Global DNS cache is general badness. Do not use. This will be removed in
@@ -128,6 +104,45 @@ bool Curl_ipv6works(void);
  */
 bool Curl_ipvalid(struct connectdata *conn);
 
+/*
+ * Curl_resolver_global_init() - the generic low-level name resolver API.
+ * Called from curl_global_init() to initialize global resolver environment.
+ * Returning anything else than CURLE_OK fails curl_global_init().
+ */
+int Curl_resolver_global_init(void);
+
+/*
+ * Curl_resolver_global_cleanup() - the generic low-level name resolver API.
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ */
+void Curl_resolver_global_cleanup(void);
+
+/*
+ * Curl_resolver_init() - the generic low-level name resolve API.
+ * Called from curl_easy_init() -> Curl_open() to initialize resolver URL-state specific environment
+ * ('resolver' member of the UrlState structure).
+ * Should fill the passed pointer by the initialized handler.
+ * Returning anything else than CURLE_OK fails curl_easy_init() with the correspondent code.
+ */
+int Curl_resolver_init(void **resolver);
+
+/*
+ * Curl_resolver_cleanup() - the generic low-level name resolve API.
+ * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver URL-state specific environment
+ * ('resolver' member of the UrlState structure).
+ * Should destroy the handler and free all resources connected to it.
+ */
+void Curl_resolver_cleanup(void *resolver);
+
+/*
+ * Curl_resolver_duphandle() - the generic low-level name resolve API.
+ * Called from curl_easy_duphandle() to duplicate resolver URL-state specific environment
+ * ('resolver' member of the UrlState structure).
+ * Should duplicate the 'from' handle and pass the resulting handle to the 'to' pointer.
+ * Returning anything else than CURLE_OK causes failed curl_easy_duphandle() call.
+ */
+int Curl_resolver_duphandle(void **to, void *from);
+
 /*
  * Curl_getaddrinfo() is the generic low-level name resolve API within this
  * source file. There are several versions of this function - for different
@@ -139,6 +154,15 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
                                 int port,
                                 int *waitp);
 
+#ifdef CURLRES_ASYNCH
+/*
+ * Curl_async_cancel() is the generic low-level asynchronous name resolve API.
+ * It is called from inside other functions to cancel currently performing resolver
+ * request. Should also free any temporary resources allocated to perform a request.
+ */
+void Curl_async_cancel(struct connectdata *conn);
+#endif
+
 CURLcode Curl_is_resolved(struct connectdata *conn,
                           struct Curl_dns_entry **dns);
 CURLcode Curl_wait_for_resolv(struct connectdata *conn,
@@ -209,13 +233,6 @@ struct Curl_dns_entry *
 Curl_cache_addr(struct SessionHandle *data, Curl_addrinfo *addr,
                 const char *hostname, int port);
 
-/*
- * Curl_destroy_thread_data() cleans up async resolver data.
- * Complementary of ares_destroy.
- */
-struct Curl_async; /* forward-declaration */
-void Curl_destroy_thread_data(struct Curl_async *async);
-
 #ifndef INADDR_NONE
 #define CURL_INADDR_NONE (in_addr_t) ~0
 #else
diff --git a/lib/hostip4.c b/lib/hostip4.c
index 6dc5257656..67aefda291 100644
--- a/lib/hostip4.c
+++ b/lib/hostip4.c
@@ -87,6 +87,7 @@ bool Curl_ipvalid(struct connectdata *conn)
 }
 
 #ifdef CURLRES_SYNCH
+
 /*
  * Curl_getaddrinfo() - the ipv4 synchronous version.
  *
diff --git a/lib/hostsyn.c b/lib/hostsyn.c
index b68e4702ea..799aa69912 100644
--- a/lib/hostsyn.c
+++ b/lib/hostsyn.c
@@ -72,6 +72,61 @@
  **********************************************************************/
 #ifdef CURLRES_SYNCH
 
+/*
+ * Curl_resolver_global_init() - the generic low-level name resolve API.
+ * Called from curl_global_init() to initialize global resolver environment.
+ * Does nothing here.
+ */
+int Curl_resolver_global_init()
+{
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_global_cleanup() - the generic low-level name resolve API.
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ * Does nothing here.
+ */
+void Curl_resolver_global_cleanup()
+{
+}
+
+/*
+ * Curl_resolver_init() - the generic low-level name resolve API.
+ * Called from curl_easy_init() -> Curl_open() to initialize resolver URL-state specific environment
+ * ('resolver' member of the UrlState structure).
+ * Does nothing here.
+ */
+int Curl_resolver_init(void **resolver)
+{
+  (void)resolver;
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_cleanup() - the generic low-level name resolve API.
+ * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver URL-state specific environment
+ * ('resolver' member of the UrlState structure).
+ * Does nothing here.
+ */
+void Curl_resolver_cleanup(void *resolver)
+{
+  (void)resolver;
+}
+
+/*
+ * Curl_resolver_duphandle() - the generic low-level name resolve API.
+ * Called from curl_easy_duphandle() to duplicate resolver URL state-specific environment
+ * ('resolver' member of the UrlState structure).
+ * Does nothing here.
+ */
+int Curl_resolver_duphandle(void **to, void *from)
+{
+  (void)to;
+  (void)from;
+  return CURLE_OK;
+}
+
 /*
  * Curl_wait_for_resolv() for synch-builds.  Curl_resolv() can never return
  * wait==TRUE, so this function will never be called. If it still gets called,
diff --git a/lib/hostthre.c b/lib/hostthre.c
index dc8e4af743..d91a5d77ca 100644
--- a/lib/hostthre.c
+++ b/lib/hostthre.c
@@ -88,6 +88,70 @@
  **********************************************************************/
 #ifdef CURLRES_THREADED
 
+/*
+ * Curl_resolver_global_init() - the generic low-level name resolve API.
+ * Called from curl_global_init() to initialize global resolver environment.
+ * Does nothing here.
+ */
+int Curl_resolver_global_init()
+{
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_global_cleanup() - the generic low-level name resolve API.
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ * Does nothing here.
+ */
+void Curl_resolver_global_cleanup()
+{
+}
+
+/*
+ * Curl_resolver_init() - the generic low-level name resolve API.
+ * Called from curl_easy_init() -> Curl_open() to initialize resolver URL-state specific environment
+ * ('resolver' member of the UrlState structure).
+ * Does nothing here.
+ */
+int Curl_resolver_init(void **resolver)
+{
+  (void)resolver;
+  return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_cleanup() - the generic low-level name resolve API.
+ * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver URL-state specific environment
+ * ('resolver' member of the UrlState structure).
+ * Does nothing here.
+ */
+void Curl_resolver_cleanup(void *resolver)
+{
+  (void)resolver;
+}
+
+/*
+ * Curl_resolver_duphandle() - the generic low-level name resolve API.
+ * Called from curl_easy_duphandle() to duplicate resolver URL state-specific environment
+ * ('resolver' member of the UrlState structure).
+ * Does nothing here.
+ */
+int Curl_resolver_duphandle(void **to, void *from)
+{
+  (void)to;
+  (void)from;
+  return CURLE_OK;
+}
+
+static void destroy_async_data(struct Curl_async *);
+/*
+ * Cancel all possibly still on-going resolves for this connection.
+ */
+void Curl_async_cancel(struct connectdata *conn)
+{
+  destroy_async_data(&conn->async);
+}
+
 /* This function is used to init a threaded resolve */
 static bool init_resolve_thread(struct connectdata *conn,
                                 const char *hostname, int port,
@@ -253,10 +317,9 @@ static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
 #endif /* HAVE_GETADDRINFO */
 
 /*
- * Curl_destroy_thread_data() cleans up async resolver data and thread handle.
- * Complementary of ares_destroy.
+ * destroy_async_data() cleans up async resolver data and thread handle.
  */
-void Curl_destroy_thread_data (struct Curl_async *async)
+static void destroy_async_data (struct Curl_async *async)
 {
   if(async->hostname)
     free(async->hostname);
@@ -336,7 +399,7 @@ static bool init_resolve_thread (struct connectdata *conn,
   return TRUE;
 
  err_exit:
-  Curl_destroy_thread_data(&conn->async);
+  destroy_async_data(&conn->async);
 
   SET_ERRNO(err);
 
@@ -386,7 +449,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
     }
   }
 
-  Curl_destroy_thread_data(&conn->async);
+  destroy_async_data(&conn->async);
 
   if(!conn->async.dns)
     conn->bits.close = TRUE;
@@ -419,7 +482,7 @@ CURLcode Curl_is_resolved(struct connectdata *conn,
 
   if (done) {
     getaddrinfo_complete(conn);
-    Curl_destroy_thread_data(&conn->async);
+    destroy_async_data(&conn->async);
 
     if(!conn->async.dns) {
       failf(data, "Could not resolve host: %s; %s",
diff --git a/lib/url.c b/lib/url.c
index 3bc8db06d0..12c70975d0 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -526,7 +526,7 @@ CURLcode Curl_close(struct SessionHandle *data)
   Curl_safefree(data->info.wouldredirect);
 
   /* this destroys the channel and we cannot use it anymore after this */
-  ares_destroy(data->state.areschannel);
+  Curl_resolver_cleanup(data->state.resolver);
 
   Curl_convert_close(data);
 
@@ -766,9 +766,7 @@ CURLcode Curl_open(struct SessionHandle **curl)
 {
   CURLcode res = CURLE_OK;
   struct SessionHandle *data;
-#ifdef USE_ARES
   int status;
-#endif
 
   /* Very simple start-up: alloc the struct, init it with zeroes and return */
   data = calloc(1, sizeof(struct SessionHandle));
@@ -780,18 +778,11 @@ CURLcode Curl_open(struct SessionHandle **curl)
 
   data->magic = CURLEASY_MAGIC_NUMBER;
 
-#ifdef USE_ARES
-  if((status = ares_init(&data->state.areschannel)) != ARES_SUCCESS) {
-    DEBUGF(fprintf(stderr, "Error: ares_init failed\n"));
+  if( (status=Curl_resolver_init(&data->state.resolver)) != CURLE_OK ) {
+    DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
     free(data);
-    if(status == ARES_ENOMEM)
-      return CURLE_OUT_OF_MEMORY;
-    else
-      return CURLE_FAILED_INIT;
+    return status;
   }
-  /* make sure that all other returns from this function should destroy the
-     ares channel before returning error! */
-#endif
 
   /* We do some initial setup here, all those fields that can't be just 0 */
 
@@ -823,7 +814,7 @@ CURLcode Curl_open(struct SessionHandle **curl)
   }
 
   if(res) {
-    ares_destroy(data->state.areschannel);
+    Curl_resolver_cleanup(data->state.resolver);
     if(data->state.headerbuff)
       free(data->state.headerbuff);
     Curl_freeset(data);
@@ -2521,6 +2512,11 @@ static void conn_free(struct connectdata *conn)
   if(!conn)
     return;
 
+  /* possible left-overs from the async name resolvers */
+#if defined(CURLRES_ASYNCH)
+  Curl_async_cancel(conn);
+#endif
+
   /* close the SSL stuff before we close any sockets since they will/may
      write to the sockets */
   Curl_ssl_close(conn, FIRSTSOCKET);
@@ -2564,6 +2560,7 @@ static void conn_free(struct connectdata *conn)
   Curl_safefree(conn->async.os_specific);
 #endif
   Curl_safefree(conn->localdev);
+
   Curl_free_ssl_config(&conn->ssl_config);
 
   free(conn); /* free all the connection oriented data */
@@ -5195,7 +5192,9 @@ CURLcode Curl_done(struct connectdata **connp,
     data->req.location = NULL;
   }
 
+#if defined(CURLRES_ASYNCH)
   Curl_async_cancel(conn);
+#endif
 
   if(conn->dns_entry) {
     Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
diff --git a/lib/urldata.h b/lib/urldata.h
index c77cc34da5..01f4ffddf2 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -145,14 +145,6 @@
 #endif
 #endif
 
-#ifdef USE_ARES
-#  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
-     (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
-#    define CARES_STATICLIB
-#  endif
-#  include <ares.h>
-#endif
-
 #include <curl/curl.h>
 
 #include "http_chunks.h" /* for the structs and enum stuff */
@@ -508,8 +500,6 @@ struct Curl_async {
   bool done;  /* set TRUE when the lookup is complete */
   int status; /* if done is TRUE, this is the status from the callback */
   void *os_specific;  /* 'struct thread_data' for Windows */
-  int num_pending; /* number of ares_gethostbyname() requests */
-  Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
 };
 #endif
 
@@ -1153,9 +1143,7 @@ struct UrlState {
 
   bool authproblem; /* TRUE if there's some problem authenticating */
 
-#ifdef USE_ARES
-  ares_channel areschannel; /* for name resolves */
-#endif
+  void *resolver; /* resolver state, if it is used in the URL state - ares_channel f.e. */
 
 #if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
   ENGINE *engine;
diff --git a/lib/version.c b/lib/version.c
index 97dd16395e..7c19c9c80f 100644
--- a/lib/version.c
+++ b/lib/version.c
@@ -33,7 +33,7 @@
 #include <curl/mprintf.h>
 
 #ifdef USE_ARES
-#include <ares_version.h>
+#include <ares.h>
 #endif
 
 #ifdef USE_LIBIDN
-- 
GitLab