asynch resolvers: unified

Introducing an internal API for handling of different async resolver
backends.
This commit is contained in:
Vsevolod Novikov 2011-01-29 20:12:10 +01:00 committed by Daniel Stenberg
parent 722f286f80
commit ca015f1a45
10 changed files with 393 additions and 181 deletions

View File

@ -269,12 +269,10 @@ CURLcode curl_global_init(long flags)
idna_init(); idna_init();
#endif #endif
#ifdef CARES_HAVE_ARES_LIBRARY_INIT if( Curl_resolver_global_init() != CURLE_OK ) {
if(ares_library_init(ARES_LIB_INIT_ALL)) { DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
DEBUGF(fprintf(stderr, "Error: ares_library_init failed\n"));
return CURLE_FAILED_INIT; return CURLE_FAILED_INIT;
} }
#endif
#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_INIT) #if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_INIT)
if(libssh2_init(0)) { if(libssh2_init(0)) {
@ -340,9 +338,7 @@ void curl_global_cleanup(void)
if(init_flags & CURL_GLOBAL_SSL) if(init_flags & CURL_GLOBAL_SSL)
Curl_ssl_cleanup(); Curl_ssl_cleanup();
#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP Curl_resolver_global_cleanup();
ares_library_cleanup();
#endif
if(init_flags & CURL_GLOBAL_WIN32) if(init_flags & CURL_GLOBAL_WIN32)
win32_cleanup(); win32_cleanup();
@ -676,12 +672,9 @@ CURL *curl_easy_duphandle(CURL *incurl)
outcurl->change.referer_alloc = TRUE; outcurl->change.referer_alloc = TRUE;
} }
#ifdef USE_ARES /* Clone the resolver handle, if present, for the new handle */
/* If we use ares, we clone the ares channel for the new handle */ if( Curl_resolver_duphandle(&outcurl->state.resolver, data->state.resolver) != CURLE_OK )
if(ARES_SUCCESS != ares_dup(&outcurl->state.areschannel,
data->state.areschannel))
goto fail; goto fail;
#endif
Curl_convert_setup(outcurl); Curl_convert_setup(outcurl);

View File

