Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel (at) haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at http://curl.haxx.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  ***************************************************************************/
     22 
     23 #include "curl_setup.h"
     24 
     25 #ifdef HAVE_NETINET_IN_H
     26 #include <netinet/in.h>
     27 #endif
     28 #ifdef HAVE_NETDB_H
     29 #include <netdb.h>
     30 #endif
     31 #ifdef HAVE_ARPA_INET_H
     32 #include <arpa/inet.h>
     33 #endif
     34 #ifdef __VMS
     35 #include <in.h>
     36 #include <inet.h>
     37 #endif
     38 
     39 #ifdef HAVE_SETJMP_H
     40 #include <setjmp.h>
     41 #endif
     42 #ifdef HAVE_SIGNAL_H
     43 #include <signal.h>
     44 #endif
     45 
     46 #ifdef HAVE_PROCESS_H
     47 #include <process.h>
     48 #endif
     49 
     50 #include "urldata.h"
     51 #include "sendf.h"
     52 #include "hostip.h"
     53 #include "hash.h"
     54 #include "share.h"
     55 #include "strerror.h"
     56 #include "url.h"
     57 #include "inet_ntop.h"
     58 #include "warnless.h"
     59 #include "curl_printf.h"
     60 #include "curl_memory.h"
     61 /* The last #include file should be: */
     62 #include "memdebug.h"
     63 
     64 #if defined(CURLRES_SYNCH) && \
     65     defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
     66 /* alarm-based timeouts can only be used with all the dependencies satisfied */
     67 #define USE_ALARM_TIMEOUT
     68 #endif
     69 
     70 /*
     71  * hostip.c explained
     72  * ==================
     73  *
     74  * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
     75  * source file are these:
     76  *
     77  * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
     78  * that. The host may not be able to resolve IPv6, but we don't really have to
     79  * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
     80  * defined.
     81  *
     82  * CURLRES_ARES - is defined if libcurl is built to use c-ares for
     83  * asynchronous name resolves. This can be Windows or *nix.
     84  *
     85  * CURLRES_THREADED - is defined if libcurl is built to run under (native)
     86  * Windows, and then the name resolve will be done in a new thread, and the
     87  * supported API will be the same as for ares-builds.
     88  *
     89  * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
     90  * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
     91  * defined.
     92  *
     93  * The host*.c sources files are split up like this:
     94  *
     95  * hostip.c   - method-independent resolver functions and utility functions
     96  * hostasyn.c - functions for asynchronous name resolves
     97  * hostsyn.c  - functions for synchronous name resolves
     98  * hostip4.c  - IPv4 specific functions
     99  * hostip6.c  - IPv6 specific functions
    100  *
    101  * The two asynchronous name resolver backends are implemented in:
    102  * asyn-ares.c   - functions for ares-using name resolves
    103  * asyn-thread.c - functions for threaded name resolves
    104 
    105  * The hostip.h is the united header file for all this. It defines the
    106  * CURLRES_* defines based on the config*.h and curl_setup.h defines.
    107  */
    108 
    109 /* These two symbols are for the global DNS cache */
    110 static struct curl_hash hostname_cache;
    111 static int host_cache_initialized;
    112 
    113 static void freednsentry(void *freethis);
    114 
    115 /*
    116  * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
    117  * Global DNS cache is general badness. Do not use. This will be removed in
    118  * a future version. Use the share interface instead!
    119  *
    120  * Returns a struct curl_hash pointer on success, NULL on failure.
    121  */
    122 struct curl_hash *Curl_global_host_cache_init(void)
    123 {
    124   int rc = 0;
    125   if(!host_cache_initialized) {
    126     rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
    127                         Curl_str_key_compare, freednsentry);
    128     if(!rc)
    129       host_cache_initialized = 1;
    130   }
    131   return rc?NULL:&hostname_cache;
    132 }
    133 
    134 /*
    135  * Destroy and cleanup the global DNS cache
    136  */
    137 void Curl_global_host_cache_dtor(void)
    138 {
    139   if(host_cache_initialized) {
    140     Curl_hash_destroy(&hostname_cache);
    141     host_cache_initialized = 0;
    142   }
    143 }
    144 
    145 /*
    146  * Return # of adresses in a Curl_addrinfo struct
    147  */
    148 int Curl_num_addresses(const Curl_addrinfo *addr)
    149 {
    150   int i = 0;
    151   while(addr) {
    152     addr = addr->ai_next;
    153     i++;
    154   }
    155   return i;
    156 }
    157 
    158 /*
    159  * Curl_printable_address() returns a printable version of the 1st address
    160  * given in the 'ai' argument. The result will be stored in the buf that is
    161  * bufsize bytes big.
    162  *
    163  * If the conversion fails, it returns NULL.
    164  */
    165 const char *
    166 Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
    167 {
    168   const struct sockaddr_in *sa4;
    169   const struct in_addr *ipaddr4;
    170 #ifdef ENABLE_IPV6
    171   const struct sockaddr_in6 *sa6;
    172   const struct in6_addr *ipaddr6;
    173 #endif
    174 
    175   switch (ai->ai_family) {
    176     case AF_INET:
    177       sa4 = (const void *)ai->ai_addr;
    178       ipaddr4 = &sa4->sin_addr;
    179       return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
    180                             bufsize);
    181 #ifdef ENABLE_IPV6
    182     case AF_INET6:
    183       sa6 = (const void *)ai->ai_addr;
    184       ipaddr6 = &sa6->sin6_addr;
    185       return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
    186                             bufsize);
    187 #endif
    188     default:
    189       break;
    190   }
    191   return NULL;
    192 }
    193 
    194 /*
    195  * Return a hostcache id string for the provided host + port, to be used by
    196  * the DNS caching.
    197  */
    198 static char *
    199 create_hostcache_id(const char *name, int port)
    200 {
    201   /* create and return the new allocated entry */
    202   char *id = aprintf("%s:%d", name, port);
    203   char *ptr = id;
    204   if(ptr) {
    205     /* lower case the name part */
    206     while(*ptr && (*ptr != ':')) {
    207       *ptr = (char)TOLOWER(*ptr);
    208       ptr++;
    209     }
    210   }
    211   return id;
    212 }
    213 
    214 struct hostcache_prune_data {
    215   long cache_timeout;
    216   time_t now;
    217 };
    218 
    219 /*
    220  * This function is set as a callback to be called for every entry in the DNS
    221  * cache when we want to prune old unused entries.
    222  *
    223  * Returning non-zero means remove the entry, return 0 to keep it in the
    224  * cache.
    225  */
    226 static int
    227 hostcache_timestamp_remove(void *datap, void *hc)
    228 {
    229   struct hostcache_prune_data *data =
    230     (struct hostcache_prune_data *) datap;
    231   struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
    232 
    233   return (0 != c->timestamp)
    234     && (data->now - c->timestamp >= data->cache_timeout);
    235 }
    236 
    237 /*
    238  * Prune the DNS cache. This assumes that a lock has already been taken.
    239  */
    240 static void
    241 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
    242 {
    243   struct hostcache_prune_data user;
    244 
    245   user.cache_timeout = cache_timeout;
    246   user.now = now;
    247 
    248   Curl_hash_clean_with_criterium(hostcache,
    249                                  (void *) &user,
    250                                  hostcache_timestamp_remove);
    251 }
    252 
    253 /*
    254  * Library-wide function for pruning the DNS cache. This function takes and
    255  * returns the appropriate locks.
    256  */
    257 void Curl_hostcache_prune(struct SessionHandle *data)
    258 {
    259   time_t now;
    260 
    261   if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
    262     /* cache forever means never prune, and NULL hostcache means
    263        we can't do it */
    264     return;
    265 
    266   if(data->share)
    267     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    268 
    269   time(&now);
    270 
    271   /* Remove outdated and unused entries from the hostcache */
    272   hostcache_prune(data->dns.hostcache,
    273                   data->set.dns_cache_timeout,
    274                   now);
    275 
    276   if(data->share)
    277     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    278 }
    279 
    280 #ifdef HAVE_SIGSETJMP
    281 /* Beware this is a global and unique instance. This is used to store the
    282    return address that we can jump back to from inside a signal handler. This
    283    is not thread-safe stuff. */
    284 sigjmp_buf curl_jmpenv;
    285 #endif
    286 
    287 /* lookup address, returns entry if found and not stale */
    288 static struct Curl_dns_entry *
    289 fetch_addr(struct connectdata *conn,
    290                 const char *hostname,
    291                 int port)
    292 {
    293   char *entry_id = NULL;
    294   struct Curl_dns_entry *dns = NULL;
    295   size_t entry_len;
    296   struct SessionHandle *data = conn->data;
    297 
    298   /* Create an entry id, based upon the hostname and port */
    299   entry_id = create_hostcache_id(hostname, port);
    300   /* If we can't create the entry id, fail */
    301   if(!entry_id)
    302     return dns;
    303 
    304   entry_len = strlen(entry_id);
    305 
    306   /* See if its already in our dns cache */
    307   dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
    308 
    309   if(dns && (data->set.dns_cache_timeout != -1))  {
    310     /* See whether the returned entry is stale. Done before we release lock */
    311     struct hostcache_prune_data user;
    312 
    313     time(&user.now);
    314     user.cache_timeout = data->set.dns_cache_timeout;
    315 
    316     if(hostcache_timestamp_remove(&user, dns)) {
    317       infof(data, "Hostname in DNS cache was stale, zapped\n");
    318       dns = NULL; /* the memory deallocation is being handled by the hash */
    319       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1);
    320     }
    321   }
    322 
    323   /* free the allocated entry_id again */
    324   free(entry_id);
    325 
    326   return dns;
    327 }
    328 
    329 /*
    330  * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
    331  *
    332  * Curl_resolv() checks initially and multi_runsingle() checks each time
    333  * it discovers the handle in the state WAITRESOLVE whether the hostname
    334  * has already been resolved and the address has already been stored in
    335  * the DNS cache. This short circuits waiting for a lot of pending
    336  * lookups for the same hostname requested by different handles.
    337  *
    338  * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
    339  *
    340  * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
    341  * use, or we'll leak memory!
    342  */
    343 struct Curl_dns_entry *
    344 Curl_fetch_addr(struct connectdata *conn,
    345                 const char *hostname,
    346                 int port)
    347 {
    348   struct SessionHandle *data = conn->data;
    349   struct Curl_dns_entry *dns = NULL;
    350 
    351   if(data->share)
    352     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    353 
    354   dns = fetch_addr(conn, hostname, port);
    355 
    356   if(dns) dns->inuse++; /* we use it! */
    357 
    358   if(data->share)
    359     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    360 
    361   return dns;
    362 }
    363 
    364 /*
    365  * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
    366  *
    367  * When calling Curl_resolv() has resulted in a response with a returned
    368  * address, we call this function to store the information in the dns
    369  * cache etc
    370  *
    371  * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
    372  */
    373 struct Curl_dns_entry *
    374 Curl_cache_addr(struct SessionHandle *data,
    375                 Curl_addrinfo *addr,
    376                 const char *hostname,
    377                 int port)
    378 {
    379   char *entry_id;
    380   size_t entry_len;
    381   struct Curl_dns_entry *dns;
    382   struct Curl_dns_entry *dns2;
    383 
    384   /* Create an entry id, based upon the hostname and port */
    385   entry_id = create_hostcache_id(hostname, port);
    386   /* If we can't create the entry id, fail */
    387   if(!entry_id)
    388     return NULL;
    389   entry_len = strlen(entry_id);
    390 
    391   /* Create a new cache entry */
    392   dns = calloc(1, sizeof(struct Curl_dns_entry));
    393   if(!dns) {
    394     free(entry_id);
    395     return NULL;
    396   }
    397 
    398   dns->inuse = 1;   /* the cache has the first reference */
    399   dns->addr = addr; /* this is the address(es) */
    400   time(&dns->timestamp);
    401   if(dns->timestamp == 0)
    402     dns->timestamp = 1;   /* zero indicates CURLOPT_RESOLVE entry */
    403 
    404   /* Store the resolved data in our DNS cache. */
    405   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
    406                        (void *)dns);
    407   if(!dns2) {
    408     free(dns);
    409     free(entry_id);
    410     return NULL;
    411   }
    412 
    413   dns = dns2;
    414   dns->inuse++;         /* mark entry as in-use */
    415 
    416   /* free the allocated entry_id */
    417   free(entry_id);
    418 
    419   return dns;
    420 }
    421 
    422 /*
    423  * Curl_resolv() is the main name resolve function within libcurl. It resolves
    424  * a name and returns a pointer to the entry in the 'entry' argument (if one
    425  * is provided). This function might return immediately if we're using asynch
    426  * resolves. See the return codes.
    427  *
    428  * The cache entry we return will get its 'inuse' counter increased when this
    429  * function is used. You MUST call Curl_resolv_unlock() later (when you're
    430  * done using this struct) to decrease the counter again.
    431  *
    432  * In debug mode, we specifically test for an interface name "LocalHost"
    433  * and resolve "localhost" instead as a means to permit test cases
    434  * to connect to a local test server with any host name.
    435  *
    436  * Return codes:
    437  *
    438  * CURLRESOLV_ERROR   (-1) = error, no pointer
    439  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
    440  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
    441  */
    442 
    443 int Curl_resolv(struct connectdata *conn,
    444                 const char *hostname,
    445                 int port,
    446                 struct Curl_dns_entry **entry)
    447 {
    448   struct Curl_dns_entry *dns = NULL;
    449   struct SessionHandle *data = conn->data;
    450   CURLcode result;
    451   int rc = CURLRESOLV_ERROR; /* default to failure */
    452 
    453   *entry = NULL;
    454 
    455   if(data->share)
    456     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    457 
    458   dns = fetch_addr(conn, hostname, port);
    459 
    460   if(dns) {
    461     infof(data, "Hostname %s was found in DNS cache\n", hostname);
    462     dns->inuse++; /* we use it! */
    463     rc = CURLRESOLV_RESOLVED;
    464   }
    465 
    466   if(data->share)
    467     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    468 
    469   if(!dns) {
    470     /* The entry was not in the cache. Resolve it to IP address */
    471 
    472     Curl_addrinfo *addr;
    473     int respwait;
    474 
    475     /* Check what IP specifics the app has requested and if we can provide it.
    476      * If not, bail out. */
    477     if(!Curl_ipvalid(conn))
    478       return CURLRESOLV_ERROR;
    479 
    480     /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
    481        non-zero value indicating that we need to wait for the response to the
    482        resolve call */
    483     addr = Curl_getaddrinfo(conn,
    484 #ifdef DEBUGBUILD
    485                             (data->set.str[STRING_DEVICE]
    486                              && !strcmp(data->set.str[STRING_DEVICE],
    487                                         "LocalHost"))?"localhost":
    488 #endif
    489                             hostname, port, &respwait);
    490 
    491     if(!addr) {
    492       if(respwait) {
    493         /* the response to our resolve call will come asynchronously at
    494            a later time, good or bad */
    495         /* First, check that we haven't received the info by now */
    496         result = Curl_resolver_is_resolved(conn, &dns);
    497         if(result) /* error detected */
    498           return CURLRESOLV_ERROR;
    499         if(dns)
    500           rc = CURLRESOLV_RESOLVED; /* pointer provided */
    501         else
    502           rc = CURLRESOLV_PENDING; /* no info yet */
    503       }
    504     }
    505     else {
    506       if(data->share)
    507         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    508 
    509       /* we got a response, store it in the cache */
    510       dns = Curl_cache_addr(data, addr, hostname, port);
    511 
    512       if(data->share)
    513         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    514 
    515       if(!dns)
    516         /* returned failure, bail out nicely */
    517         Curl_freeaddrinfo(addr);
    518       else
    519         rc = CURLRESOLV_RESOLVED;
    520     }
    521   }
    522 
    523   *entry = dns;
    524 
    525   return rc;
    526 }
    527 
    528 #ifdef USE_ALARM_TIMEOUT
    529 /*
    530  * This signal handler jumps back into the main libcurl code and continues
    531  * execution.  This effectively causes the remainder of the application to run
    532  * within a signal handler which is nonportable and could lead to problems.
    533  */
    534 static
    535 RETSIGTYPE alarmfunc(int sig)
    536 {
    537   /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
    538   (void)sig;
    539   siglongjmp(curl_jmpenv, 1);
    540   return;
    541 }
    542 #endif /* USE_ALARM_TIMEOUT */
    543 
    544 /*
    545  * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
    546  * timeout.  This function might return immediately if we're using asynch
    547  * resolves. See the return codes.
    548  *
    549  * The cache entry we return will get its 'inuse' counter increased when this
    550  * function is used. You MUST call Curl_resolv_unlock() later (when you're
    551  * done using this struct) to decrease the counter again.
    552  *
    553  * If built with a synchronous resolver and use of signals is not
    554  * disabled by the application, then a nonzero timeout will cause a
    555  * timeout after the specified number of milliseconds. Otherwise, timeout
    556  * is ignored.
    557  *
    558  * Return codes:
    559  *
    560  * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
    561  * CURLRESOLV_ERROR   (-1) = error, no pointer
    562  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
    563  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
    564  */
    565 
    566 int Curl_resolv_timeout(struct connectdata *conn,
    567                         const char *hostname,
    568                         int port,
    569                         struct Curl_dns_entry **entry,
    570                         long timeoutms)
    571 {
    572 #ifdef USE_ALARM_TIMEOUT
    573 #ifdef HAVE_SIGACTION
    574   struct sigaction keep_sigact;   /* store the old struct here */
    575   volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */
    576   struct sigaction sigact;
    577 #else
    578 #ifdef HAVE_SIGNAL
    579   void (*keep_sigact)(int);       /* store the old handler here */
    580 #endif /* HAVE_SIGNAL */
    581 #endif /* HAVE_SIGACTION */
    582   volatile long timeout;
    583   volatile unsigned int prev_alarm = 0;
    584   struct SessionHandle *data = conn->data;
    585 #endif /* USE_ALARM_TIMEOUT */
    586   int rc;
    587 
    588   *entry = NULL;
    589 
    590   if(timeoutms < 0)
    591     /* got an already expired timeout */
    592     return CURLRESOLV_TIMEDOUT;
    593 
    594 #ifdef USE_ALARM_TIMEOUT
    595   if(data->set.no_signal)
    596     /* Ignore the timeout when signals are disabled */
    597     timeout = 0;
    598   else
    599     timeout = timeoutms;
    600 
    601   if(!timeout)
    602     /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
    603     return Curl_resolv(conn, hostname, port, entry);
    604 
    605   if(timeout < 1000)
    606     /* The alarm() function only provides integer second resolution, so if
    607        we want to wait less than one second we must bail out already now. */
    608     return CURLRESOLV_TIMEDOUT;
    609 
    610   /* This allows us to time-out from the name resolver, as the timeout
    611      will generate a signal and we will siglongjmp() from that here.
    612      This technique has problems (see alarmfunc).
    613      This should be the last thing we do before calling Curl_resolv(),
    614      as otherwise we'd have to worry about variables that get modified
    615      before we invoke Curl_resolv() (and thus use "volatile"). */
    616   if(sigsetjmp(curl_jmpenv, 1)) {
    617     /* this is coming from a siglongjmp() after an alarm signal */
    618     failf(data, "name lookup timed out");
    619     rc = CURLRESOLV_ERROR;
    620     goto clean_up;
    621   }
    622   else {
    623     /*************************************************************
    624      * Set signal handler to catch SIGALRM
    625      * Store the old value to be able to set it back later!
    626      *************************************************************/
    627 #ifdef HAVE_SIGACTION
    628     sigaction(SIGALRM, NULL, &sigact);
    629     keep_sigact = sigact;
    630     keep_copysig = TRUE; /* yes, we have a copy */
    631     sigact.sa_handler = alarmfunc;
    632 #ifdef SA_RESTART
    633     /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
    634     sigact.sa_flags &= ~SA_RESTART;
    635 #endif
    636     /* now set the new struct */
    637     sigaction(SIGALRM, &sigact, NULL);
    638 #else /* HAVE_SIGACTION */
    639     /* no sigaction(), revert to the much lamer signal() */
    640 #ifdef HAVE_SIGNAL
    641     keep_sigact = signal(SIGALRM, alarmfunc);
    642 #endif
    643 #endif /* HAVE_SIGACTION */
    644 
    645     /* alarm() makes a signal get sent when the timeout fires off, and that
    646        will abort system calls */
    647     prev_alarm = alarm(curlx_sltoui(timeout/1000L));
    648   }
    649 
    650 #else
    651 #ifndef CURLRES_ASYNCH
    652   if(timeoutms)
    653     infof(conn->data, "timeout on name lookup is not supported\n");
    654 #else
    655   (void)timeoutms; /* timeoutms not used with an async resolver */
    656 #endif
    657 #endif /* USE_ALARM_TIMEOUT */
    658 
    659   /* Perform the actual name resolution. This might be interrupted by an
    660    * alarm if it takes too long.
    661    */
    662   rc = Curl_resolv(conn, hostname, port, entry);
    663 
    664 #ifdef USE_ALARM_TIMEOUT
    665 clean_up:
    666 
    667   if(!prev_alarm)
    668     /* deactivate a possibly active alarm before uninstalling the handler */
    669     alarm(0);
    670 
    671 #ifdef HAVE_SIGACTION
    672   if(keep_copysig) {
    673     /* we got a struct as it looked before, now put that one back nice
    674        and clean */
    675     sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
    676   }
    677 #else
    678 #ifdef HAVE_SIGNAL
    679   /* restore the previous SIGALRM handler */
    680   signal(SIGALRM, keep_sigact);
    681 #endif
    682 #endif /* HAVE_SIGACTION */
    683 
    684   /* switch back the alarm() to either zero or to what it was before minus
    685      the time we spent until now! */
    686   if(prev_alarm) {
    687     /* there was an alarm() set before us, now put it back */
    688     unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
    689 
    690     /* the alarm period is counted in even number of seconds */
    691     unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
    692 
    693     if(!alarm_set ||
    694        ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
    695       /* if the alarm time-left reached zero or turned "negative" (counted
    696          with unsigned values), we should fire off a SIGALRM here, but we
    697          won't, and zero would be to switch it off so we never set it to
    698          less than 1! */
    699       alarm(1);
    700       rc = CURLRESOLV_TIMEDOUT;
    701       failf(data, "Previous alarm fired off!");
    702     }
    703     else
    704       alarm((unsigned int)alarm_set);
    705   }
    706 #endif /* USE_ALARM_TIMEOUT */
    707 
    708   return rc;
    709 }
    710 
    711 /*
    712  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
    713  * made, the struct may be destroyed due to pruning. It is important that only
    714  * one unlock is made for each Curl_resolv() call.
    715  *
    716  * May be called with 'data' == NULL for global cache.
    717  */
    718 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
    719 {
    720   if(data && data->share)
    721     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    722 
    723   freednsentry(dns);
    724 
    725   if(data && data->share)
    726     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    727 }
    728 
    729 /*
    730  * File-internal: release cache dns entry reference, free if inuse drops to 0
    731  */
    732 static void freednsentry(void *freethis)
    733 {
    734   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
    735   DEBUGASSERT(dns && (dns->inuse>0));
    736 
    737   dns->inuse--;
    738   if(dns->inuse == 0) {
    739     Curl_freeaddrinfo(dns->addr);
    740     free(dns);
    741   }
    742 }
    743 
    744 /*
    745  * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
    746  */
    747 int Curl_mk_dnscache(struct curl_hash *hash)
    748 {
    749   return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
    750                         freednsentry);
    751 }
    752 
    753 /*
    754  * Curl_hostcache_clean()
    755  *
    756  * This _can_ be called with 'data' == NULL but then of course no locking
    757  * can be done!
    758  */
    759 
    760 void Curl_hostcache_clean(struct SessionHandle *data,
    761                           struct curl_hash *hash)
    762 {
    763   if(data && data->share)
    764     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    765 
    766   Curl_hash_clean(hash);
    767 
    768   if(data && data->share)
    769     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    770 }
    771 
    772 
    773 CURLcode Curl_loadhostpairs(struct SessionHandle *data)
    774 {
    775   struct curl_slist *hostp;
    776   char hostname[256];
    777   char address[256];
    778   int port;
    779 
    780   for(hostp = data->change.resolve; hostp; hostp = hostp->next ) {
    781     if(!hostp->data)
    782       continue;
    783     if(hostp->data[0] == '-') {
    784       char *entry_id;
    785       size_t entry_len;
    786 
    787       if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
    788         infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
    789               hostp->data);
    790         continue;
    791       }
    792 
    793       /* Create an entry id, based upon the hostname and port */
    794       entry_id = create_hostcache_id(hostname, port);
    795       /* If we can't create the entry id, fail */
    796       if(!entry_id) {
    797         return CURLE_OUT_OF_MEMORY;
    798       }
    799 
    800       entry_len = strlen(entry_id);
    801 
    802       if(data->share)
    803         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    804 
    805       /* delete entry, ignore if it didn't exist */
    806       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1);
    807 
    808       if(data->share)
    809         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    810 
    811       /* free the allocated entry_id again */
    812       free(entry_id);
    813     }
    814     else {
    815       struct Curl_dns_entry *dns;
    816       Curl_addrinfo *addr;
    817       char *entry_id;
    818       size_t entry_len;
    819 
    820       if(3 != sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
    821                      address)) {
    822         infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
    823               hostp->data);
    824         continue;
    825       }
    826 
    827       addr = Curl_str2addr(address, port);
    828       if(!addr) {
    829         infof(data, "Address in '%s' found illegal!\n", hostp->data);
    830         continue;
    831       }
    832 
    833       /* Create an entry id, based upon the hostname and port */
    834       entry_id = create_hostcache_id(hostname, port);
    835       /* If we can't create the entry id, fail */
    836       if(!entry_id) {
    837         Curl_freeaddrinfo(addr);
    838         return CURLE_OUT_OF_MEMORY;
    839       }
    840 
    841       entry_len = strlen(entry_id);
    842 
    843       if(data->share)
    844         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    845 
    846       /* See if its already in our dns cache */
    847       dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
    848 
    849       /* free the allocated entry_id again */
    850       free(entry_id);
    851 
    852       if(!dns) {
    853         /* if not in the cache already, put this host in the cache */
    854         dns = Curl_cache_addr(data, addr, hostname, port);
    855         if(dns) {
    856           dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
    857           /* release the returned reference; the cache itself will keep the
    858            * entry alive: */
    859           dns->inuse--;
    860         }
    861       }
    862       else
    863         /* this is a duplicate, free it again */
    864         Curl_freeaddrinfo(addr);
    865 
    866       if(data->share)
    867         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    868 
    869       if(!dns) {
    870         Curl_freeaddrinfo(addr);
    871         return CURLE_OUT_OF_MEMORY;
    872       }
    873       infof(data, "Added %s:%d:%s to DNS cache\n",
    874             hostname, port, address);
    875     }
    876   }
    877   data->change.resolve = NULL; /* dealt with now */
    878 
    879   return CURLE_OK;
    880 }
    881