From ca015f1a45c68aa1d641678cfc13ce0df0c58fe0 Mon Sep 17 00:00:00 2001 From: Vsevolod Novikov Date: Sat, 29 Jan 2011 20:12:10 +0100 Subject: 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 05d8ded9d..9ce80d086 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 a165cb91f..1b6978d51 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 +# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ + (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__)) +# define CARES_STATICLIB +# endif +# include + +#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 710ff50b7..5269c08dc 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 a7c334568..70ce7cd74 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 -#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 6dc525765..67aefda29 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 b68e4702e..799aa6991 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 dc8e4af74..d91a5d77c 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 3bc8db06d..12c70975d 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 c77cc34da..01f4ffddf 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 -#endif - #include #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 97dd16395..7c19c9c80 100644 --- a/lib/version.c +++ b/lib/version.c @@ -33,7 +33,7 @@ #include #ifdef USE_ARES -#include +#include #endif #ifdef USE_LIBIDN -- cgit v1.2.3