Hi Daniel,

The applied patch (against the developer branch) is proposed to be included into the curl because: - it allows to extend asynchronous resolvers set much more easy than the existing code - it defines stable and rich set of asynchronous and synchronous resolver interface functions
  - it removes any resolver implementation details from the common code

Some details:

The resolver interface now contains the following additional functions described and documented in the hostip.h:

int Curl_resolver_global_init(void);
void Curl_resolver_global_cleanup(void);
int Curl_resolver_init(void **resolver);
void Curl_resolver_cleanup(void *resolver);
int Curl_resolver_duphandle(void **to, void *from);

The function

void Curl_async_cancel(struct connectdata *conn);

now totally replaces the function Curl_destroy_thread_data() which has been moved to the static part of the threaded resolver and renamed to destroy_async_data().

All resolver function calls except asynchronous are unconditional. The asynchronous Curl_async_cancel() call depends only from CURLRES_ASYNCH define.

Resolver-specific part is removed from the async member of the connection and moved to os_specific which now is allocated and deallocated totally from inside a resolver.

The patch has been tested on the applied test (which is a little modification of the previous one) with c-ares, threaded, and default resolvers. The starting part of the test suite has been passed, I don't have a time right now to pass the whole set, hope they will be passed also.

Regards,
Vsevolod

diff --git a/lib/easy.c b/lib/easy.c
index 6f55347..f63bb4e 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -280,12 +280,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)) {
@@ -351,9 +349,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();
@@ -692,12 +688,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;
 
 #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
   outcurl->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
diff --git a/lib/hostares.c b/lib/hostares.c
index a165cb9..177d7ca 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,128 @@
 #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;
+  if((status = ares_init((ares_channel*)resolver)) != 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)
+{
+  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 = 0;
+        }
+        free(res);
+    }
+    async->os_specific = NULL;
+  }
+
+  async->hostname = NULL;
+}
 
 /*
  * Curl_resolv_fdset() is called when someone from the outside world (using
@@ -103,14 +222,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 +257,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 +284,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 +308,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 +346,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 +364,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
@@ -250,7 +374,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
     else
       timeout_ms = 1000;
 
-    waitperform(conn, timeout_ms);
+    Curl_is_resolved(conn,&temp_entry);
 
     if(conn->async.done)
       break;
@@ -267,7 +391,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 +437,19 @@ 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 = 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 +463,42 @@ 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 */
+    res->last_status = status;
     break;
   }
-
-  (void)Curl_addrinfo_callback(arg, status, ai);
 }
 
 /*
@@ -404,31 +557,32 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
   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 *)(conn->async.os_specific = calloc(sizeof(struct ResolverResults),1));
 #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 710ff50..5269c08 100644
--- a/lib/hostasyn.c
+++ b/lib/hostasyn.c
@@ -73,20 +73,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 3f27a2b..ac5704e 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
@@ -120,6 +96,45 @@ int Curl_resolv_timeout(struct connectdata *conn, const char *hostname,
 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
  * name resolve layers (selected at build-time). They all take this same set
@@ -130,6 +145,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,
@@ -200,13 +224,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 6dc5257..67aefda 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 b68e470..f920999 100644
--- a/lib/hostsyn.c
+++ b/lib/hostsyn.c
@@ -73,6 +73,57 @@
 #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,
  * we return failure at once.
diff --git a/lib/hostthre.c b/lib/hostthre.c
index d45a899..39e8afa 100644
--- a/lib/hostthre.c
+++ b/lib/hostthre.c
@@ -88,6 +88,66 @@
  **********************************************************************/
 #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 +313,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 +395,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 +445,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 +478,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 c075541..3958326 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -556,7 +556,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);
 
 #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
   /* close iconv conversion descriptors */
@@ -807,9 +807,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));
@@ -821,18 +819,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 */
 
@@ -869,7 +860,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);
@@ -2563,6 +2554,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);
@@ -2597,13 +2593,6 @@ static void conn_free(struct connectdata *conn)
   Curl_llist_destroy(conn->pend_pipe, NULL);
   Curl_llist_destroy(conn->done_pipe, NULL);
 
-  /* possible left-overs from the async name resolvers */
-#if defined(CURLRES_THREADED)
-  Curl_destroy_thread_data(&conn->async);
-#elif defined(CURLRES_ASYNCH)
-  Curl_safefree(conn->async.hostname);
-  Curl_safefree(conn->async.os_specific);
-#endif
 
   Curl_free_ssl_config(&conn->ssl_config);
 
@@ -5241,7 +5230,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 bf74aaf..90b28bf 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -141,14 +141,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 */
@@ -499,8 +491,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
 
@@ -1157,9 +1147,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 52989cd..4e0a3db 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
#include <curl/curl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "memdebug.h"

#define MAIN_LOOP_HANG_TIMEOUT     90 * 1000
#define MULTI_PERFORM_HANG_TIMEOUT 60 * 1000

static int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
  (void)clientp;
  fprintf(stderr, "Progress %f %f %f %f ...\n", dltotal, dlnow, ultotal, ulnow);
  return 0;
}

static int start_and_cancel(char *URL,int steps)
{
  int res = 0;
  int counter = 0;
  CURL *curl;
  int running;
  CURLM *m = NULL;

  fprintf(stderr, "Starting ...\n");
  if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
    fprintf(stderr, "curl_global_init() failed\n");
    return 2;
  }

  if ((curl = curl_easy_init()) == NULL) {
    fprintf(stderr, "curl_easy_init() failed\n");
    curl_global_cleanup();
    return 2;
  }

  curl_easy_setopt(curl, CURLOPT_URL, URL);
  curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);

  /* we want to use our own progress function */
  curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
  curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback);

  fprintf(stderr, "Initializing ...\n");
  if ((m = curl_multi_init()) == NULL) {
    fprintf(stderr, "curl_multi_init() failed\n");
    curl_easy_cleanup(curl);
    curl_global_cleanup();
    return 2;
  }

  if ((res = (int)curl_multi_add_handle(m, curl)) != CURLM_OK) {
    fprintf(stderr, "curl_multi_add_handle() failed, "
            "with code %d\n", res);
    curl_multi_cleanup(m);
    curl_easy_cleanup(curl);
    curl_global_cleanup();
    return 2;
  }

  fprintf(stderr, "Start at URL %s, steps = %d\n",URL,steps);

  for(counter=0; counter < steps; counter++) {
      usleep(1000);
      fprintf(stderr, "Step for URL %s:%d started ...\n",URL,counter);
      res = (int)curl_multi_perform(m, &running);
      fprintf(stderr, "Step for URL %s:%d finished, res = %d\n",URL,counter,res);
  }

  fprintf(stderr, "Canceling URL %s\n",URL);

  curl_easy_cleanup(curl);
  curl_multi_cleanup(m);
  curl_global_cleanup();

  sleep(2);

  return CURLE_OK;
}

int test(char *URL)
{
  int steps=0;
  for(steps=10; steps > 0; steps--) {
    start_and_cancel(URL,steps);
  }
}

int main(int ac, char **av)
{
    if( ac > 1 )
        test(av[1]);
    else {
        fprintf(stderr, "Usage: %s URL\n",av[0]);
        return 1;
    }
    return 0;
}
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html

Reply via email to