ares awareness/usage/support added. If configure --enable-ares is used, we

build libcurl to use ares for asynch name resolves.
This commit is contained in:
Daniel Stenberg 2003-08-05 14:40:59 +00:00
parent f85935f0f9
commit b73612392d
11 changed files with 629 additions and 182 deletions

View File

@ -12,10 +12,16 @@ EXTRA_DIST = getdate.y Makefile.b32 Makefile.b32.resp Makefile.m32 \
lib_LTLIBRARIES = libcurl.la lib_LTLIBRARIES = libcurl.la
if ARES
ARESINC = -I$(top_srcdir)/ares
endif
# we use srcdir/include for the static global include files # we use srcdir/include for the static global include files
# we use builddir/lib for the generated lib/config.h file to get found # we use builddir/lib for the generated lib/config.h file to get found
# we use srcdir/lib for the lib-private header files # we use srcdir/lib for the lib-private header files
INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/lib -I$(top_srcdir)/lib INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/lib -I$(top_srcdir)/lib $(ARESINC)
LDFLAGS = -L$(top_srcdir)/lib
VERSION=-version-info 2:2:0 VERSION=-version-info 2:2:0
@ -48,15 +54,18 @@ VERSION=-version-info 2:2:0
# #
if NO_UNDEFINED if NO_UNDEFINED
# The -no-undefined flag is CRUCIAL for this to build fine on Cygwin. If we # The -no-undefined flag is CRUCIAL for this to build fine on Cygwin.
# find a case in which we need to remove this flag, we should most likely UNDEF = -no-undefined
# write a configure check that detects when this flag is needed and when its
# not.
libcurl_la_LDFLAGS = -no-undefined $(VERSION)
else else
libcurl_la_LDFLAGS = $(VERSION) UNDEF =
endif endif
if ARES
ARESLIB = -lares -L$(top_builddir)/ares
endif
libcurl_la_LDFLAGS = $(UNDEF) $(VERSION) $(ARESLIB)
libcurl_la_SOURCES = arpa_telnet.h file.c getpass.h netrc.h timeval.c \ libcurl_la_SOURCES = arpa_telnet.h file.c getpass.h netrc.h timeval.c \
base64.c file.h hostip.c progress.c timeval.h base64.h formdata.c \ base64.c file.h hostip.c progress.c timeval.h base64.h formdata.c \
hostip.h progress.h cookie.c formdata.h http.c sendf.c cookie.h ftp.c \ hostip.h progress.h cookie.c formdata.h http.c sendf.c cookie.h ftp.c \

View File