@ -60,6 +60,12 @@
#define in_addr_t unsigned long #define in_addr_t unsigned long
#endif #endif
/***********************************************************************
* Only for ares-enabled builds
**********************************************************************/
#ifdef CURLRES_ARES
#include "urldata.h" #include "urldata.h"
#include "sendf.h" #include "sendf.h"
#include "hostip.h" #include "hostip.h"
@ -76,15 +82,132 @@
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #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" #include "curl_memory.h"
/* The last #include file should be: */ /* The last #include file should be: */
#include "memdebug.h" #include "memdebug.h"
/*********************************************************************** struct ResolverResults {
* Only for ares-enabled builds 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 * 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 maxtime;
struct timeval timebuf; struct timeval timebuf;
struct timeval *timeout; 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); (ares_socket_t *)socks, numsocks);
maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
maxtime.tv_usec = 0; 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, Curl_expire(conn->data,
(timeout->tv_sec * 1000) + (timeout->tv_usec/1000)); (timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
@ -138,7 +261,7 @@ static int waitperform(struct connectdata *conn, int timeout_ms)
int i; int i;
int num = 0; 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++) { for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
pfd[i].events = 0; pfd[i].events = 0;
@ -165,11 +288,11 @@ static int waitperform(struct connectdata *conn, int timeout_ms)
if(!nfds) if(!nfds)
/* Call ares_process() unconditonally here, even if we simply timed out /* Call ares_process() unconditonally here, even if we simply timed out
above, as otherwise the ares name resolve won't timeout! */ 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 { else {
/* move through the descriptors and ask for processing on them */ /* move through the descriptors and ask for processing on them */
for(i=0; i < num; i++) 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].revents & (POLLRDNORM|POLLIN)?
pfd[i].fd:ARES_SOCKET_BAD, pfd[i].fd:ARES_SOCKET_BAD,
pfd[i].revents & (POLLWRNORM|POLLOUT)? pfd[i].revents & (POLLWRNORM|POLLOUT)?
@ -189,13 +312,17 @@ CURLcode Curl_is_resolved(struct connectdata *conn,
struct Curl_dns_entry **dns) struct Curl_dns_entry **dns)
{ {
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
struct ResolverResults *res = (struct ResolverResults *)conn->async.os_specific;
*dns = NULL; *dns = NULL;
waitperform(conn, 0); waitperform(conn, 0);
if(conn->async.done) { if( res && !res->num_pending ) {
/* we're done, kill the ares handle */ (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) { if(!conn->async.dns) {
failf(data, "Could not resolve host: %s (%s)", conn->host.dispname, failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
ares_strerror(conn->async.status)); ares_strerror(conn->async.status));
@ -223,6 +350,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
long timeout; long timeout;
struct timeval now = Curl_tvnow(); struct timeval now = Curl_tvnow();
struct Curl_dns_entry *temp_entry;
timeout = Curl_timeleft(data, &now, TRUE); timeout = Curl_timeleft(data, &now, TRUE);
if(!timeout) if(!timeout)
@ -240,7 +368,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
store.tv_sec = itimeout/1000; store.tv_sec = itimeout/1000;
store.tv_usec = (itimeout%1000)*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 /* 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 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; timeout_ms = 1000;
waitperform(conn, timeout_ms); waitperform(conn, timeout_ms);
Curl_is_resolved(conn,&temp_entry);
if(conn->async.done) if(conn->async.done)
break; break;
@ -267,7 +396,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
} }
if(timeout < 0) { if(timeout < 0) {
/* our timeout, so we cancel the ares operation */ /* our timeout, so we cancel the ares operation */
ares_cancel(data->state.areschannel); ares_cancel((ares_channel)data->state.resolver);
break; break;
} }
} }
@ -313,6 +442,22 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn,
return rc; 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 * ares_query_completed_cb() is the callback that ares will call when
* the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(), * 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 hostent *hostent)
{ {
struct connectdata *conn = (struct connectdata *)arg; 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 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
(void)timeouts; /* ignored */ (void)timeouts; /* ignored */
#endif #endif
res->num_pending--;
switch(status) { switch(status) {
case CURL_ASYNC_SUCCESS: case CURL_ASYNC_SUCCESS:
ai = Curl_he2ai(hostent, conn->async.port); ares_compound_results(res,Curl_he2ai(hostent, conn->async.port));
break; 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! */ valid! */
return; /* conn->magic check instead
case ARES_EDESTRUCTION:
return; */
default: default:
/* do nothing */
break; break;
} }
/* The successfull result empties any error */
(void)Curl_addrinfo_callback(arg, status, ai); if( res->last_status != ARES_SUCCESS )
res->last_status = status;
} }
/* /*
@ -402,33 +565,41 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
#endif /* CURLRES_IPV6 */ #endif /* CURLRES_IPV6 */
bufp = strdup(hostname); bufp = strdup(hostname);
if(bufp) { if(bufp) {
struct ResolverResults *res = NULL;
Curl_safefree(conn->async.hostname); Curl_safefree(conn->async.hostname);
conn->async.hostname = bufp; conn->async.hostname = bufp;
conn->async.port = port; conn->async.port = port;
conn->async.done = FALSE; /* not done */ conn->async.done = FALSE; /* not done */
conn->async.status = 0; /* clear */ conn->async.status = 0; /* clear */
conn->async.dns = NULL; /* 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 */ #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
if(family == PF_UNSPEC) { if(family == PF_UNSPEC) {
conn->async.num_pending = 2; res->num_pending = 2;
/* areschannel is already setup in the Curl_open() function */ /* 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_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); ares_query_completed_cb, conn);
} }
else else
#endif /* CURLRES_IPV6 */ #endif /* CURLRES_IPV6 */
{ {
conn->async.num_pending = 1; res->num_pending = 1;
/* areschannel is already setup in the Curl_open() function */ /* 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); ares_query_completed_cb, conn);
} }

View File

@ -72,20 +72,6 @@
**********************************************************************/ **********************************************************************/
#ifdef CURLRES_ASYNCH #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() * Curl_addrinfo_callback() gets called by ares, gethostbyname_thread()
* or getaddrinfo_thread() when we got the name resolved (or not!). * or getaddrinfo_thread() when we got the name resolved (or not!).
@ -109,24 +95,6 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn,
if(ai) { if(ai) {
struct SessionHandle *data = conn->data; 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) if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); 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); Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
} }
else { 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; 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; conn->async.dns = dns;

View File

@ -35,14 +35,6 @@
#define in_addr_t unsigned long #define in_addr_t unsigned long
#endif #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 /* Allocate enough memory to hold the full name information structs and
* everything. OSF1 is known to require at least 8872 bytes. The buffer * everything. OSF1 is known to require at least 8872 bytes. The buffer
* required for storing all possible aliases and IP numbers is according to * 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 #define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this
many seconds for a name resolve */ 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 CURL_ASYNC_SUCCESS CURLE_OK
#define ares_cancel(x) do {} while(0)
#define ares_destroy(x) do {} while(0)
#endif
struct addrinfo; struct addrinfo;
struct hostent; struct hostent;
struct SessionHandle; struct SessionHandle;
struct connectdata; 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. * 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 * 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); 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 * Curl_getaddrinfo() is the generic low-level name resolve API within this
* source file. There are several versions of this function - for different * 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 port,
int *waitp); 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, CURLcode Curl_is_resolved(struct connectdata *conn,
struct Curl_dns_entry **dns); struct Curl_dns_entry **dns);
CURLcode Curl_wait_for_resolv(struct connectdata *conn, 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, Curl_cache_addr(struct SessionHandle *data, Curl_addrinfo *addr,
const char *hostname, int port); 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 #ifndef INADDR_NONE
#define CURL_INADDR_NONE (in_addr_t) ~0 #define CURL_INADDR_NONE (in_addr_t) ~0
#else #else

View File

@ -87,6 +87,7 @@ bool Curl_ipvalid(struct connectdata *conn)
} }
#ifdef CURLRES_SYNCH #ifdef CURLRES_SYNCH
/* /*
* Curl_getaddrinfo() - the ipv4 synchronous version. * Curl_getaddrinfo() - the ipv4 synchronous version.
* *

View File

@ -72,6 +72,61 @@
**********************************************************************/ **********************************************************************/
#ifdef CURLRES_SYNCH #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 * 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, * wait==TRUE, so this function will never be called. If it still gets called,

