Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2016, 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 https://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 /* The last 3 #include files should be in this order */
     60 #include "curl_printf.h"
     61 #include "curl_memory.h"
     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 Curl_easy *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 Curl_easy *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 Curl_easy *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)
    357     dns->inuse++; /* we use it! */
    358 
    359   if(data->share)
    360     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    361 
    362   return dns;
    363 }
    364 
    365 /*
    366  * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
    367  *
    368  * When calling Curl_resolv() has resulted in a response with a returned
    369  * address, we call this function to store the information in the dns
    370  * cache etc
    371  *
    372  * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
    373  */
    374 struct Curl_dns_entry *
    375 Curl_cache_addr(struct Curl_easy *data,
    376                 Curl_addrinfo *addr,
    377                 const char *hostname,
    378                 int port)
    379 {
    380   char *entry_id;
    381   size_t entry_len;
    382   struct Curl_dns_entry *dns;
    383   struct Curl_dns_entry *dns2;
    384 
    385   /* Create an entry id, based upon the hostname and port */
    386   entry_id = create_hostcache_id(hostname, port);
    387   /* If we can't create the entry id, fail */
    388   if(!entry_id)
    389     return NULL;
    390   entry_len = strlen(entry_id);
    391 
    392   /* Create a new cache entry */
    393   dns = calloc(1, sizeof(struct Curl_dns_entry));
    394   if(!dns) {
    395     free(entry_id);
    396     return NULL;
    397   }
    398 
    399   dns->inuse = 1;   /* the cache has the first reference */
    400   dns->addr = addr; /* this is the address(es) */
    401   time(&dns->timestamp);
    402   if(dns->timestamp == 0)
    403     dns->timestamp = 1;   /* zero indicates CURLOPT_RESOLVE entry */
    404 
    405   /* Store the resolved data in our DNS cache. */
    406   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
    407                        (void *)dns);
    408   if(!dns2) {
    409     free(dns);
    410     free(entry_id);
    411     return NULL;
    412   }
    413 
    414   dns = dns2;
    415   dns->inuse++;         /* mark entry as in-use */
    416 
    417   /* free the allocated entry_id */
    418   free(entry_id);
    419 
    420   return dns;
    421 }
    422 
    423 /*
    424  * Curl_resolv() is the main name resolve function within libcurl. It resolves
    425  * a name and returns a pointer to the entry in the 'entry' argument (if one
    426  * is provided). This function might return immediately if we're using asynch
    427  * resolves. See the return codes.
    428  *
    429  * The cache entry we return will get its 'inuse' counter increased when this
    430  * function is used. You MUST call Curl_resolv_unlock() later (when you're
    431  * done using this struct) to decrease the counter again.
    432  *
    433  * In debug mode, we specifically test for an interface name "LocalHost"
    434  * and resolve "localhost" instead as a means to permit test cases
    435  * to connect to a local test server with any host name.
    436  *
    437  * Return codes:
    438  *
    439  * CURLRESOLV_ERROR   (-1) = error, no pointer
    440  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
    441  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
    442  */
    443 
    444 int Curl_resolv(struct connectdata *conn,
    445                 const char *hostname,
    446                 int port,
    447                 struct Curl_dns_entry **entry)
    448 {
    449   struct Curl_dns_entry *dns = NULL;
    450   struct Curl_easy *data = conn->data;
    451   CURLcode result;
    452   int rc = CURLRESOLV_ERROR; /* default to failure */
    453 
    454   *entry = NULL;
    455 
    456   if(data->share)
    457     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    458 
    459   dns = fetch_addr(conn, hostname, port);
    460 
    461   if(dns) {
    462     infof(data, "Hostname %s was found in DNS cache\n", hostname);
    463     dns->inuse++; /* we use it! */
    464     rc = CURLRESOLV_RESOLVED;
    465   }
    466 
    467   if(data->share)
    468     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    469 
    470   if(!dns) {
    471     /* The entry was not in the cache. Resolve it to IP address */
    472 
    473     Curl_addrinfo *addr;
    474     int respwait;
    475 
    476     /* Check what IP specifics the app has requested and if we can provide it.
    477      * If not, bail out. */
    478     if(!Curl_ipvalid(conn))
    479       return CURLRESOLV_ERROR;
    480 
    481     /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
    482        non-zero value indicating that we need to wait for the response to the
    483        resolve call */
    484     addr = Curl_getaddrinfo(conn,
    485 #ifdef DEBUGBUILD
    486                             (data->set.str[STRING_DEVICE]
    487                              && !strcmp(data->set.str[STRING_DEVICE],
    488                                         "LocalHost"))?"localhost":
    489 #endif
    490                             hostname, port, &respwait);
    491 
    492     if(!addr) {
    493       if(respwait) {
    494         /* the response to our resolve call will come asynchronously at
    495            a later time, good or bad */
    496         /* First, check that we haven't received the info by now */
    497         result = Curl_resolver_is_resolved(conn, &dns);
    498         if(result) /* error detected */
    499           return CURLRESOLV_ERROR;
    500         if(dns)
    501           rc = CURLRESOLV_RESOLVED; /* pointer provided */
    502         else
    503           rc = CURLRESOLV_PENDING; /* no info yet */
    504       }
    505     }
    506     else {
    507       if(data->share)
    508         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    509 
    510       /* we got a response, store it in the cache */
    511       dns = Curl_cache_addr(data, addr, hostname, port);
    512 
    513       if(data->share)
    514         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    515 
    516       if(!dns)
    517         /* returned failure, bail out nicely */
    518         Curl_freeaddrinfo(addr);
    519       else
    520         rc = CURLRESOLV_RESOLVED;
    521     }
    522   }
    523 
    524   *entry = dns;
    525 
    526   return rc;
    527 }
    528 
    529 #ifdef USE_ALARM_TIMEOUT
    530 /*
    531  * This signal handler jumps back into the main libcurl code and continues
    532  * execution.  This effectively causes the remainder of the application to run
    533  * within a signal handler which is nonportable and could lead to problems.
    534  */
    535 static
    536 RETSIGTYPE alarmfunc(int sig)
    537 {
    538   /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
    539   (void)sig;
    540   siglongjmp(curl_jmpenv, 1);
    541   return;
    542 }
    543 #endif /* USE_ALARM_TIMEOUT */
    544 
    545 /*
    546  * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
    547  * timeout.  This function might return immediately if we're using asynch
    548  * resolves. See the return codes.
    549  *
    550  * The cache entry we return will get its 'inuse' counter increased when this
    551  * function is used. You MUST call Curl_resolv_unlock() later (when you're
    552  * done using this struct) to decrease the counter again.
    553  *
    554  * If built with a synchronous resolver and use of signals is not
    555  * disabled by the application, then a nonzero timeout will cause a
    556  * timeout after the specified number of milliseconds. Otherwise, timeout
    557  * is ignored.
    558  *
    559  * Return codes:
    560  *
    561  * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
    562  * CURLRESOLV_ERROR   (-1) = error, no pointer
    563  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
    564  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
    565  */
    566 
    567 int Curl_resolv_timeout(struct connectdata *conn,
    568                         const char *hostname,
    569                         int port,
    570                         struct Curl_dns_entry **entry,
    571                         time_t timeoutms)
    572 {
    573 #ifdef USE_ALARM_TIMEOUT
    574 #ifdef HAVE_SIGACTION
    575   struct sigaction keep_sigact;   /* store the old struct here */
    576   volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */
    577   struct sigaction sigact;
    578 #else
    579 #ifdef HAVE_SIGNAL
    580   void (*keep_sigact)(int);       /* store the old handler here */
    581 #endif /* HAVE_SIGNAL */
    582 #endif /* HAVE_SIGACTION */
    583   volatile long timeout;
    584   volatile unsigned int prev_alarm = 0;
    585   struct Curl_easy *data = conn->data;
    586 #endif /* USE_ALARM_TIMEOUT */
    587   int rc;
    588 
    589   *entry = NULL;
    590 
    591   if(timeoutms < 0)
    592     /* got an already expired timeout */
    593     return CURLRESOLV_TIMEDOUT;
    594 
    595 #ifdef USE_ALARM_TIMEOUT
    596   if(data->set.no_signal)
    597     /* Ignore the timeout when signals are disabled */
    598     timeout = 0;
    599   else
    600     timeout = timeoutms;
    601 
    602   if(!timeout)
    603     /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
    604     return Curl_resolv(conn, hostname, port, entry);
    605 
    606   if(timeout < 1000) {
    607     /* The alarm() function only provides integer second resolution, so if
    608        we want to wait less than one second we must bail out already now. */
    609     failf(data,
    610         "remaining timeout of %ld too small to resolve via SIGALRM method",
    611         timeout);
    612     return CURLRESOLV_TIMEDOUT;
    613   }
    614   /* This allows us to time-out from the name resolver, as the timeout
    615      will generate a signal and we will siglongjmp() from that here.
    616      This technique has problems (see alarmfunc).
    617      This should be the last thing we do before calling Curl_resolv(),
    618      as otherwise we'd have to worry about variables that get modified
    619      before we invoke Curl_resolv() (and thus use "volatile"). */
    620   if(sigsetjmp(curl_jmpenv, 1)) {
    621     /* this is coming from a siglongjmp() after an alarm signal */
    622     failf(data, "name lookup timed out");
    623     rc = CURLRESOLV_ERROR;
    624     goto clean_up;
    625   }
    626   else {
    627     /*************************************************************
    628      * Set signal handler to catch SIGALRM
    629      * Store the old value to be able to set it back later!
    630      *************************************************************/
    631 #ifdef HAVE_SIGACTION
    632     sigaction(SIGALRM, NULL, &sigact);
    633     keep_sigact = sigact;
    634     keep_copysig = TRUE; /* yes, we have a copy */
    635     sigact.sa_handler = alarmfunc;
    636 #ifdef SA_RESTART
    637     /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
    638     sigact.sa_flags &= ~SA_RESTART;
    639 #endif
    640     /* now set the new struct */
    641     sigaction(SIGALRM, &sigact, NULL);
    642 #else /* HAVE_SIGACTION */
    643     /* no sigaction(), revert to the much lamer signal() */
    644 #ifdef HAVE_SIGNAL
    645     keep_sigact = signal(SIGALRM, alarmfunc);
    646 #endif
    647 #endif /* HAVE_SIGACTION */
    648 
    649     /* alarm() makes a signal get sent when the timeout fires off, and that
    650        will abort system calls */
    651     prev_alarm = alarm(curlx_sltoui(timeout/1000L));
    652   }
    653 
    654 #else
    655 #ifndef CURLRES_ASYNCH
    656   if(timeoutms)
    657     infof(conn->data, "timeout on name lookup is not supported\n");
    658 #else
    659   (void)timeoutms; /* timeoutms not used with an async resolver */
    660 #endif
    661 #endif /* USE_ALARM_TIMEOUT */
    662 
    663   /* Perform the actual name resolution. This might be interrupted by an
    664    * alarm if it takes too long.
    665    */
    666   rc = Curl_resolv(conn, hostname, port, entry);
    667 
    668 #ifdef USE_ALARM_TIMEOUT
    669 clean_up:
    670 
    671   if(!prev_alarm)
    672     /* deactivate a possibly active alarm before uninstalling the handler */
    673     alarm(0);
    674 
    675 #ifdef HAVE_SIGACTION
    676   if(keep_copysig) {
    677     /* we got a struct as it looked before, now put that one back nice
    678        and clean */
    679     sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
    680   }
    681 #else
    682 #ifdef HAVE_SIGNAL
    683   /* restore the previous SIGALRM handler */
    684   signal(SIGALRM, keep_sigact);
    685 #endif
    686 #endif /* HAVE_SIGACTION */
    687 
    688   /* switch back the alarm() to either zero or to what it was before minus
    689      the time we spent until now! */
    690   if(prev_alarm) {
    691     /* there was an alarm() set before us, now put it back */
    692     unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
    693 
    694     /* the alarm period is counted in even number of seconds */
    695     unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
    696 
    697     if(!alarm_set ||
    698        ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
    699       /* if the alarm time-left reached zero or turned "negative" (counted
    700          with unsigned values), we should fire off a SIGALRM here, but we
    701          won't, and zero would be to switch it off so we never set it to
    702          less than 1! */
    703       alarm(1);
    704       rc = CURLRESOLV_TIMEDOUT;
    705       failf(data, "Previous alarm fired off!");
    706     }
    707     else
    708       alarm((unsigned int)alarm_set);
    709   }
    710 #endif /* USE_ALARM_TIMEOUT */
    711 
    712   return rc;
    713 }
    714 
    715 /*
    716  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
    717  * made, the struct may be destroyed due to pruning. It is important that only
    718  * one unlock is made for each Curl_resolv() call.
    719  *
    720  * May be called with 'data' == NULL for global cache.
    721  */
    722 void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
    723 {
    724   if(data && data->share)
    725     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    726 
    727   freednsentry(dns);
    728 
    729   if(data && data->share)
    730     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    731 }
    732 
    733 /*
    734  * File-internal: release cache dns entry reference, free if inuse drops to 0
    735  */
    736 static void freednsentry(void *freethis)
    737 {
    738   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
    739   DEBUGASSERT(dns && (dns->inuse>0));
    740 
    741   dns->inuse--;
    742   if(dns->inuse == 0) {
    743     Curl_freeaddrinfo(dns->addr);
    744     free(dns);
    745   }
    746 }
    747 
    748 /*
    749  * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
    750  */
    751 int Curl_mk_dnscache(struct curl_hash *hash)
    752 {
    753   return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
    754                         freednsentry);
    755 }
    756 
    757 /*
    758  * Curl_hostcache_clean()
    759  *
    760  * This _can_ be called with 'data' == NULL but then of course no locking
    761  * can be done!
    762  */
    763 
    764 void Curl_hostcache_clean(struct Curl_easy *data,
    765                           struct curl_hash *hash)
    766 {
    767   if(data && data->share)
    768     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    769 
    770   Curl_hash_clean(hash);
    771 
    772   if(data && data->share)
    773     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    774 }
    775 
    776 
    777 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
    778 {
    779   struct curl_slist *hostp;
    780   char hostname[256];
    781   char address[256];
    782   int port;
    783 
    784   for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
    785     if(!hostp->data)
    786       continue;
    787     if(hostp->data[0] == '-') {
    788       char *entry_id;
    789       size_t entry_len;
    790 
    791       if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
    792         infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
    793               hostp->data);
    794         continue;
    795       }
    796 
    797       /* Create an entry id, based upon the hostname and port */
    798       entry_id = create_hostcache_id(hostname, port);
    799       /* If we can't create the entry id, fail */
    800       if(!entry_id) {
    801         return CURLE_OUT_OF_MEMORY;
    802       }
    803 
    804       entry_len = strlen(entry_id);
    805 
    806       if(data->share)
    807         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    808 
    809       /* delete entry, ignore if it didn't exist */
    810       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1);
    811 
    812       if(data->share)
    813         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    814 
    815       /* free the allocated entry_id again */
    816       free(entry_id);
    817     }
    818     else {
    819       struct Curl_dns_entry *dns;
    820       Curl_addrinfo *addr;
    821       char *entry_id;
    822       size_t entry_len;
    823 
    824       if(3 != sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
    825                      address)) {
    826         infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
    827               hostp->data);
    828         continue;
    829       }
    830 
    831       addr = Curl_str2addr(address, port);
    832       if(!addr) {
    833         infof(data, "Address in '%s' found illegal!\n", hostp->data);
    834         continue;
    835       }
    836 
    837       /* Create an entry id, based upon the hostname and port */
    838       entry_id = create_hostcache_id(hostname, port);
    839       /* If we can't create the entry id, fail */
    840       if(!entry_id) {
    841         Curl_freeaddrinfo(addr);
    842         return CURLE_OUT_OF_MEMORY;
    843       }
    844 
    845       entry_len = strlen(entry_id);
    846 
    847       if(data->share)
    848         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    849 
    850       /* See if its already in our dns cache */
    851       dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
    852 
    853       /* free the allocated entry_id again */
    854       free(entry_id);
    855 
    856       if(!dns) {
    857         /* if not in the cache already, put this host in the cache */
    858         dns = Curl_cache_addr(data, addr, hostname, port);
    859         if(dns) {
    860           dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
    861           /* release the returned reference; the cache itself will keep the
    862            * entry alive: */
    863           dns->inuse--;
    864         }
    865       }
    866       else
    867         /* this is a duplicate, free it again */
    868         Curl_freeaddrinfo(addr);
    869 
    870       if(data->share)
    871         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    872 
    873       if(!dns) {
    874         Curl_freeaddrinfo(addr);
    875         return CURLE_OUT_OF_MEMORY;
    876       }
    877       infof(data, "Added %s:%d:%s to DNS cache\n",
    878             hostname, port, address);
    879     }
    880   }
    881   data->change.resolve = NULL; /* dealt with now */
    882 
    883   return CURLE_OK;
    884 }
    885