@ -208,6 +208,7 @@ static CURLcode bindlocal(struct connectdata *conn,
size_t size; size_t size;
char myhost[256] = ""; char myhost[256] = "";
in_addr_t in; in_addr_t in;
int rc;
/* First check if the given name is an IP address */ /* First check if the given name is an IP address */
in=inet_addr(data->set.device); in=inet_addr(data->set.device);
@ -217,7 +218,10 @@ static CURLcode bindlocal(struct connectdata *conn,
/* /*
* We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer * We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer
*/ */
h = Curl_resolv(data, myhost, 0); rc = Curl_resolv(conn, myhost, 0, &h);
if(rc == 1)
rc = Curl_wait_for_resolv(conn, &h);
} }
else { else {
if(strlen(data->set.device)>1) { if(strlen(data->set.device)>1) {
@ -225,11 +229,14 @@ static CURLcode bindlocal(struct connectdata *conn,
* This was not an interface, resolve the name as a host name * This was not an interface, resolve the name as a host name
* or IP number * or IP number
*/ */
h = Curl_resolv(data, data->set.device, 0); rc = Curl_resolv(conn, data->set.device, 0, &h);
if(h) { if(rc == 1)
rc = Curl_wait_for_resolv(conn, &h);
if(h)
/* we know data->set.device is shorter than the myhost array */ /* we know data->set.device is shorter than the myhost array */
strcpy(myhost, data->set.device); strcpy(myhost, data->set.device);
}
} }
} }

View File

@ -1231,18 +1231,24 @@ CURLcode ftp_use_port(struct connectdata *conn)
if(data->set.ftpport) { if(data->set.ftpport) {
in_addr_t in; in_addr_t in;
int rc;
/* First check if the given name is an IP address */ /* First check if the given name is an IP address */
in=inet_addr(data->set.ftpport); in=inet_addr(data->set.ftpport);
if((in == CURL_INADDR_NONE) && if((in == CURL_INADDR_NONE) &&
Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) { Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
h = Curl_resolv(data, myhost, 0); rc = Curl_resolv(conn, myhost, 0, &h);
if(rc == 1)
rc = Curl_wait_for_resolv(conn, &h);
} }
else { else {
int len = strlen(data->set.ftpport); int len = strlen(data->set.ftpport);
if(len>1) if(len>1) {
h = Curl_resolv(data, data->set.ftpport, 0); rc = Curl_resolv(conn, data->set.ftpport, 0, &h);
if(rc == 1)
rc = Curl_wait_for_resolv(conn, &h);
}
if(h) if(h)
strcpy(myhost, data->set.ftpport); /* buffer overflow risk */ strcpy(myhost, data->set.ftpport); /* buffer overflow risk */
} }
@ -1381,6 +1387,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
CURLcode result; CURLcode result;
struct Curl_dns_entry *addr=NULL; struct Curl_dns_entry *addr=NULL;
Curl_ipconnect *conninfo; Curl_ipconnect *conninfo;
int rc;
/* /*
Here's the excecutive summary on what to do: Here's the excecutive summary on what to do:
@ -1505,14 +1512,20 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
* We don't want to rely on a former host lookup that might've expired * We don't want to rely on a former host lookup that might've expired
* now, instead we remake the lookup here and now! * now, instead we remake the lookup here and now!
*/ */
addr = Curl_resolv(data, conn->proxyhost, conn->port); rc = Curl_resolv(conn, conn->proxyhost, conn->port, &addr);
if(rc == 1)
rc = Curl_wait_for_resolv(conn, &addr);
connectport = connectport =
(unsigned short)conn->port; /* we connect to the proxy's port */ (unsigned short)conn->port; /* we connect to the proxy's port */
} }
else { else {
/* normal, direct, ftp connection */ /* normal, direct, ftp connection */
addr = Curl_resolv(data, newhostp, newport); rc = Curl_resolv(conn, newhostp, newport, &addr);
if(rc == 1)
rc = Curl_wait_for_resolv(conn, &addr);
if(!addr) { if(!addr) {
failf(data, "Can't resolve new host %s:%d", newhostp, newport); failf(data, "Can't resolve new host %s:%d", newhostp, newport);
return CURLE_FTP_CANT_GET_HOST; return CURLE_FTP_CANT_GET_HOST;

View File

@ -65,6 +65,7 @@
#include "hostip.h" #include "hostip.h"
#include "hash.h" #include "hash.h"
#include "share.h" #include "share.h"
#include "url.h"
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -81,10 +82,13 @@
static curl_hash hostname_cache; static curl_hash hostname_cache;
static int host_cache_initialized; static int host_cache_initialized;
static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data, static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
char *hostname, char *hostname,
int port, int port,
char **bufp); int *waitp);
#if !defined(HAVE_GETHOSTBYNAME_R) || defined(USE_ARES)
static struct hostent* pack_hostent(char** buf, struct hostent* orig);
#endif
void Curl_global_host_cache_init(void) void Curl_global_host_cache_init(void)
{ {
@ -135,15 +139,14 @@ create_hostcache_id(char *server, int port, ssize_t *entry_len)
char *id = NULL; char *id = NULL;
/* Get the length of the new entry id */ /* Get the length of the new entry id */
*entry_len = *entry_len + /* Hostname length */ *entry_len = *entry_len + /* Hostname length */
1 + /* The ':' seperator */ 1 + /* ':' seperator */
_num_chars(port); /* The number of characters the port will take up */ _num_chars(port); /* number of characters the port will take up */
/* Allocate the new entry id */ /* Allocate the new entry id */
id = malloc(*entry_len + 1); id = malloc(*entry_len + 1);
if (!id) { if (!id)
return NULL; return NULL;
}
/* Create the new entry */ /* Create the new entry */
/* If sprintf() doesn't return the entry length, that signals failure */ /* If sprintf() doesn't return the entry length, that signals failure */
@ -192,57 +195,26 @@ hostcache_prune(curl_hash *hostcache, int cache_timeout, int now)
hostcache_timestamp_remove); hostcache_timestamp_remove);
} }
#if defined(CURLDEBUG) && defined(AGGRESIVE_TEST)
/* Called from Curl_done() to check that there's no DNS cache entry with
a non-zero counter left. */
void Curl_scan_cache_used(void *user, void *ptr)
{
struct Curl_dns_entry *e = ptr;
(void)user; /* prevent compiler warning */
if(e->inuse) {
fprintf(stderr, "*** WARNING: locked DNS cache entry detected: %s\n",
e->entry_id);
/* perform a segmentation fault to draw attention */
*(void **)0 = 0;
}
}
#endif
/* Macro to save redundant free'ing of entry_id */
#define HOSTCACHE_RETURN(dns) \
{ \
free(entry_id); \
if(data->share) \
{ \
Curl_share_unlock(data, CURL_LOCK_DATA_DNS); \
} \
return dns; \
}
#ifdef HAVE_SIGSETJMP #ifdef HAVE_SIGSETJMP
/* Beware this is a global and unique instance */ /* Beware this is a global and unique instance */
sigjmp_buf curl_jmpenv; sigjmp_buf curl_jmpenv;
#endif #endif
struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data,
char *hostname,
int port)
{
char *entry_id = NULL;
struct Curl_dns_entry *dns = NULL;
ssize_t entry_len;
time_t now;
char *bufp;
#ifdef HAVE_SIGSETJMP /* When calling Curl_resolv() has resulted in a response with a returned
/* this allows us to time-out from the name resolver, as the timeout address, we call this function to store the information in the dns
will generate a signal and we will siglongjmp() from that here */ cache etc */
if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) {
/* this is coming from a siglongjmp() */ static struct Curl_dns_entry *
failf(data, "name lookup timed out"); cache_resolv_response(struct SessionHandle *data,
return NULL; Curl_addrinfo *addr,
} char *hostname,
#endif int port)
{
char *entry_id;
int entry_len;
struct Curl_dns_entry *dns;
time_t now;
/* Create an entry id, based upon the hostname and port */ /* Create an entry id, based upon the hostname and port */
entry_len = strlen(hostname); entry_len = strlen(hostname);
@ -251,45 +223,112 @@ struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data,
if (!entry_id) if (!entry_id)
return NULL; return NULL;
/* Create a new cache entry */
dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry));
if (!dns) {
Curl_freeaddrinfo(addr);
free(entry_id);
return NULL;
}
dns->inuse = 0;
dns->addr = addr;
/* Store it in our dns cache */
Curl_hash_add(data->hostcache, entry_id, entry_len+1,
(const void *) dns);
time(&now);
dns->timestamp = now;
dns->inuse++; /* mark entry as in-use */
/* Remove outdated and unused entries from the hostcache */
hostcache_prune(data->hostcache,
data->set.dns_cache_timeout,
now);
/* free the allocated entry_id again */
free(entry_id);
return dns;
}
/* Resolve a name and return a pointer in the 'entry' argument if one
is available.
Return codes:
-1 = error, no pointer
0 = OK, pointer provided
1 = waiting for response, no pointer
*/
int Curl_resolv(struct connectdata *conn,
char *hostname,
int port,
struct Curl_dns_entry **entry)
{
char *entry_id = NULL;
struct Curl_dns_entry *dns = NULL;
ssize_t entry_len;
int wait;
struct SessionHandle *data = conn->data;
/* default to failure */
int rc = -1;
*entry = NULL;
#ifdef HAVE_SIGSETJMP
/* this allows us to time-out from the name resolver, as the timeout
will generate a signal and we will siglongjmp() from that here */
if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) {
/* this is coming from a siglongjmp() */
failf(data, "name lookup timed out");
return -1;
}
#endif
/* Create an entry id, based upon the hostname and port */
entry_len = strlen(hostname);
entry_id = create_hostcache_id(hostname, port, &entry_len);
/* If we can't create the entry id, fail */
if (!entry_id)
return -1;
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);
/* See if its already in our dns cache */ /* See if its already in our dns cache */
dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1); dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1);
/* free the allocated entry_id again */
free(entry_id);
if (!dns) { if (!dns) {
Curl_addrinfo *addr = my_getaddrinfo(data, hostname, port, &bufp); /* The entry was not in the cache. Resolve it to IP address */
/* If my_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
value indicating that we need to wait for the response to the resolve
call */
Curl_addrinfo *addr = my_getaddrinfo(conn, hostname, port, &wait);
if (!addr) { if (!addr) {
HOSTCACHE_RETURN(NULL); if(wait)
/* the response to our resolve call will come asynchronously at
a later time, good or bad */
rc = 1;
} }
else
/* Create a new cache entry */ /* we got a response, store it in the cache */
dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry)); dns = cache_resolv_response(data, addr, hostname, port);
if (!dns) {
Curl_freeaddrinfo(addr);
HOSTCACHE_RETURN(NULL);
}
dns->inuse = 0;
dns->addr = addr;
/* Save it in our host cache */
Curl_hash_add(data->hostcache, entry_id, entry_len+1, (const void *) dns);
} }
time(&now);
dns->timestamp = now; if(data->share)
dns->inuse++; /* mark entry as in-use */ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
#ifdef CURLDEBUG
dns->entry_id = entry_id;
#endif
/* Remove outdated and unused entries from the hostcache */ *entry = dns;
hostcache_prune(data->hostcache,
data->set.dns_cache_timeout,
now);
HOSTCACHE_RETURN(dns); return rc;
} }
void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns) void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
@ -314,7 +353,7 @@ void Curl_freeaddrinfo(Curl_addrinfo *p)
#ifdef ENABLE_IPV6 #ifdef ENABLE_IPV6
freeaddrinfo(p); freeaddrinfo(p);
#else #else
free(p); free(p); /* works fine for the ARES case too */
#endif #endif
} }
@ -332,7 +371,203 @@ void Curl_freednsinfo(void *freethis)
/* --- resolve name or IP-number --- */ /* --- resolve name or IP-number --- */
#ifdef ENABLE_IPV6 /* 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
* Stevens' Unix Network Programming 2nd edition, p. 304: 8192 bytes!
*/
#define CURL_NAMELOOKUP_SIZE 9000
#ifdef USE_ARES
CURLcode Curl_multi_ares_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp)
{
int max = ares_fds(conn->data->state.areschannel,
read_fd_set, write_fd_set);
*max_fdp = max;
return CURLE_OK;
}
/* called to check if the name is resolved now */
CURLcode Curl_is_resolved(struct connectdata *conn, bool *done)
{
fd_set read_fds, write_fds;
static const struct timeval tv={0,0};
int count;
struct SessionHandle *data = conn->data;
int nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
count = select(nfds, &read_fds, &write_fds, NULL,
(struct timeval *)&tv);
if(count)
ares_process(data->state.areschannel, &read_fds, &write_fds);
if(conn->async.done) {
*done = TRUE;
if(!conn->async.dns)
return CURLE_COULDNT_RESOLVE_HOST;
}
else
*done = FALSE;
return CURLE_OK;
}
/* This is a function that locks and waits until the name resolve operation
has completed.
If 'entry' is non-NULL, make it point to the resolved dns entry
Return CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
CURLE_OPERATION_TIMEDOUT if a time-out occurred.
*/
CURLcode Curl_wait_for_resolv(struct connectdata *conn,
struct Curl_dns_entry **entry)
{
CURLcode rc=CURLE_OK;
struct SessionHandle *data = conn->data;
/* Wait for the name resolve query to complete. */
while (1) {
int nfds=0;
fd_set read_fds, write_fds;
struct timeval *tvp, tv;
int count;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
if (nfds == 0)
break;
tvp = ares_timeout(data->state.areschannel,
NULL, /* pass in our maximum time here */
&tv);
count = select(nfds, &read_fds, &write_fds, NULL, tvp);
if (count < 0 && errno != EINVAL)
break;
ares_process(data->state.areschannel, &read_fds, &write_fds);
}
/* Operation complete, if the lookup was successful we now have the entry
in the cache. */
/* this destroys the channel and we cannot use it anymore after this */
ares_destroy(data->state.areschannel);
if(entry)
*entry = conn->async.dns;
if(!conn->async.dns) {
/* a name was not resolved */
if(conn->async.done)
rc = CURLE_COULDNT_RESOLVE_HOST;
else
rc = CURLE_OPERATION_TIMEDOUT;
/* close the connection, since we can't return failure here without
cleaning up this connection properly */
Curl_disconnect(conn);
}
return rc;
}
/* this function gets called by ares when we got the name resolved */
static void host_callback(void *arg, /* "struct connectdata *" */
int status,
struct hostent *hostent)
{
struct connectdata *conn = (struct connectdata *)arg;
struct Curl_dns_entry *dns = NULL;
conn->async.done = TRUE;
conn->async.status = status;
if(ARES_SUCCESS == status) {
/* we got a resolved name in 'hostent' */
char *bufp = (char *)malloc(CURL_NAMELOOKUP_SIZE);
if(bufp) {
/* pack_hostent() copies to and shrinks the target buffer */
struct hostent *he = pack_hostent(&bufp, hostent);
dns = cache_resolv_response(conn->data, he,
conn->async.hostname, conn->async.port);
}
}
conn->async.dns = dns;
/* The input hostent struct will be freed by ares when we return from this
function */
}
/*
* Return name information about the given hostname and port number. If
* successful, the 'hostent' is returned and the forth argument will point to
* memory we need to free after use. That meory *MUST* be freed with
* Curl_freeaddrinfo(), nothing else.
*/
static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
char *hostname,
int port,
int *waitp)
{
int rc;
char *bufp;
struct SessionHandle *data = conn->data;
rc = ares_init(&data->state.areschannel);
*waitp = FALSE;
if(!rc) {
/* only if success */
bufp = strdup(hostname);
if(bufp) {
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 */
ares_gethostbyname(data->state.areschannel, hostname, PF_INET,
host_callback, conn);
*waitp = TRUE; /* please wait for the response */
}
else
ares_destroy(data->state.areschannel);
}
return NULL; /* no struct yet */
}
#else
/* For builds without ARES, Curl_resolv() can never return wait==TRUE,
so this function will never be called. If it still gets called, we
return failure at once. */
CURLcode Curl_wait_for_resolv(struct connectdata *conn,
struct Curl_dns_entry **entry)
{
(void)conn;
*entry=NULL;
return CURLE_COULDNT_RESOLVE_HOST;
}
#endif
#if defined(ENABLE_IPV6) && !defined(USE_ARES)
#ifdef CURLDEBUG #ifdef CURLDEBUG
/* These two are strictly for memory tracing and are using the same /* These two are strictly for memory tracing and are using the same
@ -377,15 +612,16 @@ void curl_freeaddrinfo(struct addrinfo *freethis,
* memory we need to free after use. That meory *MUST* be freed with * memory we need to free after use. That meory *MUST* be freed with
* Curl_freeaddrinfo(), nothing else. * Curl_freeaddrinfo(), nothing else.
*/ */
static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data, static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
char *hostname, char *hostname,
int port, int port,
char **bufp) int *waitp)
{ {
struct addrinfo hints, *res; struct addrinfo hints, *res;
int error; int error;
char sbuf[NI_MAXSERV]; char sbuf[NI_MAXSERV];
int s, pf = PF_UNSPEC; int s, pf = PF_UNSPEC;
struct SessionHandle *data = conn->data;
/* see if we have an IPv6 stack */ /* see if we have an IPv6 stack */
s = socket(PF_INET6, SOCK_DGRAM, 0); s = socket(PF_INET6, SOCK_DGRAM, 0);
@ -410,20 +646,18 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port); infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
return NULL; return NULL;
} }
*bufp=(char *)res; /* make it point to the result struct */ *waitp=0; /* don't wait, we have the response now */
return res; return res;
} }
#else /* following code is IPv4-only */ #else /* following code is IPv4-only */
#ifndef HAVE_GETHOSTBYNAME_R #if !defined(HAVE_GETHOSTBYNAME_R) || defined(USE_ARES)
static void hostcache_fixoffset(struct hostent *h, int offset); static void hostcache_fixoffset(struct hostent *h, int offset);
/** /*
* Performs a "deep" copy of a hostent into a buffer (returns a pointer to the * Performs a "deep" copy of a hostent into a buffer (returns a pointer to the
* copy). Make absolutely sure the destination buffer is big enough! * copy). Make absolutely sure the destination buffer is big enough!
* */
* Keith McGuigan
* 10/3/2001 */
static struct hostent* pack_hostent(char** buf, struct hostent* orig) static struct hostent* pack_hostent(char** buf, struct hostent* orig)
{ {
char *bufptr; char *bufptr;
@ -512,6 +746,25 @@ static struct hostent* pack_hostent(char** buf, struct hostent* orig)
} }
#endif #endif
static void hostcache_fixoffset(struct hostent *h, int offset)
{
int i=0;
h->h_name=(char *)((long)h->h_name+offset);
h->h_aliases=(char **)((long)h->h_aliases+offset);
while(h->h_aliases[i]) {
h->h_aliases[i]=(char *)((long)h->h_aliases[i]+offset);
i++;
}
h->h_addr_list=(char **)((long)h->h_addr_list+offset);
i=0;
while(h->h_addr_list[i]) {
h->h_addr_list[i]=(char *)((long)h->h_addr_list[i]+offset);
i++;
}
}
#ifndef USE_ARES
static char *MakeIP(unsigned long num, char *addr, int addr_len) static char *MakeIP(unsigned long num, char *addr, int addr_len)
{ {
#if defined(HAVE_INET_NTOA) || defined(HAVE_INET_NTOA_R) #if defined(HAVE_INET_NTOA) || defined(HAVE_INET_NTOA_R)
@ -533,43 +786,24 @@ static char *MakeIP(unsigned long num, char *addr, int addr_len)
return (addr); return (addr);
} }
static void hostcache_fixoffset(struct hostent *h, int offset)
{
int i=0;
h->h_name=(char *)((long)h->h_name+offset);
h->h_aliases=(char **)((long)h->h_aliases+offset);
while(h->h_aliases[i]) {
h->h_aliases[i]=(char *)((long)h->h_aliases[i]+offset);
i++;
}
h->h_addr_list=(char **)((long)h->h_addr_list+offset);
i=0;
while(h->h_addr_list[i]) {
h->h_addr_list[i]=(char *)((long)h->h_addr_list[i]+offset);
i++;
}
}
/* The original code to this function was once stolen from the Dancer source /* The original code to this function was once stolen from the Dancer source
code, written by Bjorn Reese, it has since been patched and modified code, written by Bjorn Reese, it has since been patched and modified
considerably. */ considerably. */
static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data, static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
char *hostname, char *hostname,
int port, int port,
char **bufp) int *waitp)
{ {
struct hostent *h = NULL; struct hostent *h = NULL;
in_addr_t in; in_addr_t in;
int ret; /* this variable is unused on several platforms but used on some */ int ret; /* this variable is unused on several platforms but used on some */
struct SessionHandle *data = conn->data;
#define CURL_NAMELOOKUP_SIZE 9000 (void)port; /* unused in IPv4 code */
/* 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
* Stevens' Unix Network Programming 2nd editor, p. 304: 8192 bytes! */
port=0; /* unused in IPv4 code */
ret = 0; /* to prevent the compiler warning */ ret = 0; /* to prevent the compiler warning */
*waitp = 0; /* don't wait, we act synchronously */
in=inet_addr(hostname); in=inet_addr(hostname);
if (in != CURL_INADDR_NONE) { if (in != CURL_INADDR_NONE) {
struct in_addr *addrentry; struct in_addr *addrentry;
@ -581,7 +815,6 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
} *buf = (struct namebuf *)malloc(sizeof(struct namebuf)); } *buf = (struct namebuf *)malloc(sizeof(struct namebuf));
if(!buf) if(!buf)
return NULL; /* major failure */ return NULL; /* major failure */
*bufp = (char *)buf;
h = &buf->hostentry; h = &buf->hostentry;
h->h_addr_list = &buf->h_addr_list[0]; h->h_addr_list = &buf->h_addr_list[0];
@ -602,7 +835,6 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE); int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE);
if(!buf) if(!buf)
return NULL; /* major failure */ return NULL; /* major failure */
*bufp=(char *)buf;
/* Workaround for gethostbyname_r bug in qnx nto. It is also _required_ /* Workaround for gethostbyname_r bug in qnx nto. It is also _required_
for some of these functions. */ for some of these functions. */
@ -638,7 +870,6 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
offset=(long)h-(long)buf; offset=(long)h-(long)buf;
hostcache_fixoffset(h, offset); hostcache_fixoffset(h, offset);
buf=(int *)h; buf=(int *)h;
*bufp=(char *)buf;
} }
else else
#endif #endif
@ -687,7 +918,6 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
offset=(long)h-(long)buf; offset=(long)h-(long)buf;
hostcache_fixoffset(h, offset); hostcache_fixoffset(h, offset);
buf=(int *)h; buf=(int *)h;
*bufp=(char *)buf;
} }
else else
#endif #endif
@ -730,13 +960,11 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
infof(data, "gethostbyname_r(2) failed for %s\n", hostname); infof(data, "gethostbyname_r(2) failed for %s\n", hostname);
h = NULL; /* set return code to NULL */ h = NULL; /* set return code to NULL */
free(buf); free(buf);
*bufp=NULL;
} }
#else #else
else { else {
if ((h = gethostbyname(hostname)) == NULL ) { if ((h = gethostbyname(hostname)) == NULL ) {
infof(data, "gethostbyname(2) failed for %s\n", hostname); infof(data, "gethostbyname(2) failed for %s\n", hostname);
*bufp=NULL;
} }
else else
{ {
@ -745,7 +973,6 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
static one we got a pointer to might get removed when we don't static one we got a pointer to might get removed when we don't
want/expect that */ want/expect that */
h = pack_hostent(&buf, h); h = pack_hostent(&buf, h);
*bufp=(char *)buf;
} }
#endif #endif
} }
@ -753,3 +980,5 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
} }
#endif /* end of IPv4-specific code */ #endif /* end of IPv4-specific code */
#endif /* end of !USE_ARES */