View File

@ -88,6 +88,70 @@
**********************************************************************/ **********************************************************************/
#ifdef CURLRES_THREADED #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 */ /* This function is used to init a threaded resolve */
static bool init_resolve_thread(struct connectdata *conn, static bool init_resolve_thread(struct connectdata *conn,
const char *hostname, int port, const char *hostname, int port,
@ -253,10 +317,9 @@ static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
#endif /* HAVE_GETADDRINFO */ #endif /* HAVE_GETADDRINFO */
/* /*
* Curl_destroy_thread_data() cleans up async resolver data and thread handle. * destroy_async_data() cleans up async resolver data and thread handle.
* Complementary of ares_destroy.
*/ */
void Curl_destroy_thread_data (struct Curl_async *async) static void destroy_async_data (struct Curl_async *async)
{ {
if(async->hostname) if(async->hostname)
free(async->hostname); free(async->hostname);
@ -336,7 +399,7 @@ static bool init_resolve_thread (struct connectdata *conn,
return TRUE; return TRUE;
err_exit: err_exit:
Curl_destroy_thread_data(&conn->async); destroy_async_data(&conn->async);
SET_ERRNO(err); 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) if(!conn->async.dns)
conn->bits.close = TRUE; conn->bits.close = TRUE;
@ -419,7 +482,7 @@ CURLcode Curl_is_resolved(struct connectdata *conn,
if (done) { if (done) {
getaddrinfo_complete(conn); getaddrinfo_complete(conn);
Curl_destroy_thread_data(&conn->async); destroy_async_data(&conn->async);
if(!conn->async.dns) { if(!conn->async.dns) {
failf(data, "Could not resolve host: %s; %s", failf(data, "Could not resolve host: %s; %s",

View File

@ -526,7 +526,7 @@ CURLcode Curl_close(struct SessionHandle *data)
Curl_safefree(data->info.wouldredirect); Curl_safefree(data->info.wouldredirect);
/* this destroys the channel and we cannot use it anymore after this */ /* 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); Curl_convert_close(data);
@ -766,9 +766,7 @@ CURLcode Curl_open(struct SessionHandle **curl)
{ {
CURLcode res = CURLE_OK; CURLcode res = CURLE_OK;
struct SessionHandle *data; struct SessionHandle *data;
#ifdef USE_ARES
int status; int status;
#endif
/* Very simple start-up: alloc the struct, init it with zeroes and return */ /* Very simple start-up: alloc the struct, init it with zeroes and return */
data = calloc(1, sizeof(struct SessionHandle)); data = calloc(1, sizeof(struct SessionHandle));
@ -780,18 +778,11 @@ CURLcode Curl_open(struct SessionHandle **curl)
data->magic = CURLEASY_MAGIC_NUMBER; data->magic = CURLEASY_MAGIC_NUMBER;
#ifdef USE_ARES if( (status=Curl_resolver_init(&data->state.resolver)) != CURLE_OK ) {
if((status = ares_init(&data->state.areschannel)) != ARES_SUCCESS) { DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
DEBUGF(fprintf(stderr, "Error: ares_init failed\n"));
free(data); free(data);
if(status == ARES_ENOMEM) return status;
return CURLE_OUT_OF_MEMORY;
else
return CURLE_FAILED_INIT;
} }
/* 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 */ /* 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) { if(res) {
ares_destroy(data->state.areschannel); Curl_resolver_cleanup(data->state.resolver);
if(data->state.headerbuff) if(data->state.headerbuff)
free(data->state.headerbuff); free(data->state.headerbuff);
Curl_freeset(data); Curl_freeset(data);
@ -2521,6 +2512,11 @@ static void conn_free(struct connectdata *conn)
if(!conn) if(!conn)
return; 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 /* close the SSL stuff before we close any sockets since they will/may
write to the sockets */ write to the sockets */
Curl_ssl_close(conn, FIRSTSOCKET); Curl_ssl_close(conn, FIRSTSOCKET);
@ -2564,6 +2560,7 @@ static void conn_free(struct connectdata *conn)
Curl_safefree(conn->async.os_specific); Curl_safefree(conn->async.os_specific);
#endif #endif
Curl_safefree(conn->localdev); Curl_safefree(conn->localdev);
Curl_free_ssl_config(&conn->ssl_config); Curl_free_ssl_config(&conn->ssl_config);
free(conn); /* free all the connection oriented data */ free(conn); /* free all the connection oriented data */
@ -5195,7 +5192,9 @@ CURLcode Curl_done(struct connectdata **connp,
data->req.location = NULL; data->req.location = NULL;
} }
#if defined(CURLRES_ASYNCH)
Curl_async_cancel(conn); Curl_async_cancel(conn);
#endif
if(conn->dns_entry) { if(conn->dns_entry) {
Curl_resolv_unlock(data, conn->dns_entry); /* done with this */ Curl_resolv_unlock(data, conn->dns_entry); /* done with this */

View File

@ -145,14 +145,6 @@
#endif #endif
#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 <curl/curl.h>
#include "http_chunks.h" /* for the structs and enum stuff */ #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 */ bool done; /* set TRUE when the lookup is complete */
int status; /* if done is TRUE, this is the status from the callback */ int status; /* if done is TRUE, this is the status from the callback */
void *os_specific; /* 'struct thread_data' for Windows */ 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 #endif
@ -1153,9 +1143,7 @@ struct UrlState {
bool authproblem; /* TRUE if there's some problem authenticating */ bool authproblem; /* TRUE if there's some problem authenticating */
#ifdef USE_ARES void *resolver; /* resolver state, if it is used in the URL state - ares_channel f.e. */
ares_channel areschannel; /* for name resolves */
#endif
#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) #if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
ENGINE *engine; ENGINE *engine;

View File

@ -33,7 +33,7 @@
#include <curl/mprintf.h> #include <curl/mprintf.h>
#ifdef USE_ARES #ifdef USE_ARES
#include <ares_version.h> #include <ares.h>
#endif #endif
#ifdef USE_LIBIDN #ifdef USE_LIBIDN