aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Makefile.am23
-rw-r--r--lib/connect.c15
-rw-r--r--lib/ftp.c23
-rw-r--r--lib/hostip.c447
-rw-r--r--lib/hostip.h18
-rw-r--r--lib/multi.c51
-rw-r--r--lib/setup.h2
-rw-r--r--lib/transfer.c17
-rw-r--r--lib/url.c166
-rw-r--r--lib/url.h4
-rw-r--r--lib/urldata.h23
11 files changed, 618 insertions, 171 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 421b4fecb..ffa6be2b1 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -12,10 +12,16 @@ EXTRA_DIST = getdate.y Makefile.b32 Makefile.b32.resp Makefile.m32 \
lib_LTLIBRARIES = libcurl.la
+if ARES
+ARESINC = -I$(top_srcdir)/ares
+endif
+
# 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 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
@@ -48,15 +54,18 @@ VERSION=-version-info 2:2:0
#
if NO_UNDEFINED
-# The -no-undefined flag is CRUCIAL for this to build fine on Cygwin. If we
-# find a case in which we need to remove this flag, we should most likely
-# write a configure check that detects when this flag is needed and when its
-# not.
-libcurl_la_LDFLAGS = -no-undefined $(VERSION)
+# The -no-undefined flag is CRUCIAL for this to build fine on Cygwin.
+UNDEF = -no-undefined
else
-libcurl_la_LDFLAGS = $(VERSION)
+UNDEF =
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 \
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 \
diff --git a/lib/connect.c b/lib/connect.c
index 681fc0039..9bc7d5076 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -208,6 +208,7 @@ static CURLcode bindlocal(struct connectdata *conn,
size_t size;
char myhost[256] = "";
in_addr_t in;
+ int rc;
/* First check if the given name is an IP address */
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
*/
- h = Curl_resolv(data, myhost, 0);
+ rc = Curl_resolv(conn, myhost, 0, &h);
+ if(rc == 1)
+ rc = Curl_wait_for_resolv(conn, &h);
+
}
else {
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
* or IP number
*/
- h = Curl_resolv(data, data->set.device, 0);
- if(h) {
+ rc = Curl_resolv(conn, data->set.device, 0, &h);
+ if(rc == 1)
+ rc = Curl_wait_for_resolv(conn, &h);
+
+ if(h)
/* we know data->set.device is shorter than the myhost array */
strcpy(myhost, data->set.device);
- }
+
}
}
diff --git a/lib/ftp.c b/lib/ftp.c
index 6acab245b..bb3b9feb3 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -1231,18 +1231,24 @@ CURLcode ftp_use_port(struct connectdata *conn)
if(data->set.ftpport) {
in_addr_t in;
+ int rc;
/* First check if the given name is an IP address */
in=inet_addr(data->set.ftpport);
if((in == CURL_INADDR_NONE) &&
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 {
int len = strlen(data->set.ftpport);
- if(len>1)
- h = Curl_resolv(data, data->set.ftpport, 0);
+ if(len>1) {
+ rc = Curl_resolv(conn, data->set.ftpport, 0, &h);
+ if(rc == 1)
+ rc = Curl_wait_for_resolv(conn, &h);
+ }
if(h)
strcpy(myhost, data->set.ftpport); /* buffer overflow risk */
}
@@ -1381,6 +1387,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
CURLcode result;
struct Curl_dns_entry *addr=NULL;
Curl_ipconnect *conninfo;
+ int rc;
/*
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
* 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 =
(unsigned short)conn->port; /* we connect to the proxy's port */
}
else {
/* 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) {
failf(data, "Can't resolve new host %s:%d", newhostp, newport);
return CURLE_FTP_CANT_GET_HOST;
diff --git a/lib/hostip.c b/lib/hostip.c
index b1b175d9e..3e69d4ea9 100644
--- a/lib/hostip.c
+++ b/lib/hostip.c
@@ -65,6 +65,7 @@
#include "hostip.h"
#include "hash.h"
#include "share.h"
+#include "url.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@@ -81,10 +82,13 @@
static curl_hash hostname_cache;
static int host_cache_initialized;
-static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
- char *hostname,
- int port,
- char **bufp);
+static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
+ char *hostname,
+ int port,
+ 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)
{
@@ -135,15 +139,14 @@ create_hostcache_id(char *server, int port, ssize_t *entry_len)
char *id = NULL;
/* Get the length of the new entry id */
- *entry_len = *entry_len + /* Hostname length */
- 1 + /* The ':' seperator */
- _num_chars(port); /* The number of characters the port will take up */
+ *entry_len = *entry_len + /* Hostname length */
+ 1 + /* ':' seperator */
+ _num_chars(port); /* number of characters the port will take up */
/* Allocate the new entry id */
id = malloc(*entry_len + 1);
- if (!id) {
+ if (!id)
return NULL;
- }
/* Create the new entry */
/* If sprintf() doesn't return the entry length, that signals failure */
@@ -192,47 +195,88 @@ hostcache_prune(curl_hash *hostcache, int cache_timeout, int now)
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)
+#ifdef HAVE_SIGSETJMP
+/* Beware this is a global and unique instance */
+sigjmp_buf curl_jmpenv;
+#endif
+
+
+/* When calling Curl_resolv() has resulted in a response with a returned
+ address, we call this function to store the information in the dns
+ cache etc */
+
+static struct Curl_dns_entry *
+cache_resolv_response(struct SessionHandle *data,
+ Curl_addrinfo *addr,
+ char *hostname,
+ int port)
{
- 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;
+ char *entry_id;
+ int entry_len;
+ struct Curl_dns_entry *dns;
+ time_t now;
+
+ /* 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 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;
}
-}
-#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; \
+ 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;
}
-#ifdef HAVE_SIGSETJMP
-/* Beware this is a global and unique instance */
-sigjmp_buf curl_jmpenv;
-#endif
+/* Resolve a name and return a pointer in the 'entry' argument if one
+ is available.
-struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data,
- char *hostname,
- int port)
+ 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;
- time_t now;
- char *bufp;
+ 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
@@ -240,7 +284,7 @@ struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data,
if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) {
/* this is coming from a siglongjmp() */
failf(data, "name lookup timed out");
- return NULL;
+ return -1;
}
#endif
@@ -249,7 +293,7 @@ struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data,
entry_id = create_hostcache_id(hostname, port, &entry_len);
/* If we can't create the entry id, fail */
if (!entry_id)
- return NULL;
+ return -1;
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
@@ -257,39 +301,34 @@ struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data,
/* See if its already in our dns cache */
dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1);
+ /* free the allocated entry_id again */
+ free(entry_id);
+
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) {
- HOSTCACHE_RETURN(NULL);
+ if(wait)
+ /* the response to our resolve call will come asynchronously at
+ a later time, good or bad */
+ rc = 1;
}
-
- /* Create a new cache entry */
- dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry));
- 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);
+ else
+ /* we got a response, store it in the cache */
+ dns = cache_resolv_response(data, addr, hostname, port);
}
- time(&now);
- dns->timestamp = now;
- dns->inuse++; /* mark entry as in-use */
-#ifdef CURLDEBUG
- dns->entry_id = entry_id;
-#endif
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
- /* Remove outdated and unused entries from the hostcache */
- hostcache_prune(data->hostcache,
- data->set.dns_cache_timeout,
- now);
+ *entry = dns;
- HOSTCACHE_RETURN(dns);
+ return rc;
}
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
freeaddrinfo(p);
#else
- free(p);
+ free(p); /* works fine for the ARES case too */
#endif
}
@@ -332,7 +371,203 @@ void Curl_freednsinfo(void *freethis)
/* --- 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
/* 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
* Curl_freeaddrinfo(), nothing else.
*/
-static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
+static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
char *hostname,
int port,
- char **bufp)
+ int *waitp)
{
struct addrinfo hints, *res;
int error;
char sbuf[NI_MAXSERV];
int s, pf = PF_UNSPEC;
+ struct SessionHandle *data = conn->data;
/* see if we have an IPv6 stack */
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);
return NULL;
}
- *bufp=(char *)res; /* make it point to the result struct */
+ *waitp=0; /* don't wait, we have the response now */
return res;
}
#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);
-/**
+/*
* 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!
- *
- * Keith McGuigan
- * 10/3/2001 */
+ */
static struct hostent* pack_hostent(char** buf, struct hostent* orig)
{
char *bufptr;
@@ -512,6 +746,25 @@ static struct hostent* pack_hostent(char** buf, struct hostent* orig)
}
#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)
{
#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);
}
-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
code, written by Bjorn Reese, it has since been patched and modified
considerably. */
-static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
+static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
char *hostname,
int port,
- char **bufp)
+ int *waitp)
{
struct hostent *h = NULL;
in_addr_t in;
int ret; /* this variable is unused on several platforms but used on some */
+ struct SessionHandle *data = conn->data;
-#define CURL_NAMELOOKUP_SIZE 9000
- /* 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 */
+ (void)port; /* unused in IPv4 code */
ret = 0; /* to prevent the compiler warning */
+ *waitp = 0; /* don't wait, we act synchronously */
+
in=inet_addr(hostname);
if (in != CURL_INADDR_NONE) {
struct in_addr *addrentry;
@@ -581,7 +815,6 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
} *buf = (struct namebuf *)malloc(sizeof(struct namebuf));
if(!buf)
return NULL; /* major failure */
- *bufp = (char *)buf;
h = &buf->hostentry;
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);
if(!buf)
return NULL; /* major failure */
- *bufp=(char *)buf;
/* Workaround for gethostbyname_r bug in qnx nto. It is also _required_
for some of these functions. */
@@ -638,7 +870,6 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
offset=(long)h-(long)buf;
hostcache_fixoffset(h, offset);
buf=(int *)h;
- *bufp=(char *)buf;
}
else
#endif
@@ -687,7 +918,6 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
offset=(long)h-(long)buf;
hostcache_fixoffset(h, offset);
buf=(int *)h;
- *bufp=(char *)buf;
}
else
#endif
@@ -730,13 +960,11 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
infof(data, "gethostbyname_r(2) failed for %s\n", hostname);
h = NULL; /* set return code to NULL */
free(buf);
- *bufp=NULL;
}
#else
else {
if ((h = gethostbyname(hostname)) == NULL ) {
infof(data, "gethostbyname(2) failed for %s\n", hostname);
- *bufp=NULL;
}
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
want/expect that */
h = pack_hostent(&buf, h);
- *bufp=(char *)buf;
}
#endif
}
@@ -753,3 +980,5 @@ static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data,
}
#endif /* end of IPv4-specific code */
+
+#endif /* end of !USE_ARES */
diff --git a/lib/hostip.h b/lib/hostip.h
index 36828ee1b..2f53f4f16 100644
--- a/lib/hostip.h
+++ b/lib/hostip.h
@@ -29,6 +29,7 @@
struct addrinfo;
struct hostent;
struct SessionHandle;
+struct connectdata;
void Curl_global_host_cache_init(void);
void Curl_global_host_cache_dtor(void);
@@ -41,9 +42,6 @@ struct Curl_dns_entry {
time_t timestamp;
long inuse; /* use-counter, make very sure you decrease this
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!
*/
-struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data,
- char *hostname,
- int port);
+int Curl_resolv(struct connectdata *conn,
+ char *hostname,
+ 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 */
void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns);
diff --git a/lib/multi.c b/lib/multi.c
index f6749f18f..0d2fcf7ff 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -56,7 +56,8 @@ struct Curl_message {
typedef enum {
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_DO, /* send off the request (part 1) */
CURLM_STATE_DO_MORE, /* send off the request (part 2) */
@@ -239,6 +240,14 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle,
switch(easy->state) {
default:
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_DO_MORE:
{
@@ -293,6 +302,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
CURLMcode result=CURLM_OK;
struct Curl_message *msg = NULL;
bool connected;
+ bool async;
*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;
}
break;
+
case CURLM_STATE_CONNECT:
if (Curl_global_host_cache_use(easy->easy_handle)) {
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. */
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) {
- easy->state = CURLM_STATE_WAITCONNECT;
- result = CURLM_CALL_MULTI_PERFORM;
+ if(async)
+ /* 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;
case CURLM_STATE_WAITCONNECT:
+ /* awaiting a completion of an asynch connect */
{
bool connected;
easy->result = Curl_is_connected(easy->easy_conn,
diff --git a/lib/setup.h b/lib/setup.h
index 4af61f97e..ef1514505 100644
--- a/lib/setup.h
+++ b/lib/setup.h
@@ -185,7 +185,7 @@ int fileno( FILE *stream);
* Information regarding a single IP witin a Curl_addrinfo MUST be stored in
* a Curl_ipconnect struct.
*/
-#ifdef ENABLE_IPV6
+#if defined(ENABLE_IPV6) && !defined(USE_ARES)
typedef struct addrinfo Curl_addrinfo;
typedef struct addrinfo Curl_ipconnect;
#else
diff --git a/lib/transfer.c b/lib/transfer.c
index 445975b0a..c4b1c90fa 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -605,8 +605,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
len = end-start+1;
/* allocate memory of a cloned copy */
- if(data->info.contenttype)
- free(data->info.contenttype);
+ Curl_safefree(data->info.contenttype);
data->info.contenttype = malloc(len + 1);
if (NULL == data->info.contenttype)
@@ -1903,10 +1902,22 @@ CURLcode Curl_perform(struct SessionHandle *data)
do {
int urlchanged = FALSE;
do {
+ bool async;
Curl_pgrsTime(data, TIMER_STARTSINGLE);
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
the Curl_connect(), we detect it here and act as if we are redirected
to the new URL */
diff --git a/lib/url.c b/lib/url.c
index f1095bcee..84f3bbb0f 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -147,7 +147,11 @@ static unsigned int ConnectionStore(struct SessionHandle *data,
struct connectdata *conn);
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
#define RETSIGTYPE void
#endif
@@ -165,6 +169,7 @@ RETSIGTYPE alarmfunc(int signal)
return;
}
#endif
+#endif /* USE_ARES */
void Curl_safefree(void *ptr)
{
@@ -1286,7 +1291,11 @@ CURLcode Curl_disconnect(struct connectdata *conn)
Curl_safefree(conn->allocptr.host);
Curl_safefree(conn->allocptr.cookiehost);
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);
free(conn); /* free all the connection oriented data */
@@ -1632,7 +1641,15 @@ static int handleSock5Proxy(
#ifndef ENABLE_IPV6
struct Curl_dns_entry *dns;
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
* 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 */
}
+/*
+ * 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,
- struct connectdata **in_connect)
+ struct connectdata **in_connect,
+ struct Curl_dns_entry **addr,
+ bool *async)
{
char *tmp;
CURLcode result=CURLE_OK;
@@ -1859,7 +1887,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
char passwd[MAX_CURL_PASSWORD_LENGTH];
bool passwdgiven=FALSE; /* set TRUE if an application-provided password has
been set */
-
+ int rc;
#ifdef HAVE_SIGACTION
struct sigaction keep_sigact; /* store the old struct here */
@@ -1870,6 +1898,9 @@ static CURLcode CreateConnection(struct SessionHandle *data,
#endif
#endif
+ *addr = NULL; /* nothing yet */
+ *async = FALSE;
+
/*************************************************************
* Check input data
*************************************************************/
@@ -2875,8 +2906,10 @@ static CURLcode CreateConnection(struct SessionHandle *data,
/* else, no chunky upload */
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) {
/*************************************************************
@@ -2919,7 +2952,8 @@ static CURLcode CreateConnection(struct SessionHandle *data,
has been done since then until now. */
#endif
}
-
+#endif
+
/*************************************************************
* 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 */
/* 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);
result = CURLE_COULDNT_RESOLVE_HOST;
/* 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. */
/* resolve proxy */
- hostaddr = Curl_resolv(data, conn->proxyhost, conn->port);
+ rc = Curl_resolv(conn, conn->proxyhost, conn->port, &hostaddr);
+
+ if(rc == 1)
+ *async = TRUE;
- if(!hostaddr) {
+ else if(!hostaddr) {
failf(data, "Couldn't resolve proxy '%s'", conn->proxyhost);
result = CURLE_COULDNT_RESOLVE_PROXY;
/* don't return yet, we need to clean up the timeout first */
}
}
- Curl_pgrsTime(data, TIMER_NAMELOOKUP);
+ *addr = hostaddr;
+
#ifdef HAVE_ALARM
if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
#ifdef HAVE_SIGACTION
@@ -2995,7 +3035,25 @@ static CURLcode CreateConnection(struct SessionHandle *data,
alarm(0); /* just shut it off */
}
#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;
/*************************************************************
@@ -3007,8 +3065,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->proxyuser, conn->proxypasswd);
if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer),
&authorization) >= 0) {
- if(conn->allocptr.proxyuserpwd)
- free(conn->allocptr.proxyuserpwd);
+ Curl_safefree(conn->allocptr.proxyuserpwd);
conn->allocptr.proxyuserpwd =
aprintf("Proxy-authorization: Basic %s\015\012", authorization);
free(authorization);
@@ -3022,16 +3079,14 @@ static CURLcode CreateConnection(struct SessionHandle *data,
if((conn->protocol&PROT_HTTP) ||
(data->change.proxy && *data->change.proxy)) {
if(data->set.useragent) {
- if(conn->allocptr.uagent)
- free(conn->allocptr.uagent);
+ Curl_safefree(conn->allocptr.uagent);
conn->allocptr.uagent =
aprintf("User-Agent: %s\015\012", data->set.useragent);
}
}
if(data->set.encoding) {
- if(conn->allocptr.accept_encoding)
- free(conn->allocptr.accept_encoding);
+ Curl_safefree(conn->allocptr.accept_encoding);
conn->allocptr.accept_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,
- struct connectdata **in_connect)
+ struct connectdata **in_connect,
+ bool *asyncp)
{
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 */
- 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) {
/* We're not allowed to return failure with memory left allocated
in the connectdata struct, free those here */
- conn = (struct connectdata *)*in_connect;
- if(conn) {
- Curl_disconnect(conn); /* close the connection */
- *in_connect = NULL; /* return a NULL */
+ if(*in_connect) {
+ Curl_disconnect(*in_connect); /* close the connection */
+ *in_connect = NULL; /* return a NULL */
}
}
+
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)
{
struct SessionHandle *data=conn->data;
@@ -3179,11 +3268,28 @@ CURLcode Curl_do(struct connectdata **connp)
conn->bits.close = TRUE; /* enforce close of this connetion */
result = Curl_done(conn); /* we are so done with this */
if(CURLE_OK == result) {
+ bool async;
/* Now, redo the connect and get a new connection */
- result = Curl_connect(data, connp);
- if(CURLE_OK == result)
+ result = Curl_connect(data, connp, &async);
+ 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 */
- result = conn->curl_do(*connp);
+ result = conn->curl_do(conn);
+ }
}
}
}
diff --git a/lib/url.h b/lib/url.h
index 59ac39058..002b11249 100644
--- a/lib/url.h
+++ b/lib/url.h
@@ -30,7 +30,9 @@
CURLcode Curl_open(struct SessionHandle **curl);
CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...);
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_more(struct connectdata *);
CURLcode Curl_done(struct connectdata *);
diff --git a/lib/urldata.h b/lib/urldata.h
index 165b90fe1..0218935ab 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -90,6 +90,10 @@
#include <gssapi.h>
#endif
+#ifdef USE_ARES
+#include <ares.h>
+#endif
+
/* Download buffer size, keep it fairly big for speed reasons */
#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! */
};
+#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
* unique for an entire connection.
@@ -538,6 +552,11 @@ struct connectdata {
because it authenticates connections, not
single requests! */
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. */
@@ -669,6 +688,10 @@ struct UrlState {
long authwant; /* inherited from what the user set with CURLOPT_HTTPAUTH */
long authavail; /* what the server reports */
+
+#ifdef USE_ARES
+ ares_channel areschannel; /* for name resolves */
+#endif
};