View File

@ -29,6 +29,7 @@
struct addrinfo; struct addrinfo;
struct hostent; struct hostent;
struct SessionHandle; struct SessionHandle;
struct connectdata;
void Curl_global_host_cache_init(void); void Curl_global_host_cache_init(void);
void Curl_global_host_cache_dtor(void); void Curl_global_host_cache_dtor(void);
@ -41,9 +42,6 @@ struct Curl_dns_entry {
time_t timestamp; time_t timestamp;
long inuse; /* use-counter, make very sure you decrease this long inuse; /* use-counter, make very sure you decrease this
when you're done using the address you received */ when you're done using the address you received */
#ifdef CURLDEBUG
char *entry_id;
#endif
}; };
/* /*
@ -54,10 +52,18 @@ struct Curl_dns_entry {
* use, or we'll leak memory! * use, or we'll leak memory!
*/ */
struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data, int Curl_resolv(struct connectdata *conn,
char *hostname, char *hostname,
int port); int port,
struct Curl_dns_entry **dnsentry);
CURLcode Curl_is_resolved(struct connectdata *conn, bool *done);
CURLcode Curl_wait_for_resolv(struct connectdata *conn,
struct Curl_dns_entry **dnsentry);
CURLcode Curl_multi_ares_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp);
/* unlock a previously resolved dns entry */ /* unlock a previously resolved dns entry */
void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns); void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns);

