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