quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

hostip.c (46838B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@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.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  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 #include "curl_setup.h"
     26 
     27 #ifdef HAVE_NETINET_IN_H
     28 #include <netinet/in.h>
     29 #endif
     30 #ifdef HAVE_NETINET_IN6_H
     31 #include <netinet/in6.h>
     32 #endif
     33 #ifdef HAVE_NETDB_H
     34 #include <netdb.h>
     35 #endif
     36 #ifdef HAVE_ARPA_INET_H
     37 #include <arpa/inet.h>
     38 #endif
     39 #ifdef __VMS
     40 #include <in.h>
     41 #include <inet.h>
     42 #endif
     43 
     44 #include <setjmp.h>
     45 #ifndef UNDER_CE
     46 #include <signal.h>
     47 #endif
     48 
     49 #include "urldata.h"
     50 #include "sendf.h"
     51 #include "connect.h"
     52 #include "hostip.h"
     53 #include "hash.h"
     54 #include "rand.h"
     55 #include "share.h"
     56 #include "url.h"
     57 #include "curlx/inet_ntop.h"
     58 #include "curlx/inet_pton.h"
     59 #include "multiif.h"
     60 #include "doh.h"
     61 #include "curlx/warnless.h"
     62 #include "select.h"
     63 #include "strcase.h"
     64 #include "easy_lock.h"
     65 #include "curlx/strparse.h"
     66 
     67 /* The last 3 #include files should be in this order */
     68 #include "curl_printf.h"
     69 #include "curl_memory.h"
     70 #include "memdebug.h"
     71 
     72 #if defined(CURLRES_SYNCH) &&                   \
     73   defined(HAVE_ALARM) &&                        \
     74   defined(SIGALRM) &&                           \
     75   defined(HAVE_SIGSETJMP) &&                    \
     76   defined(GLOBAL_INIT_IS_THREADSAFE)
     77 /* alarm-based timeouts can only be used with all the dependencies satisfied */
     78 #define USE_ALARM_TIMEOUT
     79 #endif
     80 
     81 #define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
     82 
     83 #define MAX_DNS_CACHE_SIZE 29999
     84 
     85 /*
     86  * hostip.c explained
     87  * ==================
     88  *
     89  * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
     90  * source file are these:
     91  *
     92  * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
     93  * that. The host may not be able to resolve IPv6, but we do not really have to
     94  * take that into account. Hosts that are not IPv6-enabled have CURLRES_IPV4
     95  * defined.
     96  *
     97  * CURLRES_ARES - is defined if libcurl is built to use c-ares for
     98  * asynchronous name resolves. This can be Windows or *nix.
     99  *
    100  * CURLRES_THREADED - is defined if libcurl is built to run under (native)
    101  * Windows, and then the name resolve will be done in a new thread, and the
    102  * supported API will be the same as for ares-builds.
    103  *
    104  * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
    105  * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
    106  * defined.
    107  *
    108  * The host*.c sources files are split up like this:
    109  *
    110  * hostip.c   - method-independent resolver functions and utility functions
    111  * hostip4.c  - IPv4 specific functions
    112  * hostip6.c  - IPv6 specific functions
    113  * asyn.h     - common functions for all async resolvers
    114  * The two asynchronous name resolver backends are implemented in:
    115  * asyn-ares.c - async resolver using c-ares
    116  * asyn-thread.c - async resolver using POSIX threads
    117  *
    118  * The hostip.h is the united header file for all this. It defines the
    119  * CURLRES_* defines based on the config*.h and curl_setup.h defines.
    120  */
    121 
    122 static void dnscache_entry_free(struct Curl_dns_entry *dns);
    123 
    124 #ifndef CURL_DISABLE_VERBOSE_STRINGS
    125 static void show_resolve_info(struct Curl_easy *data,
    126                               struct Curl_dns_entry *dns);
    127 #else
    128 #define show_resolve_info(x,y) Curl_nop_stmt
    129 #endif
    130 
    131 /*
    132  * Curl_printable_address() stores a printable version of the 1st address
    133  * given in the 'ai' argument. The result will be stored in the buf that is
    134  * bufsize bytes big.
    135  *
    136  * If the conversion fails, the target buffer is empty.
    137  */
    138 void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
    139                             size_t bufsize)
    140 {
    141   DEBUGASSERT(bufsize);
    142   buf[0] = 0;
    143 
    144   switch(ai->ai_family) {
    145   case AF_INET: {
    146     const struct sockaddr_in *sa4 = (const void *)ai->ai_addr;
    147     const struct in_addr *ipaddr4 = &sa4->sin_addr;
    148     (void)curlx_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
    149     break;
    150   }
    151 #ifdef USE_IPV6
    152   case AF_INET6: {
    153     const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr;
    154     const struct in6_addr *ipaddr6 = &sa6->sin6_addr;
    155     (void)curlx_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
    156     break;
    157   }
    158 #endif
    159   default:
    160     break;
    161   }
    162 }
    163 
    164 /*
    165  * Create a hostcache id string for the provided host + port, to be used by
    166  * the DNS caching. Without alloc. Return length of the id string.
    167  */
    168 static size_t
    169 create_dnscache_id(const char *name,
    170                    size_t nlen, /* 0 or actual name length */
    171                    int port, char *ptr, size_t buflen)
    172 {
    173   size_t len = nlen ? nlen : strlen(name);
    174   DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
    175   if(len > (buflen - 7))
    176     len = buflen - 7;
    177   /* store and lower case the name */
    178   Curl_strntolower(ptr, name, len);
    179   return msnprintf(&ptr[len], 7, ":%u", port) + len;
    180 }
    181 
    182 struct dnscache_prune_data {
    183   time_t now;
    184   time_t oldest; /* oldest time in cache not pruned. */
    185   int max_age_sec;
    186 };
    187 
    188 /*
    189  * This function is set as a callback to be called for every entry in the DNS
    190  * cache when we want to prune old unused entries.
    191  *
    192  * Returning non-zero means remove the entry, return 0 to keep it in the
    193  * cache.
    194  */
    195 static int
    196 dnscache_entry_is_stale(void *datap, void *hc)
    197 {
    198   struct dnscache_prune_data *prune =
    199     (struct dnscache_prune_data *) datap;
    200   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) hc;
    201 
    202   if(dns->timestamp) {
    203     /* age in seconds */
    204     time_t age = prune->now - dns->timestamp;
    205     if(age >= (time_t)prune->max_age_sec)
    206       return TRUE;
    207     if(age > prune->oldest)
    208       prune->oldest = age;
    209   }
    210   return FALSE;
    211 }
    212 
    213 /*
    214  * Prune the DNS cache. This assumes that a lock has already been taken.
    215  * Returns the 'age' of the oldest still kept entry.
    216  */
    217 static time_t
    218 dnscache_prune(struct Curl_hash *hostcache, int cache_timeout,
    219                time_t now)
    220 {
    221   struct dnscache_prune_data user;
    222 
    223   user.max_age_sec = cache_timeout;
    224   user.now = now;
    225   user.oldest = 0;
    226 
    227   Curl_hash_clean_with_criterium(hostcache,
    228                                  (void *) &user,
    229                                  dnscache_entry_is_stale);
    230 
    231   return user.oldest;
    232 }
    233 
    234 static struct Curl_dnscache *dnscache_get(struct Curl_easy *data)
    235 {
    236   if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS))
    237     return &data->share->dnscache;
    238   if(data->multi)
    239     return &data->multi->dnscache;
    240   return NULL;
    241 }
    242 
    243 static void dnscache_lock(struct Curl_easy *data,
    244                           struct Curl_dnscache *dnscache)
    245 {
    246   if(data->share && dnscache == &data->share->dnscache)
    247     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
    248 }
    249 
    250 static void dnscache_unlock(struct Curl_easy *data,
    251                             struct Curl_dnscache *dnscache)
    252 {
    253   if(data->share && dnscache == &data->share->dnscache)
    254     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
    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_dnscache_prune(struct Curl_easy *data)
    262 {
    263   struct Curl_dnscache *dnscache = dnscache_get(data);
    264   time_t now;
    265   /* the timeout may be set -1 (forever) */
    266   int timeout = data->set.dns_cache_timeout;
    267 
    268   if(!dnscache)
    269     /* NULL hostcache means we cannot do it */
    270     return;
    271 
    272   dnscache_lock(data, dnscache);
    273 
    274   now = time(NULL);
    275 
    276   do {
    277     /* Remove outdated and unused entries from the hostcache */
    278     time_t oldest = dnscache_prune(&dnscache->entries, timeout, now);
    279 
    280     if(oldest < INT_MAX)
    281       timeout = (int)oldest; /* we know it fits */
    282     else
    283       timeout = INT_MAX - 1;
    284 
    285     /* if the cache size is still too big, use the oldest age as new
    286        prune limit */
    287   } while(timeout &&
    288           (Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE));
    289 
    290   dnscache_unlock(data, dnscache);
    291 }
    292 
    293 #ifdef USE_ALARM_TIMEOUT
    294 /* Beware this is a global and unique instance. This is used to store the
    295    return address that we can jump back to from inside a signal handler. This
    296    is not thread-safe stuff. */
    297 static sigjmp_buf curl_jmpenv;
    298 static curl_simple_lock curl_jmpenv_lock;
    299 #endif
    300 
    301 /* lookup address, returns entry if found and not stale */
    302 static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
    303                                          struct Curl_dnscache *dnscache,
    304                                          const char *hostname,
    305                                          int port,
    306                                          int ip_version)
    307 {
    308   struct Curl_dns_entry *dns = NULL;
    309   char entry_id[MAX_HOSTCACHE_LEN];
    310   size_t entry_len;
    311 
    312   if(!dnscache)
    313     return NULL;
    314 
    315   /* Create an entry id, based upon the hostname and port */
    316   entry_len = create_dnscache_id(hostname, 0, port,
    317                                  entry_id, sizeof(entry_id));
    318 
    319   /* See if it is already in our dns cache */
    320   dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
    321 
    322   /* No entry found in cache, check if we might have a wildcard entry */
    323   if(!dns && data->state.wildcard_resolve) {
    324     entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id));
    325 
    326     /* See if it is already in our dns cache */
    327     dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
    328   }
    329 
    330   if(dns && (data->set.dns_cache_timeout != -1)) {
    331     /* See whether the returned entry is stale. Done before we release lock */
    332     struct dnscache_prune_data user;
    333 
    334     user.now = time(NULL);
    335     user.max_age_sec = data->set.dns_cache_timeout;
    336     user.oldest = 0;
    337 
    338     if(dnscache_entry_is_stale(&user, dns)) {
    339       infof(data, "Hostname in DNS cache was stale, zapped");
    340       dns = NULL; /* the memory deallocation is being handled by the hash */
    341       Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
    342     }
    343   }
    344 
    345   /* See if the returned entry matches the required resolve mode */
    346   if(dns && ip_version != CURL_IPRESOLVE_WHATEVER) {
    347     int pf = PF_INET;
    348     bool found = FALSE;
    349     struct Curl_addrinfo *addr = dns->addr;
    350 
    351 #ifdef PF_INET6
    352     if(ip_version == CURL_IPRESOLVE_V6)
    353       pf = PF_INET6;
    354 #endif
    355 
    356     while(addr) {
    357       if(addr->ai_family == pf) {
    358         found = TRUE;
    359         break;
    360       }
    361       addr = addr->ai_next;
    362     }
    363 
    364     if(!found) {
    365       infof(data, "Hostname in DNS cache does not have needed family, zapped");
    366       dns = NULL; /* the memory deallocation is being handled by the hash */
    367       Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
    368     }
    369   }
    370   return dns;
    371 }
    372 
    373 /*
    374  * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
    375  *
    376  * Curl_resolv() checks initially and multi_runsingle() checks each time
    377  * it discovers the handle in the state WAITRESOLVE whether the hostname
    378  * has already been resolved and the address has already been stored in
    379  * the DNS cache. This short circuits waiting for a lot of pending
    380  * lookups for the same hostname requested by different handles.
    381  *
    382  * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
    383  *
    384  * The returned data *MUST* be "released" with Curl_resolv_unlink() after
    385  * use, or we will leak memory!
    386  */
    387 struct Curl_dns_entry *
    388 Curl_dnscache_get(struct Curl_easy *data,
    389                   const char *hostname,
    390                   int port,
    391                   int ip_version)
    392 {
    393   struct Curl_dnscache *dnscache = dnscache_get(data);
    394   struct Curl_dns_entry *dns = NULL;
    395 
    396   dnscache_lock(data, dnscache);
    397 
    398   dns = fetch_addr(data, dnscache, hostname, port, ip_version);
    399   if(dns)
    400     dns->refcount++; /* we use it! */
    401 
    402   dnscache_unlock(data, dnscache);
    403 
    404   return dns;
    405 }
    406 
    407 #ifndef CURL_DISABLE_SHUFFLE_DNS
    408 /*
    409  * Return # of addresses in a Curl_addrinfo struct
    410  */
    411 static int num_addresses(const struct Curl_addrinfo *addr)
    412 {
    413   int i = 0;
    414   while(addr) {
    415     addr = addr->ai_next;
    416     i++;
    417   }
    418   return i;
    419 }
    420 
    421 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
    422                                     struct Curl_addrinfo **addr);
    423 /*
    424  * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
    425  * struct by re-linking its linked list.
    426  *
    427  * The addr argument should be the address of a pointer to the head node of a
    428  * `Curl_addrinfo` list and it will be modified to point to the new head after
    429  * shuffling.
    430  *
    431  * Not declared static only to make it easy to use in a unit test!
    432  *
    433  * @unittest: 1608
    434  */
    435 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
    436                                     struct Curl_addrinfo **addr)
    437 {
    438   CURLcode result = CURLE_OK;
    439   const int num_addrs = num_addresses(*addr);
    440 
    441   if(num_addrs > 1) {
    442     struct Curl_addrinfo **nodes;
    443     infof(data, "Shuffling %i addresses", num_addrs);
    444 
    445     nodes = malloc(num_addrs*sizeof(*nodes));
    446     if(nodes) {
    447       int i;
    448       unsigned int *rnd;
    449       const size_t rnd_size = num_addrs * sizeof(*rnd);
    450 
    451       /* build a plain array of Curl_addrinfo pointers */
    452       nodes[0] = *addr;
    453       for(i = 1; i < num_addrs; i++) {
    454         nodes[i] = nodes[i-1]->ai_next;
    455       }
    456 
    457       rnd = malloc(rnd_size);
    458       if(rnd) {
    459         /* Fisher-Yates shuffle */
    460         if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
    461           struct Curl_addrinfo *swap_tmp;
    462           for(i = num_addrs - 1; i > 0; i--) {
    463             swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)];
    464             nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i];
    465             nodes[i] = swap_tmp;
    466           }
    467 
    468           /* relink list in the new order */
    469           for(i = 1; i < num_addrs; i++) {
    470             nodes[i-1]->ai_next = nodes[i];
    471           }
    472 
    473           nodes[num_addrs-1]->ai_next = NULL;
    474           *addr = nodes[0];
    475         }
    476         free(rnd);
    477       }
    478       else
    479         result = CURLE_OUT_OF_MEMORY;
    480       free(nodes);
    481     }
    482     else
    483       result = CURLE_OUT_OF_MEMORY;
    484   }
    485   return result;
    486 }
    487 #endif
    488 
    489 struct Curl_dns_entry *
    490 Curl_dnscache_mk_entry(struct Curl_easy *data,
    491                        struct Curl_addrinfo *addr,
    492                        const char *hostname,
    493                        size_t hostlen, /* length or zero */
    494                        int port,
    495                        bool permanent)
    496 {
    497   struct Curl_dns_entry *dns;
    498 
    499 #ifndef CURL_DISABLE_SHUFFLE_DNS
    500   /* shuffle addresses if requested */
    501   if(data->set.dns_shuffle_addresses) {
    502     CURLcode result = Curl_shuffle_addr(data, &addr);
    503     if(result) {
    504       Curl_freeaddrinfo(addr);
    505       return NULL;
    506     }
    507   }
    508 #else
    509   (void)data;
    510 #endif
    511   if(!hostlen)
    512     hostlen = strlen(hostname);
    513 
    514   /* Create a new cache entry */
    515   dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
    516   if(!dns) {
    517     Curl_freeaddrinfo(addr);
    518     return NULL;
    519   }
    520 
    521   dns->refcount = 1; /* the cache has the first reference */
    522   dns->addr = addr; /* this is the address(es) */
    523   if(permanent)
    524     dns->timestamp = 0; /* an entry that never goes stale */
    525   else {
    526     dns->timestamp = time(NULL);
    527     if(dns->timestamp == 0)
    528       dns->timestamp = 1;
    529   }
    530   dns->hostport = port;
    531   if(hostlen)
    532     memcpy(dns->hostname, hostname, hostlen);
    533 
    534   return dns;
    535 }
    536 
    537 static struct Curl_dns_entry *
    538 dnscache_add_addr(struct Curl_easy *data,
    539                   struct Curl_dnscache *dnscache,
    540                   struct Curl_addrinfo *addr,
    541                   const char *hostname,
    542                   size_t hlen, /* length or zero */
    543                   int port,
    544                   bool permanent)
    545 {
    546   char entry_id[MAX_HOSTCACHE_LEN];
    547   size_t entry_len;
    548   struct Curl_dns_entry *dns;
    549   struct Curl_dns_entry *dns2;
    550 
    551   dns = Curl_dnscache_mk_entry(data, addr, hostname, hlen, port, permanent);
    552   if(!dns)
    553     return NULL;
    554 
    555   /* Create an entry id, based upon the hostname and port */
    556   entry_len = create_dnscache_id(hostname, hlen, port,
    557                                  entry_id, sizeof(entry_id));
    558 
    559   /* Store the resolved data in our DNS cache. */
    560   dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1,
    561                        (void *)dns);
    562   if(!dns2) {
    563     dnscache_entry_free(dns);
    564     return NULL;
    565   }
    566 
    567   dns = dns2;
    568   dns->refcount++;         /* mark entry as in-use */
    569   return dns;
    570 }
    571 
    572 CURLcode Curl_dnscache_add(struct Curl_easy *data,
    573                            struct Curl_dns_entry *entry)
    574 {
    575   struct Curl_dnscache *dnscache = dnscache_get(data);
    576   char id[MAX_HOSTCACHE_LEN];
    577   size_t idlen;
    578 
    579   if(!dnscache)
    580     return CURLE_FAILED_INIT;
    581   /* Create an entry id, based upon the hostname and port */
    582   idlen = create_dnscache_id(entry->hostname, 0, entry->hostport,
    583                              id, sizeof(id));
    584 
    585   /* Store the resolved data in our DNS cache and up ref count */
    586   dnscache_lock(data, dnscache);
    587   if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) {
    588     dnscache_unlock(data, dnscache);
    589     return CURLE_OUT_OF_MEMORY;
    590   }
    591   entry->refcount++;
    592   dnscache_unlock(data, dnscache);
    593   return CURLE_OK;
    594 }
    595 
    596 #ifdef USE_IPV6
    597 /* return a static IPv6 ::1 for the name */
    598 static struct Curl_addrinfo *get_localhost6(int port, const char *name)
    599 {
    600   struct Curl_addrinfo *ca;
    601   const size_t ss_size = sizeof(struct sockaddr_in6);
    602   const size_t hostlen = strlen(name);
    603   struct sockaddr_in6 sa6;
    604   unsigned char ipv6[16];
    605   unsigned short port16 = (unsigned short)(port & 0xffff);
    606   ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
    607   if(!ca)
    608     return NULL;
    609 
    610   sa6.sin6_family = AF_INET6;
    611   sa6.sin6_port = htons(port16);
    612   sa6.sin6_flowinfo = 0;
    613 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
    614   sa6.sin6_scope_id = 0;
    615 #endif
    616 
    617   (void)curlx_inet_pton(AF_INET6, "::1", ipv6);
    618   memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
    619 
    620   ca->ai_flags     = 0;
    621   ca->ai_family    = AF_INET6;
    622   ca->ai_socktype  = SOCK_STREAM;
    623   ca->ai_protocol  = IPPROTO_TCP;
    624   ca->ai_addrlen   = (curl_socklen_t)ss_size;
    625   ca->ai_next      = NULL;
    626   ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
    627   memcpy(ca->ai_addr, &sa6, ss_size);
    628   ca->ai_canonname = (char *)ca->ai_addr + ss_size;
    629   strcpy(ca->ai_canonname, name);
    630   return ca;
    631 }
    632 #else
    633 #define get_localhost6(x,y) NULL
    634 #endif
    635 
    636 /* return a static IPv4 127.0.0.1 for the given name */
    637 static struct Curl_addrinfo *get_localhost(int port, const char *name)
    638 {
    639   struct Curl_addrinfo *ca;
    640   struct Curl_addrinfo *ca6;
    641   const size_t ss_size = sizeof(struct sockaddr_in);
    642   const size_t hostlen = strlen(name);
    643   struct sockaddr_in sa;
    644   unsigned int ipv4;
    645   unsigned short port16 = (unsigned short)(port & 0xffff);
    646 
    647   /* memset to clear the sa.sin_zero field */
    648   memset(&sa, 0, sizeof(sa));
    649   sa.sin_family = AF_INET;
    650   sa.sin_port = htons(port16);
    651   if(curlx_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
    652     return NULL;
    653   memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
    654 
    655   ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
    656   if(!ca)
    657     return NULL;
    658   ca->ai_flags     = 0;
    659   ca->ai_family    = AF_INET;
    660   ca->ai_socktype  = SOCK_STREAM;
    661   ca->ai_protocol  = IPPROTO_TCP;
    662   ca->ai_addrlen   = (curl_socklen_t)ss_size;
    663   ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
    664   memcpy(ca->ai_addr, &sa, ss_size);
    665   ca->ai_canonname = (char *)ca->ai_addr + ss_size;
    666   strcpy(ca->ai_canonname, name);
    667 
    668   ca6 = get_localhost6(port, name);
    669   if(!ca6)
    670     return ca;
    671   ca6->ai_next = ca;
    672   return ca6;
    673 }
    674 
    675 #ifdef USE_IPV6
    676 /*
    677  * Curl_ipv6works() returns TRUE if IPv6 seems to work.
    678  */
    679 bool Curl_ipv6works(struct Curl_easy *data)
    680 {
    681   if(data) {
    682     /* the nature of most system is that IPv6 status does not come and go
    683        during a program's lifetime so we only probe the first time and then we
    684        have the info kept for fast reuse */
    685     DEBUGASSERT(data);
    686     DEBUGASSERT(data->multi);
    687     if(data->multi->ipv6_up == IPV6_UNKNOWN) {
    688       bool works = Curl_ipv6works(NULL);
    689       data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD;
    690     }
    691     return data->multi->ipv6_up == IPV6_WORKS;
    692   }
    693   else {
    694     int ipv6_works = -1;
    695     /* probe to see if we have a working IPv6 stack */
    696     curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
    697     if(s == CURL_SOCKET_BAD)
    698       /* an IPv6 address was requested but we cannot get/use one */
    699       ipv6_works = 0;
    700     else {
    701       ipv6_works = 1;
    702       sclose(s);
    703     }
    704     return ipv6_works > 0;
    705   }
    706 }
    707 #endif /* USE_IPV6 */
    708 
    709 /*
    710  * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
    711  * (or IPv6 if supported) address.
    712  */
    713 bool Curl_host_is_ipnum(const char *hostname)
    714 {
    715   struct in_addr in;
    716 #ifdef USE_IPV6
    717   struct in6_addr in6;
    718 #endif
    719   if(curlx_inet_pton(AF_INET, hostname, &in) > 0
    720 #ifdef USE_IPV6
    721      || curlx_inet_pton(AF_INET6, hostname, &in6) > 0
    722 #endif
    723     )
    724     return TRUE;
    725   return FALSE;
    726 }
    727 
    728 
    729 /* return TRUE if 'part' is a case insensitive tail of 'full' */
    730 static bool tailmatch(const char *full, size_t flen,
    731                       const char *part, size_t plen)
    732 {
    733   if(plen > flen)
    734     return FALSE;
    735   return curl_strnequal(part, &full[flen - plen], plen);
    736 }
    737 
    738 static struct Curl_addrinfo *
    739 convert_ipaddr_direct(const char *hostname, int port, bool *is_ipaddr)
    740 {
    741   struct in_addr in;
    742   *is_ipaddr = FALSE;
    743   /* First check if this is an IPv4 address string */
    744   if(curlx_inet_pton(AF_INET, hostname, &in) > 0) {
    745     /* This is a dotted IP address 123.123.123.123-style */
    746     *is_ipaddr = TRUE;
    747 #ifdef USE_RESOLVE_ON_IPS
    748     (void)port;
    749     return NULL;
    750 #else
    751     return Curl_ip2addr(AF_INET, &in, hostname, port);
    752 #endif
    753   }
    754 #ifdef USE_IPV6
    755   else {
    756     struct in6_addr in6;
    757     /* check if this is an IPv6 address string */
    758     if(curlx_inet_pton(AF_INET6, hostname, &in6) > 0) {
    759       /* This is an IPv6 address literal */
    760       *is_ipaddr = TRUE;
    761 #ifdef USE_RESOLVE_ON_IPS
    762       return NULL;
    763 #else
    764       return Curl_ip2addr(AF_INET6, &in6, hostname, port);
    765 #endif
    766     }
    767   }
    768 #endif /* USE_IPV6 */
    769   return NULL;
    770 }
    771 
    772 static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version)
    773 {
    774 #ifdef CURLRES_IPV6
    775   if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
    776     return FALSE;
    777 #elif defined(CURLRES_IPV4)
    778   (void)data;
    779   if(ip_version == CURL_IPRESOLVE_V6)
    780     return FALSE;
    781 #else
    782 #error either CURLRES_IPV6 or CURLRES_IPV4 need to be defined
    783 #endif
    784   return TRUE;
    785 }
    786 
    787 /*
    788  * Curl_resolv() is the main name resolve function within libcurl. It resolves
    789  * a name and returns a pointer to the entry in the 'entry' argument (if one
    790  * is provided). This function might return immediately if we are using asynch
    791  * resolves. See the return codes.
    792  *
    793  * The cache entry we return will get its 'inuse' counter increased when this
    794  * function is used. You MUST call Curl_resolv_unlink() later (when you are
    795  * done using this struct) to decrease the reference counter again.
    796  *
    797  * Return codes:
    798  * CURLE_OK = success, *entry set to non-NULL
    799  * CURLE_AGAIN = resolving in progress, *entry == NULL
    800  * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL
    801  * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
    802  */
    803 CURLcode Curl_resolv(struct Curl_easy *data,
    804                      const char *hostname,
    805                      int port,
    806                      int ip_version,
    807                      bool allowDOH,
    808                      struct Curl_dns_entry **entry)
    809 {
    810   struct Curl_dnscache *dnscache = dnscache_get(data);
    811   struct Curl_dns_entry *dns = NULL;
    812   struct Curl_addrinfo *addr = NULL;
    813   int respwait = 0;
    814   bool is_ipaddr;
    815   size_t hostname_len;
    816 
    817 #ifndef CURL_DISABLE_DOH
    818   data->conn->bits.doh = FALSE; /* default is not */
    819 #else
    820   (void)allowDOH;
    821 #endif
    822   if(!dnscache)
    823     goto error;
    824 
    825   /* We should intentionally error and not resolve .onion TLDs */
    826   hostname_len = strlen(hostname);
    827   if(hostname_len >= 7 &&
    828      (curl_strequal(&hostname[hostname_len - 6], ".onion") ||
    829       curl_strequal(&hostname[hostname_len - 7], ".onion."))) {
    830     failf(data, "Not resolving .onion address (RFC 7686)");
    831     goto error;
    832   }
    833 
    834   /* Let's check our DNS cache first */
    835   dnscache_lock(data, dnscache);
    836   dns = fetch_addr(data, dnscache, hostname, port, ip_version);
    837   if(dns)
    838     dns->refcount++; /* we pass out the reference. */
    839   dnscache_unlock(data, dnscache);
    840   if(dns) {
    841     infof(data, "Hostname %s was found in DNS cache", hostname);
    842     goto out;
    843   }
    844 
    845   /* No luck, we need to resolve hostname. Notify user callback. */
    846   if(data->set.resolver_start) {
    847     void *resolver = NULL;
    848     int st;
    849 #ifdef CURLRES_ASYNCH
    850     if(Curl_async_get_impl(data, &resolver))
    851       goto error;
    852 #endif
    853     Curl_set_in_callback(data, TRUE);
    854     st = data->set.resolver_start(resolver, NULL,
    855                                   data->set.resolver_start_client);
    856     Curl_set_in_callback(data, FALSE);
    857     if(st)
    858       goto error;
    859   }
    860 
    861   /* shortcut literal IP addresses, if we are not told to resolve them. */
    862   addr = convert_ipaddr_direct(hostname, port, &is_ipaddr);
    863   if(addr)
    864     goto out;
    865 
    866 #ifndef USE_RESOLVE_ON_IPS
    867   /* allowed to convert, hostname is IP address, then NULL means error */
    868   if(is_ipaddr)
    869     goto error;
    870 #endif
    871 
    872   /* Really need a resolver for hostname. */
    873   if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
    874     goto error;
    875 
    876   if(!is_ipaddr &&
    877      (curl_strequal(hostname, "localhost") ||
    878       curl_strequal(hostname, "localhost.") ||
    879       tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
    880       tailmatch(hostname, hostname_len, STRCONST(".localhost.")))) {
    881     addr = get_localhost(port, hostname);
    882   }
    883 #ifndef CURL_DISABLE_DOH
    884   else if(!is_ipaddr && allowDOH && data->set.doh) {
    885     addr = Curl_doh(data, hostname, port, ip_version, &respwait);
    886   }
    887 #endif
    888   else {
    889     /* Can we provide the requested IP specifics in resolving? */
    890     if(!can_resolve_ip_version(data, ip_version))
    891       goto error;
    892 
    893 #ifdef CURLRES_ASYNCH
    894     addr = Curl_async_getaddrinfo(data, hostname, port, ip_version, &respwait);
    895 #else
    896     respwait = 0; /* no async waiting here */
    897     addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version);
    898 #endif
    899   }
    900 
    901 out:
    902   /* We either have found a `dns` or looked up the `addr`
    903    * or `respwait` is set for an async operation.
    904    * Everything else is a failure to resolve. */
    905   if(dns) {
    906     *entry = dns;
    907     return CURLE_OK;
    908   }
    909   else if(addr) {
    910     /* we got a response, create a dns entry, add to cache, return */
    911     dns = Curl_dnscache_mk_entry(data, addr, hostname, 0, port, FALSE);
    912     if(!dns)
    913       goto error;
    914     if(Curl_dnscache_add(data, dns))
    915       goto error;
    916     show_resolve_info(data, dns);
    917     *entry = dns;
    918     return CURLE_OK;
    919   }
    920   else if(respwait) {
    921     if(!Curl_resolv_check(data, &dns)) {
    922       *entry = dns;
    923       return dns ? CURLE_OK : CURLE_AGAIN;
    924     }
    925   }
    926 error:
    927   if(dns)
    928     Curl_resolv_unlink(data, &dns);
    929   *entry = NULL;
    930   Curl_async_shutdown(data);
    931   return CURLE_COULDNT_RESOLVE_HOST;
    932 }
    933 
    934 CURLcode Curl_resolv_blocking(struct Curl_easy *data,
    935                               const char *hostname,
    936                               int port,
    937                               int ip_version,
    938                               struct Curl_dns_entry **dnsentry)
    939 {
    940   CURLcode result;
    941 
    942   *dnsentry = NULL;
    943   result = Curl_resolv(data, hostname, port, ip_version, FALSE, dnsentry);
    944   switch(result) {
    945   case CURLE_OK:
    946     DEBUGASSERT(*dnsentry);
    947     return CURLE_OK;
    948   case CURLE_AGAIN:
    949     DEBUGASSERT(!*dnsentry);
    950     result = Curl_async_await(data, dnsentry);
    951     if(result || !*dnsentry) {
    952       /* close the connection, since we cannot return failure here without
    953          cleaning up this connection properly. */
    954       connclose(data->conn, "async resolve failed");
    955     }
    956     return result;
    957   default:
    958     return result;
    959   }
    960 }
    961 
    962 #ifdef USE_ALARM_TIMEOUT
    963 /*
    964  * This signal handler jumps back into the main libcurl code and continues
    965  * execution. This effectively causes the remainder of the application to run
    966  * within a signal handler which is nonportable and could lead to problems.
    967  */
    968 CURL_NORETURN static
    969 void alarmfunc(int sig)
    970 {
    971   (void)sig;
    972   siglongjmp(curl_jmpenv, 1);
    973 }
    974 #endif /* USE_ALARM_TIMEOUT */
    975 
    976 /*
    977  * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
    978  * timeout. This function might return immediately if we are using asynch
    979  * resolves. See the return codes.
    980  *
    981  * The cache entry we return will get its 'inuse' counter increased when this
    982  * function is used. You MUST call Curl_resolv_unlink() later (when you are
    983  * done using this struct) to decrease the reference counter again.
    984  *
    985  * If built with a synchronous resolver and use of signals is not
    986  * disabled by the application, then a nonzero timeout will cause a
    987  * timeout after the specified number of milliseconds. Otherwise, timeout
    988  * is ignored.
    989  *
    990  * Return codes:
    991  * CURLE_OK = success, *entry set to non-NULL
    992  * CURLE_AGAIN = resolving in progress, *entry == NULL
    993  * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL
    994  * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
    995  */
    996 
    997 CURLcode Curl_resolv_timeout(struct Curl_easy *data,
    998                              const char *hostname,
    999                              int port,
   1000                              int ip_version,
   1001                              struct Curl_dns_entry **entry,
   1002                              timediff_t timeoutms)
   1003 {
   1004 #ifdef USE_ALARM_TIMEOUT
   1005 #ifdef HAVE_SIGACTION
   1006   struct sigaction keep_sigact;   /* store the old struct here */
   1007   volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
   1008   struct sigaction sigact;
   1009 #else
   1010 #ifdef HAVE_SIGNAL
   1011   void (*keep_sigact)(int);       /* store the old handler here */
   1012 #endif /* HAVE_SIGNAL */
   1013 #endif /* HAVE_SIGACTION */
   1014   volatile long timeout;
   1015   volatile unsigned int prev_alarm = 0;
   1016 #endif /* USE_ALARM_TIMEOUT */
   1017   CURLcode result;
   1018 
   1019   *entry = NULL;
   1020 
   1021   if(timeoutms < 0)
   1022     /* got an already expired timeout */
   1023     return CURLE_OPERATION_TIMEDOUT;
   1024 
   1025 #ifdef USE_ALARM_TIMEOUT
   1026   if(data->set.no_signal)
   1027     /* Ignore the timeout when signals are disabled */
   1028     timeout = 0;
   1029   else
   1030     timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
   1031 
   1032   if(!timeout
   1033 #ifndef CURL_DISABLE_DOH
   1034      || data->set.doh
   1035 #endif
   1036     )
   1037     /* USE_ALARM_TIMEOUT defined, but no timeout actually requested or resolve
   1038        done using DoH */
   1039     return Curl_resolv(data, hostname, port, ip_version, TRUE, entry);
   1040 
   1041   if(timeout < 1000) {
   1042     /* The alarm() function only provides integer second resolution, so if
   1043        we want to wait less than one second we must bail out already now. */
   1044     failf(data,
   1045         "remaining timeout of %ld too small to resolve via SIGALRM method",
   1046         timeout);
   1047     return CURLE_OPERATION_TIMEDOUT;
   1048   }
   1049   /* This allows us to time-out from the name resolver, as the timeout
   1050      will generate a signal and we will siglongjmp() from that here.
   1051      This technique has problems (see alarmfunc).
   1052      This should be the last thing we do before calling Curl_resolv(),
   1053      as otherwise we would have to worry about variables that get modified
   1054      before we invoke Curl_resolv() (and thus use "volatile"). */
   1055   curl_simple_lock_lock(&curl_jmpenv_lock);
   1056 
   1057   if(sigsetjmp(curl_jmpenv, 1)) {
   1058     /* this is coming from a siglongjmp() after an alarm signal */
   1059     failf(data, "name lookup timed out");
   1060     result = CURLE_OPERATION_TIMEDOUT;
   1061     goto clean_up;
   1062   }
   1063   else {
   1064     /*************************************************************
   1065      * Set signal handler to catch SIGALRM
   1066      * Store the old value to be able to set it back later!
   1067      *************************************************************/
   1068 #ifdef HAVE_SIGACTION
   1069     sigaction(SIGALRM, NULL, &sigact);
   1070     keep_sigact = sigact;
   1071     keep_copysig = TRUE; /* yes, we have a copy */
   1072     sigact.sa_handler = alarmfunc;
   1073 #ifdef SA_RESTART
   1074     /* HP-UX does not have SA_RESTART but defaults to that behavior! */
   1075     sigact.sa_flags &= ~SA_RESTART;
   1076 #endif
   1077     /* now set the new struct */
   1078     sigaction(SIGALRM, &sigact, NULL);
   1079 #else /* HAVE_SIGACTION */
   1080     /* no sigaction(), revert to the much lamer signal() */
   1081 #ifdef HAVE_SIGNAL
   1082     keep_sigact = signal(SIGALRM, alarmfunc);
   1083 #endif
   1084 #endif /* HAVE_SIGACTION */
   1085 
   1086     /* alarm() makes a signal get sent when the timeout fires off, and that
   1087        will abort system calls */
   1088     prev_alarm = alarm(curlx_sltoui(timeout/1000L));
   1089   }
   1090 
   1091 #else /* USE_ALARM_TIMEOUT */
   1092 #ifndef CURLRES_ASYNCH
   1093   if(timeoutms)
   1094     infof(data, "timeout on name lookup is not supported");
   1095 #else
   1096   (void)timeoutms; /* timeoutms not used with an async resolver */
   1097 #endif
   1098 #endif /* else USE_ALARM_TIMEOUT */
   1099 
   1100   /* Perform the actual name resolution. This might be interrupted by an
   1101    * alarm if it takes too long.
   1102    */
   1103   result = Curl_resolv(data, hostname, port, ip_version, TRUE, entry);
   1104 
   1105 #ifdef USE_ALARM_TIMEOUT
   1106 clean_up:
   1107 
   1108   if(!prev_alarm)
   1109     /* deactivate a possibly active alarm before uninstalling the handler */
   1110     alarm(0);
   1111 
   1112 #ifdef HAVE_SIGACTION
   1113   if(keep_copysig) {
   1114     /* we got a struct as it looked before, now put that one back nice
   1115        and clean */
   1116     sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
   1117   }
   1118 #else
   1119 #ifdef HAVE_SIGNAL
   1120   /* restore the previous SIGALRM handler */
   1121   signal(SIGALRM, keep_sigact);
   1122 #endif
   1123 #endif /* HAVE_SIGACTION */
   1124 
   1125   curl_simple_lock_unlock(&curl_jmpenv_lock);
   1126 
   1127   /* switch back the alarm() to either zero or to what it was before minus
   1128      the time we spent until now! */
   1129   if(prev_alarm) {
   1130     /* there was an alarm() set before us, now put it back */
   1131     timediff_t elapsed_secs = curlx_timediff(curlx_now(),
   1132                                             data->conn->created) / 1000;
   1133 
   1134     /* the alarm period is counted in even number of seconds */
   1135     unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs);
   1136 
   1137     if(!alarm_set ||
   1138        ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
   1139       /* if the alarm time-left reached zero or turned "negative" (counted
   1140          with unsigned values), we should fire off a SIGALRM here, but we
   1141          will not, and zero would be to switch it off so we never set it to
   1142          less than 1! */
   1143       alarm(1);
   1144       result = CURLE_OPERATION_TIMEDOUT;
   1145       failf(data, "Previous alarm fired off");
   1146     }
   1147     else
   1148       alarm((unsigned int)alarm_set);
   1149   }
   1150 #endif /* USE_ALARM_TIMEOUT */
   1151 
   1152   return result;
   1153 }
   1154 
   1155 static void dnscache_entry_free(struct Curl_dns_entry *dns)
   1156 {
   1157   Curl_freeaddrinfo(dns->addr);
   1158 #ifdef USE_HTTPSRR
   1159   if(dns->hinfo) {
   1160     Curl_httpsrr_cleanup(dns->hinfo);
   1161     free(dns->hinfo);
   1162   }
   1163 #endif
   1164   free(dns);
   1165 }
   1166 
   1167 /*
   1168  * Curl_resolv_unlink() releases a reference to the given cached DNS entry.
   1169  * When the reference count reaches 0, the entry is destroyed. It is important
   1170  * that only one unlink is made for each Curl_resolv() call.
   1171  *
   1172  * May be called with 'data' == NULL for global cache.
   1173  */
   1174 void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns)
   1175 {
   1176   if(*pdns) {
   1177     struct Curl_dnscache *dnscache = dnscache_get(data);
   1178     struct Curl_dns_entry *dns = *pdns;
   1179     *pdns = NULL;
   1180     dnscache_lock(data, dnscache);
   1181     dns->refcount--;
   1182     if(dns->refcount == 0)
   1183       dnscache_entry_free(dns);
   1184     dnscache_unlock(data, dnscache);
   1185   }
   1186 }
   1187 
   1188 static void dnscache_entry_dtor(void *entry)
   1189 {
   1190   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) entry;
   1191   DEBUGASSERT(dns && (dns->refcount > 0));
   1192   dns->refcount--;
   1193   if(dns->refcount == 0)
   1194     dnscache_entry_free(dns);
   1195 }
   1196 
   1197 /*
   1198  * Curl_dnscache_init() inits a new DNS cache.
   1199  */
   1200 void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size)
   1201 {
   1202   Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare,
   1203                  dnscache_entry_dtor);
   1204 }
   1205 
   1206 void Curl_dnscache_destroy(struct Curl_dnscache *dns)
   1207 {
   1208   Curl_hash_destroy(&dns->entries);
   1209 }
   1210 
   1211 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
   1212 {
   1213   struct Curl_dnscache *dnscache = dnscache_get(data);
   1214   struct curl_slist *hostp;
   1215 
   1216   if(!dnscache)
   1217     return CURLE_FAILED_INIT;
   1218 
   1219   /* Default is no wildcard found */
   1220   data->state.wildcard_resolve = FALSE;
   1221 
   1222   for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
   1223     char entry_id[MAX_HOSTCACHE_LEN];
   1224     const char *host = hostp->data;
   1225     struct Curl_str source;
   1226     if(!host)
   1227       continue;
   1228     if(*host == '-') {
   1229       curl_off_t num = 0;
   1230       size_t entry_len;
   1231       host++;
   1232       if(!curlx_str_single(&host, '[')) {
   1233         if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
   1234            curlx_str_single(&host, ']') ||
   1235            curlx_str_single(&host, ':'))
   1236           continue;
   1237       }
   1238       else {
   1239         if(curlx_str_until(&host, &source, 4096, ':') ||
   1240            curlx_str_single(&host, ':')) {
   1241           continue;
   1242         }
   1243       }
   1244 
   1245       if(!curlx_str_number(&host, &num, 0xffff)) {
   1246         /* Create an entry id, based upon the hostname and port */
   1247         entry_len = create_dnscache_id(curlx_str(&source),
   1248                                        curlx_strlen(&source), (int)num,
   1249                                        entry_id, sizeof(entry_id));
   1250         dnscache_lock(data, dnscache);
   1251         /* delete entry, ignore if it did not exist */
   1252         Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
   1253         dnscache_unlock(data, dnscache);
   1254       }
   1255     }
   1256     else {
   1257       struct Curl_dns_entry *dns;
   1258       struct Curl_addrinfo *head = NULL, *tail = NULL;
   1259       size_t entry_len;
   1260       char address[64];
   1261 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
   1262       const char *addresses = NULL;
   1263 #endif
   1264       curl_off_t port = 0;
   1265       bool permanent = TRUE;
   1266       bool error = TRUE;
   1267 
   1268       if(*host == '+') {
   1269         host++;
   1270         permanent = FALSE;
   1271       }
   1272       if(!curlx_str_single(&host, '[')) {
   1273         if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
   1274            curlx_str_single(&host, ']'))
   1275           continue;
   1276       }
   1277       else {
   1278         if(curlx_str_until(&host, &source, 4096, ':'))
   1279           continue;
   1280       }
   1281       if(curlx_str_single(&host, ':') ||
   1282          curlx_str_number(&host, &port, 0xffff) ||
   1283          curlx_str_single(&host, ':'))
   1284         goto err;
   1285 
   1286 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
   1287       addresses = host;
   1288 #endif
   1289 
   1290       /* start the address section */
   1291       while(*host) {
   1292         struct Curl_str target;
   1293         struct Curl_addrinfo *ai;
   1294 
   1295         if(!curlx_str_single(&host, '[')) {
   1296           if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') ||
   1297              curlx_str_single(&host, ']'))
   1298             goto err;
   1299         }
   1300         else {
   1301           if(curlx_str_until(&host, &target, 4096, ',')) {
   1302             if(curlx_str_single(&host, ','))
   1303               goto err;
   1304             /* survive nothing but just a comma */
   1305             continue;
   1306           }
   1307         }
   1308 #ifndef USE_IPV6
   1309         if(memchr(target.str, ':', target.len)) {
   1310           infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
   1311                 address);
   1312           if(curlx_str_single(&host, ','))
   1313             goto err;
   1314           continue;
   1315         }
   1316 #endif
   1317 
   1318         if(curlx_strlen(&target) >= sizeof(address))
   1319           goto err;
   1320 
   1321         memcpy(address, curlx_str(&target), curlx_strlen(&target));
   1322         address[curlx_strlen(&target)] = '\0';
   1323 
   1324         ai = Curl_str2addr(address, (int)port);
   1325         if(!ai) {
   1326           infof(data, "Resolve address '%s' found illegal", address);
   1327           goto err;
   1328         }
   1329 
   1330         if(tail) {
   1331           tail->ai_next = ai;
   1332           tail = tail->ai_next;
   1333         }
   1334         else {
   1335           head = tail = ai;
   1336         }
   1337         if(curlx_str_single(&host, ','))
   1338           break;
   1339       }
   1340 
   1341       if(!head)
   1342         goto err;
   1343 
   1344       error = FALSE;
   1345 err:
   1346       if(error) {
   1347         failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'",
   1348               hostp->data);
   1349         Curl_freeaddrinfo(head);
   1350         return CURLE_SETOPT_OPTION_SYNTAX;
   1351       }
   1352 
   1353       /* Create an entry id, based upon the hostname and port */
   1354       entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source),
   1355                                      (int)port,
   1356                                      entry_id, sizeof(entry_id));
   1357 
   1358       dnscache_lock(data, dnscache);
   1359 
   1360       /* See if it is already in our dns cache */
   1361       dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
   1362 
   1363       if(dns) {
   1364         infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T
   1365               " - old addresses discarded", (int)curlx_strlen(&source),
   1366               curlx_str(&source), port);
   1367         /* delete old entry, there are two reasons for this
   1368          1. old entry may have different addresses.
   1369          2. even if entry with correct addresses is already in the cache,
   1370             but if it is close to expire, then by the time next http
   1371             request is made, it can get expired and pruned because old
   1372             entry is not necessarily marked as permanent.
   1373          3. when adding a non-permanent entry, we want it to remove and
   1374             replace an existing permanent entry.
   1375          4. when adding a non-permanent entry, we want it to get a "fresh"
   1376             timeout that starts _now_. */
   1377 
   1378         Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
   1379       }
   1380 
   1381       /* put this new host in the cache */
   1382       dns = dnscache_add_addr(data, dnscache, head, curlx_str(&source),
   1383                               curlx_strlen(&source), (int)port, permanent);
   1384       if(dns) {
   1385         /* release the returned reference; the cache itself will keep the
   1386          * entry alive: */
   1387         dns->refcount--;
   1388       }
   1389 
   1390       dnscache_unlock(data, dnscache);
   1391 
   1392       if(!dns)
   1393         return CURLE_OUT_OF_MEMORY;
   1394 
   1395 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   1396       infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s",
   1397             (int)curlx_strlen(&source), curlx_str(&source), port, addresses,
   1398             permanent ? "" : " (non-permanent)");
   1399 #endif
   1400 
   1401       /* Wildcard hostname */
   1402       if(curlx_str_casecompare(&source, "*")) {
   1403         infof(data, "RESOLVE *:%" CURL_FORMAT_CURL_OFF_T " using wildcard",
   1404               port);
   1405         data->state.wildcard_resolve = TRUE;
   1406       }
   1407     }
   1408   }
   1409   data->state.resolve = NULL; /* dealt with now */
   1410 
   1411   return CURLE_OK;
   1412 }
   1413 
   1414 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   1415 static void show_resolve_info(struct Curl_easy *data,
   1416                               struct Curl_dns_entry *dns)
   1417 {
   1418   struct Curl_addrinfo *a;
   1419   CURLcode result = CURLE_OK;
   1420 #ifdef CURLRES_IPV6
   1421   struct dynbuf out[2];
   1422 #else
   1423   struct dynbuf out[1];
   1424 #endif
   1425   DEBUGASSERT(data);
   1426   DEBUGASSERT(dns);
   1427 
   1428   if(!data->set.verbose ||
   1429      /* ignore no name or numerical IP addresses */
   1430      !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname))
   1431     return;
   1432 
   1433   a = dns->addr;
   1434 
   1435   infof(data, "Host %s:%d was resolved.",
   1436         (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport);
   1437 
   1438   curlx_dyn_init(&out[0], 1024);
   1439 #ifdef CURLRES_IPV6
   1440   curlx_dyn_init(&out[1], 1024);
   1441 #endif
   1442 
   1443   while(a) {
   1444     if(
   1445 #ifdef CURLRES_IPV6
   1446        a->ai_family == PF_INET6 ||
   1447 #endif
   1448        a->ai_family == PF_INET) {
   1449       char buf[MAX_IPADR_LEN];
   1450       struct dynbuf *d = &out[(a->ai_family != PF_INET)];
   1451       Curl_printable_address(a, buf, sizeof(buf));
   1452       if(curlx_dyn_len(d))
   1453         result = curlx_dyn_addn(d, ", ", 2);
   1454       if(!result)
   1455         result = curlx_dyn_add(d, buf);
   1456       if(result) {
   1457         infof(data, "too many IP, cannot show");
   1458         goto fail;
   1459       }
   1460     }
   1461     a = a->ai_next;
   1462   }
   1463 
   1464 #ifdef CURLRES_IPV6
   1465   infof(data, "IPv6: %s",
   1466         (curlx_dyn_len(&out[1]) ? curlx_dyn_ptr(&out[1]) : "(none)"));
   1467 #endif
   1468   infof(data, "IPv4: %s",
   1469         (curlx_dyn_len(&out[0]) ? curlx_dyn_ptr(&out[0]) : "(none)"));
   1470 
   1471 fail:
   1472   curlx_dyn_free(&out[0]);
   1473 #ifdef CURLRES_IPV6
   1474   curlx_dyn_free(&out[1]);
   1475 #endif
   1476 }
   1477 #endif
   1478 
   1479 #ifdef USE_CURL_ASYNC
   1480 CURLcode Curl_resolv_check(struct Curl_easy *data,
   1481                            struct Curl_dns_entry **dns)
   1482 {
   1483   CURLcode result;
   1484 
   1485   /* If async resolving is ongoing, this must be set */
   1486   if(!data->state.async.hostname)
   1487     return CURLE_FAILED_INIT;
   1488 
   1489   /* check if we have the name resolved by now (from someone else) */
   1490   *dns = Curl_dnscache_get(data, data->state.async.hostname,
   1491                            data->state.async.port,
   1492                            data->state.async.ip_version);
   1493   if(*dns) {
   1494     /* Tell a possibly async resolver we no longer need the results. */
   1495     infof(data, "Hostname '%s' was found in DNS cache",
   1496           data->state.async.hostname);
   1497     Curl_async_shutdown(data);
   1498     data->state.async.dns = *dns;
   1499     data->state.async.done = TRUE;
   1500     return CURLE_OK;
   1501   }
   1502 
   1503 #ifndef CURL_DISABLE_DOH
   1504   if(data->conn->bits.doh) {
   1505     result = Curl_doh_is_resolved(data, dns);
   1506   }
   1507   else
   1508 #endif
   1509   result = Curl_async_is_resolved(data, dns);
   1510   if(*dns)
   1511     show_resolve_info(data, *dns);
   1512   return result;
   1513 }
   1514 #endif
   1515 
   1516 int Curl_resolv_getsock(struct Curl_easy *data,
   1517                         curl_socket_t *socks)
   1518 {
   1519 #ifdef CURLRES_ASYNCH
   1520 #ifndef CURL_DISABLE_DOH
   1521   if(data->conn->bits.doh)
   1522     /* nothing to wait for during DoH resolve, those handles have their own
   1523        sockets */
   1524     return GETSOCK_BLANK;
   1525 #endif
   1526   return Curl_async_getsock(data, socks);
   1527 #else
   1528   (void)data;
   1529   (void)socks;
   1530   return GETSOCK_BLANK;
   1531 #endif
   1532 }
   1533 
   1534 /* Call this function after Curl_connect() has returned async=TRUE and
   1535    then a successful name resolve has been received.
   1536 
   1537    Note: this function disconnects and frees the conn data in case of
   1538    resolve failure */
   1539 CURLcode Curl_once_resolved(struct Curl_easy *data,
   1540                             struct Curl_dns_entry *dns,
   1541                             bool *protocol_done)
   1542 {
   1543   CURLcode result;
   1544   struct connectdata *conn = data->conn;
   1545 
   1546 #ifdef USE_CURL_ASYNC
   1547   if(data->state.async.dns) {
   1548     DEBUGASSERT(data->state.async.dns == dns);
   1549     data->state.async.dns = NULL;
   1550   }
   1551 #endif
   1552 
   1553   result = Curl_setup_conn(data, dns, protocol_done);
   1554 
   1555   if(result) {
   1556     Curl_detach_connection(data);
   1557     Curl_conn_terminate(data, conn, TRUE);
   1558   }
   1559   return result;
   1560 }
   1561 
   1562 /*
   1563  * Curl_resolver_error() calls failf() with the appropriate message after a
   1564  * resolve error
   1565  */
   1566 
   1567 #ifdef USE_CURL_ASYNC
   1568 CURLcode Curl_resolver_error(struct Curl_easy *data)
   1569 {
   1570   struct connectdata *conn = data->conn;
   1571   const char *host_or_proxy = "host";
   1572   const char *name = conn->host.dispname;
   1573   CURLcode result = CURLE_COULDNT_RESOLVE_HOST;
   1574 
   1575 #ifndef CURL_DISABLE_PROXY
   1576   if(conn->bits.proxy) {
   1577     host_or_proxy = "proxy";
   1578     result = CURLE_COULDNT_RESOLVE_PROXY;
   1579     name = conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
   1580       conn->http_proxy.host.dispname;
   1581   }
   1582 #endif
   1583 
   1584   failf(data, "Could not resolve %s: %s", host_or_proxy, name);
   1585   return result;
   1586 }
   1587 #endif /* USE_CURL_ASYNC */