Skip to content
Snippets Groups Projects
hostip.c 42.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • /***************************************************************************
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *                                  _   _ ____  _     
     *  Project                     ___| | | |  _ \| |    
     *                             / __| | | | |_) | |    
     *                            | (__| |_| |  _ <| |___ 
     *                             \___|\___/|_| \_\_____|
     *
    
     * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
     * 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.
     * 
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * 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.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     * KIND, either express or implied.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * $Id$
    
     ***************************************************************************/
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <string.h>
    
    #include <errno.h>
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    #define _REENTRANT
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
    
    #include <malloc.h>
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #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
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <netinet/in.h>
    
    #endif
    #ifdef HAVE_NETDB_H
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <netdb.h>
    
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #ifdef HAVE_ARPA_INET_H
    #include <arpa/inet.h>
    #endif
    
    #ifdef HAVE_STDLIB_H
    #include <stdlib.h>	/* required for free() prototypes */
    #endif
    
    #ifdef	VMS
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <in.h>
    
    #include <inet.h>
    #include <stdlib.h>
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #endif
    
    
    #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
    #undef in_addr_t
    #define in_addr_t unsigned long
    #endif
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include "urldata.h"
    #include "sendf.h"
    
    #include "hostip.h"
    
    #include "share.h"
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    #define _MPRINTF_REPLACE /* use our functions only */
    #include <curl/mprintf.h>
    
    
    #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include "inet_ntoa_r.h"
    #endif
    
    
    /* The last #include file should be: */
    
    #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 */
    
    
    /* 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)
    
    static struct hostent* pack_hostent(char** buf, struct hostent* orig);
    #endif
    
    #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, ...);
    
    static struct hostent* pack_hostent (char** buf, struct hostent* orig);
    static bool init_gethostbyname_thread (struct connectdata *conn,
                                           const char *hostname, int port);
    struct thread_data {
      HANDLE thread_hnd;
      DWORD  thread_id;
      DWORD  thread_status;
    };
    #endif
    
    
    /*
     * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
     * Global DNS cache is general badness. Do not use. This will be removed in
     * a future version. Use the share interface instead!
     */
    
    void Curl_global_host_cache_init(void)
    
      if (!host_cache_initialized) {
    
        Curl_hash_init(&hostname_cache, 7, freednsentry);
    
    /*
     * Return a pointer to the global cache
     */
    
    curl_hash *Curl_global_host_cache_get(void)
    
    /*
     * Destroy and cleanup the global DNS cache
     */
    
    void Curl_global_host_cache_dtor(void)
    {
      if (host_cache_initialized) {
    
        host_cache_initialized = 0;
    
    /*
     * Minor utility-function:
     * Count the number of characters that an integer takes up.
     */
    
    static int _num_chars(int i)
    {
      int chars = 0;
    
    
      /* While the number divided by 10 is greater than one, 
       * re-divide the number by 10, and increment the number of 
       * characters by 1.
       *
       * this relies on the fact that for every multiple of 10, 
       * a new digit is added onto every number
       */
    
    /*
     * Minor utility-function:
     * Create a hostcache id string for the DNS caching.
     */
    
    create_hostcache_id(char *server, int port, size_t *entry_len)
    
      /* Get the length of the new entry id */
    
      *entry_len = strlen(server) + /* Hostname length */
        1 +                         /* ':' seperator */
        _num_chars(port);     /* number of characters the port will take up */
    
      id = malloc(*entry_len + 1); /* 1 extra for the zero terminator */
    
      return id; /* return pointer to the string */
    
      int cache_timeout;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      time_t now;
    
    /*
     * This function is set as a callback to be called for every entry in the DNS
     * cache when we want to prune old unused entries.
     *
     * Returning non-zero means remove the entry, return 0 to keep it in the
     * cache.
     */
    
    hostcache_timestamp_remove(void *datap, void *hc)
    
      struct hostcache_prune_data *data = 
        (struct hostcache_prune_data *) datap;
    
      struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
    
      if ((data->now - c->timestamp < data->cache_timeout) ||
          c->inuse) {
        /* please don't remove */
    
    /*
     * Prune the DNS cache. This assumes that a lock has already been taken.
     */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    hostcache_prune(curl_hash *hostcache, int cache_timeout, time_t now)
    
    
      user.cache_timeout = cache_timeout;
      user.now = now;
    
      Curl_hash_clean_with_criterium(hostcache, 
    
    /*
     * Library-wide function for pruning the DNS cache. This function takes and
     * returns the appropriate locks.
     */
    
    void Curl_hostcache_prune(struct SessionHandle *data)
    {
      time_t now;
    
    
      if(data->set.dns_cache_timeout == -1)
        /* cache forever means never prune! */
        return;
    
    
      if(data->share)
        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    
      time(&now);
    
      /* Remove outdated and unused entries from the hostcache */
      hostcache_prune(data->hostcache,
                      data->set.dns_cache_timeout,
                      now);
    
      if(data->share)
        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    }
    
    
    /* Beware this is a global and unique instance. This is used to store the
       return address that we can jump back to from inside a signal handler. This
       is not thread-safe stuff. */
    
    /*
     * cache_resolv_response() 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
     * cache etc
     *
     * 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 *dns;
      time_t now;
    
      /* Create an entry id, based upon the hostname and port */
      entry_id = create_hostcache_id(hostname, port, &entry_len);
      /* If we can't create the entry id, fail */
      if (!entry_id)
        return NULL;
    
      /* Create a new cache entry */
      dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry));
      if (!dns) {
        Curl_freeaddrinfo(addr);
        free(entry_id);
        return NULL;
    
      dns->inuse = 0;   /* init to not used */
      dns->addr = addr; /* this is the address(es) */
    
      /* 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. */
    
      dns = Curl_hash_add(data->hostcache, entry_id, entry_len+1, (void *)dns);
    
        /* Major badness, run away. When this happens, the 'dns' data has
           already been cleared up by Curl_hash_add(). */
    
      dns->inuse++;         /* mark entry as in-use */
    
      /* free the allocated entry_id again */
      free(entry_id);
    
      return dns;
    
    /*
     * Curl_resolv() is the main name resolve function within libcurl. It resolves
     * a name and returns a pointer to the entry in the 'entry' argument (if one
     * is provided). This function might return immediately if we're using asynch
     * resolves. See the return codes.
     *
     * The cache entry we return will get its 'inuse' counter increased when this
     * function is used. You MUST call Curl_resolv_unlock() later (when you're
     * done using this struct) to decrease the counter again.
     *
     * Return codes:
     *
     * -1 = error, no pointer
     * 0 = OK, pointer provided
     * 1 = waiting for response, no pointer
     */
    
    int Curl_resolv(struct connectdata *conn,
                    char *hostname,
                    int port,
                    struct Curl_dns_entry **entry)
    
      int wait;
      struct SessionHandle *data = conn->data;
    
    
      /* default to failure */
      int rc = -1;
      *entry = NULL;
    
      /* 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() */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        failf(data, "name lookup timed out");
    
      /* Create an entry id, based upon the hostname and port */
    
      entry_id = create_hostcache_id(hostname, port, &entry_len);
      /* If we can't create the entry id, fail */
      if (!entry_id)
    
    
      if(data->share)
        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);
    
      
      if(data->share)
        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    
      /* free the allocated entry_id again */
      free(entry_id);
    
    
        /* The entry was not in the cache. Resolve it to IP address */
          
        /* If my_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
           value indicating that we need to wait for the response to the resolve
           call */
        Curl_addrinfo *addr = my_getaddrinfo(conn, hostname, port, &wait);
    
            /* 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 */
    
            result = Curl_is_resolved(conn, &dns);
            if(result) /* error detected */
              return -1;
    
            if(dns)
              rc = 0; /* pointer provided */
            else
              rc = 1; /* no info yet */
          }
    
          if(data->share)
            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);
    
          
          if(data->share)
            Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    
            /* returned failure, bail out nicely */
            Curl_freeaddrinfo(addr);
    
    /*
     * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
     * made, the struct may be destroyed due to pruning. It is important that only
     * one unlock is made for each Curl_resolv() call.
     */
    
    void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
    {
      if(data->share)
        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    
    #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
    
    void Curl_freeaddrinfo(Curl_addrinfo *p)
    
      free(p); /* works fine for the ARES case too */
    
     * File-internal: free a cache dns entry.
    
    static void freednsentry(void *freethis)
    
    {
      struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
    
      Curl_freeaddrinfo(p->addr);
    
    /*
     * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
     */
    curl_hash *Curl_mk_dnscache(void)
    {
      return Curl_hash_alloc(7, freednsentry);
    }
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    /* --- 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)
    
      int count;
      struct SessionHandle *data = conn->data;
    
      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);
    
        /* we're done, kill the ares handle */
    
        if(!conn->async.dns)
          return CURLE_COULDNT_RESOLVE_HOST;
    
    /*
     * 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;
    
    
      /* Wait for the name resolve query to complete. */
    
        struct timeval now = Curl_tvnow();
    
        
        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 */
    
        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);
    
        timeout -= Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
    
        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->name);
          rc = CURLE_OPERATION_TIMEDOUT;
        }
        else if(conn->async.done) {
    
          failf(data, "Could not resolve host: %s (%s)", conn->name,
    
                ares_strerror(conn->async.status));
    
        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);
    
    
          if(data->share)
            Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    
        }
      }
    
      conn->async.dns = dns;
    
      /* The input hostent struct will be freed by ares when we return from this
         function */
    }
    
     * 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(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 */
    
    #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.
     *
     * We provide this function only to allow multi.c to remain unaware if we are
     * doing asynch resolves or not.
     */
    
    CURLcode Curl_multi_ares_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
    
    #if defined(ENABLE_IPV6) && !defined(USE_ARES)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    /* 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);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        /* 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);
      }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      return res;
    }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    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);
    
     * my_getaddrinfo() when built ipv6-enabled.
     *
     * Returns name information about the given hostname and port number. If
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * 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
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * Curl_freeaddrinfo(), nothing else.
     */
    
    static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
    
    {
      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. */
        pf = PF_INET;
    
        /* 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);    
    
    #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);
    
     * 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!
    
    static struct hostent* pack_hostent(char** buf, struct hostent* orig)
    
      size_t len;
    
      copy = (struct hostent*)bufptr;
    
      bufptr += sizeof(struct hostent);
      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);
      
      copy->h_aliases = (char**)bufptr;
    
      for (i = 0; orig->h_aliases && orig->h_aliases[i]; ++i);
    
    
      /* Reserve room for the array */
      bufptr += (i + 1) * sizeof(char*);
    
    
      if(orig->h_aliases) {
        for(i = 0; (str = orig->h_aliases[i]); i++) {
          len = strlen(str) + 1;
          strncpy(bufptr, str, len);
          copy->h_aliases[i] = bufptr;
          bufptr += len;
        }
    
      /* if(!orig->h_aliases) i was already set to 0 */
    
    
      /* Terminate the alias list with a NULL */
    
      copy->h_aliases[i] = NULL;
    
      copy->h_addrtype = orig->h_addrtype;
      copy->h_length = orig->h_length;
        
    
      /* align it for (at least) 32bit accesses */
    
      bufptr = MEMALIGN(bufptr);
    
      copy->h_addr_list = (char**)bufptr;
    
      /* Figure out how many addresses there are */
      for (i = 0; orig->h_addr_list[i] != NULL; ++i);
    
      /* Reserve room for the array */
      bufptr += (i + 1) * sizeof(char*);
    
      i = 0;
      len = orig->h_length;
      str = orig->h_addr_list[i];
      while (str != NULL) {