Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2019, 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_NETINET_IN6_H
     29 #include <netinet/in6.h>
     30 #endif
     31 #ifdef HAVE_NETDB_H
     32 #include <netdb.h>
     33 #endif
     34 #ifdef HAVE_ARPA_INET_H
     35 #include <arpa/inet.h>
     36 #endif
     37 #ifdef __VMS
     38 #include <in.h>
     39 #include <inet.h>
     40 #endif
     41 
     42 #ifdef HAVE_SETJMP_H
     43 #include <setjmp.h>
     44 #endif
     45 #ifdef HAVE_SIGNAL_H
     46 #include <signal.h>
     47 #endif
     48 
     49 #ifdef HAVE_PROCESS_H
     50 #include <process.h>
     51 #endif
     52 
     53 #include "urldata.h"
     54 #include "sendf.h"
     55 #include "hostip.h"
     56 #include "hash.h"
     57 #include "rand.h"
     58 #include "share.h"
     59 #include "strerror.h"
     60 #include "url.h"
     61 #include "inet_ntop.h"
     62 #include "multiif.h"
     63 #include "doh.h"
     64 #include "warnless.h"
     65 /* The last 3 #include files should be in this order */
     66 #include "curl_printf.h"
     67 #include "curl_memory.h"
     68 #include "memdebug.h"
     69 
     70 #if defined(CURLRES_SYNCH) && \
     71     defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
     72 /* alarm-based timeouts can only be used with all the dependencies satisfied */
     73 #define USE_ALARM_TIMEOUT
     74 #endif
     75 
     76 #define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
     77 
     78 /*
     79  * hostip.c explained
     80  * ==================
     81  *
     82  * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
     83  * source file are these:
     84  *
     85  * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
     86  * that. The host may not be able to resolve IPv6, but we don't really have to
     87  * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
     88  * defined.
     89  *
     90  * CURLRES_ARES - is defined if libcurl is built to use c-ares for
     91  * asynchronous name resolves. This can be Windows or *nix.
     92  *
     93  * CURLRES_THREADED - is defined if libcurl is built to run under (native)
     94  * Windows, and then the name resolve will be done in a new thread, and the
     95  * supported API will be the same as for ares-builds.
     96  *
     97  * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
     98  * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
     99  * defined.
    100  *
    101  * The host*.c sources files are split up like this:
    102  *
    103  * hostip.c   - method-independent resolver functions and utility functions
    104  * hostasyn.c - functions for asynchronous name resolves
    105  * hostsyn.c  - functions for synchronous name resolves
    106  * hostip4.c  - IPv4 specific functions
    107  * hostip6.c  - IPv6 specific functions
    108  *
    109  * The two asynchronous name resolver backends are implemented in:
    110  * asyn-ares.c   - functions for ares-using name resolves
    111  * asyn-thread.c - functions for threaded name resolves
    112 
    113  * The hostip.h is the united header file for all this. It defines the
    114  * CURLRES_* defines based on the config*.h and curl_setup.h defines.
    115  */
    116 
    117 /* These two symbols are for the global DNS cache */
    118 static struct curl_hash hostname_cache;
    119 static int host_cache_initialized;
    120 
    121 static void freednsentry(void *freethis);
    122 
    123 /*
    124  * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
    125  * Global DNS cache is general badness. Do not use. This will be removed in
    126  * a future version. Use the share interface instead!
    127  *
    128  * Returns a struct curl_hash pointer on success, NULL on failure.
    129  */
    130 struct curl_hash *Curl_global_host_cache_init(void)
    131 {
    132   int rc = 0;
    133   if(!host_cache_initialized) {
    134     rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
    135                         Curl_str_key_compare, freednsentry);
    136     if(!rc)
    137       host_cache_initialized = 1;
    138   }
    139   return rc?NULL:&hostname_cache;
    140 }
    141 
    142 /*
    143  * Destroy and cleanup the global DNS cache
    144  */
    145 void Curl_global_host_cache_dtor(void)
    146 {
    147   if(host_cache_initialized) {
    148     Curl_hash_destroy(&hostname_cache);
    149     host_cache_initialized = 0;
    150   }
    151 }
    152 
    153 /*
    154  * Return # of addresses in a Curl_addrinfo struct
    155  */
    156 int Curl_num_addresses(const Curl_addrinfo *addr)
    157 {
    158   int i = 0;
    159   while(addr) {
    160     addr = addr->ai_next;
    161     i++;
    162   }
    163   return i;
    164 }
    165 
    166 /*
    167  * Curl_printable_address() returns a printable version of the 1st address
    168  * given in the 'ai' argument. The result will be stored in the buf that is
    169  * bufsize bytes big.
    170  *
    171  * If the conversion fails, it returns NULL.
    172  */
    173 const char *
    174 Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
    175 {
    176   const struct sockaddr_in *sa4;
    177   const struct in_addr *ipaddr4;
    178 #ifdef ENABLE_IPV6
    179   const struct sockaddr_in6 *sa6;
    180   const struct in6_addr *ipaddr6;
    181 #endif
    182 
    183   switch(ai->ai_family) {
    184     case AF_INET:
    185       sa4 = (const void *)ai->ai_addr;
    186       ipaddr4 = &sa4->sin_addr;
    187       return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
    188                             bufsize);
    189 #ifdef ENABLE_IPV6
    190     case AF_INET6:
    191       sa6 = (const void *)ai->ai_addr;
    192       ipaddr6 = &sa6->sin6_addr;
    193       return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
    194                             bufsize);
    195 #endif
    196     default:
    197       break;
    198   }
    199   return NULL;
    200 }
    201 
    202 /*
    203  * Create a hostcache id string for the provided host + port, to be used by
    204  * the DNS caching. Without alloc.
    205  */
    206 static void
    207 create_hostcache_id(const char *name, int port, char *ptr, size_t buflen)
    208 {
    209   size_t len = strlen(name);
    210   if(len > (buflen - 7))
    211     len = buflen - 7;
    212   /* store and lower case the name */
    213   while(len--)
    214     *ptr++ = (char)TOLOWER(*name++);
    215   msnprintf(ptr, 7, ":%u", port);
    216 }
    217 
    218 struct hostcache_prune_data {
    219   long cache_timeout;
    220   time_t now;
    221 };
    222 
    223 /*
    224  * This function is set as a callback to be called for every entry in the DNS
    225  * cache when we want to prune old unused entries.
    226  *
    227  * Returning non-zero means remove the entry, return 0 to keep it in the
    228  * cache.
    229  */
    230 static int
    231 hostcache_timestamp_remove(void *datap, void *hc)
    232 {
    233   struct hostcache_prune_data *data =
    234     (struct hostcache_prune_data *) datap;
    235   struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
    236 
    237   return (0 != c->timestamp)
    238     && (data->now - c->timestamp >= data->cache_timeout);
    239 }
    240 
    241 /*
    242  * Prune the DNS cache. This assumes that a lock has already been taken.
    243  */
    244 static void
    245 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
    246 {
    247   struct hostcache_prune_data user;
    248 
    249   user.cache_timeout = cache_timeout;
    250   user.now = now;
    251 
    252   Curl_hash_clean_with_criterium(hostcache,
    253                                  (void *) &user,
    254                                  hostcache_timestamp_remove);
    255 }
    256 
    257 /*
    258  * Library-wide function for pruning the DNS cache. This function takes and
    259  * returns the appropriate locks.
    260  */
    261 void Curl_hostcache_prune(struct Curl_easy *data)
    262 {
    263   time_t now;
    264 
    265   if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
    266     /* cache forever means never prune, and NULL hostcache means
    267        we can't do it */
    268     return;
    269 
    270   if(data->share)
    271     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    272 
    273   time(&now);
    274 
    275   /* Remove outdated and unused entries from the hostcache */
    276   hostcache_prune(data->dns.hostcache,
    277                   data->set.dns_cache_timeout,
    278                   now);
    279 
    280   if(data->share)
    281     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    282 }
    283 
    284 #ifdef HAVE_SIGSETJMP
    285 /* Beware this is a global and unique instance. This is used to store the
    286    return address that we can jump back to from inside a signal handler. This
    287    is not thread-safe stuff. */
    288 sigjmp_buf curl_jmpenv;
    289 #endif
    290 
    291 /* lookup address, returns entry if found and not stale */
    292 static struct Curl_dns_entry *
    293 fetch_addr(struct connectdata *conn,
    294                 const char *hostname,
    295                 int port)
    296 {
    297   struct Curl_dns_entry *dns = NULL;
    298   size_t entry_len;
    299   struct Curl_easy *data = conn->data;
    300   char entry_id[MAX_HOSTCACHE_LEN];
    301 
    302   /* Create an entry id, based upon the hostname and port */
    303   create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
    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   /* No entry found in cache, check if we might have a wildcard entry */
    310   if(!dns && data->change.wildcard_resolve) {
    311     create_hostcache_id("*", port, entry_id, sizeof(entry_id));
    312     entry_len = strlen(entry_id);
    313 
    314     /* See if it's already in our dns cache */
    315     dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
    316   }
    317 
    318   if(dns && (data->set.dns_cache_timeout != -1)) {
    319     /* See whether the returned entry is stale. Done before we release lock */
    320     struct hostcache_prune_data user;
    321 
    322     time(&user.now);
    323     user.cache_timeout = data->set.dns_cache_timeout;
    324 
    325     if(hostcache_timestamp_remove(&user, dns)) {
    326       infof(data, "Hostname in DNS cache was stale, zapped\n");
    327       dns = NULL; /* the memory deallocation is being handled by the hash */
    328       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
    329     }
    330   }
    331 
    332   return dns;
    333 }
    334 
    335 /*
    336  * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
    337  *
    338  * Curl_resolv() checks initially and multi_runsingle() checks each time
    339  * it discovers the handle in the state WAITRESOLVE whether the hostname
    340  * has already been resolved and the address has already been stored in
    341  * the DNS cache. This short circuits waiting for a lot of pending
    342  * lookups for the same hostname requested by different handles.
    343  *
    344  * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
    345  *
    346  * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
    347  * use, or we'll leak memory!
    348  */
    349 struct Curl_dns_entry *
    350 Curl_fetch_addr(struct connectdata *conn,
    351                 const char *hostname,
    352                 int port)
    353 {
    354   struct Curl_easy *data = conn->data;
    355   struct Curl_dns_entry *dns = NULL;
    356 
    357   if(data->share)
    358     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    359 
    360   dns = fetch_addr(conn, hostname, port);
    361 
    362   if(dns)
    363     dns->inuse++; /* we use it! */
    364 
    365   if(data->share)
    366     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    367 
    368   return dns;
    369 }
    370 
    371 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
    372                                     Curl_addrinfo **addr);
    373 
    374 /*
    375  * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
    376  * struct by re-linking its linked list.
    377  *
    378  * The addr argument should be the address of a pointer to the head node of a
    379  * `Curl_addrinfo` list and it will be modified to point to the new head after
    380  * shuffling.
    381  *
    382  * Not declared static only to make it easy to use in a unit test!
    383  *
    384  * @unittest: 1608
    385  */
    386 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
    387                                     Curl_addrinfo **addr)
    388 {
    389   CURLcode result = CURLE_OK;
    390   const int num_addrs = Curl_num_addresses(*addr);
    391 
    392   if(num_addrs > 1) {
    393     Curl_addrinfo **nodes;
    394     infof(data, "Shuffling %i addresses", num_addrs);
    395 
    396     nodes = malloc(num_addrs*sizeof(*nodes));
    397     if(nodes) {
    398       int i;
    399       unsigned int *rnd;
    400       const size_t rnd_size = num_addrs * sizeof(*rnd);
    401 
    402       /* build a plain array of Curl_addrinfo pointers */
    403       nodes[0] = *addr;
    404       for(i = 1; i < num_addrs; i++) {
    405         nodes[i] = nodes[i-1]->ai_next;
    406       }
    407 
    408       rnd = malloc(rnd_size);
    409       if(rnd) {
    410         /* Fisher-Yates shuffle */
    411         if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
    412           Curl_addrinfo *swap_tmp;
    413           for(i = num_addrs - 1; i > 0; i--) {
    414             swap_tmp = nodes[rnd[i] % (i + 1)];
    415             nodes[rnd[i] % (i + 1)] = nodes[i];
    416             nodes[i] = swap_tmp;
    417           }
    418 
    419           /* relink list in the new order */
    420           for(i = 1; i < num_addrs; i++) {
    421             nodes[i-1]->ai_next = nodes[i];
    422           }
    423 
    424           nodes[num_addrs-1]->ai_next = NULL;
    425           *addr = nodes[0];
    426         }
    427         free(rnd);
    428       }
    429       else
    430         result = CURLE_OUT_OF_MEMORY;
    431       free(nodes);
    432     }
    433     else
    434       result = CURLE_OUT_OF_MEMORY;
    435   }
    436   return result;
    437 }
    438 
    439 /*
    440  * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
    441  *
    442  * When calling Curl_resolv() has resulted in a response with a returned
    443  * address, we call this function to store the information in the dns
    444  * cache etc
    445  *
    446  * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
    447  */
    448 struct Curl_dns_entry *
    449 Curl_cache_addr(struct Curl_easy *data,
    450                 Curl_addrinfo *addr,
    451                 const char *hostname,
    452                 int port)
    453 {
    454   char entry_id[MAX_HOSTCACHE_LEN];
    455   size_t entry_len;
    456   struct Curl_dns_entry *dns;
    457   struct Curl_dns_entry *dns2;
    458 
    459   /* shuffle addresses if requested */
    460   if(data->set.dns_shuffle_addresses) {
    461     CURLcode result = Curl_shuffle_addr(data, &addr);
    462     if(result)
    463       return NULL;
    464   }
    465 
    466   /* Create a new cache entry */
    467   dns = calloc(1, sizeof(struct Curl_dns_entry));
    468   if(!dns) {
    469     return NULL;
    470   }
    471 
    472   /* Create an entry id, based upon the hostname and port */
    473   create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
    474   entry_len = strlen(entry_id);
    475 
    476   dns->inuse = 1;   /* the cache has the first reference */
    477   dns->addr = addr; /* this is the address(es) */
    478   time(&dns->timestamp);
    479   if(dns->timestamp == 0)
    480     dns->timestamp = 1;   /* zero indicates CURLOPT_RESOLVE entry */
    481 
    482   /* Store the resolved data in our DNS cache. */
    483   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
    484                        (void *)dns);
    485   if(!dns2) {
    486     free(dns);
    487     return NULL;
    488   }
    489 
    490   dns = dns2;
    491   dns->inuse++;         /* mark entry as in-use */
    492   return dns;
    493 }
    494 
    495 /*
    496  * Curl_resolv() is the main name resolve function within libcurl. It resolves
    497  * a name and returns a pointer to the entry in the 'entry' argument (if one
    498  * is provided). This function might return immediately if we're using asynch
    499  * resolves. See the return codes.
    500  *
    501  * The cache entry we return will get its 'inuse' counter increased when this
    502  * function is used. You MUST call Curl_resolv_unlock() later (when you're
    503  * done using this struct) to decrease the counter again.
    504  *
    505  * In debug mode, we specifically test for an interface name "LocalHost"
    506  * and resolve "localhost" instead as a means to permit test cases
    507  * to connect to a local test server with any host name.
    508  *
    509  * Return codes:
    510  *
    511  * CURLRESOLV_ERROR   (-1) = error, no pointer
    512  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
    513  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
    514  */
    515 
    516 int Curl_resolv(struct connectdata *conn,
    517                 const char *hostname,
    518                 int port,
    519                 struct Curl_dns_entry **entry)
    520 {
    521   struct Curl_dns_entry *dns = NULL;
    522   struct Curl_easy *data = conn->data;
    523   CURLcode result;
    524   int rc = CURLRESOLV_ERROR; /* default to failure */
    525 
    526   *entry = NULL;
    527 
    528   if(data->share)
    529     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    530 
    531   dns = fetch_addr(conn, hostname, port);
    532 
    533   if(dns) {
    534     infof(data, "Hostname %s was found in DNS cache\n", hostname);
    535     dns->inuse++; /* we use it! */
    536     rc = CURLRESOLV_RESOLVED;
    537   }
    538 
    539   if(data->share)
    540     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    541 
    542   if(!dns) {
    543     /* The entry was not in the cache. Resolve it to IP address */
    544 
    545     Curl_addrinfo *addr;
    546     int respwait = 0;
    547 
    548     /* Check what IP specifics the app has requested and if we can provide it.
    549      * If not, bail out. */
    550     if(!Curl_ipvalid(conn))
    551       return CURLRESOLV_ERROR;
    552 
    553     /* notify the resolver start callback */
    554     if(data->set.resolver_start) {
    555       int st;
    556       Curl_set_in_callback(data, true);
    557       st = data->set.resolver_start(data->state.resolver, NULL,
    558                                     data->set.resolver_start_client);
    559       Curl_set_in_callback(data, false);
    560       if(st)
    561         return CURLRESOLV_ERROR;
    562     }
    563 
    564     if(data->set.doh) {
    565       addr = Curl_doh(conn, hostname, port, &respwait);
    566     }
    567     else {
    568       /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
    569          non-zero value indicating that we need to wait for the response to the
    570          resolve call */
    571       addr = Curl_getaddrinfo(conn,
    572 #ifdef DEBUGBUILD
    573                               (data->set.str[STRING_DEVICE]
    574                                && !strcmp(data->set.str[STRING_DEVICE],
    575                                           "LocalHost"))?"localhost":
    576 #endif
    577                               hostname, port, &respwait);
    578     }
    579     if(!addr) {
    580       if(respwait) {
    581         /* the response to our resolve call will come asynchronously at
    582            a later time, good or bad */
    583         /* First, check that we haven't received the info by now */
    584         result = Curl_resolv_check(conn, &dns);
    585         if(result) /* error detected */
    586           return CURLRESOLV_ERROR;
    587         if(dns)
    588           rc = CURLRESOLV_RESOLVED; /* pointer provided */
    589         else
    590           rc = CURLRESOLV_PENDING; /* no info yet */
    591       }
    592     }
    593     else {
    594       if(data->share)
    595         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    596 
    597       /* we got a response, store it in the cache */
    598       dns = Curl_cache_addr(data, addr, hostname, port);
    599 
    600       if(data->share)
    601         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    602 
    603       if(!dns)
    604         /* returned failure, bail out nicely */
    605         Curl_freeaddrinfo(addr);
    606       else
    607         rc = CURLRESOLV_RESOLVED;
    608     }
    609   }
    610 
    611   *entry = dns;
    612 
    613   return rc;
    614 }
    615 
    616 #ifdef USE_ALARM_TIMEOUT
    617 /*
    618  * This signal handler jumps back into the main libcurl code and continues
    619  * execution.  This effectively causes the remainder of the application to run
    620  * within a signal handler which is nonportable and could lead to problems.
    621  */
    622 static
    623 RETSIGTYPE alarmfunc(int sig)
    624 {
    625   /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
    626   (void)sig;
    627   siglongjmp(curl_jmpenv, 1);
    628 }
    629 #endif /* USE_ALARM_TIMEOUT */
    630 
    631 /*
    632  * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
    633  * timeout.  This function might return immediately if we're using asynch
    634  * resolves. See the return codes.
    635  *
    636  * The cache entry we return will get its 'inuse' counter increased when this
    637  * function is used. You MUST call Curl_resolv_unlock() later (when you're
    638  * done using this struct) to decrease the counter again.
    639  *
    640  * If built with a synchronous resolver and use of signals is not
    641  * disabled by the application, then a nonzero timeout will cause a
    642  * timeout after the specified number of milliseconds. Otherwise, timeout
    643  * is ignored.
    644  *
    645  * Return codes:
    646  *
    647  * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
    648  * CURLRESOLV_ERROR   (-1) = error, no pointer
    649  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
    650  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
    651  */
    652 
    653 int Curl_resolv_timeout(struct connectdata *conn,
    654                         const char *hostname,
    655                         int port,
    656                         struct Curl_dns_entry **entry,
    657                         time_t timeoutms)
    658 {
    659 #ifdef USE_ALARM_TIMEOUT
    660 #ifdef HAVE_SIGACTION
    661   struct sigaction keep_sigact;   /* store the old struct here */
    662   volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
    663   struct sigaction sigact;
    664 #else
    665 #ifdef HAVE_SIGNAL
    666   void (*keep_sigact)(int);       /* store the old handler here */
    667 #endif /* HAVE_SIGNAL */
    668 #endif /* HAVE_SIGACTION */
    669   volatile long timeout;
    670   volatile unsigned int prev_alarm = 0;
    671   struct Curl_easy *data = conn->data;
    672 #endif /* USE_ALARM_TIMEOUT */
    673   int rc;
    674 
    675   *entry = NULL;
    676 
    677   if(timeoutms < 0)
    678     /* got an already expired timeout */
    679     return CURLRESOLV_TIMEDOUT;
    680 
    681 #ifdef USE_ALARM_TIMEOUT
    682   if(data->set.no_signal)
    683     /* Ignore the timeout when signals are disabled */
    684     timeout = 0;
    685   else
    686     timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
    687 
    688   if(!timeout)
    689     /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
    690     return Curl_resolv(conn, hostname, port, entry);
    691 
    692   if(timeout < 1000) {
    693     /* The alarm() function only provides integer second resolution, so if
    694        we want to wait less than one second we must bail out already now. */
    695     failf(data,
    696         "remaining timeout of %ld too small to resolve via SIGALRM method",
    697         timeout);
    698     return CURLRESOLV_TIMEDOUT;
    699   }
    700   /* This allows us to time-out from the name resolver, as the timeout
    701      will generate a signal and we will siglongjmp() from that here.
    702      This technique has problems (see alarmfunc).
    703      This should be the last thing we do before calling Curl_resolv(),
    704      as otherwise we'd have to worry about variables that get modified
    705      before we invoke Curl_resolv() (and thus use "volatile"). */
    706   if(sigsetjmp(curl_jmpenv, 1)) {
    707     /* this is coming from a siglongjmp() after an alarm signal */
    708     failf(data, "name lookup timed out");
    709     rc = CURLRESOLV_ERROR;
    710     goto clean_up;
    711   }
    712   else {
    713     /*************************************************************
    714      * Set signal handler to catch SIGALRM
    715      * Store the old value to be able to set it back later!
    716      *************************************************************/
    717 #ifdef HAVE_SIGACTION
    718     sigaction(SIGALRM, NULL, &sigact);
    719     keep_sigact = sigact;
    720     keep_copysig = TRUE; /* yes, we have a copy */
    721     sigact.sa_handler = alarmfunc;
    722 #ifdef SA_RESTART
    723     /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
    724     sigact.sa_flags &= ~SA_RESTART;
    725 #endif
    726     /* now set the new struct */
    727     sigaction(SIGALRM, &sigact, NULL);
    728 #else /* HAVE_SIGACTION */
    729     /* no sigaction(), revert to the much lamer signal() */
    730 #ifdef HAVE_SIGNAL
    731     keep_sigact = signal(SIGALRM, alarmfunc);
    732 #endif
    733 #endif /* HAVE_SIGACTION */
    734 
    735     /* alarm() makes a signal get sent when the timeout fires off, and that
    736        will abort system calls */
    737     prev_alarm = alarm(curlx_sltoui(timeout/1000L));
    738   }
    739 
    740 #else
    741 #ifndef CURLRES_ASYNCH
    742   if(timeoutms)
    743     infof(conn->data, "timeout on name lookup is not supported\n");
    744 #else
    745   (void)timeoutms; /* timeoutms not used with an async resolver */
    746 #endif
    747 #endif /* USE_ALARM_TIMEOUT */
    748 
    749   /* Perform the actual name resolution. This might be interrupted by an
    750    * alarm if it takes too long.
    751    */
    752   rc = Curl_resolv(conn, hostname, port, entry);
    753 
    754 #ifdef USE_ALARM_TIMEOUT
    755 clean_up:
    756 
    757   if(!prev_alarm)
    758     /* deactivate a possibly active alarm before uninstalling the handler */
    759     alarm(0);
    760 
    761 #ifdef HAVE_SIGACTION
    762   if(keep_copysig) {
    763     /* we got a struct as it looked before, now put that one back nice
    764        and clean */
    765     sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
    766   }
    767 #else
    768 #ifdef HAVE_SIGNAL
    769   /* restore the previous SIGALRM handler */
    770   signal(SIGALRM, keep_sigact);
    771 #endif
    772 #endif /* HAVE_SIGACTION */
    773 
    774   /* switch back the alarm() to either zero or to what it was before minus
    775      the time we spent until now! */
    776   if(prev_alarm) {
    777     /* there was an alarm() set before us, now put it back */
    778     timediff_t elapsed_secs = Curl_timediff(Curl_now(),
    779                                             conn->created) / 1000;
    780 
    781     /* the alarm period is counted in even number of seconds */
    782     unsigned long alarm_set = prev_alarm - elapsed_secs;
    783 
    784     if(!alarm_set ||
    785        ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
    786       /* if the alarm time-left reached zero or turned "negative" (counted
    787          with unsigned values), we should fire off a SIGALRM here, but we
    788          won't, and zero would be to switch it off so we never set it to
    789          less than 1! */
    790       alarm(1);
    791       rc = CURLRESOLV_TIMEDOUT;
    792       failf(data, "Previous alarm fired off!");
    793     }
    794     else
    795       alarm((unsigned int)alarm_set);
    796   }
    797 #endif /* USE_ALARM_TIMEOUT */
    798 
    799   return rc;
    800 }
    801 
    802 /*
    803  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
    804  * made, the struct may be destroyed due to pruning. It is important that only
    805  * one unlock is made for each Curl_resolv() call.
    806  *
    807  * May be called with 'data' == NULL for global cache.
    808  */
    809 void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
    810 {
    811   if(data && data->share)
    812     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    813 
    814   freednsentry(dns);
    815 
    816   if(data && data->share)
    817     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    818 }
    819 
    820 /*
    821  * File-internal: release cache dns entry reference, free if inuse drops to 0
    822  */
    823 static void freednsentry(void *freethis)
    824 {
    825   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
    826   DEBUGASSERT(dns && (dns->inuse>0));
    827 
    828   dns->inuse--;
    829   if(dns->inuse == 0) {
    830     Curl_freeaddrinfo(dns->addr);
    831     free(dns);
    832   }
    833 }
    834 
    835 /*
    836  * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
    837  */
    838 int Curl_mk_dnscache(struct curl_hash *hash)
    839 {
    840   return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
    841                         freednsentry);
    842 }
    843 
    844 /*
    845  * Curl_hostcache_clean()
    846  *
    847  * This _can_ be called with 'data' == NULL but then of course no locking
    848  * can be done!
    849  */
    850 
    851 void Curl_hostcache_clean(struct Curl_easy *data,
    852                           struct curl_hash *hash)
    853 {
    854   if(data && data->share)
    855     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    856 
    857   Curl_hash_clean(hash);
    858 
    859   if(data && data->share)
    860     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    861 }
    862 
    863 
    864 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
    865 {
    866   struct curl_slist *hostp;
    867   char hostname[256];
    868   int port = 0;
    869 
    870   /* Default is no wildcard found */
    871   data->change.wildcard_resolve = false;
    872 
    873   for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
    874     char entry_id[MAX_HOSTCACHE_LEN];
    875     if(!hostp->data)
    876       continue;
    877     if(hostp->data[0] == '-') {
    878       size_t entry_len;
    879 
    880       if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
    881         infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
    882               hostp->data);
    883         continue;
    884       }
    885 
    886       /* Create an entry id, based upon the hostname and port */
    887       create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
    888       entry_len = strlen(entry_id);
    889 
    890       if(data->share)
    891         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    892 
    893       /* delete entry, ignore if it didn't exist */
    894       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
    895 
    896       if(data->share)
    897         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    898     }
    899     else {
    900       struct Curl_dns_entry *dns;
    901       Curl_addrinfo *head = NULL, *tail = NULL;
    902       size_t entry_len;
    903       char address[64];
    904 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
    905       char *addresses = NULL;
    906 #endif
    907       char *addr_begin;
    908       char *addr_end;
    909       char *port_ptr;
    910       char *end_ptr;
    911       char *host_end;
    912       unsigned long tmp_port;
    913       bool error = true;
    914 
    915       host_end = strchr(hostp->data, ':');
    916       if(!host_end ||
    917          ((host_end - hostp->data) >= (ptrdiff_t)sizeof(hostname)))
    918         goto err;
    919 
    920       memcpy(hostname, hostp->data, host_end - hostp->data);
    921       hostname[host_end - hostp->data] = '\0';
    922 
    923       port_ptr = host_end + 1;
    924       tmp_port = strtoul(port_ptr, &end_ptr, 10);
    925       if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
    926         goto err;
    927 
    928       port = (int)tmp_port;
    929 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
    930       addresses = end_ptr + 1;
    931 #endif
    932 
    933       while(*end_ptr) {
    934         size_t alen;
    935         Curl_addrinfo *ai;
    936 
    937         addr_begin = end_ptr + 1;
    938         addr_end = strchr(addr_begin, ',');
    939         if(!addr_end)
    940           addr_end = addr_begin + strlen(addr_begin);
    941         end_ptr = addr_end;
    942 
    943         /* allow IP(v6) address within [brackets] */
    944         if(*addr_begin == '[') {
    945           if(addr_end == addr_begin || *(addr_end - 1) != ']')
    946             goto err;
    947           ++addr_begin;
    948           --addr_end;
    949         }
    950 
    951         alen = addr_end - addr_begin;
    952         if(!alen)
    953           continue;
    954 
    955         if(alen >= sizeof(address))
    956           goto err;
    957 
    958         memcpy(address, addr_begin, alen);
    959         address[alen] = '\0';
    960 
    961 #ifndef ENABLE_IPV6
    962         if(strchr(address, ':')) {
    963           infof(data, "Ignoring resolve address '%s', missing IPv6 support.\n",
    964                 address);
    965           continue;
    966         }
    967 #endif
    968 
    969         ai = Curl_str2addr(address, port);
    970         if(!ai) {
    971           infof(data, "Resolve address '%s' found illegal!\n", address);
    972           goto err;
    973         }
    974 
    975         if(tail) {
    976           tail->ai_next = ai;
    977           tail = tail->ai_next;
    978         }
    979         else {
    980           head = tail = ai;
    981         }
    982       }
    983 
    984       if(!head)
    985         goto err;
    986 
    987       error = false;
    988    err:
    989       if(error) {
    990         infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
    991               hostp->data);
    992         Curl_freeaddrinfo(head);
    993         continue;
    994       }
    995 
    996       /* Create an entry id, based upon the hostname and port */
    997       create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
    998       entry_len = strlen(entry_id);
    999 
   1000       if(data->share)
   1001         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
   1002 
   1003       /* See if its already in our dns cache */
   1004       dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
   1005 
   1006       if(dns) {
   1007         infof(data, "RESOLVE %s:%d is - old addresses discarded!\n",
   1008                 hostname, port);
   1009         /* delete old entry entry, there are two reasons for this
   1010          1. old entry may have different addresses.
   1011          2. even if entry with correct addresses is already in the cache,
   1012             but if it is close to expire, then by the time next http
   1013             request is made, it can get expired and pruned because old
   1014             entry is not necessarily marked as added by CURLOPT_RESOLVE. */
   1015 
   1016         Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
   1017       }
   1018 
   1019       /* put this new host in the cache */
   1020       dns = Curl_cache_addr(data, head, hostname, port);
   1021       if(dns) {
   1022         dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
   1023         /* release the returned reference; the cache itself will keep the
   1024          * entry alive: */
   1025             dns->inuse--;
   1026       }
   1027 
   1028       if(data->share)
   1029         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
   1030 
   1031       if(!dns) {
   1032         Curl_freeaddrinfo(head);
   1033         return CURLE_OUT_OF_MEMORY;
   1034       }
   1035       infof(data, "Added %s:%d:%s to DNS cache\n",
   1036             hostname, port, addresses);
   1037 
   1038       /* Wildcard hostname */
   1039       if(hostname[0] == '*' && hostname[1] == '\0') {
   1040         infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks\n",
   1041               hostname, port);
   1042         data->change.wildcard_resolve = true;
   1043       }
   1044     }
   1045   }
   1046   data->change.resolve = NULL; /* dealt with now */
   1047 
   1048   return CURLE_OK;
   1049 }
   1050 
   1051 CURLcode Curl_resolv_check(struct connectdata *conn,
   1052                            struct Curl_dns_entry **dns)
   1053 {
   1054   if(conn->data->set.doh)
   1055     return Curl_doh_is_resolved(conn, dns);
   1056   return Curl_resolver_is_resolved(conn, dns);
   1057 }
   1058 
   1059 int Curl_resolv_getsock(struct connectdata *conn,
   1060                         curl_socket_t *socks,
   1061                         int numsocks)
   1062 {
   1063 #ifdef CURLRES_ASYNCH
   1064   if(conn->data->set.doh)
   1065     /* nothing to wait for during DOH resolve, those handles have their own
   1066        sockets */
   1067     return GETSOCK_BLANK;
   1068   return Curl_resolver_getsock(conn, socks, numsocks);
   1069 #else
   1070   (void)conn;
   1071   (void)socks;
   1072   (void)numsocks;
   1073   return GETSOCK_BLANK;
   1074 #endif
   1075 }
   1076 
   1077 /* Call this function after Curl_connect() has returned async=TRUE and
   1078    then a successful name resolve has been received.
   1079 
   1080    Note: this function disconnects and frees the conn data in case of
   1081    resolve failure */
   1082 CURLcode Curl_once_resolved(struct connectdata *conn,
   1083                             bool *protocol_done)
   1084 {
   1085   CURLcode result;
   1086 
   1087   if(conn->async.dns) {
   1088     conn->dns_entry = conn->async.dns;
   1089     conn->async.dns = NULL;
   1090   }
   1091 
   1092   result = Curl_setup_conn(conn, protocol_done);
   1093 
   1094   if(result)
   1095     /* We're not allowed to return failure with memory left allocated
   1096        in the connectdata struct, free those here */
   1097     Curl_disconnect(conn->data, conn, TRUE); /* close the connection */
   1098 
   1099   return result;
   1100 }
   1101