View File

@ -56,7 +56,8 @@ struct Curl_message {
typedef enum { typedef enum {
CURLM_STATE_INIT, CURLM_STATE_INIT,
CURLM_STATE_CONNECT, /* connect has been sent off */ CURLM_STATE_CONNECT, /* resolve/connect has been sent off */
CURLM_STATE_WAITRESOLVE, /* we're awaiting the resolve to finalize */
CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */ CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */
CURLM_STATE_DO, /* send off the request (part 1) */ CURLM_STATE_DO, /* send off the request (part 1) */
CURLM_STATE_DO_MORE, /* send off the request (part 2) */ CURLM_STATE_DO_MORE, /* send off the request (part 2) */
@ -239,6 +240,14 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle,
switch(easy->state) { switch(easy->state) {
default: default:
break; break;
case CURLM_STATE_WAITRESOLVE:
/* waiting for a resolve to complete */
Curl_multi_ares_fdset(easy->easy_conn, read_fd_set, write_fd_set,
&this_max_fd);
if(this_max_fd > *max_fd)
*max_fd = this_max_fd;
break;
case CURLM_STATE_WAITCONNECT: case CURLM_STATE_WAITCONNECT:
case CURLM_STATE_DO_MORE: case CURLM_STATE_DO_MORE:
{ {
@ -293,6 +302,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
CURLMcode result=CURLM_OK; CURLMcode result=CURLM_OK;
struct Curl_message *msg = NULL; struct Curl_message *msg = NULL;
bool connected; bool connected;
bool async;
*running_handles = 0; /* bump this once for every living handle */ *running_handles = 0; /* bump this once for every living handle */
@ -320,6 +330,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
easy->easy_handle->state.used_interface = Curl_if_multi; easy->easy_handle->state.used_interface = Curl_if_multi;
} }
break; break;
case CURLM_STATE_CONNECT: case CURLM_STATE_CONNECT:
if (Curl_global_host_cache_use(easy->easy_handle)) { if (Curl_global_host_cache_use(easy->easy_handle)) {
easy->easy_handle->hostcache = Curl_global_host_cache_get(); easy->easy_handle->hostcache = Curl_global_host_cache_get();
@ -333,16 +344,46 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
/* Connect. We get a connection identifier filled in. */ /* Connect. We get a connection identifier filled in. */
Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, &async);
/* after the connect has been sent off, go WAITCONNECT */
if(CURLE_OK == easy->result) { if(CURLE_OK == easy->result) {
easy->state = CURLM_STATE_WAITCONNECT; if(async)
result = CURLM_CALL_MULTI_PERFORM; /* We're now waiting for an asynchronous name lookup */
easy->state = CURLM_STATE_WAITRESOLVE;
else {
/* after the connect has been sent off, go WAITCONNECT */
easy->state = CURLM_STATE_WAITCONNECT;
result = CURLM_CALL_MULTI_PERFORM;
}
}
break;
case CURLM_STATE_WAITRESOLVE:
/* awaiting an asynch name resolve to complete */
{
bool done;
/* check if we have the name resolved by now */
easy->result = Curl_is_resolved(easy->easy_conn, &done);
if(done) {
/* Perform the next step in the connection phase, and then move on
to the WAITCONNECT state */
easy->result = Curl_async_resolved(easy->easy_conn);
easy->state = CURLM_STATE_WAITCONNECT;
}
if(CURLE_OK != easy->result) {
/* failure detected */
easy->easy_conn = NULL; /* no more connection */
break;
}
} }
break; break;
case CURLM_STATE_WAITCONNECT: case CURLM_STATE_WAITCONNECT:
/* awaiting a completion of an asynch connect */
{ {
bool connected; bool connected;
easy->result = Curl_is_connected(easy->easy_conn, easy->result = Curl_is_connected(easy->easy_conn,

View File

@ -185,7 +185,7 @@ int fileno( FILE *stream);
* Information regarding a single IP witin a Curl_addrinfo MUST be stored in * Information regarding a single IP witin a Curl_addrinfo MUST be stored in
* a Curl_ipconnect struct. * a Curl_ipconnect struct.
*/ */
#ifdef ENABLE_IPV6 #if defined(ENABLE_IPV6) && !defined(USE_ARES)
typedef struct addrinfo Curl_addrinfo; typedef struct addrinfo Curl_addrinfo;
typedef struct addrinfo Curl_ipconnect; typedef struct addrinfo Curl_ipconnect;
#else #else

View File

@ -605,8 +605,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
len = end-start+1; len = end-start+1;
/* allocate memory of a cloned copy */ /* allocate memory of a cloned copy */
if(data->info.contenttype) Curl_safefree(data->info.contenttype);
free(data->info.contenttype);
data->info.contenttype = malloc(len + 1); data->info.contenttype = malloc(len + 1);
if (NULL == data->info.contenttype) if (NULL == data->info.contenttype)
@ -1903,10 +1902,22 @@ CURLcode Curl_perform(struct SessionHandle *data)
do { do {
int urlchanged = FALSE; int urlchanged = FALSE;
do { do {
bool async;
Curl_pgrsTime(data, TIMER_STARTSINGLE); Curl_pgrsTime(data, TIMER_STARTSINGLE);
data->change.url_changed = FALSE; data->change.url_changed = FALSE;
res = Curl_connect(data, &conn); res = Curl_connect(data, &conn, &async);
if((CURLE_OK == res) && async) {
/* Now, if async is TRUE here, we need to wait for the name
to resolve */
res = Curl_wait_for_resolv(conn, NULL);
if(CURLE_OK == res)
/* Resolved, continue with the connection */
res = Curl_async_resolved(conn);
}
if(res)
break;
/* If a callback (or something) has altered the URL we should use within /* If a callback (or something) has altered the URL we should use within
the Curl_connect(), we detect it here and act as if we are redirected the Curl_connect(), we detect it here and act as if we are redirected
to the new URL */ to the new URL */

164
lib/url.c
View File

@ -147,7 +147,11 @@ static unsigned int ConnectionStore(struct SessionHandle *data,
struct connectdata *conn); struct connectdata *conn);
static bool safe_strequal(char* str1, char* str2); static bool safe_strequal(char* str1, char* str2);
#if !defined(WIN32)||defined(__CYGWIN32__) #ifndef USE_ARES
/* not for Win32, unless it is cygwin
not for ares builds */
#if !defined(WIN32) || defined(__CYGWIN32__)
#ifndef RETSIGTYPE #ifndef RETSIGTYPE
#define RETSIGTYPE void #define RETSIGTYPE void
#endif #endif
@ -165,6 +169,7 @@ RETSIGTYPE alarmfunc(int signal)
return; return;
} }
#endif #endif
#endif /* USE_ARES */
void Curl_safefree(void *ptr) void Curl_safefree(void *ptr)
{ {
@ -1286,7 +1291,11 @@ CURLcode Curl_disconnect(struct connectdata *conn)
Curl_safefree(conn->allocptr.host); Curl_safefree(conn->allocptr.host);
Curl_safefree(conn->allocptr.cookiehost); Curl_safefree(conn->allocptr.cookiehost);
Curl_safefree(conn->proxyhost); Curl_safefree(conn->proxyhost);
#ifdef USE_ARES
/* possible left-overs from the async name resolve */
Curl_safefree(conn->async.hostname);
#endif
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 */
@ -1632,7 +1641,15 @@ static int handleSock5Proxy(
#ifndef ENABLE_IPV6 #ifndef ENABLE_IPV6
struct Curl_dns_entry *dns; struct Curl_dns_entry *dns;
Curl_addrinfo *hp=NULL; Curl_addrinfo *hp=NULL;
dns = Curl_resolv(conn->data, conn->hostname, conn->remote_port); int rc = Curl_resolv(conn, conn->hostname, conn->remote_port, &dns);
if(rc == -1)
return CURLE_COULDNT_RESOLVE_HOST;
if(rc == 1)
/* this requires that we're in "wait for resolve" state */
rc = Curl_wait_for_resolv(conn, &dns);
/* /*
* We cannot use 'hostent' as a struct that Curl_resolv() returns. It * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
* returns a Curl_addrinfo pointer that may not always look the same. * returns a Curl_addrinfo pointer that may not always look the same.
@ -1841,8 +1858,19 @@ CURLcode Curl_protocol_connect(struct connectdata *conn,
return result; /* pass back status */ return result; /* pass back status */
} }
/*
* CreateConnection() sets up a new connectdata struct, or re-uses an already
* existing one, and resolves host name.
*
* if this function returns CURLE_OK and *async is set to TRUE, the resolve
* response will be coming asynchronously. If *async is FALSE, the name is
* already resolved.
*/
static CURLcode CreateConnection(struct SessionHandle *data, static CURLcode CreateConnection(struct SessionHandle *data,
struct connectdata **in_connect) struct connectdata **in_connect,
struct Curl_dns_entry **addr,
bool *async)
{ {
char *tmp; char *tmp;
CURLcode result=CURLE_OK; CURLcode result=CURLE_OK;
@ -1859,7 +1887,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
char passwd[MAX_CURL_PASSWORD_LENGTH]; char passwd[MAX_CURL_PASSWORD_LENGTH];
bool passwdgiven=FALSE; /* set TRUE if an application-provided password has bool passwdgiven=FALSE; /* set TRUE if an application-provided password has
been set */ been set */
int rc;
#ifdef HAVE_SIGACTION #ifdef HAVE_SIGACTION
struct sigaction keep_sigact; /* store the old struct here */ struct sigaction keep_sigact; /* store the old struct here */
@ -1870,6 +1898,9 @@ static CURLcode CreateConnection(struct SessionHandle *data,
#endif #endif
#endif #endif
*addr = NULL; /* nothing yet */
*async = FALSE;
/************************************************************* /*************************************************************
* Check input data * Check input data
*************************************************************/ *************************************************************/
@ -2875,8 +2906,10 @@ static CURLcode CreateConnection(struct SessionHandle *data,
/* else, no chunky upload */ /* else, no chunky upload */
FALSE; FALSE;
#ifndef USE_ARES
/************************************************************* /*************************************************************
* Set timeout if that is being used * Set timeout if that is being used, and we're not using an asynchronous
* name resolve.
*************************************************************/ *************************************************************/
if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) { if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
/************************************************************* /*************************************************************
@ -2919,7 +2952,8 @@ static CURLcode CreateConnection(struct SessionHandle *data,
has been done since then until now. */ has been done since then until now. */
#endif #endif
} }
#endif
/************************************************************* /*************************************************************
* Resolve the name of the server or proxy * Resolve the name of the server or proxy
*************************************************************/ *************************************************************/
@ -2935,9 +2969,11 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->port = conn->remote_port; /* it is the same port */ conn->port = conn->remote_port; /* it is the same port */
/* Resolve target host right on */ /* Resolve target host right on */
hostaddr = Curl_resolv(data, conn->name, conn->port); rc = Curl_resolv(conn, conn->name, conn->port, &hostaddr);
if(rc == 1)
*async = TRUE;
if(!hostaddr) { else if(!hostaddr) {
failf(data, "Couldn't resolve host '%s'", conn->name); failf(data, "Couldn't resolve host '%s'", conn->name);
result = CURLE_COULDNT_RESOLVE_HOST; result = CURLE_COULDNT_RESOLVE_HOST;
/* don't return yet, we need to clean up the timeout first */ /* don't return yet, we need to clean up the timeout first */
@ -2947,15 +2983,19 @@ static CURLcode CreateConnection(struct SessionHandle *data,
/* This is a proxy that hasn't been resolved yet. */ /* This is a proxy that hasn't been resolved yet. */
/* resolve proxy */ /* resolve proxy */
hostaddr = Curl_resolv(data, conn->proxyhost, conn->port); rc = Curl_resolv(conn, conn->proxyhost, conn->port, &hostaddr);
if(!hostaddr) { if(rc == 1)
*async = TRUE;
else if(!hostaddr) {
failf(data, "Couldn't resolve proxy '%s'", conn->proxyhost); failf(data, "Couldn't resolve proxy '%s'", conn->proxyhost);
result = CURLE_COULDNT_RESOLVE_PROXY; result = CURLE_COULDNT_RESOLVE_PROXY;
/* don't return yet, we need to clean up the timeout first */ /* don't return yet, we need to clean up the timeout first */
} }
} }
Curl_pgrsTime(data, TIMER_NAMELOOKUP); *addr = hostaddr;
#ifdef HAVE_ALARM #ifdef HAVE_ALARM
if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) { if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
#ifdef HAVE_SIGACTION #ifdef HAVE_SIGACTION
@ -2995,7 +3035,25 @@ static CURLcode CreateConnection(struct SessionHandle *data,
alarm(0); /* just shut it off */ alarm(0); /* just shut it off */
} }
#endif #endif
if(result)
return result;
}
/* SetupConnection() should be called after the name resolve initiated in
* CreateConnection() is all done.
*/
static CURLcode SetupConnection(struct connectdata *conn,
struct Curl_dns_entry *hostaddr)
{
struct SessionHandle *data = conn->data;
CURLcode result=CURLE_OK;
Curl_pgrsTime(data, TIMER_NAMELOOKUP);
if(conn->protocol & PROT_FILE)
/* There's nothing in this function to setup if we're only doing
a file:// transfer */
return result; return result;
/************************************************************* /*************************************************************
@ -3007,8 +3065,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->proxyuser, conn->proxypasswd); conn->proxyuser, conn->proxypasswd);
if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer), if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer),
&authorization) >= 0) { &authorization) >= 0) {
if(conn->allocptr.proxyuserpwd) Curl_safefree(conn->allocptr.proxyuserpwd);
free(conn->allocptr.proxyuserpwd);
conn->allocptr.proxyuserpwd = conn->allocptr.proxyuserpwd =
aprintf("Proxy-authorization: Basic %s\015\012", authorization); aprintf("Proxy-authorization: Basic %s\015\012", authorization);
free(authorization); free(authorization);
@ -3022,16 +3079,14 @@ static CURLcode CreateConnection(struct SessionHandle *data,
if((conn->protocol&PROT_HTTP) || if((conn->protocol&PROT_HTTP) ||
(data->change.proxy && *data->change.proxy)) { (data->change.proxy && *data->change.proxy)) {
if(data->set.useragent) { if(data->set.useragent) {
if(conn->allocptr.uagent) Curl_safefree(conn->allocptr.uagent);
free(conn->allocptr.uagent);
conn->allocptr.uagent = conn->allocptr.uagent =
aprintf("User-Agent: %s\015\012", data->set.useragent); aprintf("User-Agent: %s\015\012", data->set.useragent);
} }
} }
if(data->set.encoding) { if(data->set.encoding) {
if(conn->allocptr.accept_encoding) Curl_safefree(conn->allocptr.accept_encoding);
free(conn->allocptr.accept_encoding);
conn->allocptr.accept_encoding = conn->allocptr.accept_encoding =
aprintf("Accept-Encoding: %s\015\012", data->set.encoding); aprintf("Accept-Encoding: %s\015\012", data->set.encoding);
} }
@ -3083,26 +3138,60 @@ static CURLcode CreateConnection(struct SessionHandle *data,
} }
CURLcode Curl_connect(struct SessionHandle *data, CURLcode Curl_connect(struct SessionHandle *data,
struct connectdata **in_connect) struct connectdata **in_connect,
bool *asyncp)
{ {
CURLcode code; CURLcode code;
struct connectdata *conn; struct Curl_dns_entry *dns;
*asyncp = FALSE; /* assume synchronous resolves by default */
/* call the stuff that needs to be called */ /* call the stuff that needs to be called */
code = CreateConnection(data, in_connect); code = CreateConnection(data, in_connect, &dns, asyncp);
if(CURLE_OK == code) {
/* no error */
if(dns || !*asyncp)
/* If an address is available it means that we already have the name
resolved, OR it isn't async.
If so => continue connecting from here */
code = SetupConnection(*in_connect, dns);
/* else
response will be received and treated async wise */
}
if(CURLE_OK != code) { if(CURLE_OK != code) {
/* We're not allowed to return failure with memory left allocated /* We're not allowed to return failure with memory left allocated
in the connectdata struct, free those here */ in the connectdata struct, free those here */
conn = (struct connectdata *)*in_connect; if(*in_connect) {
if(conn) { Curl_disconnect(*in_connect); /* close the connection */
Curl_disconnect(conn); /* close the connection */ *in_connect = NULL; /* return a NULL */
*in_connect = NULL; /* return a NULL */
} }
} }
return code; return code;
} }
/* Call this function after Curl_connect() has returned async=TRUE and
then a successful name resolve has been received */
CURLcode Curl_async_resolved(struct connectdata *conn)
{
#ifdef USE_ARES
CURLcode code = SetupConnection(conn, conn->async.dns);
if(code)
/* We're not allowed to return failure with memory left allocated
in the connectdata struct, free those here */
Curl_disconnect(conn); /* close the connection */
return code;
#else
(void)conn;
return CURLE_OK;
#endif
}
CURLcode Curl_done(struct connectdata *conn) CURLcode Curl_done(struct connectdata *conn)
{ {
struct SessionHandle *data=conn->data; struct SessionHandle *data=conn->data;
@ -3179,11 +3268,28 @@ CURLcode Curl_do(struct connectdata **connp)
conn->bits.close = TRUE; /* enforce close of this connetion */ conn->bits.close = TRUE; /* enforce close of this connetion */
result = Curl_done(conn); /* we are so done with this */ result = Curl_done(conn); /* we are so done with this */
if(CURLE_OK == result) { if(CURLE_OK == result) {
bool async;
/* Now, redo the connect and get a new connection */ /* Now, redo the connect and get a new connection */
result = Curl_connect(data, connp); result = Curl_connect(data, connp, &async);
if(CURLE_OK == result) if(CURLE_OK == result) {
/* We have connected or sent away a name resolve query fine */
if(async) {
/* Now, if async is TRUE here, we need to wait for the name
to resolve */
result = Curl_wait_for_resolv(conn, NULL);
if(result)
return result;
/* Resolved, continue with the connection */
result = Curl_async_resolved(conn);
if(result)
return result;
}
/* ... finally back to actually retry the DO phase */ /* ... finally back to actually retry the DO phase */
result = conn->curl_do(*connp); result = conn->curl_do(conn);
}
} }
} }
} }

View File

@ -30,7 +30,9 @@
CURLcode Curl_open(struct SessionHandle **curl); CURLcode Curl_open(struct SessionHandle **curl);
CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...); CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...);
CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */ CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */
CURLcode Curl_connect(struct SessionHandle *, struct connectdata **); CURLcode Curl_connect(struct SessionHandle *, struct connectdata **,
bool *async);
CURLcode Curl_async_resolved(struct connectdata *conn);
CURLcode Curl_do(struct connectdata **); CURLcode Curl_do(struct connectdata **);
CURLcode Curl_do_more(struct connectdata *); CURLcode Curl_do_more(struct connectdata *);
CURLcode Curl_done(struct connectdata *); CURLcode Curl_done(struct connectdata *);

View File

@ -90,6 +90,10 @@
#include <gssapi.h> #include <gssapi.h>
#endif #endif
#ifdef USE_ARES
#include <ares.h>
#endif
/* Download buffer size, keep it fairly big for speed reasons */ /* Download buffer size, keep it fairly big for speed reasons */
#define BUFSIZE CURL_MAX_WRITE_SIZE #define BUFSIZE CURL_MAX_WRITE_SIZE
@ -364,6 +368,16 @@ struct Curl_transfer_keeper {
bool ignorebody; /* we read a response-body but we ignore it! */ bool ignorebody; /* we read a response-body but we ignore it! */
}; };
#ifdef USE_ARES
struct Curl_async {
char *hostname;
int port;
struct Curl_dns_entry *dns;
bool done; /* set TRUE when the lookup is complete */
int status; /* if done is TRUE, this is the status from the callback */
};
#endif
/* /*
* The connectdata struct contains all fields and variables that should be * The connectdata struct contains all fields and variables that should be
* unique for an entire connection. * unique for an entire connection.
@ -538,6 +552,11 @@ struct connectdata {
because it authenticates connections, not because it authenticates connections, not
single requests! */ single requests! */
struct ntlmdata proxyntlm; /* NTLM data for proxy */ struct ntlmdata proxyntlm; /* NTLM data for proxy */
#ifdef USE_ARES
/* data used for the asynch name resolve callback */
struct Curl_async async;
#endif
}; };
/* The end of connectdata. */ /* The end of connectdata. */
@ -669,6 +688,10 @@ struct UrlState {
long authwant; /* inherited from what the user set with CURLOPT_HTTPAUTH */ long authwant; /* inherited from what the user set with CURLOPT_HTTPAUTH */
long authavail; /* what the server reports */ long authavail; /* what the server reports */
#ifdef USE_ARES
ares_channel areschannel; /* for name resolves */
#endif
}; };