aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2004-04-26 07:20:11 +0000
committerDaniel Stenberg <daniel@haxx.se>2004-04-26 07:20:11 +0000
commit648e82f05d8bce097f9f81b78d9df40f647f6170 (patch)
tree3853fbb58d25240561337356c5a99e44ffd57a72
parent1dbe60b8b71c2308fab987f5b4c6c6e9a264dd66 (diff)
Major hostip.c cleanup and split into multiple files and easier #ifdef
usage.
-rw-r--r--lib/Makefile.am3
-rw-r--r--lib/hostares.c298
-rw-r--r--lib/hostasyn.c152
-rw-r--r--lib/hostip.c1137
-rw-r--r--lib/hostip.h141
-rw-r--r--lib/hostip4.c400
-rw-r--r--lib/hostip6.c284
-rw-r--r--lib/hostsyn.c150
-rw-r--r--lib/hostthre.c556
-rw-r--r--lib/inet_ntop.c189
-rw-r--r--lib/inet_ntop.h37
-rw-r--r--lib/memdebug.h7
-rw-r--r--lib/multi.c5
-rw-r--r--lib/setup.h12
-rw-r--r--lib/url.c130
-rw-r--r--lib/url.h3
-rw-r--r--lib/urldata.h17
17 files changed, 2399 insertions, 1122 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index e50cd6b7b..5dbaaf0cb 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -93,7 +93,8 @@ libcurl_la_SOURCES = arpa_telnet.h file.c netrc.h timeval.c base64.c \
content_encoding.c content_encoding.h share.c share.h http_digest.c \
md5.c md5.h http_digest.h http_negotiate.c http_negotiate.h \
http_ntlm.c http_ntlm.h ca-bundle.h inet_pton.c inet_pton.h \
- strtoofft.c strtoofft.h strerror.c strerror.h
+ strtoofft.c strtoofft.h strerror.c strerror.h hostares.c hostasyn.c \
+ hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c inet_ntop.h
noinst_HEADERS = setup.h transfer.h
diff --git a/lib/hostares.c b/lib/hostares.c
new file mode 100644
index 000000000..1db87e483
--- /dev/null
+++ b/lib/hostares.c
@@ -0,0 +1,298 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <errno.h>
+
+#define _REENTRANT
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <malloc.h>
+#else
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* required for free() prototypes */
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef WIN32
+#include <process.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+#include "inet_ntoa_r.h"
+#endif
+
+/* The last #include file should be: */
+#ifdef CURLDEBUG
+#include "memdebug.h"
+#endif
+
+/***********************************************************************
+ * Only for ares-enabled builds
+ **********************************************************************/
+
+#ifdef CURLRES_ARES
+
+/*
+ * Curl_fdset() is called when someone from the outside world (using
+ * curl_multi_fdset()) wants to get our fd_set setup and we're talking with
+ * ares. The caller must make sure that this function is only called when we
+ * have a working ares channel.
+ *
+ * Returns: CURLE_OK always!
+ */
+
+CURLcode Curl_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;
+}
+
+/*
+ * Curl_is_resolved() is called repeatedly to check if a previous name resolve
+ * request has completed. It should also make sure to time-out if the
+ * operation seems to take too long.
+ *
+ * Returns normal CURLcode errors.
+ */
+CURLcode Curl_is_resolved(struct connectdata *conn,
+ struct Curl_dns_entry **dns)
+{
+ fd_set read_fds, write_fds;
+ struct timeval tv={0,0};
+ int count;
+ struct SessionHandle *data = conn->data;
+ int nfds;
+
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+
+ nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
+
+ count = select(nfds, &read_fds, &write_fds, NULL,
+ (struct timeval *)&tv);
+
+ /* Call ares_process() unconditonally here, even if we simply timed out
+ above, as otherwise the ares name resolve won't timeout! */
+ ares_process(data->state.areschannel, &read_fds, &write_fds);
+
+ *dns = NULL;
+
+ if(conn->async.done) {
+ /* we're done, kill the ares handle */
+ if(!conn->async.dns)
+ return CURLE_COULDNT_RESOLVE_HOST;
+ *dns = conn->async.dns;
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_wait_for_resolv() waits for a resolve to finish. This function should
+ * be avoided since using this risk getting the multi interface to "hang".
+ *
+ * If 'entry' is non-NULL, make it point to the resolved dns entry
+ *
+ * Returns 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;
+ long timeout = CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */
+
+ /* now, see if there's a connect timeout or a regular timeout to
+ use instead of the default one */
+ if(conn->data->set.connecttimeout)
+ timeout = conn->data->set.connecttimeout;
+ else if(conn->data->set.timeout)
+ timeout = conn->data->set.timeout;
+
+ /* We convert the number of seconds into number of milliseconds here: */
+ if(timeout < 2147483)
+ /* maximum amount of seconds that can be multiplied with 1000 and
+ still fit within 31 bits */
+ timeout *= 1000;
+ else
+ timeout = 0x7fffffff; /* ridiculous amount of time anyway */
+
+ /* Wait for the name resolve query to complete. */
+ while (1) {
+ int nfds=0;
+ fd_set read_fds, write_fds;
+ struct timeval *tvp, tv, store;
+ int count;
+ struct timeval now = Curl_tvnow();
+ long timediff;
+
+ store.tv_sec = (int)timeout/1000;
+ store.tv_usec = (timeout%1000)*1000;
+
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+ nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
+ if (nfds == 0)
+ /* no file descriptors means we're done waiting */
+ break;
+ tvp = ares_timeout(data->state.areschannel, &store, &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);
+
+ timediff = Curl_tvdiff(Curl_tvnow(), now); /* spent time */
+ timeout -= timediff?timediff:1; /* always deduct at least 1 */
+ if (timeout < 0) {
+ /* our timeout, so we cancel the ares operation */
+ ares_cancel(data->state.areschannel);
+ break;
+ }
+ }
+
+ /* Operation complete, if the lookup was successful we now have the entry
+ in the cache. */
+
+ if(entry)
+ *entry = conn->async.dns;
+
+ if(!conn->async.dns) {
+ /* a name was not resolved */
+ if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) {
+ failf(data, "Resolving host timed out: %s", conn->hostname);
+ rc = CURLE_OPERATION_TIMEDOUT;
+ }
+ else if(conn->async.done) {
+ failf(data, "Could not resolve host: %s (%s)", conn->hostname,
+ ares_strerror(conn->async.status));
+ 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;
+}
+
+/*
+ * Curl_getaddrinfo() - when using ares
+ *
+ * Returns 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 memory *MUST* be freed with
+ * Curl_freeaddrinfo(), nothing else.
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ char *hostname,
+ int port,
+ int *waitp)
+{
+ char *bufp;
+ struct SessionHandle *data = conn->data;
+ in_addr_t in = inet_addr(hostname);
+
+ *waitp = FALSE;
+
+ if (in != CURL_INADDR_NONE)
+ /* This is a dotted IP address 123.123.123.123-style */
+ return Curl_ip2addr(in, hostname);
+
+ 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 */
+
+ /* areschannel is already setup in the Curl_open() function */
+ ares_gethostbyname(data->state.areschannel, hostname, PF_INET,
+ Curl_addrinfo_callback, conn);
+
+ *waitp = TRUE; /* please wait for the response */
+ }
+ return NULL; /* no struct yet */
+}
+
+#endif /* CURLRES_ARES */
diff --git a/lib/hostasyn.c b/lib/hostasyn.c
new file mode 100644
index 000000000..a38c772d2
--- /dev/null
+++ b/lib/hostasyn.c
@@ -0,0 +1,152 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <errno.h>
+
+#define _REENTRANT
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <malloc.h>
+#else
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* required for free() prototypes */
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef WIN32
+#include <process.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+#include "inet_ntoa_r.h"
+#endif
+
+/* The last #include file should be: */
+#ifdef CURLDEBUG
+#include "memdebug.h"
+#endif
+
+/***********************************************************************
+ * Only for builds using asynchronous name resolves
+ **********************************************************************/
+#ifdef CURLRES_ASYNCH
+/*
+ * Curl_addrinfo_callback() gets called by ares/gethostbyname_thread() when we
+ * got the name resolved (or not!).
+ *
+ * If the status argument is CURL_ASYNC_SUCCESS, we might need to copy the
+ * address field since it might be freed when this function returns. This
+ * operation stores the resolved data in the DNS cache.
+ *
+ * NOTE: for IPv6 operations, Curl_addrinfo_copy() returns the same
+ * pointer it is given as argument!
+ *
+ * The storage operation locks and unlocks the DNS cache.
+ */
+void Curl_addrinfo_callback(void *arg, /* "struct connectdata *" */
+ int status,
+ Curl_addrinfo *hostent)
+{
+ struct connectdata *conn = (struct connectdata *)arg;
+ struct Curl_dns_entry *dns = NULL;
+
+ conn->async.done = TRUE;
+ conn->async.status = status;
+
+ if(CURL_ASYNC_SUCCESS == status) {
+
+ /*
+ * IPv4: Curl_addrinfo_copy() copies the address and returns an allocated
+ * version.
+ *
+ * IPv6: Curl_addrinfo_copy() returns the input pointer!
+ */
+ Curl_addrinfo *he = Curl_addrinfo_copy(hostent);
+ if(he) {
+ 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, he,
+ conn->async.hostname,
+ conn->async.port);
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ }
+ }
+
+ conn->async.dns = dns;
+
+ /* ipv4: The input hostent struct will be freed by ares when we return from
+ this function */
+}
+
+#endif /* CURLRES_ASYNC */
diff --git a/lib/hostip.c b/lib/hostip.c
index 32bb03de3..4b57051d7 100644
--- a/lib/hostip.c
+++ b/lib/hostip.c
@@ -92,59 +92,51 @@
#include "memdebug.h"
#endif
-#ifndef ARES_SUCCESS
-#define ARES_SUCCESS CURLE_OK
-#endif
-
-#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this
- many seconds for a name resolve */
+/*
+ * hostip.c explained
+ * ==================
+ *
+ * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
+ * source file are these:
+ *
+ * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
+ * that. The host may not be able to resolve IPv6, but we don't really have to
+ * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
+ * defined.
+ *
+ * CURLRES_ARES - is defined if libcurl is built to use c-ares for
+ * asynchronous name resolves. It cannot have ENABLE_IPV6 defined at the same
+ * time, as c-ares has no ipv6 support. This can be Windows or *nix.
+ *
+ * CURLRES_THREADED - is defined if libcurl is built to run under (native)
+ * Windows, and then the name resolve will be done in a new thread, and the
+ * supported API will be the same as for ares-builds.
+ *
+ * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
+ * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
+ * defined.
+ *
+ * The host*.c sources files are split up like this:
+ *
+ * hostip.c - method-independent resolver functions and utility functions
+ * hostasyn.c - functions for asynchronous name resolves
+ * hostsyn.c - functions for synchronous name resolves
+ * hostares.c - functions for ares-using name resolves
+ * hostthre.c - functions for threaded name resolves
+ * hostip4.c - ipv4-specific functions
+ * hostip6.c - ipv6-specific functions
+ *
+ * The hostip.h is the united header file for all this. It defines the
+ * CURLRES_* defines based on the config*.h and setup.h defines.
+ */
/* These two symbols are for the global DNS cache */
static curl_hash hostname_cache;
static int host_cache_initialized;
-
static void freednsentry(void *freethis);
/*
- * my_getaddrinfo() is the generic low-level name resolve API within this
- * source file. There exist three versions of this function - for different
- * name resolve layers (selected at build-time). They all take this same set
- * of arguments
- */
-static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
- char *hostname,
- int port,
- int *waitp);
-
-#if (!defined(HAVE_GETHOSTBYNAME_R) || defined(USE_ARES) || \
- defined(USE_THREADING_GETHOSTBYNAME)) && \
- !defined(ENABLE_IPV6)
-static struct hostent* pack_hostent(char** buf, struct hostent* orig);
-#endif
-
-#ifdef USE_THREADING_GETHOSTBYNAME
-#ifdef DEBUG_THREADING_GETHOSTBYNAME
-/* If this is defined, provide tracing */
-#define TRACE(args) \
- do { trace_it("%u: ", __LINE__); trace_it args; } while (0)
-
-static void trace_it (const char *fmt, ...);
-#else
-#define TRACE(x)
-#endif
-
-static bool init_gethostbyname_thread (struct connectdata *conn,
- const char *hostname, int port);
-struct thread_data {
- HANDLE thread_hnd;
- unsigned thread_id;
- DWORD thread_status;
- curl_socket_t dummy_sock; /* dummy for Curl_multi_ares_fdset() */
-};
-#endif
-
-/*
* Curl_global_host_cache_init() initializes and sets up a global DNS cache.
* Global DNS cache is general badness. Do not use. This will be removed in
* a future version. Use the share interface instead!
@@ -177,8 +169,8 @@ void Curl_global_host_cache_dtor(void)
}
/*
- * Minor utility-function:
- * Count the number of characters that an integer takes up.
+ * Count the number of characters that an integer would use in a string
+ * (base 10).
*/
static int _num_chars(int i)
{
@@ -201,8 +193,8 @@ static int _num_chars(int i)
}
/*
- * Minor utility-function:
- * Create a hostcache id string for the DNS caching.
+ * Return a hostcache id string for the providing host + port, to be used by
+ * the DNS caching.
*/
static char *
create_hostcache_id(char *server, int port, size_t *entry_len)
@@ -305,7 +297,7 @@ sigjmp_buf curl_jmpenv;
/*
- * cache_resolv_response() stores a 'Curl_addrinfo' struct in the DNS cache.
+ * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
*
* When calling Curl_resolv() has resulted in a response with a returned
* address, we call this function to store the information in the dns
@@ -313,11 +305,11 @@ sigjmp_buf curl_jmpenv;
*
* Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
*/
-static struct Curl_dns_entry *
-cache_resolv_response(struct SessionHandle *data,
- Curl_addrinfo *addr,
- char *hostname,
- int port)
+struct Curl_dns_entry *
+Curl_cache_addr(struct SessionHandle *data,
+ Curl_addrinfo *addr,
+ char *hostname,
+ int port)
{
char *entry_id;
size_t entry_len;
@@ -424,11 +416,18 @@ int Curl_resolv(struct connectdata *conn,
if (!dns) {
/* 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
+
+ Curl_addrinfo *addr;
+
+ /* Check what IP specifics the app has requested and if we can provide it.
+ * If not, bail out. */
+ if(!Curl_ipvalid(data))
+ return -1;
+
+ /* If Curl_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);
+ addr = Curl_getaddrinfo(conn, hostname, port, &wait);
if (!addr) {
if(wait) {
@@ -449,7 +448,7 @@ int Curl_resolv(struct connectdata *conn,
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
/* we got a response, store it in the cache */
- dns = cache_resolv_response(data, addr, hostname, port);
+ dns = Curl_cache_addr(data, addr, hostname, port);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
@@ -478,37 +477,18 @@ int Curl_resolv(struct connectdata *conn,
*/
void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
{
+ curlassert(dns && (dns->inuse>0));
+
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
dns->inuse--;
-#ifdef CURLDEBUG
- if(dns->inuse < 0) {
- infof(data, "Interal host cache screw-up!");
- *(char **)0=NULL;
- }
-#endif
-
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
}
/*
- * This is a wrapper function for freeing name information in a protocol
- * independent way. This takes care of using the appropriate underlaying
- * function.
- */
-void Curl_freeaddrinfo(Curl_addrinfo *p)
-{
-#ifdef ENABLE_IPV6
- freeaddrinfo(p);
-#else
- free(p); /* works fine for the ARES case too */
-#endif
-}
-
-/*
* File-internal: free a cache dns entry.
*/
static void freednsentry(void *freethis)
@@ -528,470 +508,66 @@ curl_hash *Curl_mk_dnscache(void)
return Curl_hash_alloc(7, freednsentry);
}
-/* --- resolve name or IP-number --- */
-
-/* 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
-
-/*
- * Curl_multi_ares_fdset() is called when someone from the outside world
- * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
- * with ares. The caller must make sure that this function is only called when
- * we have a working ares channel.
- *
- * Returns: CURLE_OK always!
- */
-
-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;
-}
-
-/*
- * Curl_is_resolved() is called repeatedly to check if a previous name resolve
- * request has completed. It should also make sure to time-out if the
- * operation seems to take too long.
- *
- * Returns normal CURLcode errors.
- */
-CURLcode Curl_is_resolved(struct connectdata *conn,
- struct Curl_dns_entry **dns)
-{
- fd_set read_fds, write_fds;
- struct timeval tv={0,0};
- int count;
- struct SessionHandle *data = conn->data;
- int nfds;
-
- FD_ZERO(&read_fds);
- FD_ZERO(&write_fds);
-
- nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
-
- count = select(nfds, &read_fds, &write_fds, NULL,
- (struct timeval *)&tv);
-
- /* Call ares_process() unconditonally here, even if we simply timed out
- above, as otherwise the ares name resolve won't timeout! */
- ares_process(data->state.areschannel, &read_fds, &write_fds);
-
- *dns = NULL;
-
- if(conn->async.done) {
- /* we're done, kill the ares handle */
- if(!conn->async.dns)
- return CURLE_COULDNT_RESOLVE_HOST;
- *dns = conn->async.dns;
- }
-
- return CURLE_OK;
-}
-
+#ifdef CURLRES_HOSTENT_RELOCATE
/*
- * Curl_wait_for_resolv() waits for a resolve to finish. This function should
- * be avoided since using this risk getting the multi interface to "hang".
- *
- * If 'entry' is non-NULL, make it point to the resolved dns entry
- *
- * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
- * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
+ * Curl_hostent_relocate() ajusts all pointers in the given hostent struct
+ * according to the offset. This is typically used when a hostent has been
+ * reallocated and needs to be setup properly on the new address.
*/
-CURLcode Curl_wait_for_resolv(struct connectdata *conn,
- struct Curl_dns_entry **entry)
+void Curl_hostent_relocate(struct hostent *h, long offset)
{
- CURLcode rc=CURLE_OK;
- struct SessionHandle *data = conn->data;
- long timeout = CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */
-
- /* now, see if there's a connect timeout or a regular timeout to
- use instead of the default one */
- if(conn->data->set.connecttimeout)
- timeout = conn->data->set.connecttimeout;
- else if(conn->data->set.timeout)
- timeout = conn->data->set.timeout;
-
- /* We convert the number of seconds into number of milliseconds here: */
- if(timeout < 2147483)
- /* maximum amount of seconds that can be multiplied with 1000 and
- still fit within 31 bits */
- timeout *= 1000;
- else
- timeout = 0x7fffffff; /* ridiculous amount of time anyway */
-
- /* Wait for the name resolve query to complete. */
- while (1) {
- int nfds=0;
- fd_set read_fds, write_fds;
- struct timeval *tvp, tv, store;
- int count;
- struct timeval now = Curl_tvnow();
- long timediff;
-
- store.tv_sec = (int)timeout/1000;
- store.tv_usec = (timeout%1000)*1000;
-
- FD_ZERO(&read_fds);
- FD_ZERO(&write_fds);
- nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
- if (nfds == 0)
- /* no file descriptors means we're done waiting */
- break;
- tvp = ares_timeout(data->state.areschannel, &store, &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);
-
- timediff = Curl_tvdiff(Curl_tvnow(), now); /* spent time */
- timeout -= timediff?timediff:1; /* always deduct at least 1 */
- if (timeout < 0) {
- /* our timeout, so we cancel the ares operation */
- ares_cancel(data->state.areschannel);
- break;
- }
- }
-
- /* Operation complete, if the lookup was successful we now have the entry
- in the cache. */
-
- if(entry)
- *entry = conn->async.dns;
-
- if(!conn->async.dns) {
- /* a name was not resolved */
- if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) {
- failf(data, "Resolving host timed out: %s", conn->hostname);
- rc = CURLE_OPERATION_TIMEDOUT;
- }
- else if(conn->async.done) {
- failf(data, "Could not resolve host: %s (%s)", conn->hostname,
- ares_strerror(conn->async.status));
- 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;
-}
-#endif
-
-#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME)
-
-/*
- * host_callback() gets called by ares/gethostbyname_thread() when we got the
- * name resolved (or not!).
- *
- * If the status argument is ARES_SUCCESS, we must copy the hostent field
- * since ares will free it when this function returns. This operation stores
- * the resolved data in the DNS cache.
- *
- * The storage operation locks and unlocks the DNS cache.
- */
-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);
-
- struct SessionHandle *data = conn->data;
-
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
- dns = cache_resolv_response(data, he,
- conn->async.hostname, conn->async.port);
+ int i=0;
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ h->h_name=(char *)((long)h->h_name+offset);
+ if(h->h_aliases) {
+ /* only relocate aliases if there are any! */
+ 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++;
}
}
- conn->async.dns = dns;
-
- /* The input hostent struct will be freed by ares when we return from this
- function */
-}
-#endif
-
-#ifdef USE_ARES
-/*
- * my_getaddrinfo() when using ares for name resolves.
- *
- * Returns 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 memory *MUST* be freed with
- * Curl_freeaddrinfo(), nothing else.
- */
-static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
- char *hostname,
- int port,
- int *waitp)
-{
- char *bufp;
- struct SessionHandle *data = conn->data;
-
- *waitp = FALSE;
-
- if(data->set.ip_version == CURL_IPRESOLVE_V6)
- /* an ipv6 address was requested and we can't get/use one */
- return NULL;
-
- 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 */
-
- /* areschannel is already setup in the Curl_open() function */
- ares_gethostbyname(data->state.areschannel, hostname, PF_INET,
- host_callback, conn);
-
- *waitp = TRUE; /* please wait for the response */
- }
- return NULL; /* no struct yet */
-}
-#endif
-
-#if !defined(USE_ARES) && !defined(USE_THREADING_GETHOSTBYNAME)
-
-/*
- * Curl_wait_for_resolv() for builds without ARES and threaded gethostbyname,
- * Curl_resolv() can never return wait==TRUE, so this function will never be
- * called. If it still gets called, we return failure at once.
- *
- * We provide this function only to allow multi.c to remain unaware if we are
- * doing asynch resolves or not.
- */
-CURLcode Curl_wait_for_resolv(struct connectdata *conn,
- struct Curl_dns_entry **entry)
-{
- (void)conn;
- *entry=NULL;
- return CURLE_COULDNT_RESOLVE_HOST;
-}
-
-/*
- * This function will never be called when built with ares or threaded
- * resolves. If it still gets called, we return failure at once.
- *
- * We provide this function only to allow multi.c to remain unaware if we are
- * doing asynch resolves or not.
- */
-CURLcode Curl_is_resolved(struct connectdata *conn,
- struct Curl_dns_entry **dns)
-{
- (void)conn;
- *dns = NULL;
-
- return CURLE_COULDNT_RESOLVE_HOST;
-}
-#endif
-
-#if !defined(USE_ARES)
-/*
- * Non-ares build. If we are using threading gethostbyname, then this must
- * set the fd_set for the threaded resolve socket. If not, we just return OK.
- */
-CURLcode Curl_multi_ares_fdset(struct connectdata *conn,
- fd_set *read_fd_set,
- fd_set *write_fd_set,
- int *max_fdp)
-{
-#ifdef USE_THREADING_GETHOSTBYNAME
- const struct thread_data *td =
- (const struct thread_data *) conn->async.os_specific;
-
- if (td && td->dummy_sock != CURL_SOCKET_BAD) {
- FD_SET(td->dummy_sock,write_fd_set);
- *max_fdp = td->dummy_sock;
- }
-#else /* if not USE_THREADING_GETHOSTBYNAME */
- (void)conn;
- (void)read_fd_set;
- (void)write_fd_set;
- (void)max_fdp;
-#endif
- return CURLE_OK;
-}
-#endif /* !USE_ARES */
-
-#if defined(ENABLE_IPV6) && !defined(USE_ARES)
-
-#ifdef CURLDEBUG
-/* These two are strictly for memory tracing and are using the same
- * style as the family otherwise present in memdebug.c. I put these ones
- * here since they require a bunch of struct types I didn't wanna include
- * in memdebug.c
- */
-int curl_getaddrinfo(char *hostname, char *service,
- struct addrinfo *hints,
- struct addrinfo **result,
- int line, const char *source)
-{
- int res=(getaddrinfo)(hostname, service, hints, result);
- if(0 == res) {
- /* success */
- if(logfile)
- fprintf(logfile, "ADDR %s:%d getaddrinfo() = %p\n",
- source, line, (void *)*result);
- }
- else {
- if(logfile)
- fprintf(logfile, "ADDR %s:%d getaddrinfo() failed\n",
- source, line);
+ 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++;
}
- return res;
}
+#endif /* CURLRES_HOSTENT_RELOCATE */
-void curl_freeaddrinfo(struct addrinfo *freethis,
- int line, const char *source)
-{
- (freeaddrinfo)(freethis);
- if(logfile)
- fprintf(logfile, "ADDR %s:%d freeaddrinfo(%p)\n",
- source, line, (void *)freethis);
-}
-
-#endif
+#ifdef CURLRES_ADDRINFO_COPY
-/*
- * my_getaddrinfo() when built ipv6-enabled.
- *
- * Returns name information about the given hostname and port number. If
- * successful, the 'addrinfo' is returned and the forth argument will point to
- * memory we need to free after use. That memory *MUST* be freed with
- * Curl_freeaddrinfo(), nothing else.
- */
-static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
- char *hostname,
- int port,
- int *waitp)
-{
- struct addrinfo hints, *res;
- int error;
- char sbuf[NI_MAXSERV];
- int s, pf;
- struct SessionHandle *data = conn->data;
-
- *waitp=0; /* don't wait, we have the response now */
-
- /* see if we have an IPv6 stack */
- s = socket(PF_INET6, SOCK_DGRAM, 0);
- if (s < 0) {
- /* Some non-IPv6 stacks have been found to make very slow name resolves
- * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
- * the stack seems to be a non-ipv6 one. */
-
- if(data->set.ip_version == CURL_IPRESOLVE_V6)
- /* an ipv6 address was requested and we can't get/use one */
- return NULL;
-
- pf = PF_INET;
- }
- else {
- /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
- * possible checks. And close the socket again.
- */
- sclose(s);
-
- /*
- * Check if a more limited name resolve has been requested.
- */
- switch(data->set.ip_version) {
- case CURL_IPRESOLVE_V4:
- pf = PF_INET;
- break;
- case CURL_IPRESOLVE_V6:
- pf = PF_INET6;
- break;
- default:
- pf = PF_UNSPEC;
- break;
- }
- }
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = pf;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_CANONNAME;
- snprintf(sbuf, sizeof(sbuf), "%d", port);
- error = getaddrinfo(hostname, sbuf, &hints, &res);
- if (error) {
- infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
- return NULL;
- }
-
- return res;
-}
-#else /* following code is IPv4-only */
-
-#if !defined(HAVE_GETHOSTBYNAME_R) || defined(USE_ARES) || \
- defined(USE_THREADING_GETHOSTBYNAME)
-static void hostcache_fixoffset(struct hostent *h, long offset);
+/* align on even 64bit boundaries */
+#define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
/*
- * pack_hostent() is a file-local function that 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!
+ * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and
+ * returns a pointer to the malloc()ed copy. You need to call free() on the
+ * returned buffer when you're done with it.
*/
-static struct hostent* pack_hostent(char** buf, struct hostent* orig)
+Curl_addrinfo *Curl_addrinfo_copy(Curl_addrinfo *orig)
{
- char *bufptr;
char *newbuf;
- struct hostent* copy;
-
+ Curl_addrinfo *copy;
int i;
char *str;
size_t len;
+ char *aptr = (char *)malloc(CURL_HOSTENT_SIZE);
+ char *bufptr = aptr;
+
+ if(!bufptr)
+ return NULL; /* major bad */
- bufptr = *buf;
- copy = (struct hostent*)bufptr;
+ copy = (Curl_addrinfo *)bufptr;
- bufptr += sizeof(struct hostent);
+ bufptr += sizeof(Curl_addrinfo);
copy->h_name = bufptr;
len = strlen(orig->h_name) + 1;
strncpy(bufptr, orig->h_name, len);
bufptr += len;
- /* we align on even 64bit boundaries for safety */
-#define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
-
/* This must be aligned properly to work on many CPU architectures! */
bufptr = MEMALIGN(bufptr);
@@ -1044,528 +620,15 @@ static struct hostent* pack_hostent(char** buf, struct hostent* orig)
/* now, shrink the allocated buffer to the size we actually need, which
most often is only a fraction of the original alloc */
- newbuf=(char *)realloc(*buf, (long)(bufptr-*buf));
+ newbuf=(char *)realloc(aptr, (long)(bufptr-aptr));
- /* if the alloc moved, we need to adjust things again */
- if(newbuf != *buf)
- hostcache_fixoffset((struct hostent*)newbuf, (long)(newbuf-*buf));
+ /* if the alloc moved, we need to adjust the hostent struct */
+ if(newbuf != aptr)
+ Curl_hostent_relocate((struct hostent*)newbuf, (long)(newbuf-aptr));
/* setup the return */
- *buf = newbuf;
- copy = (struct hostent*)newbuf;
+ copy = (Curl_addrinfo *)newbuf;
return copy;
}
-#endif
-
-/*
- * hostcache_fixoffset() is a utility-function that corrects all pointers in
- * the given hostent struct according to the offset. This is typically used
- * when a hostent has been reallocated and needs to be setup properly on the
- * new address.
- */
-static void hostcache_fixoffset(struct hostent *h, long offset)
-{
- int i=0;
-
- h->h_name=(char *)((long)h->h_name+offset);
- if(h->h_aliases) {
- /* only relocate aliases if there are any! */
- 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
-
-/*
- * MakeIP() converts the input binary ipv4-address to an ascii string in the
- * dotted numerical format. 'addr' is a pointer to a buffer that is 'addr_len'
- * bytes big. 'num' is the 32 bit IP number.
- */
-static char *MakeIP(unsigned long num, char *addr, int addr_len)
-{
-#if defined(HAVE_INET_NTOA) || defined(HAVE_INET_NTOA_R)
- struct in_addr in;
- in.s_addr = htonl(num);
-
-#if defined(HAVE_INET_NTOA_R)
- inet_ntoa_r(in,addr,addr_len);
-#else
- strncpy(addr,inet_ntoa(in),addr_len);
-#endif
-#else
- unsigned char *paddr;
-
- num = htonl(num); /* htonl() added to avoid endian probs */
- paddr = (unsigned char *)&num;
- sprintf(addr, "%u.%u.%u.%u", paddr[0], paddr[1], paddr[2], paddr[3]);
-#endif
- return (addr);
-}
-
-/*
- * my_getaddrinfo() - the ipv4 "traditional" version.
- *
- * 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 connectdata *conn,
- char *hostname,
- int port,
- int *waitp)
-{
- struct hostent *h = NULL;
- in_addr_t in;
- struct SessionHandle *data = conn->data;
- (void)port; /* unused in IPv4 code */
-
- *waitp = 0; /* don't wait, we act synchronously */
-
- if(data->set.ip_version == CURL_IPRESOLVE_V6)
- /* an ipv6 address was requested and we can't get/use one */
- return NULL;
-
- in=inet_addr(hostname);
- if (in != CURL_INADDR_NONE) {
- struct in_addr *addrentry;
- struct namebuf {
- struct hostent hostentry;
- char *h_addr_list[2];
- struct in_addr addrentry;
- char h_name[128];
- } *buf = (struct namebuf *)malloc(sizeof(struct namebuf));
- if(!buf)
- return NULL; /* major failure */
-
- h = &buf->hostentry;
- h->h_addr_list = &buf->h_addr_list[0];
- addrentry = &buf->addrentry;
- addrentry->s_addr = in;
- h->h_addr_list[0] = (char*)addrentry;
- h->h_addr_list[1] = NULL;
- h->h_addrtype = AF_INET;
- h->h_length = sizeof(*addrentry);
- h->h_name = &buf->h_name[0];
- MakeIP(ntohl(in), (char *)h->h_name, sizeof(buf->h_name));
- }
-#if defined(HAVE_GETHOSTBYNAME_R)
- else {
- int h_errnop;
- int res=ERANGE;
- int step_size=200;
- int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE);
- if(!buf)
- return NULL; /* major failure */
-
- /* Workaround for gethostbyname_r bug in qnx nto. It is also _required_
- for some of these functions. */
- memset(buf, 0, CURL_NAMELOOKUP_SIZE);
-#ifdef HAVE_GETHOSTBYNAME_R_5
- /* Solaris, IRIX and more */
- (void)res; /* prevent compiler warning */
- while(!h) {
- h = gethostbyname_r(hostname,
- (struct hostent *)buf,
- (char *)buf + sizeof(struct hostent),
- step_size - sizeof(struct hostent),
- &h_errnop);
-
- /* If the buffer is too small, it returns NULL and sets errno to
- ERANGE. The errno is thread safe if this is compiled with
- -D_REENTRANT as then the 'errno' variable is a macro defined to
- get used properly for threads. */
-
- if(h || (errno != ERANGE))
- break;
-
- step_size+=200;
- }
-
-#ifdef CURLDEBUG
- infof(data, "gethostbyname_r() uses %d bytes\n", step_size);
-#endif
-
- if(h) {
- int offset;
- h=(struct hostent *)realloc(buf, step_size);
- offset=(long)h-(long)buf;
- hostcache_fixoffset(h, offset);
- buf=(int *)h;
- }
- else
-#endif /* HAVE_GETHOSTBYNAME_R_5 */
-#ifdef HAVE_GETHOSTBYNAME_R_6
- /* Linux */
- do {
- res=gethostbyname_r(hostname,
- (struct hostent *)buf,
- (char *)buf + sizeof(struct hostent),
- step_size - sizeof(struct hostent),
- &h, /* DIFFERENCE */
- &h_errnop);
- /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a
- sudden this function returns EAGAIN if the given buffer size is too
- small. Previous versions are known to return ERANGE for the same
- problem.
-
- This wouldn't be such a big problem if older versions wouldn't
- sometimes return EAGAIN on a common failure case. Alas, we can't
- assume that EAGAIN *or* ERANGE means ERANGE for any given version of
- glibc.
-
- For now, we do that and thus we may call the function repeatedly and
- fail for older glibc versions that return EAGAIN, until we run out
- of buffer size (step_size grows beyond CURL_NAMELOOKUP_SIZE).
-
- If anyone has a better fix, please tell us!
-
- -------------------------------------------------------------------
-
- On October 23rd 2003, Dan C dug up more details on the mysteries of
- gethostbyname_r() in glibc:
-
- In glibc 2.2.5 the interface is different (this has also been
- discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't
- explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32
- (shipped/upgraded by Redhat 7.2) don't show this behavior!
-
- In this "buggy" version, the return code is -1 on error and 'errno'
- is set to the ERANGE or EAGAIN code. Note that 'errno' is not a
- thread-safe variable.
-
- */
-
- if(((ERANGE == res) || (EAGAIN == res)) ||
- ((res<0) && ((ERANGE == errno) || (EAGAIN == errno))))
- step_size+=200;
- else
- break;
- } while(step_size <= CURL_NAMELOOKUP_SIZE);
-
- if(!h) /* failure */
- res=1;
-
-#ifdef CURLDEBUG
- infof(data, "gethostbyname_r() uses %d bytes\n", step_size);
-#endif
- if(!res) {
- int offset;
- h=(struct hostent *)realloc(buf, step_size);
- offset=(long)h-(long)buf;
- hostcache_fixoffset(h, offset);
- buf=(int *)h;
- }
- else
-#endif/* HAVE_GETHOSTBYNAME_R_6 */
-#ifdef HAVE_GETHOSTBYNAME_R_3
- /* AIX, Digital Unix/Tru64, HPUX 10, more? */
-
- /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of
- the plain fact that it does not return unique full buffers on each
- call, but instead several of the pointers in the hostent structs will
- point to the same actual data! This have the unfortunate down-side that
- our caching system breaks down horribly. Luckily for us though, AIX 4.3
- and more recent versions have a completely thread-safe libc where all
- the data is stored in thread-specific memory areas making calls to the
- plain old gethostbyname() work fine even for multi-threaded programs.
-
- This AIX 4.3 or later detection is all made in the configure script.
-
- Troels Walsted Hansen helped us work this out on March 3rd, 2003. */
-
- if(CURL_NAMELOOKUP_SIZE >=
- (sizeof(struct hostent)+sizeof(struct hostent_data))) {
-
- /* August 22nd, 2000: Albert Chin-A-Young brought an updated version
- * that should work! September 20: Richard Prescott worked on the buffer
- * size dilemma. */
-
- res = gethostbyname_r(hostname,
- (struct hostent *)buf,
- (struct hostent_data *)((char *)buf +
- sizeof(struct hostent)));
- h_errnop= errno; /* we don't deal with this, but set it anyway */
- }
- else
- res = -1; /* failure, too smallish buffer size */
-
- if(!res) { /* success */
-
- h = (struct hostent*)buf; /* result expected in h */
-
- /* This is the worst kind of the different gethostbyname_r() interfaces.
- Since we don't know how big buffer this particular lookup required,
- we can't realloc down the huge alloc without doing closer analysis of
- the returned data. Thus, we always use CURL_NAMELOOKUP_SIZE for every
- name lookup. Fixing this would require an extra malloc() and then
- calling pack_hostent() that subsequent realloc()s down the new memory
- area to the actually used amount. */
- }
- else
-#endif /* HAVE_GETHOSTBYNAME_R_3 */
- {
- infof(data, "gethostbyname_r(2) failed for %s\n", hostname);
- h = NULL; /* set return code to NULL */
- free(buf);
- }
-#else /* HAVE_GETHOSTBYNAME_R */
- else {
-
-#ifdef USE_THREADING_GETHOSTBYNAME
- /* fire up a new resolver thread! */
- if (init_gethostbyname_thread(conn,hostname,port)) {
- *waitp = TRUE; /* please wait for the response */
- return NULL;
- }
- infof(data, "init_gethostbyname_thread() failed for %s; code %lu\n",
- hostname, GetLastError());
-#endif
- h = gethostbyname(hostname);
- if (!h)
- infof(data, "gethostbyname(2) failed for %s\n", hostname);
- else {
- char *buf=(char *)malloc(CURL_NAMELOOKUP_SIZE);
- /* we make a copy of the hostent right now, right here, as the static
- one we got a pointer to might get removed when we don't want/expect
- that */
- h = pack_hostent(&buf, h);
- }
-#endif /*HAVE_GETHOSTBYNAME_R */
- }
-
- return h;
-}
-
-#endif /* end of IPv4-specific code */
-
-#endif /* end of !USE_ARES */
-
-
-#if defined(USE_THREADING_GETHOSTBYNAME)
-#ifdef DEBUG_THREADING_GETHOSTBYNAME
-static void trace_it (const char *fmt, ...)
-{
- static int do_trace = -1;
- va_list args;
-
- if (do_trace == -1) {
- const char *env = getenv("CURL_TRACE");
- do_trace = (env && atoi(env) > 0);
- }
- if (!do_trace)
- return;
- va_start (args, fmt);
- vfprintf (stderr, fmt, args);
-/*fflush (stderr); */ /* seems a bad idea in a multi-threaded app */
- va_end (args);
-}
-#endif
-
-/*
- * gethostbyname_thread() resolves a name, calls the host_callback and then
- * exits.
- *
- * For builds without ARES/USE_IPV6, create a resolver thread and wait on it.
- */
-static unsigned __stdcall gethostbyname_thread (void *arg)
-{
- struct connectdata *conn = (struct connectdata*) arg;
- struct hostent *he;
- int rc;
-
- WSASetLastError (conn->async.status = NO_DATA); /* pending status */
- he = gethostbyname (conn->async.hostname);
- if (he) {
- host_callback(conn, ARES_SUCCESS, he);
- rc = 1;
- }
- else {
- host_callback(conn, (int)WSAGetLastError(), NULL);
- rc = 0;
- }
- TRACE(("Winsock-error %d, addr %s\n", conn->async.status,
- he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown"));
- return (rc);
- /* An implicit _endthreadex() here */
-}
-
-/*
- * destroy_thread_data() cleans up async resolver data.
- * Complementary of ares_destroy.
- */
-static void destroy_thread_data (struct Curl_async *async)
-{
- if (async->hostname)
- free(async->hostname);
-
- if (async->os_specific) {
- curl_socket_t sock = ((const struct thread_data*)async->os_specific)->dummy_sock;
-
- if (sock != CURL_SOCKET_BAD)
- sclose(sock);
- free(async->os_specific);
- }
- async->hostname = NULL;
- async->os_specific = NULL;
-}
-
-/*
- * init_gethostbyname_thread() starts a new thread that performs
- * the actual resolve. This function returns before the resolve is done.
- */
-static bool init_gethostbyname_thread (struct connectdata *conn,
- const char *hostname, int port)
-{
- struct thread_data *td = calloc(sizeof(*td), 1);
-
- if (!td) {
- SetLastError(ENOMEM);
- return (0);
- }
-
- Curl_safefree(conn->async.hostname);
- conn->async.hostname = strdup(hostname);
- if (!conn->async.hostname) {
- free(td);
- SetLastError(ENOMEM);
- return (0);
- }
-
- conn->async.port = port;
- conn->async.done = FALSE;
- conn->async.status = 0;
- conn->async.dns = NULL;
- conn->async.os_specific = (void*) td;
-
- td->dummy_sock = CURL_SOCKET_BAD;
- td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, gethostbyname_thread,
- conn, 0, &td->thread_id);
- if (!td->thread_hnd) {
- SetLastError(errno);
- TRACE(("_beginthreadex() failed; %s\n", Curl_strerror(conn,errno)));
- destroy_thread_data(&conn->async);
- return (0);
- }
- /* This socket is only to keep Curl_multi_ares_fdset() and select() happy;
- * should never become signalled for read/write since it's unbound but
- * Windows needs atleast 1 socket in select().
- */
- td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0);
- return (1);
-}
-
-/*
- * Curl_wait_for_resolv() waits for a resolve to finish. This function should
- * be avoided since using this risk getting the multi interface to "hang".
- *
- * If 'entry' is non-NULL, make it point to the resolved dns entry
- *
- * This is the version for resolves-in-a-thread.
- */
-CURLcode Curl_wait_for_resolv(struct connectdata *conn,
- struct Curl_dns_entry **entry)
-{
- struct thread_data *td = (struct thread_data*) conn->async.os_specific;
- struct SessionHandle *data = conn->data;
- long timeout;
- DWORD status, ticks;
- CURLcode rc;
-
- curlassert (conn && td);
-
- /* now, see if there's a connect timeout or a regular timeout to
- use instead of the default one */
- timeout =
- conn->data->set.connecttimeout ? conn->data->set.connecttimeout :
- conn->data->set.timeout ? conn->data->set.timeout :
- CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */
- ticks = GetTickCount();
-
- status = WaitForSingleObject(td->thread_hnd, 1000UL*timeout);
- if (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) {
- /* Thread finished before timeout; propagate Winsock error to this thread.
- * 'conn->async.done = TRUE' is set in host_callback().
- */
- WSASetLastError(conn->async.status);
- GetExitCodeThread(td->thread_hnd, &td->thread_status);
- TRACE(("gethostbyname_thread() status %lu, thread retval %lu, ",
- status, td->thread_status));
- }
- else {
- conn->async.done = TRUE;
- td->thread_status = (DWORD)-1;
- TRACE(("gethostbyname_thread() timeout, "));
- }
-
- TRACE(("elapsed %lu ms\n", GetTickCount()-ticks));
-
- CloseHandle(td->thread_hnd);
-
- if(entry)
- *entry = conn->async.dns;
-
- rc = CURLE_OK;
-
- if (!conn->async.dns) {
- /* a name was not resolved */
- if (td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) {
- failf(data, "Resolving host timed out: %s", conn->hostname);
- rc = CURLE_OPERATION_TIMEDOUT;
- }
- else if(conn->async.done) {
- failf(data, "Could not resolve host: %s; %s",
- conn->hostname, Curl_strerror(conn,conn->async.status));
- rc = CURLE_COULDNT_RESOLVE_HOST;
- }
- else
- rc = CURLE_OPERATION_TIMEDOUT;
- }
-
- destroy_thread_data(&conn->async);
-
- if (CURLE_OK != rc)
- /* close the connection, since we can't return failure here without
- cleaning up this connection properly */
- Curl_disconnect(conn);
-
- return (rc);
-}
-
-/*
- * Curl_is_resolved() is called repeatedly to check if a previous name resolve
- * request has completed. It should also make sure to time-out if the
- * operation seems to take too long.
- */
-CURLcode Curl_is_resolved(struct connectdata *conn,
- struct Curl_dns_entry **entry)
-{
- *entry = NULL;
-
- if (conn->async.done) {
- /* we're done */
- destroy_thread_data(&conn->async);
- if (!conn->async.dns) {
- TRACE(("Curl_is_resolved(): CURLE_COULDNT_RESOLVE_HOST\n"));
- return CURLE_COULDNT_RESOLVE_HOST;
- }
- *entry = conn->async.dns;
- TRACE(("resolved okay, dns %p\n", *entry));
- }
- else
- TRACE(("not yet\n"));
- return CURLE_OK;
-}
-
-#endif
+#endif /* CURLRES_ADDRINFO_COPY */
diff --git a/lib/hostip.h b/lib/hostip.h
index b7d212d81..19a62c666 100644
--- a/lib/hostip.h
+++ b/lib/hostip.h
@@ -51,20 +51,40 @@ struct Curl_dns_entry {
* The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
* use, or we'll leak memory!
*/
-
int Curl_resolv(struct connectdata *conn,
char *hostname,
int port,
struct Curl_dns_entry **dnsentry);
+/*
+ * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
+ * been set and returns TRUE if they are OK.
+ */
+bool Curl_ipvalid(struct SessionHandle *data);
+
+/*
+ * Curl_getaddrinfo() is the generic low-level name resolve API within this
+ * source file. There are several versions of this function - for different
+ * name resolve layers (selected at build-time). They all take this same set
+ * of arguments
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ char *hostname,
+ int port,
+ int *waitp);
+
CURLcode Curl_is_resolved(struct connectdata *conn,
struct Curl_dns_entry **dns);
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);
+
+/* Curl_fdset() is a generic function that exists in multiple versions
+ depending on what name resolve technology we've built to use. The function
+ is called from the curl_multi_fdset() function */
+CURLcode Curl_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);
@@ -81,19 +101,120 @@ curl_hash *Curl_mk_dnscache(void);
void Curl_hostcache_prune(struct SessionHandle *data);
#ifdef CURLDEBUG
-void curl_freeaddrinfo(struct addrinfo *freethis,
+void curl_dofreeaddrinfo(struct addrinfo *freethis,
+ int line, const char *source);
+int curl_dogetaddrinfo(char *hostname, char *service,
+ struct addrinfo *hints,
+ struct addrinfo **result,
+ int line, const char *source);
+int curl_dogetnameinfo(const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen,
+ char *serv, size_t servlen, int flags,
int line, const char *source);
-int curl_getaddrinfo(char *hostname, char *service,
- struct addrinfo *hints,
- struct addrinfo **result,
- int line, const char *source);
#endif
+/* This is the callback function that is used when we build with asynch
+ resolve */
+void Curl_addrinfo_callback(void *arg,
+ int status,
+ Curl_addrinfo *hostent);
+
+/* This is a utility-function for ipv4-builds to create a hostent struct
+ from a numerical-only IP address */
+Curl_addrinfo *Curl_ip2addr(unsigned long num, char *hostname);
+
+/* relocate a hostent struct */
+void Curl_hostent_relocate(struct hostent *h, long offset);
+
+/* copy a Curl_addrinfo struct, currently this only supports copying
+ a hostent (ipv4-style) struct */
+Curl_addrinfo *Curl_addrinfo_copy(Curl_addrinfo *orig);
+
+/*
+ * (IPv6) Curl_printable_address() returns a printable version of the
+ * ai->ai_addr address given in the 2nd argument. The first should be the
+ * ai->ai_family and the result will be stored in the buf that is bufsize
+ * bytes big.
+ */
+const char *Curl_printable_address(int af, void *addr,
+ char *buf, size_t bufsize);
+
+/*
+ * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
+ */
+struct Curl_dns_entry *
+Curl_cache_addr(struct SessionHandle *data, Curl_addrinfo *addr,
+ char *hostname, int port);
+
#ifndef INADDR_NONE
#define CURL_INADDR_NONE (in_addr_t) ~0
#else
#define CURL_INADDR_NONE INADDR_NONE
#endif
+/*
+ * Setup comfortable CURLRES_* defines to use in the host*.c sources.
+ */
+
+#ifdef USE_ARES
+#define CURLRES_ASYNCH
+#define CURLRES_ARES
+#endif
+
+#ifdef USE_THREADING_GETHOSTBYNAME
+#define CURLRES_ASYNCH
+#define CURLRES_THREADED
+#endif
+
+#ifdef USE_THREADING_GETADDRINFO
+#define CURLRES_ASYNCH
+#define CURLRES_THREADED
+#endif
+
+#ifdef ENABLE_IPV6
+#define CURLRES_IPV6
+#else
+#define CURLRES_IPV4
+#endif
+
+#ifdef CURLRES_IPV4
+#if !defined(HAVE_GETHOSTBYNAME_R) || defined(CURLRES_ASYNCH)
+/* If built for ipv4 and missing gethostbyname_r(), or if using async name
+ resolve, we need the Curl_addrinfo_copy() function (which itself needs the
+ Curl_hostent_relocate() function)) */
+#define CURLRES_ADDRINFO_COPY
+#define CURLRES_HOSTENT_RELOCATE
+#endif
+#endif /* IPv4-only */
+
+#ifdef HAVE_GETHOSTBYNAME_R_6
+#define CURLRES_HOSTENT_RELOCATE
+#endif
+
+#ifdef HAVE_GETHOSTBYNAME_R_5
+#define CURLRES_HOSTENT_RELOCATE
+#endif
+
+#ifndef CURLRES_ASYNCH
+#define CURLRES_SYNCH
+#endif
+
+/* Allocate enough memory to hold the full name information structs and
+ * everything. OSF1 is known to require at least 8872 bytes. The buffer
+ * required for storing all possible aliases and IP numbers is according to
+ * Stevens' Unix Network Programming 2nd edition, p. 304: 8192 bytes!
+ */
+#define CURL_HOSTENT_SIZE 9000
+
+#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this
+ many seconds for a name resolve */
+
+#ifdef CURLRES_ARES
+#define CURL_ASYNC_SUCCESS ARES_SUCCESS
+#else
+#define CURL_ASYNC_SUCCESS CURLE_OK
+#endif
#endif
diff --git a/lib/hostip4.c b/lib/hostip4.c
new file mode 100644
index 000000000..bd6e40e73
--- /dev/null
+++ b/lib/hostip4.c
@@ -0,0 +1,400 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <errno.h>
+
+#define _REENTRANT
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <malloc.h>
+#else
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* required for free() prototypes */
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef WIN32
+#include <process.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+#include "inet_ntoa_r.h"
+#endif
+
+/* The last #include file should be: */
+#ifdef CURLDEBUG
+#include "memdebug.h"
+#endif
+
+/***********************************************************************
+ * Only for plain-ipv4 builds
+ **********************************************************************/
+#ifdef CURLRES_IPV4 /* plain ipv4 code coming up */
+
+/*
+ * This is a wrapper function for freeing name information in a protocol
+ * independent way. This takes care of using the appropriate underlying
+ * function.
+ */
+void Curl_freeaddrinfo(Curl_addrinfo *p)
+{
+ free(p); /* works fine for the ARES case too */
+}
+
+/*
+ * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
+ * been set and returns TRUE if they are OK.
+ */
+bool Curl_ipvalid(struct SessionHandle *data)
+{
+ if(data->set.ip_version == CURL_IPRESOLVE_V6)
+ /* an ipv6 address was requested and we can't get/use one */
+ return FALSE;
+
+ return TRUE; /* OK, proceed */
+}
+
+/*
+ * Curl_ip2addr() takes a 32bit ipv4 internet address as input parameter
+ * together with a pointer to the string version of the address, and it
+ * retruns a malloc()ed version of a hostent struct filled in correctly with
+ * information for this address/host.
+ *
+ * The input parameters ARE NOT checked for validity but they are expected
+ * to have been checked already when this is called.
+ */
+Curl_addrinfo *Curl_ip2addr(unsigned long num, char *hostname)
+{
+ struct hostent *h;
+ struct in_addr *addrentry;
+ struct namebuf {
+ struct hostent hostentry;
+ char *h_addr_list[2];
+ struct in_addr addrentry;
+ char h_name[16]; /* 123.123.123.123 = 15 letters is maximum */
+ } *buf = (struct namebuf *)malloc(sizeof(struct namebuf));
+
+ if(!buf)
+ return NULL; /* major failure */
+
+ h = &buf->hostentry;
+ h->h_addr_list = &buf->h_addr_list[0];
+ addrentry = &buf->addrentry;
+ addrentry->s_addr = num;
+ h->h_addr_list[0] = (char*)addrentry;
+ h->h_addr_list[1] = NULL;
+ h->h_addrtype = AF_INET;
+ h->h_length = sizeof(*addrentry);
+ h->h_name = &buf->h_name[0];
+ h->h_aliases = NULL;
+
+ /* Now store the dotted version of the address */
+ snprintf(h->h_name, 16, "%s", hostname);
+
+ return h;
+}
+
+#ifdef CURLRES_SYNCH /* the functions below are for synchronous resolves */
+
+/*
+ * Curl_getaddrinfo() - the ipv4 synchronous version.
+ *
+ * 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.
+ *
+ * gethostbyname_r() is the thread-safe version of the gethostbyname()
+ * function. When we build for plain IPv4, we attempt to use this
+ * function. There are _three_ different gethostbyname_r() versions, and we
+ * detect which one this platform supports in the configure script and set up
+ * the HAVE_GETHOSTBYNAME_R_3, HAVE_GETHOSTBYNAME_R_5 or
+ * HAVE_GETHOSTBYNAME_R_6 defines accordingly. Note that HAVE_GETADDRBYNAME
+ * has the corresponding rules. This is primarily on *nix. Note that some unix
+ * flavours have thread-safe versions of the plain gethostbyname() etc.
+ *
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ char *hostname,
+ int port,
+ int *waitp)
+{
+ struct hostent *h = NULL;
+ in_addr_t in;
+ struct SessionHandle *data = conn->data;
+ (void)port; /* unused in IPv4 code */
+
+ *waitp = 0; /* don't wait, we act synchronously */
+
+ in=inet_addr(hostname);
+ if (in != CURL_INADDR_NONE)
+ /* This is a dotted IP address 123.123.123.123-style */
+ return Curl_ip2addr(in, hostname);
+
+#if defined(HAVE_GETHOSTBYNAME_R)
+ /*
+ * gethostbyname_r() is the preferred resolve function for many platforms.
+ * Since there are three different versions of it, the following code is
+ * somewhat #ifdef-ridden.
+ */
+ else {
+ int h_errnop;
+ int res=ERANGE;
+ int step_size=200;
+ int *buf = (int *)calloc(CURL_HOSTENT_SIZE, 1);
+ if(!buf)
+ return NULL; /* major failure */
+ /*
+ * The clearing of the buffer is a workaround for a gethostbyname_r bug in
+ * qnx nto and it is also _required_ for some of these functions on some
+ * platforms.
+ */
+
+#ifdef HAVE_GETHOSTBYNAME_R_5
+ /* Solaris, IRIX and more */
+ (void)res; /* prevent compiler warning */
+ while(!h) {
+ h = gethostbyname_r(hostname,
+ (struct hostent *)buf,
+ (char *)buf + sizeof(struct hostent),
+ step_size - sizeof(struct hostent),
+ &h_errnop);
+
+ /* If the buffer is too small, it returns NULL and sets errno to
+ * ERANGE. The errno is thread safe if this is compiled with
+ * -D_REENTRANT as then the 'errno' variable is a macro defined to get
+ * used properly for threads.
+ */
+
+ if(h || (errno != ERANGE))
+ break;
+
+ step_size+=200;
+ }
+
+#ifdef CURLDEBUG
+ infof(data, "gethostbyname_r() uses %d bytes\n", step_size);
+#endif
+
+ if(h) {
+ int offset;
+ h=(struct hostent *)realloc(buf, step_size);
+ offset=(long)h-(long)buf;
+ Curl_hostent_relocate(h, offset);
+ buf=(int *)h;
+ }
+ else
+#endif /* HAVE_GETHOSTBYNAME_R_5 */
+#ifdef HAVE_GETHOSTBYNAME_R_6
+ /* Linux */
+ do {
+ res=gethostbyname_r(hostname,
+ (struct hostent *)buf,
+ (char *)buf + sizeof(struct hostent),
+ step_size - sizeof(struct hostent),
+ &h, /* DIFFERENCE */
+ &h_errnop);
+ /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a
+ * sudden this function returns EAGAIN if the given buffer size is too
+ * small. Previous versions are known to return ERANGE for the same
+ * problem.
+ *
+ * This wouldn't be such a big problem if older versions wouldn't
+ * sometimes return EAGAIN on a common failure case. Alas, we can't
+ * assume that EAGAIN *or* ERANGE means ERANGE for any given version of
+ * glibc.
+ *
+ * For now, we do that and thus we may call the function repeatedly and
+ * fail for older glibc versions that return EAGAIN, until we run out of
+ * buffer size (step_size grows beyond CURL_HOSTENT_SIZE).
+ *
+ * If anyone has a better fix, please tell us!
+ *
+ * -------------------------------------------------------------------
+ *
+ * On October 23rd 2003, Dan C dug up more details on the mysteries of
+ * gethostbyname_r() in glibc:
+ *
+ * In glibc 2.2.5 the interface is different (this has also been
+ * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't
+ * explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32
+ * (shipped/upgraded by Redhat 7.2) don't show this behavior!
+ *
+ * In this "buggy" version, the return code is -1 on error and 'errno'
+ * is set to the ERANGE or EAGAIN code. Note that 'errno' is not a
+ * thread-safe variable.
+ */
+
+ if(((ERANGE == res) || (EAGAIN == res)) ||
+ ((res<0) && ((ERANGE == errno) || (EAGAIN == errno))))
+ step_size+=200;
+ else
+ break;
+ } while(step_size <= CURL_HOSTENT_SIZE);
+
+ if(!h) /* failure */
+ res=1;
+
+#ifdef CURLDEBUG
+ infof(data, "gethostbyname_r() uses %d bytes\n", step_size);
+#endif
+ if(!res) {
+ int offset;
+ h=(struct hostent *)realloc(buf, step_size);
+ offset=(long)h-(long)buf;
+ Curl_hostent_relocate(h, offset);
+ buf=(int *)h;
+ }
+ else
+#endif/* HAVE_GETHOSTBYNAME_R_6 */
+#ifdef HAVE_GETHOSTBYNAME_R_3
+ /* AIX, Digital Unix/Tru64, HPUX 10, more? */
+
+ /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of
+ * the plain fact that it does not return unique full buffers on each
+ * call, but instead several of the pointers in the hostent structs will
+ * point to the same actual data! This have the unfortunate down-side that
+ * our caching system breaks down horribly. Luckily for us though, AIX 4.3
+ * and more recent versions have a "completely thread-safe"[*] libc where
+ * all the data is stored in thread-specific memory areas making calls to
+ * the plain old gethostbyname() work fine even for multi-threaded
+ * programs.
+ *
+ * This AIX 4.3 or later detection is all made in the configure script.
+ *
+ * Troels Walsted Hansen helped us work this out on March 3rd, 2003.
+ *
+ * [*] = much later we've found out that it isn't at all "completely
+ * thread-safe", but at least the gethostbyname() function is.
+ */
+
+ if(CURL_HOSTENT_SIZE >=
+ (sizeof(struct hostent)+sizeof(struct hostent_data))) {
+
+ /* August 22nd, 2000: Albert Chin-A-Young brought an updated version
+ * that should work! September 20: Richard Prescott worked on the buffer
+ * size dilemma.
+ */
+
+ res = gethostbyname_r(hostname,
+ (struct hostent *)buf,
+ (struct hostent_data *)((char *)buf +
+ sizeof(struct hostent)));
+ h_errnop= errno; /* we don't deal with this, but set it anyway */
+ }
+ else
+ res = -1; /* failure, too smallish buffer size */
+
+ if(!res) { /* success */
+
+ h = (struct hostent*)buf; /* result expected in h */
+
+ /* This is the worst kind of the different gethostbyname_r() interfaces.
+ * Since we don't know how big buffer this particular lookup required,
+ * we can't realloc down the huge alloc without doing closer analysis of
+ * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every
+ * name lookup. Fixing this would require an extra malloc() and then
+ * calling Curl_addrinfo_copy() that subsequent realloc()s down the new
+ * memory area to the actually used amount.
+ */
+ }
+ else
+#endif /* HAVE_GETHOSTBYNAME_R_3 */
+ {
+ infof(data, "gethostbyname_r(2) failed for %s\n", hostname);
+ h = NULL; /* set return code to NULL */
+ free(buf);
+ }
+#else /* HAVE_GETHOSTBYNAME_R */
+ /*
+ * Here is code for platforms that don't have gethostbyname_r() or for
+ * which the gethostbyname() is the preferred() function.
+ */
+ else {
+ h = gethostbyname(hostname);
+ if (!h)
+ infof(data, "gethostbyname(2) failed for %s\n", hostname);
+ else {
+ /*
+ * Copy the hostent struct right here, as the static one we got a
+ * pointer to might get removed when we don't want/expect that. Windows
+ * (other platforms?) also doesn't allow passing of the returned data
+ * between threads, which thus the copying here them allows the app to
+ * do.
+ */
+ h = Curl_addrinfo_copy(h);
+ }
+#endif /*HAVE_GETHOSTBYNAME_R */
+ }
+
+ return h;
+}
+
+#endif /* CURLRES_SYNCH */
+
+#endif /* CURLRES_IPV4 */
diff --git a/lib/hostip6.c b/lib/hostip6.c
new file mode 100644
index 000000000..61b140b14
--- /dev/null
+++ b/lib/hostip6.c
@@ -0,0 +1,284 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <errno.h>
+
+#define _REENTRANT
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <malloc.h>
+#else
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* required for free() prototypes */
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef WIN32
+#include <process.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+#include "inet_ntop.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+#include "inet_ntoa_r.h"
+#endif
+
+/* The last #include file should be: */
+#ifdef CURLDEBUG
+#include "memdebug.h"
+#endif
+
+/***********************************************************************
+ * Only for ipv6-enabled builds
+ **********************************************************************/
+#ifdef CURLRES_IPV6
+/*
+ * This is a wrapper function for freeing name information in a protocol
+ * independent way. This takes care of using the appropriate underlaying
+ * function.
+ */
+void Curl_freeaddrinfo(Curl_addrinfo *p)
+{
+ freeaddrinfo(p);
+}
+
+/*
+ * Curl_printable_address() returns a printable version of the ai->ai_addr
+ * address given in the 2nd argument. The first should be the ai->ai_family
+ * and the result will be stored in the buf that is bufsize bytes big.
+ *
+ * If the conversion fails, it returns NULL.
+ */
+const char *Curl_printable_address(int af, void *addr,
+ char *buf, size_t bufsize)
+{
+ const struct in_addr *addr4 =
+ &((const struct sockaddr_in*)addr)->sin_addr;
+ const struct in6_addr *addr6 =
+ &((const struct sockaddr_in6*)addr)->sin6_addr;
+ return Curl_inet_ntop(af, af == AF_INET6 ?
+ (const void *)addr6 :
+ (const void *)addr4, buf, bufsize);
+}
+
+
+#ifdef CURLRES_ASYNCH
+/*
+ * Curl_addrinfo_copy() is used by the asynch callback to copy a given
+ * address. But this is an ipv6 build and then we don't copy the address, we
+ * just return the same pointer!
+ */
+Curl_addrinfo *Curl_addrinfo_copy(Curl_addrinfo *source)
+{
+ return source;
+}
+#endif
+
+#ifdef CURLDEBUG
+/* These are strictly for memory tracing and are using the same style as the
+ * family otherwise present in memdebug.c. I put these ones here since they
+ * require a bunch of structs I didn't wanna include in memdebug.c
+ */
+int curl_dogetaddrinfo(char *hostname, char *service,
+ struct addrinfo *hints,
+ struct addrinfo **result,
+ int line, const char *source)
+{
+ int res=(getaddrinfo)(hostname, service, hints, result);
+ if(0 == res) {
+ /* success */
+ if(logfile)
+ fprintf(logfile, "ADDR %s:%d getaddrinfo() = %p\n",
+ source, line, (void *)*result);
+ }
+ else {
+ if(logfile)
+ fprintf(logfile, "ADDR %s:%d getaddrinfo() failed\n",
+ source, line);
+ }
+ return res;
+}
+
+int curl_dogetnameinfo(const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen,
+ char *serv, size_t servlen, int flags,
+ int line, const char *source)
+{
+ int res=(getnameinfo)(sa, salen, host, hostlen, serv, servlen, flags);
+ if(0 == res) {
+ /* success */
+ if(logfile)
+ fprintf(logfile, "GETNAME %s:%d getnameinfo()\n",
+ source, line);
+ }
+ else {
+ if(logfile)
+ fprintf(logfile, "GETNAME %s:%d getnameinfo() failed = %d\n",
+ source, line, res);
+ }
+ return res;
+}
+
+void curl_dofreeaddrinfo(struct addrinfo *freethis,
+ int line, const char *source)
+{
+ (freeaddrinfo)(freethis);
+ if(logfile)
+ fprintf(logfile, "ADDR %s:%d freeaddrinfo(%p)\n",
+ source, line, (void *)freethis);
+}
+
+#endif
+
+/*
+ * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
+ * been set and returns TRUE if they are OK.
+ */
+bool Curl_ipvalid(struct SessionHandle *data)
+{
+ if(data->set.ip_version == CURL_IPRESOLVE_V6) {
+ /* see if we have an IPv6 stack */
+ curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (s < 0)
+ /* an ipv6 address was requested and we can't get/use one */
+ return FALSE;
+ sclose(s);
+ }
+ return TRUE;
+}
+
+#ifndef USE_THREADING_GETADDRINFO
+/*
+ * Curl_getaddrinfo() when built ipv6-enabled (non-threading version).
+ *
+ * Returns name information about the given hostname and port number. If
+ * successful, the 'addrinfo' is returned and the forth argument will point to
+ * memory we need to free after use. That memory *MUST* be freed with
+ * Curl_freeaddrinfo(), nothing else.
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ char *hostname,
+ int port,
+ int *waitp)
+{
+ struct addrinfo hints, *res;
+ int error;
+ char sbuf[NI_MAXSERV];
+ curl_socket_t s;
+ int pf;
+ struct SessionHandle *data = conn->data;
+
+ *waitp=0; /* don't wait, we have the response now */
+
+ /* see if we have an IPv6 stack */
+ s = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (s < 0) {
+ /* Some non-IPv6 stacks have been found to make very slow name resolves
+ * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
+ * the stack seems to be a non-ipv6 one. */
+
+ pf = PF_INET;
+ }
+ else {
+ /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
+ * possible checks. And close the socket again.
+ */
+ sclose(s);
+
+ /*
+ * Check if a more limited name resolve has been requested.
+ */
+ switch(data->set.ip_version) {
+ case CURL_IPRESOLVE_V4:
+ pf = PF_INET;
+ break;
+ case CURL_IPRESOLVE_V6:
+ pf = PF_INET6;
+ break;
+ default:
+ pf = PF_UNSPEC;
+ break;
+ }
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = pf;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_CANONNAME;
+ snprintf(sbuf, sizeof(sbuf), "%d", port);
+ error = getaddrinfo(hostname, sbuf, &hints, &res);
+ if (error) {
+ infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
+ return NULL;
+ }
+
+ return res;
+}
+#endif /* USE_THREADING_GETADDRINFO */
+#endif /* ipv6 */
+
diff --git a/lib/hostsyn.c b/lib/hostsyn.c
new file mode 100644
index 000000000..7ffe222f2
--- /dev/null
+++ b/lib/hostsyn.c
@@ -0,0 +1,150 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <errno.h>
+
+#define _REENTRANT
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <malloc.h>
+#else
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* required for free() prototypes */
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef WIN32
+#include <process.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+#include "inet_ntoa_r.h"
+#endif
+
+/* The last #include file should be: */
+#ifdef CURLDEBUG
+#include "memdebug.h"
+#endif
+
+/***********************************************************************
+ * Only for builds using synchronous name resolves
+ **********************************************************************/
+#ifdef CURLRES_SYNCH
+
+/*
+ * Curl_wait_for_resolv() for synch-builds. Curl_resolv() can never return
+ * wait==TRUE, so this function will never be called. If it still gets called,
+ * we return failure at once.
+ *
+ * We provide this function only to allow multi.c to remain unaware if we are
+ * doing asynch resolves or not.
+ */
+CURLcode Curl_wait_for_resolv(struct connectdata *conn,
+ struct Curl_dns_entry **entry)
+{
+ (void)conn;
+ *entry=NULL;
+ return CURLE_COULDNT_RESOLVE_HOST;
+}
+
+/*
+ * This function will never be called when synch-built. If it still gets
+ * called, we return failure at once.
+ *
+ * We provide this function only to allow multi.c to remain unaware if we are
+ * doing asynch resolves or not.
+ */
+CURLcode Curl_is_resolved(struct connectdata *conn,
+ struct Curl_dns_entry **dns)
+{
+ (void)conn;
+ *dns = NULL;
+
+ return CURLE_COULDNT_RESOLVE_HOST;
+}
+
+/*
+ * We just return OK, this function is never actually used for synch builds.
+ * It is present here to keep #ifdefs out from multi.c
+ */
+
+CURLcode Curl_fdset(struct connectdata *conn,
+ fd_set *read_fd_set,
+ fd_set *write_fd_set,
+ int *max_fdp)
+{
+ (void)conn;
+ (void)read_fd_set;
+ (void)write_fd_set;
+ (void)max_fdp;
+
+ return CURLE_OK;
+}
+
+#endif /* truly sync */
diff --git a/lib/hostthre.c b/lib/hostthre.c
new file mode 100644
index 000000000..48f4786f9
--- /dev/null
+++ b/lib/hostthre.c
@@ -0,0 +1,556 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#include <string.h>
+#include <errno.h>
+
+#define _REENTRANT
+
+#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
+#include <malloc.h>
+#else
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* required for free() prototypes */
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for the close() proto */
+#endif
+#ifdef VMS
+#include <in.h>
+#include <inet.h>
+#include <stdlib.h>
+#endif
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef WIN32
+#include <process.h>
+#endif
+
+#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#undef in_addr_t
+#define in_addr_t unsigned long
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "strerror.h"
+#include "url.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
+#include "inet_ntoa_r.h"
+#endif
+
+/* The last #include file should be: */
+#ifdef CURLDEBUG
+#include "memdebug.h"
+#endif
+
+/***********************************************************************
+ * Only for Windows threaded name resolves builds
+ **********************************************************************/
+#ifdef CURLRES_THREADED
+
+/* This function is used to init a threaded resolve */
+static bool init_resolve_thread(struct connectdata *conn,
+ const char *hostname, int port,
+ const Curl_addrinfo *hints);
+
+#ifdef CURLRES_IPV4
+ #define THREAD_FUNC gethostbyname_thread
+ #define THREAD_NAME "gethostbyname_thread"
+#else
+ #define THREAD_FUNC getaddrinfo_thread
+ #define THREAD_NAME "getaddrinfo_thread"
+#endif
+
+#if defined(DEBUG_THREADING_GETHOSTBYNAME) || \
+ defined(DEBUG_THREADING_GETADDRINFO)
+/* If this is defined, provide tracing */
+#define TRACE(args) \
+ do { trace_it("%u: ", __LINE__); trace_it args; } while (0)
+
+static void trace_it (const char *fmt, ...)
+{
+ static int do_trace = -1;
+ va_list args;
+
+ if (do_trace == -1) {
+ const char *env = getenv("CURL_TRACE");
+ do_trace = (env && atoi(env) > 0);
+ }
+ if (!do_trace)
+ return;
+ va_start (args, fmt);
+ vfprintf (stderr, fmt, args);
+ fflush (stderr);
+ va_end (args);
+}
+#else
+#define TRACE(x)
+#endif
+
+#ifdef DEBUG_THREADING_GETADDRINFO
+
+/* inet_ntop.c */
+extern const char *Curl_inet_ntop (int af, const void *addr, char *buf, size_t size);
+
+static void dump_addrinfo (struct connectdata *conn, const struct addrinfo *ai)
+{
+ TRACE(("dump_addrinfo:\n"));
+ for ( ; ai; ai = ai->ai_next) {
+ char buf [INET6_ADDRSTRLEN];
+ trace_it(" fam %2d, CNAME %s, ",
+ af, ai->ai_canonname ? ai->ai_canonname : "<none>");
+ if (Curl_printable_address(ai->ai_family, ai->ai_addr, buf, sizeof(buf)))
+ trace_it("%s\n", buf);
+ else
+ trace_it("failed; %s\n", Curl_strerror(conn,WSAGetLastError()));
+ }
+}
+#endif
+
+struct thread_data {
+ HANDLE thread_hnd;
+ unsigned thread_id;
+ DWORD thread_status;
+ curl_socket_t dummy_sock; /* dummy for Curl_fdset() */
+ FILE *stderr_file;
+#ifdef CURLRES_IPV6
+ struct addrinfo hints;
+#endif
+};
+
+#if defined(CURLRES_IPV4)
+/*
+ * gethostbyname_thread() resolves a name, calls the Curl_addrinfo_callback
+ * and then exits.
+ *
+ * For builds without ARES/ENABLE_IPV6, create a resolver thread and wait on
+ * it.
+ */
+static unsigned __stdcall gethostbyname_thread (void *arg)
+{
+ struct connectdata *conn = (struct connectdata*) arg;
+ struct thread_data *td = (struct thread_data*) conn->async.os_specific;
+ struct hostent *he;
+ int rc;
+
+ /* Sharing the same _iob[] element with our parent thread should
+ * hopefully make printouts synchronised. I'm not sure it works
+ * with a static runtime lib (MSVC's libc.lib).
+ */
+ *stderr = *td->stderr_file;
+
+ WSASetLastError (conn->async.status = NO_DATA); /* pending status */
+ he = gethostbyname (conn->async.hostname);
+ if (he) {
+ Curl_addrinfo_callback(conn, CURL_ASYNC_SUCCESS, he);
+ rc = 1;
+ }
+ else {
+ Curl_addrinfo_callback(conn, (int)WSAGetLastError(), NULL);
+ rc = 0;
+ }
+ TRACE(("Winsock-error %d, addr %s\n", conn->async.status,
+ he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown"));
+ return (rc);
+ /* An implicit _endthreadex() here */
+}
+
+#elif defined(CURLRES_IPV6)
+
+/*
+ * getaddrinfo_thread() resolves a name, calls Curl_addrinfo_callback and then
+ * exits.
+ *
+ * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
+ * and wait on it.
+ */
+static unsigned __stdcall getaddrinfo_thread (void *arg)
+{
+ struct connectdata *conn = (struct connectdata*) arg;
+ struct thread_data *td = (struct thread_data*) conn->async.os_specific;
+ struct addrinfo *res;
+ char service [NI_MAXSERV];
+ int rc;
+
+ *stderr = *td->stderr_file;
+
+ itoa(conn->async.port, service, 10);
+
+ WSASetLastError(conn->async.status = NO_DATA); /* pending status */
+
+ rc = getaddrinfo(conn->async.hostname, service, &td->hints, &res);
+
+ if (rc == 0) {
+#ifdef DEBUG_THREADING_GETADDRINFO
+ dump_addrinfo (conn, res);
+#endif
+ Curl_addrinfo_callback(conn, CURL_ASYNC_SUCCESS, res);
+ }
+ else {
+ Curl_addrinfo_callback(conn, (int)WSAGetLastError(), NULL);
+ TRACE(("Winsock-error %d, no address\n", conn->async.status));
+ }
+ return (rc);
+ /* An implicit _endthreadex() here */
+}
+#endif
+
+/*
+ * destroy_thread_data() cleans up async resolver data.
+ * Complementary of ares_destroy.
+ */
+static void destroy_thread_data (struct Curl_async *async)
+{
+ if (async->hostname)
+ free(async->hostname);
+
+ if (async->os_specific) {
+ curl_socket_t sock = ((const struct thread_data*)async->os_specific)->dummy_sock;
+
+ if (sock != CURL_SOCKET_BAD)
+ sclose(sock);
+ free(async->os_specific);
+ }
+ async->hostname = NULL;
+ async->os_specific = NULL;
+}
+
+/*
+ * init_resolve_thread() starts a new thread that performs the actual
+ * resolve. This function returns before the resolve is done.
+ *
+ * Returns FALSE in case of failure, otherwise TRUE.
+ */
+static bool init_resolve_thread (struct connectdata *conn,
+ const char *hostname, int port,
+ const Curl_addrinfo *hints)
+{
+ struct thread_data *td = calloc(sizeof(*td), 1);
+
+ if (!td) {
+ SetLastError(ENOMEM);
+ return FALSE;
+ }
+
+ Curl_safefree(conn->async.hostname);
+ conn->async.hostname = strdup(hostname);
+ if (!conn->async.hostname) {
+ free(td);
+ SetLastError(ENOMEM);
+ return FALSE;
+ }
+
+ conn->async.port = port;
+ conn->async.done = FALSE;
+ conn->async.status = 0;
+ conn->async.dns = NULL;
+ conn->async.os_specific = (void*) td;
+
+ td->dummy_sock = CURL_SOCKET_BAD;
+ td->stderr_file = stderr;
+ td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, THREAD_FUNC,
+ conn, 0, &td->thread_id);
+#ifdef CURLRES_IPV6
+ curlassert(hints);
+ td->hints = *hints;
+#else
+ (void) hints;
+#endif
+
+ if (!td->thread_hnd) {
+ SetLastError(errno);
+ TRACE(("_beginthreadex() failed; %s\n", Curl_strerror(conn,errno)));
+ destroy_thread_data(&conn->async);
+ return FALSE;
+ }
+ /* This socket is only to keep Curl_fdset() and select() happy; should never
+ * become signalled for read/write since it's unbound but Windows needs
+ * atleast 1 socket in select().
+ */
+ td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ return TRUE;
+}
+
+
+/*
+ * Curl_wait_for_resolv() waits for a resolve to finish. This function should
+ * be avoided since using this risk getting the multi interface to "hang".
+ *
+ * If 'entry' is non-NULL, make it point to the resolved dns entry
+ *
+ * This is the version for resolves-in-a-thread.
+ */
+CURLcode Curl_wait_for_resolv(struct connectdata *conn,
+ struct Curl_dns_entry **entry)
+{
+ struct thread_data *td = (struct thread_data*) conn->async.os_specific;
+ struct SessionHandle *data = conn->data;
+ long timeout;
+ DWORD status, ticks;
+ CURLcode rc;
+
+ curlassert (conn && td);
+
+ /* now, see if there's a connect timeout or a regular timeout to
+ use instead of the default one */
+ timeout =
+ conn->data->set.connecttimeout ? conn->data->set.connecttimeout :
+ conn->data->set.timeout ? conn->data->set.timeout :
+ CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */
+ ticks = GetTickCount();
+
+ status = WaitForSingleObject(td->thread_hnd, 1000UL*timeout);
+ if (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) {
+ /* Thread finished before timeout; propagate Winsock error to this thread.
+ * 'conn->async.done = TRUE' is set in Curl_addrinfo_callback().
+ */
+ WSASetLastError(conn->async.status);
+ GetExitCodeThread(td->thread_hnd, &td->thread_status);
+ TRACE(("%s() status %lu, thread retval %lu, ",
+ THREAD_NAME, status, td->thread_status));
+ }
+ else {
+ conn->async.done = TRUE;
+ td->thread_status = (DWORD)-1;
+ TRACE(("%s() timeout, ", THREAD_NAME));
+ }
+
+ TRACE(("elapsed %lu ms\n", GetTickCount()-ticks));
+
+ CloseHandle(td->thread_hnd);
+
+ if(entry)
+ *entry = conn->async.dns;
+
+ rc = CURLE_OK;
+
+ if (!conn->async.dns) {
+ /* a name was not resolved */
+ if (td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) {
+ failf(data, "Resolving host timed out: %s", conn->hostname);
+ rc = CURLE_OPERATION_TIMEDOUT;
+ }
+ else if(conn->async.done) {
+ failf(data, "Could not resolve host: %s; %s",
+ conn->hostname, Curl_strerror(conn,conn->async.status));
+ rc = CURLE_COULDNT_RESOLVE_HOST;
+ }
+ else
+ rc = CURLE_OPERATION_TIMEDOUT;
+ }
+
+ destroy_thread_data(&conn->async);
+
+ if(CURLE_OK != rc)
+ /* close the connection, since we must not return failure from here
+ without cleaning up this connection properly */
+ Curl_disconnect(conn);
+
+ return (rc);
+}
+
+/*
+ * Curl_is_resolved() is called repeatedly to check if a previous name resolve
+ * request has completed. It should also make sure to time-out if the
+ * operation seems to take too long.
+ */
+CURLcode Curl_is_resolved(struct connectdata *conn,
+ struct Curl_dns_entry **entry)
+{
+ *entry = NULL;
+
+ if (conn->async.done) {
+ /* we're done */
+ destroy_thread_data(&conn->async);
+ if (!conn->async.dns) {
+ TRACE(("Curl_is_resolved(): CURLE_COULDNT_RESOLVE_HOST\n"));
+ return CURLE_COULDNT_RESOLVE_HOST;
+ }
+ *entry = conn->async.dns;
+ TRACE(("resolved okay, dns %p\n", *entry));
+ }
+ else
+ TRACE(("not yet\n"));
+ return CURLE_OK;
+}
+
+CURLcode Curl_fdset(struct connectdata *conn,
+ fd_set *read_fd_set,
+ fd_set *write_fd_set,
+ int *max_fdp)
+{
+ const struct thread_data *td =
+ (const struct thread_data *) conn->async.os_specific;
+
+ if (td && td->dummy_sock != CURL_SOCKET_BAD) {
+ FD_SET(td->dummy_sock,write_fd_set);
+ *max_fdp = td->dummy_sock;
+ }
+ (void) read_fd_set;
+ return CURLE_OK;
+}
+
+#ifdef CURLRES_IPV4
+/*
+ * Curl_getaddrinfo() - for Windows threading without ENABLE_IPV6.
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ char *hostname,
+ int port,
+ int *waitp)
+{
+ struct hostent *h = NULL;
+ struct SessionHandle *data = conn->data;
+ in_addr_t in;
+
+ *waitp = 0; /* don't wait, we act synchronously */
+
+ in = inet_addr(hostname);
+ if (in != CURL_INADDR_NONE)
+ /* This is a dotted IP address 123.123.123.123-style */
+ return Curl_ip2addr(in, hostname);
+
+ /* fire up a new resolver thread! */
+ if (init_resolve_thread(conn, hostname, port, NULL)) {
+ *waitp = TRUE; /* please wait for the response */
+ return NULL;
+ }
+
+ /* fall-back to blocking version */
+ infof(data, "init_resolve_thread() failed for %s; code %lu\n",
+ hostname, GetLastError());
+
+ h = gethostbyname(hostname);
+ if (!h) {
+ infof(data, "gethostbyname(2) failed for %s:%d; %s\n",
+ hostname, port, Curl_strerror(conn,WSAGetLastError()));
+ return NULL;
+ }
+ return h;
+}
+#endif /* CURLRES_IPV4 */
+
+#ifdef CURLRES_IPV6
+/*
+ * Curl_getaddrinfo() - for Windows threading IPv6 enabled
+ */
+Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
+ char *hostname,
+ int port,
+ int *waitp)
+{
+ struct addrinfo hints, *res;
+ int error;
+ char sbuf[NI_MAXSERV];
+ curl_socket_t s;
+ int pf;
+ struct SessionHandle *data = conn->data;
+
+ *waitp = FALSE; /* default to synch response */
+
+ /* see if we have an IPv6 stack */
+ s = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (s < 0) {
+ /* Some non-IPv6 stacks have been found to make very slow name resolves
+ * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
+ * the stack seems to be a non-ipv6 one. */
+
+ pf = PF_INET;
+ }
+ else {
+ /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
+ * possible checks. And close the socket again.
+ */
+ sclose(s);
+
+ /*
+ * Check if a more limited name resolve has been requested.
+ */
+ switch(data->set.ip_version) {
+ case CURL_IPRESOLVE_V4:
+ pf = PF_INET;
+ break;
+ case CURL_IPRESOLVE_V6:
+ pf = PF_INET6;
+ break;
+ default:
+ pf = PF_UNSPEC;
+ break;
+ }
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = pf;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_CANONNAME;
+ itoa(port, sbuf, 10);
+
+ /* fire up a new resolver thread! */
+ if (init_resolve_thread(conn, hostname, port, &hints)) {
+ *waitp = TRUE; /* please wait for the response */
+ return NULL;
+ }
+
+ /* fall-back to blocking version */
+ infof(data, "init_resolve_thread() failed for %s; code %lu\n",
+ hostname, GetLastError());
+
+ error = getaddrinfo(hostname, sbuf, &hints, &res);
+ if (error) {
+ infof(data, "getaddrinfo() failed for %s:%d; %s\n",
+ hostname, port, Curl_strerror(conn,WSAGetLastError()));
+ return NULL;
+ }
+ return res;
+}
+#endif /* CURLRES_IPV6 */
+#endif /* CURLRES_THREADED */
diff --git a/lib/inet_ntop.c b/lib/inet_ntop.c
new file mode 100644
index 000000000..22c053e17
--- /dev/null
+++ b/lib/inet_ntop.c
@@ -0,0 +1,189 @@
+/*
+ * Original code by Paul Vixie. "curlified" by Gisle Vanem.
+ */
+
+#include "setup.h"
+
+#ifndef HAVE_INET_NTOP
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <string.h>
+#include <errno.h>
+
+#include "inet_ntop.h"
+
+#define IN6ADDRSZ 16
+#define INADDRSZ 4
+#define INT16SZ 2
+
+#ifdef WIN32
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#define SET_ERRNO(e) WSASetLastError(errno = (e))
+#else
+#define SET_ERRNO(e) errno = e
+#endif
+
+/*
+ * Format an IPv4 address, more or less like inet_ntoa().
+ *
+ * Returns `dst' (as a const)
+ * Note:
+ * - uses no statics
+ * - takes a u_char* not an in_addr as input
+ */
+static const char *inet_ntop4 (const u_char *src, char *dst, size_t size)
+{
+#ifdef HAVE_INET_NTOA_R
+ return inet_ntoa_r(*(struct in_addr*)src, dst, size);
+#else
+ const char *addr = inet_ntoa(*(struct in_addr*)src);
+
+ if (strlen(addr) >= size)
+ {
+ SET_ERRNO(ENOSPC);
+ return (NULL);
+ }
+ return strcpy(dst, addr);
+#endif
+}
+
+#ifdef ENABLE_IPV6
+/*
+ * Convert IPv6 binary address into presentation (printable) format.
+ */
+static const char *inet_ntop6 (const u_char *src, char *dst, size_t size)
+{
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp [sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ char *tp;
+ struct {
+ long base;
+ long len;
+ } best, cur;
+ u_long words [IN6ADDRSZ / INT16SZ];
+ int i;
+
+ /* Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, 0, sizeof(words));
+ for (i = 0; i < IN6ADDRSZ; i++)
+ words[i/2] |= (src[i] << ((1 - (i % 2)) << 3));
+
+ best.base = -1;
+ cur.base = -1;
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
+ {
+ if (words[i] == 0)
+ {
+ if (cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else
+ cur.len++;
+ }
+ else if (cur.base != -1)
+ {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ if ((cur.base != -1) && (best.base == -1 || cur.len > best.len))
+ best = cur;
+ if (best.base != -1 && best.len < 2)
+ best.base = -1;
+
+ /* Format the result.
+ */
+ tp = tmp;
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
+ {
+ /* Are we inside the best run of 0x00's?
+ */
+ if (best.base != -1 && i >= best.base && i < (best.base + best.len))
+ {
+ if (i == best.base)
+ *tp++ = ':';
+ continue;
+ }
+
+ /* Are we following an initial run of 0x00s or any real hex?
+ */
+ if (i != 0)
+ *tp++ = ':';
+
+ /* Is this address an encapsulated IPv4?
+ */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 || (best.len == 5 && words[5] == 0xffff)))
+ {
+ if (!inet_ntop4(src+12, tp, sizeof(tmp) - (tp - tmp)))
+ {
+ SET_ERRNO(ENOSPC);
+ return (NULL);
+ }
+ tp += strlen(tp);
+ break;
+ }
+ tp += sprintf (tp, "%lx", words[i]);
+ }
+
+ /* Was it a trailing run of 0x00's?
+ */
+ if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
+ *tp++ = ':';
+ *tp++ = '\0';
+
+ /* Check for overflow, copy, and we're done.
+ */
+ if ((size_t)(tp - tmp) > size)
+ {
+ SET_ERRNO(ENOSPC);
+ return (NULL);
+ }
+ return strcpy (dst, tmp);
+}
+#endif /* ENABLE_IPV6 */
+
+/*
+ * Convert a network format address to presentation format.
+ *
+ * Returns pointer to presentation format address (`dst'),
+ * Returns NULL on error (see errno).
+ */
+const char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size)
+{
+ switch (af) {
+ case AF_INET:
+ return inet_ntop4((const u_char*)src, buf, size);
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ return inet_ntop6((const u_char*)src, buf, size);
+#endif
+ default:
+ SET_ERRNO(EAFNOSUPPORT);
+ return NULL;
+ }
+}
+#endif /* HAVE_INET_NTOP */
diff --git a/lib/inet_ntop.h b/lib/inet_ntop.h
new file mode 100644
index 000000000..5948a120b
--- /dev/null
+++ b/lib/inet_ntop.h
@@ -0,0 +1,37 @@
+#ifndef __INET_NTOP_H
+#define __INET_NTOP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id$
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifdef HAVE_INET_NTOP
+#define Curl_inet_ntop(af,addr,buf,size) inet_ntop(af,addr,buf,size)
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#else
+const char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size);
+#endif
+
+#endif /* __INET_NTOP_H */
diff --git a/lib/memdebug.h b/lib/memdebug.h
index 40ba08033..42574cf43 100644
--- a/lib/memdebug.h
+++ b/lib/memdebug.h
@@ -84,9 +84,12 @@ int curl_fclose(FILE *file, int line, const char *source);
curl_accept(sock,addr,len,__LINE__,__FILE__)
#define getaddrinfo(host,serv,hint,res) \
- curl_getaddrinfo(host,serv,hint,res,__LINE__,__FILE__)
+ curl_dogetaddrinfo(host,serv,hint,res,__LINE__,__FILE__)
+#define getnameinfo(sa,salen,host,hostlen,serv,servlen,flags) \
+ curl_dogetnameinfo(sa,salen,host,hostlen,serv,servlen,flags, __LINE__, \
+ __FILE__)
#define freeaddrinfo(data) \
- curl_freeaddrinfo(data,__LINE__,__FILE__)
+ curl_dofreeaddrinfo(data,__LINE__,__FILE__)
/* sclose is probably already defined, redefine it! */
#undef sclose
diff --git a/lib/multi.c b/lib/multi.c
index ddc4b16cb..7d86d202e 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -251,8 +251,7 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle,
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);
+ Curl_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;
@@ -413,7 +412,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
easy->easy_conn->sock[FIRSTSOCKET],
&connected);
if(connected)
- easy->result = Curl_protocol_connect(easy->easy_conn, NULL);
+ easy->result = Curl_protocol_connect(easy->easy_conn);
if(CURLE_OK != easy->result) {
/* failure detected */
diff --git a/lib/setup.h b/lib/setup.h
index 35370f673..0bbdbd5b3 100644
--- a/lib/setup.h
+++ b/lib/setup.h
@@ -261,9 +261,13 @@ typedef int curl_socket_t;
#error "ares does not yet support IPv6. Disable IPv6 or ares and rebuild"
#endif
-#if defined(WIN32) && !defined(__CYGWIN32__) && !defined(USE_ARES) && !defined(ENABLE_IPV6)
+#if defined(WIN32) && !defined(__CYGWIN__) && !defined(USE_ARES)
+#ifdef ENABLE_IPV6
+#define USE_THREADING_GETADDRINFO
+#else
#define USE_THREADING_GETHOSTBYNAME /* Cygwin uses alarm() function */
#endif
+#endif
/*
* Curl_addrinfo MUST be used for name resolving information.
@@ -296,4 +300,10 @@ typedef struct in_addr Curl_ipconnect;
#undef HAVE_ALARM
#endif
+#ifdef HAVE_LIBIDN
+/* This could benefit from additional checks that some of the used/important
+ header files are present as well before we define the USE_* define. */
+#define USE_LIBIDN
+#endif
+
#endif /* __CONFIG_H */
diff --git a/lib/url.c b/lib/url.c
index 87d1685f2..7bdea96ef 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -84,6 +84,11 @@
#endif
+#ifdef USE_LIBIDN
+#include <idna.h>
+#include <stringprep.h>
+#endif
+
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
@@ -116,6 +121,7 @@
#include "ldap.h"
#include "url.h"
#include "connect.h"
+#include "inet_ntop.h"
#include <ca-bundle.h>
#include <curl/types.h>
@@ -145,6 +151,11 @@ static unsigned int ConnectionStore(struct SessionHandle *data,
struct connectdata *conn);
static bool safe_strequal(char* str1, char* str2);
+#ifdef USE_LIBIDN
+static bool is_ASCII_name (const char *hostname);
+static bool is_ACE_name (const char *hostname);
+#endif
+
#ifndef USE_ARES
/* not for Win32, unless it is cygwin
not for ares builds */
@@ -1384,7 +1395,8 @@ CURLcode Curl_disconnect(struct connectdata *conn)
Curl_safefree(conn->allocptr.host);
Curl_safefree(conn->allocptr.cookiehost);
Curl_safefree(conn->proxyhost);
-#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME)
+#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
+ defined(USE_THREADING_GETADDRINFO)
/* possible left-overs from the async name resolve */
Curl_safefree(conn->async.hostname);
Curl_safefree(conn->async.os_specific);
@@ -1875,64 +1887,26 @@ static CURLcode ConnectPlease(struct connectdata *conn,
}
/*
- * ALERT! The 'dns' pointer being passed in here might be NULL at times.
+ * verboseconnect() displays verbose information after a connect
*/
-static void verboseconnect(struct connectdata *conn,
- struct Curl_dns_entry *dns)
+static void verboseconnect(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
+ const char *host=NULL;
+ char addrbuf[NI_MAXHOST];
- /* Figure out the ip-number and display the first host name it shows: */
+ /* Get a printable version of the network address. */
#ifdef ENABLE_IPV6
- {
- char hbuf[NI_MAXHOST];
-#ifdef HAVE_NI_WITHSCOPEID
-#define NIFLAGS NI_NUMERICHOST | NI_WITHSCOPEID
-#else
-#define NIFLAGS NI_NUMERICHOST
-#endif
- if(dns) {
- struct addrinfo *ai = dns->addr;
-
- /* Lookup the name of the given address. This should probably be remade
- to use the DNS cache instead, as the host name is most likely cached
- already. */
- if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0,
- NIFLAGS)) {
- snprintf(hbuf, sizeof(hbuf), "unknown");
- }
- else {
- if (ai->ai_canonname) {
- infof(data, "Connected to %s (%s) port %d\n", ai->ai_canonname, hbuf,
- conn->port);
- return;
- }
- }
- }
- else {
- snprintf(hbuf, sizeof(hbuf), "same host");
- }
-
- infof(data, "Connected to %s port %d\n", hbuf, conn->port);
- }
+ struct addrinfo *ai = conn->serv_addr;
+ host = Curl_printable_address(ai->ai_family, ai->ai_addr,
+ addrbuf, sizeof(addrbuf));
#else
- {
-#ifdef HAVE_INET_NTOA_R
- char ntoa_buf[64];
-#endif
- Curl_addrinfo *hostaddr=dns?dns->addr:NULL;
- struct in_addr in;
- (void) memcpy(&in.s_addr, &conn->serv_addr.sin_addr, sizeof (in.s_addr));
- infof(data, "Connected to %s (%s) port %d\n",
- hostaddr?hostaddr->h_name:"",
-#if defined(HAVE_INET_NTOA_R)
- inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)),
-#else
- inet_ntoa(in),
-#endif
- conn->port);
- }
+ struct in_addr in;
+ (void) memcpy(&in.s_addr, &conn->serv_addr.sin_addr, sizeof (in.s_addr));
+ host = Curl_inet_ntop(AF_INET, &in, addrbuf, sizeof(addrbuf));
#endif
+ infof(data, "Connected to %s (%s) port %d\n",
+ conn->hostname, host?host:"", conn->port);
}
/*
@@ -1942,11 +1916,9 @@ static void verboseconnect(struct connectdata *conn,
* If we're using the multi interface, this host address pointer is most
* likely NULL at this point as we can't keep the resolved info around. This
* may call for some reworking, like a reference counter in the struct or
- * something. The hostaddr is not used for very much though, we have the
- * 'serv_addr' field in the connectdata struct for most of it.
+ * something.
*/
-CURLcode Curl_protocol_connect(struct connectdata *conn,
- struct Curl_dns_entry *hostaddr)
+CURLcode Curl_protocol_connect(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
CURLcode result=CURLE_OK;
@@ -1960,7 +1932,7 @@ CURLcode Curl_protocol_connect(struct connectdata *conn,
Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
if(data->set.verbose)
- verboseconnect(conn, hostaddr);
+ verboseconnect(conn);
if(conn->curl_connect) {
/* is there a protocol-specific connect() procedure? */
@@ -3173,6 +3145,26 @@ static CURLcode SetupConnection(struct connectdata *conn,
a file:// transfer */
return result;
+#ifdef USE_LIBIDN
+ /*************************************************************
+ * Check name for non-ASCII and convert hostname to ACE form.
+ *************************************************************/
+
+ if(!conn->bits.reuse && conn->remote_port) {
+ const char *host = conn->hostname;
+ char *ace_hostname;
+
+ if (!is_ASCII_name(host) && !is_ACE_name(host)) {
+ int rc = idna_to_ascii_lz (host, &ace_hostname, 0);
+
+ if (rc == IDNA_SUCCESS)
+ conn->ace_hostname = ace_hostname;
+ else
+ infof(data, "Failed to convert %s to ACE; IDNA error %d\n", host, rc);
+ }
+ }
+#endif
+
/*************************************************************
* Send user-agent to HTTP proxies even if the target protocol
* isn't HTTP.
@@ -3202,7 +3194,7 @@ static CURLcode SetupConnection(struct connectdata *conn,
result = ConnectPlease(conn, hostaddr, &connected);
if(connected) {
- result = Curl_protocol_connect(conn, hostaddr);
+ result = Curl_protocol_connect(conn);
if(CURLE_OK == result)
conn->bits.tcpconnect = TRUE;
}
@@ -3217,7 +3209,7 @@ static CURLcode SetupConnection(struct connectdata *conn,
Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
conn->bits.tcpconnect = TRUE;
if(data->set.verbose)
- verboseconnect(conn, hostaddr);
+ verboseconnect(conn);
}
conn->now = Curl_tvnow(); /* time this *after* the connect is done, we
@@ -3277,7 +3269,8 @@ CURLcode Curl_connect(struct SessionHandle *data,
then a successful name resolve has been received */
CURLcode Curl_async_resolved(struct connectdata *conn)
{
-#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME)
+#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
+ defined(USE_THREADING_GETADDRINFO)
CURLcode code = SetupConnection(conn, conn->async.dns);
if(code)
@@ -3504,3 +3497,20 @@ void Curl_free_ssl_config(struct ssl_config_data* sslc)
if(sslc->random_file)
free(sslc->random_file);
}
+
+/*
+ * Helpers for IDNA convertions. To do.
+ */
+#ifdef USE_LIBIDN
+static bool is_ASCII_name (const char *hostname)
+{
+ (void) hostname;
+ return (TRUE);
+}
+
+static bool is_ACE_name (const char *hostname)
+{
+ (void) hostname;
+ return (FALSE);
+}
+#endif
diff --git a/lib/url.h b/lib/url.h
index 1d3057ccd..170c107db 100644
--- a/lib/url.h
+++ b/lib/url.h
@@ -37,8 +37,7 @@ CURLcode Curl_do(struct connectdata **);
CURLcode Curl_do_more(struct connectdata *);
CURLcode Curl_done(struct connectdata *);
CURLcode Curl_disconnect(struct connectdata *);
-CURLcode Curl_protocol_connect(struct connectdata *conn,
- struct Curl_dns_entry *dns);
+CURLcode Curl_protocol_connect(struct connectdata *conn);
bool Curl_ssl_config_matches(struct ssl_config_data* data,
struct ssl_config_data* needle);
bool Curl_clone_ssl_config(struct ssl_config_data* source,
diff --git a/lib/urldata.h b/lib/urldata.h
index 7642cb9a1..445788f8f 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -81,6 +81,10 @@
#include <zlib.h> /* for content-encoding */
#endif
+#ifdef USE_ARES
+#include <ares.h>
+#endif
+
#include <curl/curl.h>
#include "http_chunks.h" /* for the structs and enum stuff */
@@ -96,10 +100,6 @@
#endif
#endif
-#ifdef USE_ARES
-#include <ares.h>
-#endif
-
/* Download buffer size, keep it fairly big for speed reasons */
#define BUFSIZE CURL_MAX_WRITE_SIZE
@@ -381,7 +381,8 @@ struct Curl_transfer_keeper {
bool ignorebody; /* we read a response-body but we ignore it! */
};
-#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME)
+#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
+ defined(USE_THREADING_GETADDRINFO)
struct Curl_async {
char *hostname;
int port;
@@ -430,6 +431,9 @@ struct connectdata {
char *namebuffer; /* allocated buffer to store the hostname in */
char *hostname; /* hostname to use, as parsed from url. points to
somewhere within the namebuffer[] area */
+#ifdef USE_LIBIDN
+ char *ace_hostname; /* hostname possibly converted to ACE form */
+#endif
char *pathbuffer;/* allocated buffer to store the URL's path part in */
char *path; /* path to use, points to somewhere within the pathbuffer
area */
@@ -579,7 +583,8 @@ struct connectdata {
char syserr_buf [256]; /* buffer for Curl_strerror() */
-#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME)
+#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
+ defined(USE_THREADING_GETADDRINFO)
/* data used for the asynch name resolve callback */
struct Curl_async async;
#endif