]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
Major hostip.c cleanup and split into multiple files and easier #ifdef
authorDaniel Stenberg <daniel@haxx.se>
Mon, 26 Apr 2004 07:20:11 +0000 (07:20 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 26 Apr 2004 07:20:11 +0000 (07:20 +0000)
usage.

17 files changed:
lib/Makefile.am
lib/hostares.c [new file with mode: 0644]
lib/hostasyn.c [new file with mode: 0644]
lib/hostip.c
lib/hostip.h
lib/hostip4.c [new file with mode: 0644]
lib/hostip6.c [new file with mode: 0644]
lib/hostsyn.c [new file with mode: 0644]
lib/hostthre.c [new file with mode: 0644]
lib/inet_ntop.c [new file with mode: 0644]
lib/inet_ntop.h [new file with mode: 0644]
lib/memdebug.h
lib/multi.c
lib/setup.h
lib/url.c
lib/url.h
lib/urldata.h

index e50cd6b7b1a71766a660ecca0749b70d0639e93a..5dbaaf0cb4d978803b9a244612d98fe79015f085 100644 (file)
@@ -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 (file)
index 0000000..1db87e4
--- /dev/null
@@ -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 (file)
index 0000000..a38c772
--- /dev/null
@@ -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 */
index 32bb03de3fba42211c9eadaa094b53f6c5281e3e..4b57051d78c456fc0d11cc03a92bd12e231788b2 100644 (file)
 #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
@@ -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,36 +477,17 @@ 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.
  */
@@ -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 */
index b7d212d81c8b685f4f3362f20972d8dca6483a15..19a62c666dc0b172ce47f8800179a2f6bc1da32d 100644 (file)
@@ -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 (file)
index 0000000..bd6e40e
--- /dev/null
@@ -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 (file)
index 0000000..61b140b
--- /dev/null
@@ -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 (file)
index 0000000..7ffe222
--- /dev/null
@@ -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 (file)
index 0000000..48f4786
--- /dev/null
@@ -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 (file)
index 0000000..22c053e
--- /dev/null
@@ -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 (file)
index 0000000..5948a12
--- /dev/null
@@ -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 */
index 40ba0803381632e7fb4f12ca2031abbcc41a4e67..42574cf4397d2a722f3e55bb01015d4ba0a40b72 100644 (file)
@@ -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
index ddc4b16cba2d111c66b9024066e4990f7b690873..7d86d202ef281da520e8e59ffd79e01738c7240a 100644 (file)
@@ -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 */
index 35370f6738b0dce1d89a4b92a63a941de2961653..0bbdbd5b3c595a489140f9890ecd895804eac0e0 100644 (file)
@@ -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 */
index 87d1685f297dc813ea8803d9c390d03194146bb0..7bdea96efc5b3b18a08190fdbc5349d0149f7a8f 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
 
 #endif
 
+#ifdef USE_LIBIDN
+#include <idna.h>
+#include <stringprep.h>
+#endif
+
 #ifdef HAVE_OPENSSL_ENGINE_H
 #include <openssl/engine.h>
 #endif
 #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
index 1d3057ccd3d7cf2a97e37a7a29b1ef898a7beeb5..170c107db432cc23d0e9f45d2e0396011ab733ee 100644 (file)
--- 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,
index 7642cb9a17418b2409abae8294300a789db54f00..445788f8f987137bbc470d48e85327e58c0f4634 100644 (file)
 #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 */
 #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