ntoa() and inet_ntoa_r() no longer used
[platform/upstream/curl.git] / lib / hostip.c
index 407c6b2..722c4b5 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2008, 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
 #include "setup.h"
 
 #include <string.h>
-#include <errno.h>
 
-#ifdef HAVE_MALLOC_H  /* Win32 */
+#ifdef NEED_MALLOC_H
 #include <malloc.h>
 #endif
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 #endif
 #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
-
 #include "memory.h"
 /* The last #include file should be: */
 #include "memdebug.h"
@@ -97,8 +89,7 @@
  * 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.
+ * asynchronous name resolves. 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
@@ -132,21 +123,19 @@ static void freednsentry(void *freethis);
  * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
  * Global DNS cache is general badness. Do not use. This will be removed in
  * a future version. Use the share interface instead!
+ *
+ * Returns a struct curl_hash pointer on success, NULL on failure.
  */
-void Curl_global_host_cache_init(void)
+struct curl_hash *Curl_global_host_cache_init(void)
 {
-  if (!host_cache_initialized) {
-    Curl_hash_init(&hostname_cache, 7, freednsentry);
-    host_cache_initialized = 1;
+  int rc = 0;
+  if(!host_cache_initialized) {
+    rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
+                        Curl_str_key_compare, freednsentry);
+    if(!rc)
+      host_cache_initialized = 1;
   }
-}
-
-/*
- * Return a pointer to the global cache
- */
-struct curl_hash *Curl_global_host_cache_get(void)
-{
-  return &hostname_cache;
+  return rc?NULL:&hostname_cache;
 }
 
 /*
@@ -154,7 +143,7 @@ struct curl_hash *Curl_global_host_cache_get(void)
  */
 void Curl_global_host_cache_dtor(void)
 {
-  if (host_cache_initialized) {
+  if(host_cache_initialized) {
     Curl_hash_clean(&hostname_cache);
     host_cache_initialized = 0;
   }
@@ -197,14 +186,14 @@ const char *Curl_printable_address(const Curl_addrinfo *ip,
  * the DNS caching.
  */
 static char *
-create_hostcache_id(char *server, int port)
+create_hostcache_id(const char *server, int port)
 {
   /* create and return the new allocated entry */
   return aprintf("%s:%d", server, port);
 }
 
 struct hostcache_prune_data {
-  int cache_timeout;
+  long cache_timeout;
   time_t now;
 };
 
@@ -222,7 +211,7 @@ hostcache_timestamp_remove(void *datap, void *hc)
     (struct hostcache_prune_data *) datap;
   struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
 
-  if ((data->now - c->timestamp < data->cache_timeout) ||
+  if((data->now - c->timestamp < data->cache_timeout) ||
       c->inuse) {
     /* please don't remove */
     return 0;
@@ -236,7 +225,7 @@ hostcache_timestamp_remove(void *datap, void *hc)
  * Prune the DNS cache. This assumes that a lock has already been taken.
  */
 static void
-hostcache_prune(struct curl_hash *hostcache, int cache_timeout, time_t now)
+hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
 {
   struct hostcache_prune_data user;
 
@@ -256,7 +245,7 @@ void Curl_hostcache_prune(struct SessionHandle *data)
 {
   time_t now;
 
-  if((data->set.dns_cache_timeout == -1) || !data->hostcache)
+  if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
     /* cache forever means never prune, and NULL hostcache means
        we can't do it */
     return;
@@ -267,7 +256,7 @@ void Curl_hostcache_prune(struct SessionHandle *data)
   time(&now);
 
   /* Remove outdated and unused entries from the hostcache */
-  hostcache_prune(data->hostcache,
+  hostcache_prune(data->dns.hostcache,
                   data->set.dns_cache_timeout,
                   now);
 
@@ -275,12 +264,15 @@ void Curl_hostcache_prune(struct SessionHandle *data)
     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
 }
 
+/*
+ * Check if the entry should be pruned. Assumes a locked cache.
+ */
 static int
 remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
 {
   struct hostcache_prune_data user;
 
-  if( !dns || (data->set.dns_cache_timeout == -1) || !data->hostcache)
+  if( !dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
     /* cache forever means never prune, and NULL hostcache means
        we can't do it */
     return 0;
@@ -288,22 +280,13 @@ remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
   time(&user.now);
   user.cache_timeout = data->set.dns_cache_timeout;
 
-  if ( !hostcache_timestamp_remove(&user,dns) )
+  if( !hostcache_timestamp_remove(&user,dns) )
     return 0;
 
-  /* ok, we do need to clear the cache. although we need to remove just a
-     single entry we clean the entire hash, as no explicit delete function
-     is provided */
-  if(data->share)
-    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-  Curl_hash_clean_with_criterium(data->hostcache,
+  Curl_hash_clean_with_criterium(data->dns.hostcache,
                                  (void *) &user,
                                  hostcache_timestamp_remove);
 
-  if(data->share)
-    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
   return 1;
 }
 
@@ -328,7 +311,7 @@ sigjmp_buf curl_jmpenv;
 struct Curl_dns_entry *
 Curl_cache_addr(struct SessionHandle *data,
                 Curl_addrinfo *addr,
-                char *hostname,
+                const char *hostname,
                 int port)
 {
   char *entry_id;
@@ -340,13 +323,13 @@ Curl_cache_addr(struct SessionHandle *data,
   /* Create an entry id, based upon the hostname and port */
   entry_id = create_hostcache_id(hostname, port);
   /* If we can't create the entry id, fail */
-  if (!entry_id)
+  if(!entry_id)
     return NULL;
   entry_len = strlen(entry_id);
 
   /* Create a new cache entry */
-  dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry));
-  if (!dns) {
+  dns = calloc(sizeof(struct Curl_dns_entry), 1);
+  if(!dns) {
     free(entry_id);
     return NULL;
   }
@@ -357,7 +340,8 @@ Curl_cache_addr(struct SessionHandle *data,
   /* Store the resolved data in our DNS cache. This function may return a
      pointer to an existing struct already present in the hash, and it may
      return the same argument we pass in. Make no assumptions. */
-  dns2 = Curl_hash_add(data->hostcache, entry_id, entry_len+1, (void *)dns);
+  dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
+                       (void *)dns);
   if(!dns2) {
     /* Major badness, run away. */
     free(dns);
@@ -394,34 +378,35 @@ Curl_cache_addr(struct SessionHandle *data,
  */
 
 int Curl_resolv(struct connectdata *conn,
-                char *hostname,
+                const char *hostname,
                 int port,
                 struct Curl_dns_entry **entry)
 {
   char *entry_id = NULL;
   struct Curl_dns_entry *dns = NULL;
   size_t entry_len;
-  int wait;
   struct SessionHandle *data = conn->data;
   CURLcode result;
-  int rc;
+  int rc = CURLRESOLV_ERROR; /* default to failure */
   *entry = NULL;
 
 #ifdef HAVE_SIGSETJMP
   /* this allows us to time-out from the name resolver, as the timeout
      will generate a signal and we will siglongjmp() from that here */
-  if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) {
-    /* this is coming from a siglongjmp() */
-    failf(data, "name lookup timed out");
-    return CURLRESOLV_ERROR;
+  if(!data->set.no_signal) {
+    if(sigsetjmp(curl_jmpenv, 1)) {
+      /* this is coming from a siglongjmp() */
+      failf(data, "name lookup timed out");
+      return rc;
+    }
   }
 #endif
 
   /* Create an entry id, based upon the hostname and port */
   entry_id = create_hostcache_id(hostname, port);
   /* If we can't create the entry id, fail */
-  if (!entry_id)
-    return CURLRESOLV_ERROR;
+  if(!entry_id)
+    return rc;
 
   entry_len = strlen(entry_id);
 
@@ -429,7 +414,16 @@ int Curl_resolv(struct connectdata *conn,
     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
 
   /* See if its already in our dns cache */
-  dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1);
+  dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
+
+  /* See whether the returned entry is stale. Done before we release lock */
+  if( remove_entry_if_stale(data, dns) )
+    dns = NULL; /* the memory deallocation is being handled by the hash */
+
+  if(dns) {
+    dns->inuse++; /* we use it! */
+    rc = CURLRESOLV_RESOLVED;
+  }
 
   if(data->share)
     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
@@ -437,30 +431,24 @@ int Curl_resolv(struct connectdata *conn,
   /* free the allocated entry_id again */
   free(entry_id);
 
-  /* See whether the returned entry is stale. Deliberately done after the
-     locked block */
-  if ( remove_entry_if_stale(data,dns) )
-    dns = NULL; /* the memory deallocation is being handled by the hash */
-
-  rc = CURLRESOLV_ERROR; /* default to failure */
-
-  if (!dns) {
+  if(!dns) {
     /* The entry was not in the cache. Resolve it to IP address */
 
     Curl_addrinfo *addr;
+    int respwait;
 
     /* Check what IP specifics the app has requested and if we can provide it.
      * If not, bail out. */
     if(!Curl_ipvalid(data))
       return CURLRESOLV_ERROR;
 
-    /* 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 */
-    addr = Curl_getaddrinfo(conn, hostname, port, &wait);
+    /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
+       non-zero value indicating that we need to wait for the response to the
+       resolve call */
+    addr = Curl_getaddrinfo(conn, hostname, port, &respwait);
 
-    if (!addr) {
-      if(wait) {
+    if(!addr) {
+      if(respwait) {
         /* the response to our resolve call will come asynchronously at
            a later time, good or bad */
         /* First, check that we haven't received the info by now */
@@ -490,14 +478,6 @@ int Curl_resolv(struct connectdata *conn,
         rc = CURLRESOLV_RESOLVED;
     }
   }
-  else {
-    if(data->share)
-      Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-    dns->inuse++; /* we use it! */
-    if(data->share)
-      Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-    rc = CURLRESOLV_RESOLVED;
-  }
 
   *entry = dns;
 
@@ -511,7 +491,7 @@ int Curl_resolv(struct connectdata *conn,
  */
 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
 {
-  curlassert(dns && (dns->inuse>0));
+  DEBUGASSERT(dns && (dns->inuse>0));
 
   if(data->share)
     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
@@ -539,7 +519,7 @@ static void freednsentry(void *freethis)
  */
 struct curl_hash *Curl_mk_dnscache(void)
 {
-  return Curl_hash_alloc(7, freednsentry);
+  return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry);
 }
 
 #ifdef CURLRES_ADDRINFO_COPY
@@ -552,10 +532,213 @@ struct curl_hash *Curl_mk_dnscache(void)
  * returns a pointer to the malloc()ed copy. You need to call free() on the
  * returned buffer when you're done with it.
  */
-Curl_addrinfo *Curl_addrinfo_copy(void *org, int port)
+Curl_addrinfo *Curl_addrinfo_copy(const void *org, int port)
 {
-  struct hostent *orig = org;
+  const struct hostent *orig = org;
 
   return Curl_he2ai(orig, port);
 }
 #endif /* CURLRES_ADDRINFO_COPY */
+
+/***********************************************************************
+ * Only for plain-ipv4 and c-ares builds (NOTE: c-ares builds can be IPv6
+ * enabled)
+ **********************************************************************/
+
+#if defined(CURLRES_IPV4) || defined(CURLRES_ARES)
+/*
+ * This is a function for freeing name information in a protocol independent
+ * way.
+ */
+void Curl_freeaddrinfo(Curl_addrinfo *ai)
+{
+  Curl_addrinfo *next;
+
+  /* walk over the list and free all entries */
+  while(ai) {
+    next = ai->ai_next;
+    if(ai->ai_canonname)
+      free(ai->ai_canonname);
+    free(ai);
+    ai = next;
+  }
+}
+
+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 */
+};
+
+/*
+ * Curl_ip2addr() takes a 32bit ipv4 internet address as input parameter
+ * together with a pointer to the string version of the address, and it
+ * returns a Curl_addrinfo chain 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(in_addr_t num, const char *hostname, int port)
+{
+  Curl_addrinfo *ai;
+
+#if defined(VMS) && \
+    defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
+#pragma pointer_size save
+#pragma pointer_size short
+#pragma message disable PTRMISMATCH
+#endif
+
+  struct hostent *h;
+  struct in_addr *addrentry;
+  struct namebuf buffer;
+  struct namebuf *buf = &buffer;
+
+  h = &buf->hostentry;
+  h->h_addr_list = &buf->h_addr_list[0];
+  addrentry = &buf->addrentry;
+#ifdef _CRAYC
+  /* On UNICOS, s_addr is a bit field and for some reason assigning to it
+   * doesn't work.  There must be a better fix than this ugly hack.
+   */
+  memcpy(addrentry, &num, SIZEOF_in_addr);
+#else
+  addrentry->s_addr = num;
+#endif
+  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);
+
+#if defined(VMS) && \
+    defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
+#pragma pointer_size restore
+#pragma message enable PTRMISMATCH
+#endif
+
+  ai = Curl_he2ai(h, port);
+
+  return ai;
+}
+
+/*
+ * Curl_he2ai() translates from a hostent struct to a Curl_addrinfo struct.
+ * The Curl_addrinfo is meant to work like the addrinfo struct does for IPv6
+ * stacks, but for all hosts and environments.
+ *
+ *   Curl_addrinfo defined in "lib/hostip.h"
+ *
+ *     struct Curl_addrinfo {
+ *       int                   ai_flags;
+ *       int                   ai_family;
+ *       int                   ai_socktype;
+ *       int                   ai_protocol;
+ *       socklen_t             ai_addrlen;   * Follow rfc3493 struct addrinfo *
+ *       char                 *ai_canonname;
+ *       struct sockaddr      *ai_addr;
+ *       struct Curl_addrinfo *ai_next;
+ *     };
+ *
+ *   hostent defined in <netdb.h>
+ *
+ *     struct hostent {
+ *       char    *h_name;
+ *       char    **h_aliases;
+ *       int     h_addrtype;
+ *       int     h_length;
+ *       char    **h_addr_list;
+ *     };
+ *
+ *   for backward compatibility:
+ *
+ *     #define h_addr  h_addr_list[0]
+ */
+
+Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port)
+{
+  Curl_addrinfo *ai;
+  Curl_addrinfo *prevai = NULL;
+  Curl_addrinfo *firstai = NULL;
+  struct sockaddr_in *addr;
+#ifdef CURLRES_IPV6
+  struct sockaddr_in6 *addr6;
+#endif /* CURLRES_IPV6 */
+  int i;
+  struct in_addr *curr;
+
+  if(!he)
+    /* no input == no output! */
+    return NULL;
+
+  for(i=0; (curr = (struct in_addr *)he->h_addr_list[i]) != NULL; i++) {
+
+    int ss_size;
+#ifdef CURLRES_IPV6
+    if (he->h_addrtype == AF_INET6)
+      ss_size = sizeof (struct sockaddr_in6);
+    else
+#endif /* CURLRES_IPV6 */
+      ss_size = sizeof (struct sockaddr_in);
+
+    ai = calloc(1, sizeof(Curl_addrinfo) + ss_size);
+
+    if(!ai)
+      break;
+
+    if(!firstai)
+      /* store the pointer we want to return from this function */
+      firstai = ai;
+
+    if(prevai)
+      /* make the previous entry point to this */
+      prevai->ai_next = ai;
+
+    ai->ai_family = he->h_addrtype;
+
+    /* we return all names as STREAM, so when using this address for TFTP
+       the type must be ignored and conn->socktype be used instead! */
+    ai->ai_socktype = SOCK_STREAM;
+
+    ai->ai_addrlen = ss_size;
+    /* make the ai_addr point to the address immediately following this struct
+       and use that area to store the address */
+    ai->ai_addr = (struct sockaddr *) ((char*)ai + sizeof(Curl_addrinfo));
+
+    /* need to free this eventually */
+    ai->ai_canonname = strdup(he->h_name);
+
+    /* leave the rest of the struct filled with zero */
+
+    switch (ai->ai_family) {
+    case AF_INET:
+      addr = (struct sockaddr_in *)ai->ai_addr; /* storage area for this info */
+
+      memcpy((char *)&(addr->sin_addr), curr, sizeof(struct in_addr));
+      addr->sin_family = (unsigned short)(he->h_addrtype);
+      addr->sin_port = htons((unsigned short)port);
+      break;
+
+#ifdef CURLRES_IPV6
+    case AF_INET6:
+      addr6 = (struct sockaddr_in6 *)ai->ai_addr; /* storage area for this info */
+
+      memcpy((char *)&(addr6->sin6_addr), curr, sizeof(struct in6_addr));
+      addr6->sin6_family = (unsigned short)(he->h_addrtype);
+      addr6->sin6_port = htons((unsigned short)port);
+      break;
+#endif /* CURLRES_IPV6 */
+    }
+
+    prevai = ai;
+  }
+  return firstai;
+}
+
+#endif /* CURLRES_IPV4 || CURLRES_ARES */