113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
2813498266Sopenharmony_ci#include <netinet/in.h>
2913498266Sopenharmony_ci#endif
3013498266Sopenharmony_ci#ifdef HAVE_NETINET_IN6_H
3113498266Sopenharmony_ci#include <netinet/in6.h>
3213498266Sopenharmony_ci#endif
3313498266Sopenharmony_ci#ifdef HAVE_NETDB_H
3413498266Sopenharmony_ci#include <netdb.h>
3513498266Sopenharmony_ci#endif
3613498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H
3713498266Sopenharmony_ci#include <arpa/inet.h>
3813498266Sopenharmony_ci#endif
3913498266Sopenharmony_ci#ifdef __VMS
4013498266Sopenharmony_ci#include <in.h>
4113498266Sopenharmony_ci#include <inet.h>
4213498266Sopenharmony_ci#endif
4313498266Sopenharmony_ci
4413498266Sopenharmony_ci#include <setjmp.h>
4513498266Sopenharmony_ci#include <signal.h>
4613498266Sopenharmony_ci
4713498266Sopenharmony_ci#include "urldata.h"
4813498266Sopenharmony_ci#include "sendf.h"
4913498266Sopenharmony_ci#include "hostip.h"
5013498266Sopenharmony_ci#include "hash.h"
5113498266Sopenharmony_ci#include "rand.h"
5213498266Sopenharmony_ci#include "share.h"
5313498266Sopenharmony_ci#include "url.h"
5413498266Sopenharmony_ci#include "inet_ntop.h"
5513498266Sopenharmony_ci#include "inet_pton.h"
5613498266Sopenharmony_ci#include "multiif.h"
5713498266Sopenharmony_ci#include "doh.h"
5813498266Sopenharmony_ci#include "warnless.h"
5913498266Sopenharmony_ci#include "strcase.h"
6013498266Sopenharmony_ci#include "easy_lock.h"
6113498266Sopenharmony_ci/* The last 3 #include files should be in this order */
6213498266Sopenharmony_ci#include "curl_printf.h"
6313498266Sopenharmony_ci#include "curl_memory.h"
6413498266Sopenharmony_ci#include "memdebug.h"
6513498266Sopenharmony_ci
6613498266Sopenharmony_ci#if defined(CURLRES_SYNCH) &&                   \
6713498266Sopenharmony_ci  defined(HAVE_ALARM) &&                        \
6813498266Sopenharmony_ci  defined(SIGALRM) &&                           \
6913498266Sopenharmony_ci  defined(HAVE_SIGSETJMP) &&                    \
7013498266Sopenharmony_ci  defined(GLOBAL_INIT_IS_THREADSAFE)
7113498266Sopenharmony_ci/* alarm-based timeouts can only be used with all the dependencies satisfied */
7213498266Sopenharmony_ci#define USE_ALARM_TIMEOUT
7313498266Sopenharmony_ci#endif
7413498266Sopenharmony_ci
7513498266Sopenharmony_ci#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
7613498266Sopenharmony_ci
7713498266Sopenharmony_ci#define MAX_DNS_CACHE_SIZE 29999
7813498266Sopenharmony_ci
7913498266Sopenharmony_ci/*
8013498266Sopenharmony_ci * hostip.c explained
8113498266Sopenharmony_ci * ==================
8213498266Sopenharmony_ci *
8313498266Sopenharmony_ci * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
8413498266Sopenharmony_ci * source file are these:
8513498266Sopenharmony_ci *
8613498266Sopenharmony_ci * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
8713498266Sopenharmony_ci * that. The host may not be able to resolve IPv6, but we don't really have to
8813498266Sopenharmony_ci * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
8913498266Sopenharmony_ci * defined.
9013498266Sopenharmony_ci *
9113498266Sopenharmony_ci * CURLRES_ARES - is defined if libcurl is built to use c-ares for
9213498266Sopenharmony_ci * asynchronous name resolves. This can be Windows or *nix.
9313498266Sopenharmony_ci *
9413498266Sopenharmony_ci * CURLRES_THREADED - is defined if libcurl is built to run under (native)
9513498266Sopenharmony_ci * Windows, and then the name resolve will be done in a new thread, and the
9613498266Sopenharmony_ci * supported API will be the same as for ares-builds.
9713498266Sopenharmony_ci *
9813498266Sopenharmony_ci * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
9913498266Sopenharmony_ci * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
10013498266Sopenharmony_ci * defined.
10113498266Sopenharmony_ci *
10213498266Sopenharmony_ci * The host*.c sources files are split up like this:
10313498266Sopenharmony_ci *
10413498266Sopenharmony_ci * hostip.c   - method-independent resolver functions and utility functions
10513498266Sopenharmony_ci * hostasyn.c - functions for asynchronous name resolves
10613498266Sopenharmony_ci * hostsyn.c  - functions for synchronous name resolves
10713498266Sopenharmony_ci * hostip4.c  - IPv4 specific functions
10813498266Sopenharmony_ci * hostip6.c  - IPv6 specific functions
10913498266Sopenharmony_ci *
11013498266Sopenharmony_ci * The two asynchronous name resolver backends are implemented in:
11113498266Sopenharmony_ci * asyn-ares.c   - functions for ares-using name resolves
11213498266Sopenharmony_ci * asyn-thread.c - functions for threaded name resolves
11313498266Sopenharmony_ci
11413498266Sopenharmony_ci * The hostip.h is the united header file for all this. It defines the
11513498266Sopenharmony_ci * CURLRES_* defines based on the config*.h and curl_setup.h defines.
11613498266Sopenharmony_ci */
11713498266Sopenharmony_ci
11813498266Sopenharmony_cistatic void freednsentry(void *freethis);
11913498266Sopenharmony_ci
12013498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
12113498266Sopenharmony_cistatic void show_resolve_info(struct Curl_easy *data,
12213498266Sopenharmony_ci                              struct Curl_dns_entry *dns);
12313498266Sopenharmony_ci#else
12413498266Sopenharmony_ci#define show_resolve_info(x,y) Curl_nop_stmt
12513498266Sopenharmony_ci#endif
12613498266Sopenharmony_ci
12713498266Sopenharmony_ci/*
12813498266Sopenharmony_ci * Curl_printable_address() stores a printable version of the 1st address
12913498266Sopenharmony_ci * given in the 'ai' argument. The result will be stored in the buf that is
13013498266Sopenharmony_ci * bufsize bytes big.
13113498266Sopenharmony_ci *
13213498266Sopenharmony_ci * If the conversion fails, the target buffer is empty.
13313498266Sopenharmony_ci */
13413498266Sopenharmony_civoid Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
13513498266Sopenharmony_ci                            size_t bufsize)
13613498266Sopenharmony_ci{
13713498266Sopenharmony_ci  DEBUGASSERT(bufsize);
13813498266Sopenharmony_ci  buf[0] = 0;
13913498266Sopenharmony_ci
14013498266Sopenharmony_ci  switch(ai->ai_family) {
14113498266Sopenharmony_ci  case AF_INET: {
14213498266Sopenharmony_ci    const struct sockaddr_in *sa4 = (const void *)ai->ai_addr;
14313498266Sopenharmony_ci    const struct in_addr *ipaddr4 = &sa4->sin_addr;
14413498266Sopenharmony_ci    (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
14513498266Sopenharmony_ci    break;
14613498266Sopenharmony_ci  }
14713498266Sopenharmony_ci#ifdef ENABLE_IPV6
14813498266Sopenharmony_ci  case AF_INET6: {
14913498266Sopenharmony_ci    const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr;
15013498266Sopenharmony_ci    const struct in6_addr *ipaddr6 = &sa6->sin6_addr;
15113498266Sopenharmony_ci    (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
15213498266Sopenharmony_ci    break;
15313498266Sopenharmony_ci  }
15413498266Sopenharmony_ci#endif
15513498266Sopenharmony_ci  default:
15613498266Sopenharmony_ci    break;
15713498266Sopenharmony_ci  }
15813498266Sopenharmony_ci}
15913498266Sopenharmony_ci
16013498266Sopenharmony_ci/*
16113498266Sopenharmony_ci * Create a hostcache id string for the provided host + port, to be used by
16213498266Sopenharmony_ci * the DNS caching. Without alloc. Return length of the id string.
16313498266Sopenharmony_ci */
16413498266Sopenharmony_cistatic size_t
16513498266Sopenharmony_cicreate_hostcache_id(const char *name,
16613498266Sopenharmony_ci                    size_t nlen, /* 0 or actual name length */
16713498266Sopenharmony_ci                    int port, char *ptr, size_t buflen)
16813498266Sopenharmony_ci{
16913498266Sopenharmony_ci  size_t len = nlen ? nlen : strlen(name);
17013498266Sopenharmony_ci  size_t olen = 0;
17113498266Sopenharmony_ci  DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
17213498266Sopenharmony_ci  if(len > (buflen - 7))
17313498266Sopenharmony_ci    len = buflen - 7;
17413498266Sopenharmony_ci  /* store and lower case the name */
17513498266Sopenharmony_ci  while(len--) {
17613498266Sopenharmony_ci    *ptr++ = Curl_raw_tolower(*name++);
17713498266Sopenharmony_ci    olen++;
17813498266Sopenharmony_ci  }
17913498266Sopenharmony_ci  olen += msnprintf(ptr, 7, ":%u", port);
18013498266Sopenharmony_ci  return olen;
18113498266Sopenharmony_ci}
18213498266Sopenharmony_ci
18313498266Sopenharmony_cistruct hostcache_prune_data {
18413498266Sopenharmony_ci  time_t now;
18513498266Sopenharmony_ci  time_t oldest; /* oldest time in cache not pruned. */
18613498266Sopenharmony_ci  int cache_timeout;
18713498266Sopenharmony_ci};
18813498266Sopenharmony_ci
18913498266Sopenharmony_ci/*
19013498266Sopenharmony_ci * This function is set as a callback to be called for every entry in the DNS
19113498266Sopenharmony_ci * cache when we want to prune old unused entries.
19213498266Sopenharmony_ci *
19313498266Sopenharmony_ci * Returning non-zero means remove the entry, return 0 to keep it in the
19413498266Sopenharmony_ci * cache.
19513498266Sopenharmony_ci */
19613498266Sopenharmony_cistatic int
19713498266Sopenharmony_cihostcache_timestamp_remove(void *datap, void *hc)
19813498266Sopenharmony_ci{
19913498266Sopenharmony_ci  struct hostcache_prune_data *prune =
20013498266Sopenharmony_ci    (struct hostcache_prune_data *) datap;
20113498266Sopenharmony_ci  struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
20213498266Sopenharmony_ci
20313498266Sopenharmony_ci  if(c->timestamp) {
20413498266Sopenharmony_ci    /* age in seconds */
20513498266Sopenharmony_ci    time_t age = prune->now - c->timestamp;
20613498266Sopenharmony_ci    if(age >= prune->cache_timeout)
20713498266Sopenharmony_ci      return TRUE;
20813498266Sopenharmony_ci    if(age > prune->oldest)
20913498266Sopenharmony_ci      prune->oldest = age;
21013498266Sopenharmony_ci  }
21113498266Sopenharmony_ci  return FALSE;
21213498266Sopenharmony_ci}
21313498266Sopenharmony_ci
21413498266Sopenharmony_ci/*
21513498266Sopenharmony_ci * Prune the DNS cache. This assumes that a lock has already been taken.
21613498266Sopenharmony_ci * Returns the 'age' of the oldest still kept entry.
21713498266Sopenharmony_ci */
21813498266Sopenharmony_cistatic time_t
21913498266Sopenharmony_cihostcache_prune(struct Curl_hash *hostcache, int cache_timeout,
22013498266Sopenharmony_ci                time_t now)
22113498266Sopenharmony_ci{
22213498266Sopenharmony_ci  struct hostcache_prune_data user;
22313498266Sopenharmony_ci
22413498266Sopenharmony_ci  user.cache_timeout = cache_timeout;
22513498266Sopenharmony_ci  user.now = now;
22613498266Sopenharmony_ci  user.oldest = 0;
22713498266Sopenharmony_ci
22813498266Sopenharmony_ci  Curl_hash_clean_with_criterium(hostcache,
22913498266Sopenharmony_ci                                 (void *) &user,
23013498266Sopenharmony_ci                                 hostcache_timestamp_remove);
23113498266Sopenharmony_ci
23213498266Sopenharmony_ci  return user.oldest;
23313498266Sopenharmony_ci}
23413498266Sopenharmony_ci
23513498266Sopenharmony_ci/*
23613498266Sopenharmony_ci * Library-wide function for pruning the DNS cache. This function takes and
23713498266Sopenharmony_ci * returns the appropriate locks.
23813498266Sopenharmony_ci */
23913498266Sopenharmony_civoid Curl_hostcache_prune(struct Curl_easy *data)
24013498266Sopenharmony_ci{
24113498266Sopenharmony_ci  time_t now;
24213498266Sopenharmony_ci  /* the timeout may be set -1 (forever) */
24313498266Sopenharmony_ci  int timeout = data->set.dns_cache_timeout;
24413498266Sopenharmony_ci
24513498266Sopenharmony_ci  if(!data->dns.hostcache)
24613498266Sopenharmony_ci    /* NULL hostcache means we can't do it */
24713498266Sopenharmony_ci    return;
24813498266Sopenharmony_ci
24913498266Sopenharmony_ci  if(data->share)
25013498266Sopenharmony_ci    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
25113498266Sopenharmony_ci
25213498266Sopenharmony_ci  time(&now);
25313498266Sopenharmony_ci
25413498266Sopenharmony_ci  do {
25513498266Sopenharmony_ci    /* Remove outdated and unused entries from the hostcache */
25613498266Sopenharmony_ci    time_t oldest = hostcache_prune(data->dns.hostcache, timeout, now);
25713498266Sopenharmony_ci
25813498266Sopenharmony_ci    if(oldest < INT_MAX)
25913498266Sopenharmony_ci      timeout = (int)oldest; /* we know it fits */
26013498266Sopenharmony_ci    else
26113498266Sopenharmony_ci      timeout = INT_MAX - 1;
26213498266Sopenharmony_ci
26313498266Sopenharmony_ci    /* if the cache size is still too big, use the oldest age as new
26413498266Sopenharmony_ci       prune limit */
26513498266Sopenharmony_ci  } while(timeout && (data->dns.hostcache->size > MAX_DNS_CACHE_SIZE));
26613498266Sopenharmony_ci
26713498266Sopenharmony_ci  if(data->share)
26813498266Sopenharmony_ci    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
26913498266Sopenharmony_ci}
27013498266Sopenharmony_ci
27113498266Sopenharmony_ci#ifdef USE_ALARM_TIMEOUT
27213498266Sopenharmony_ci/* Beware this is a global and unique instance. This is used to store the
27313498266Sopenharmony_ci   return address that we can jump back to from inside a signal handler. This
27413498266Sopenharmony_ci   is not thread-safe stuff. */
27513498266Sopenharmony_cistatic sigjmp_buf curl_jmpenv;
27613498266Sopenharmony_cistatic curl_simple_lock curl_jmpenv_lock;
27713498266Sopenharmony_ci#endif
27813498266Sopenharmony_ci
27913498266Sopenharmony_ci/* lookup address, returns entry if found and not stale */
28013498266Sopenharmony_cistatic struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
28113498266Sopenharmony_ci                                         const char *hostname,
28213498266Sopenharmony_ci                                         int port)
28313498266Sopenharmony_ci{
28413498266Sopenharmony_ci  struct Curl_dns_entry *dns = NULL;
28513498266Sopenharmony_ci  char entry_id[MAX_HOSTCACHE_LEN];
28613498266Sopenharmony_ci
28713498266Sopenharmony_ci  /* Create an entry id, based upon the hostname and port */
28813498266Sopenharmony_ci  size_t entry_len = create_hostcache_id(hostname, 0, port,
28913498266Sopenharmony_ci                                         entry_id, sizeof(entry_id));
29013498266Sopenharmony_ci
29113498266Sopenharmony_ci  /* See if its already in our dns cache */
29213498266Sopenharmony_ci  dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
29313498266Sopenharmony_ci
29413498266Sopenharmony_ci  /* No entry found in cache, check if we might have a wildcard entry */
29513498266Sopenharmony_ci  if(!dns && data->state.wildcard_resolve) {
29613498266Sopenharmony_ci    entry_len = create_hostcache_id("*", 1, port, entry_id, sizeof(entry_id));
29713498266Sopenharmony_ci
29813498266Sopenharmony_ci    /* See if it's already in our dns cache */
29913498266Sopenharmony_ci    dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
30013498266Sopenharmony_ci  }
30113498266Sopenharmony_ci
30213498266Sopenharmony_ci  if(dns && (data->set.dns_cache_timeout != -1)) {
30313498266Sopenharmony_ci    /* See whether the returned entry is stale. Done before we release lock */
30413498266Sopenharmony_ci    struct hostcache_prune_data user;
30513498266Sopenharmony_ci
30613498266Sopenharmony_ci    time(&user.now);
30713498266Sopenharmony_ci    user.cache_timeout = data->set.dns_cache_timeout;
30813498266Sopenharmony_ci    user.oldest = 0;
30913498266Sopenharmony_ci
31013498266Sopenharmony_ci    if(hostcache_timestamp_remove(&user, dns)) {
31113498266Sopenharmony_ci      infof(data, "Hostname in DNS cache was stale, zapped");
31213498266Sopenharmony_ci      dns = NULL; /* the memory deallocation is being handled by the hash */
31313498266Sopenharmony_ci      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
31413498266Sopenharmony_ci    }
31513498266Sopenharmony_ci  }
31613498266Sopenharmony_ci
31713498266Sopenharmony_ci  /* See if the returned entry matches the required resolve mode */
31813498266Sopenharmony_ci  if(dns && data->conn->ip_version != CURL_IPRESOLVE_WHATEVER) {
31913498266Sopenharmony_ci    int pf = PF_INET;
32013498266Sopenharmony_ci    bool found = false;
32113498266Sopenharmony_ci    struct Curl_addrinfo *addr = dns->addr;
32213498266Sopenharmony_ci
32313498266Sopenharmony_ci#ifdef PF_INET6
32413498266Sopenharmony_ci    if(data->conn->ip_version == CURL_IPRESOLVE_V6)
32513498266Sopenharmony_ci      pf = PF_INET6;
32613498266Sopenharmony_ci#endif
32713498266Sopenharmony_ci
32813498266Sopenharmony_ci    while(addr) {
32913498266Sopenharmony_ci      if(addr->ai_family == pf) {
33013498266Sopenharmony_ci        found = true;
33113498266Sopenharmony_ci        break;
33213498266Sopenharmony_ci      }
33313498266Sopenharmony_ci      addr = addr->ai_next;
33413498266Sopenharmony_ci    }
33513498266Sopenharmony_ci
33613498266Sopenharmony_ci    if(!found) {
33713498266Sopenharmony_ci      infof(data, "Hostname in DNS cache doesn't have needed family, zapped");
33813498266Sopenharmony_ci      dns = NULL; /* the memory deallocation is being handled by the hash */
33913498266Sopenharmony_ci      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
34013498266Sopenharmony_ci    }
34113498266Sopenharmony_ci  }
34213498266Sopenharmony_ci  return dns;
34313498266Sopenharmony_ci}
34413498266Sopenharmony_ci
34513498266Sopenharmony_ci/*
34613498266Sopenharmony_ci * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
34713498266Sopenharmony_ci *
34813498266Sopenharmony_ci * Curl_resolv() checks initially and multi_runsingle() checks each time
34913498266Sopenharmony_ci * it discovers the handle in the state WAITRESOLVE whether the hostname
35013498266Sopenharmony_ci * has already been resolved and the address has already been stored in
35113498266Sopenharmony_ci * the DNS cache. This short circuits waiting for a lot of pending
35213498266Sopenharmony_ci * lookups for the same hostname requested by different handles.
35313498266Sopenharmony_ci *
35413498266Sopenharmony_ci * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
35513498266Sopenharmony_ci *
35613498266Sopenharmony_ci * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
35713498266Sopenharmony_ci * use, or we'll leak memory!
35813498266Sopenharmony_ci */
35913498266Sopenharmony_cistruct Curl_dns_entry *
36013498266Sopenharmony_ciCurl_fetch_addr(struct Curl_easy *data,
36113498266Sopenharmony_ci                const char *hostname,
36213498266Sopenharmony_ci                int port)
36313498266Sopenharmony_ci{
36413498266Sopenharmony_ci  struct Curl_dns_entry *dns = NULL;
36513498266Sopenharmony_ci
36613498266Sopenharmony_ci  if(data->share)
36713498266Sopenharmony_ci    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
36813498266Sopenharmony_ci
36913498266Sopenharmony_ci  dns = fetch_addr(data, hostname, port);
37013498266Sopenharmony_ci
37113498266Sopenharmony_ci  if(dns)
37213498266Sopenharmony_ci    dns->inuse++; /* we use it! */
37313498266Sopenharmony_ci
37413498266Sopenharmony_ci  if(data->share)
37513498266Sopenharmony_ci    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
37613498266Sopenharmony_ci
37713498266Sopenharmony_ci  return dns;
37813498266Sopenharmony_ci}
37913498266Sopenharmony_ci
38013498266Sopenharmony_ci#ifndef CURL_DISABLE_SHUFFLE_DNS
38113498266Sopenharmony_ci/*
38213498266Sopenharmony_ci * Return # of addresses in a Curl_addrinfo struct
38313498266Sopenharmony_ci */
38413498266Sopenharmony_cistatic int num_addresses(const struct Curl_addrinfo *addr)
38513498266Sopenharmony_ci{
38613498266Sopenharmony_ci  int i = 0;
38713498266Sopenharmony_ci  while(addr) {
38813498266Sopenharmony_ci    addr = addr->ai_next;
38913498266Sopenharmony_ci    i++;
39013498266Sopenharmony_ci  }
39113498266Sopenharmony_ci  return i;
39213498266Sopenharmony_ci}
39313498266Sopenharmony_ci
39413498266Sopenharmony_ciUNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
39513498266Sopenharmony_ci                                    struct Curl_addrinfo **addr);
39613498266Sopenharmony_ci/*
39713498266Sopenharmony_ci * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
39813498266Sopenharmony_ci * struct by re-linking its linked list.
39913498266Sopenharmony_ci *
40013498266Sopenharmony_ci * The addr argument should be the address of a pointer to the head node of a
40113498266Sopenharmony_ci * `Curl_addrinfo` list and it will be modified to point to the new head after
40213498266Sopenharmony_ci * shuffling.
40313498266Sopenharmony_ci *
40413498266Sopenharmony_ci * Not declared static only to make it easy to use in a unit test!
40513498266Sopenharmony_ci *
40613498266Sopenharmony_ci * @unittest: 1608
40713498266Sopenharmony_ci */
40813498266Sopenharmony_ciUNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
40913498266Sopenharmony_ci                                    struct Curl_addrinfo **addr)
41013498266Sopenharmony_ci{
41113498266Sopenharmony_ci  CURLcode result = CURLE_OK;
41213498266Sopenharmony_ci  const int num_addrs = num_addresses(*addr);
41313498266Sopenharmony_ci
41413498266Sopenharmony_ci  if(num_addrs > 1) {
41513498266Sopenharmony_ci    struct Curl_addrinfo **nodes;
41613498266Sopenharmony_ci    infof(data, "Shuffling %i addresses", num_addrs);
41713498266Sopenharmony_ci
41813498266Sopenharmony_ci    nodes = malloc(num_addrs*sizeof(*nodes));
41913498266Sopenharmony_ci    if(nodes) {
42013498266Sopenharmony_ci      int i;
42113498266Sopenharmony_ci      unsigned int *rnd;
42213498266Sopenharmony_ci      const size_t rnd_size = num_addrs * sizeof(*rnd);
42313498266Sopenharmony_ci
42413498266Sopenharmony_ci      /* build a plain array of Curl_addrinfo pointers */
42513498266Sopenharmony_ci      nodes[0] = *addr;
42613498266Sopenharmony_ci      for(i = 1; i < num_addrs; i++) {
42713498266Sopenharmony_ci        nodes[i] = nodes[i-1]->ai_next;
42813498266Sopenharmony_ci      }
42913498266Sopenharmony_ci
43013498266Sopenharmony_ci      rnd = malloc(rnd_size);
43113498266Sopenharmony_ci      if(rnd) {
43213498266Sopenharmony_ci        /* Fisher-Yates shuffle */
43313498266Sopenharmony_ci        if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
43413498266Sopenharmony_ci          struct Curl_addrinfo *swap_tmp;
43513498266Sopenharmony_ci          for(i = num_addrs - 1; i > 0; i--) {
43613498266Sopenharmony_ci            swap_tmp = nodes[rnd[i] % (i + 1)];
43713498266Sopenharmony_ci            nodes[rnd[i] % (i + 1)] = nodes[i];
43813498266Sopenharmony_ci            nodes[i] = swap_tmp;
43913498266Sopenharmony_ci          }
44013498266Sopenharmony_ci
44113498266Sopenharmony_ci          /* relink list in the new order */
44213498266Sopenharmony_ci          for(i = 1; i < num_addrs; i++) {
44313498266Sopenharmony_ci            nodes[i-1]->ai_next = nodes[i];
44413498266Sopenharmony_ci          }
44513498266Sopenharmony_ci
44613498266Sopenharmony_ci          nodes[num_addrs-1]->ai_next = NULL;
44713498266Sopenharmony_ci          *addr = nodes[0];
44813498266Sopenharmony_ci        }
44913498266Sopenharmony_ci        free(rnd);
45013498266Sopenharmony_ci      }
45113498266Sopenharmony_ci      else
45213498266Sopenharmony_ci        result = CURLE_OUT_OF_MEMORY;
45313498266Sopenharmony_ci      free(nodes);
45413498266Sopenharmony_ci    }
45513498266Sopenharmony_ci    else
45613498266Sopenharmony_ci      result = CURLE_OUT_OF_MEMORY;
45713498266Sopenharmony_ci  }
45813498266Sopenharmony_ci  return result;
45913498266Sopenharmony_ci}
46013498266Sopenharmony_ci#endif
46113498266Sopenharmony_ci
46213498266Sopenharmony_ci/*
46313498266Sopenharmony_ci * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
46413498266Sopenharmony_ci *
46513498266Sopenharmony_ci * When calling Curl_resolv() has resulted in a response with a returned
46613498266Sopenharmony_ci * address, we call this function to store the information in the dns
46713498266Sopenharmony_ci * cache etc
46813498266Sopenharmony_ci *
46913498266Sopenharmony_ci * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
47013498266Sopenharmony_ci */
47113498266Sopenharmony_cistruct Curl_dns_entry *
47213498266Sopenharmony_ciCurl_cache_addr(struct Curl_easy *data,
47313498266Sopenharmony_ci                struct Curl_addrinfo *addr,
47413498266Sopenharmony_ci                const char *hostname,
47513498266Sopenharmony_ci                size_t hostlen, /* length or zero */
47613498266Sopenharmony_ci                int port)
47713498266Sopenharmony_ci{
47813498266Sopenharmony_ci  char entry_id[MAX_HOSTCACHE_LEN];
47913498266Sopenharmony_ci  size_t entry_len;
48013498266Sopenharmony_ci  struct Curl_dns_entry *dns;
48113498266Sopenharmony_ci  struct Curl_dns_entry *dns2;
48213498266Sopenharmony_ci
48313498266Sopenharmony_ci#ifndef CURL_DISABLE_SHUFFLE_DNS
48413498266Sopenharmony_ci  /* shuffle addresses if requested */
48513498266Sopenharmony_ci  if(data->set.dns_shuffle_addresses) {
48613498266Sopenharmony_ci    CURLcode result = Curl_shuffle_addr(data, &addr);
48713498266Sopenharmony_ci    if(result)
48813498266Sopenharmony_ci      return NULL;
48913498266Sopenharmony_ci  }
49013498266Sopenharmony_ci#endif
49113498266Sopenharmony_ci  if(!hostlen)
49213498266Sopenharmony_ci    hostlen = strlen(hostname);
49313498266Sopenharmony_ci
49413498266Sopenharmony_ci  /* Create a new cache entry */
49513498266Sopenharmony_ci  dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
49613498266Sopenharmony_ci  if(!dns) {
49713498266Sopenharmony_ci    return NULL;
49813498266Sopenharmony_ci  }
49913498266Sopenharmony_ci
50013498266Sopenharmony_ci  /* Create an entry id, based upon the hostname and port */
50113498266Sopenharmony_ci  entry_len = create_hostcache_id(hostname, hostlen, port,
50213498266Sopenharmony_ci                                  entry_id, sizeof(entry_id));
50313498266Sopenharmony_ci
50413498266Sopenharmony_ci  dns->inuse = 1;   /* the cache has the first reference */
50513498266Sopenharmony_ci  dns->addr = addr; /* this is the address(es) */
50613498266Sopenharmony_ci  time(&dns->timestamp);
50713498266Sopenharmony_ci  if(dns->timestamp == 0)
50813498266Sopenharmony_ci    dns->timestamp = 1;   /* zero indicates permanent CURLOPT_RESOLVE entry */
50913498266Sopenharmony_ci  dns->hostport = port;
51013498266Sopenharmony_ci  if(hostlen)
51113498266Sopenharmony_ci    memcpy(dns->hostname, hostname, hostlen);
51213498266Sopenharmony_ci
51313498266Sopenharmony_ci  /* Store the resolved data in our DNS cache. */
51413498266Sopenharmony_ci  dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
51513498266Sopenharmony_ci                       (void *)dns);
51613498266Sopenharmony_ci  if(!dns2) {
51713498266Sopenharmony_ci    free(dns);
51813498266Sopenharmony_ci    return NULL;
51913498266Sopenharmony_ci  }
52013498266Sopenharmony_ci
52113498266Sopenharmony_ci  dns = dns2;
52213498266Sopenharmony_ci  dns->inuse++;         /* mark entry as in-use */
52313498266Sopenharmony_ci  return dns;
52413498266Sopenharmony_ci}
52513498266Sopenharmony_ci
52613498266Sopenharmony_ci#ifdef ENABLE_IPV6
52713498266Sopenharmony_ci/* return a static IPv6 ::1 for the name */
52813498266Sopenharmony_cistatic struct Curl_addrinfo *get_localhost6(int port, const char *name)
52913498266Sopenharmony_ci{
53013498266Sopenharmony_ci  struct Curl_addrinfo *ca;
53113498266Sopenharmony_ci  const size_t ss_size = sizeof(struct sockaddr_in6);
53213498266Sopenharmony_ci  const size_t hostlen = strlen(name);
53313498266Sopenharmony_ci  struct sockaddr_in6 sa6;
53413498266Sopenharmony_ci  unsigned char ipv6[16];
53513498266Sopenharmony_ci  unsigned short port16 = (unsigned short)(port & 0xffff);
53613498266Sopenharmony_ci  ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
53713498266Sopenharmony_ci  if(!ca)
53813498266Sopenharmony_ci    return NULL;
53913498266Sopenharmony_ci
54013498266Sopenharmony_ci  sa6.sin6_family = AF_INET6;
54113498266Sopenharmony_ci  sa6.sin6_port = htons(port16);
54213498266Sopenharmony_ci  sa6.sin6_flowinfo = 0;
54313498266Sopenharmony_ci  sa6.sin6_scope_id = 0;
54413498266Sopenharmony_ci  if(Curl_inet_pton(AF_INET6, "::1", ipv6) < 1)
54513498266Sopenharmony_ci    return NULL;
54613498266Sopenharmony_ci  memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
54713498266Sopenharmony_ci
54813498266Sopenharmony_ci  ca->ai_flags     = 0;
54913498266Sopenharmony_ci  ca->ai_family    = AF_INET6;
55013498266Sopenharmony_ci  ca->ai_socktype  = SOCK_STREAM;
55113498266Sopenharmony_ci  ca->ai_protocol  = IPPROTO_TCP;
55213498266Sopenharmony_ci  ca->ai_addrlen   = (curl_socklen_t)ss_size;
55313498266Sopenharmony_ci  ca->ai_next      = NULL;
55413498266Sopenharmony_ci  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
55513498266Sopenharmony_ci  memcpy(ca->ai_addr, &sa6, ss_size);
55613498266Sopenharmony_ci  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
55713498266Sopenharmony_ci  strcpy(ca->ai_canonname, name);
55813498266Sopenharmony_ci  return ca;
55913498266Sopenharmony_ci}
56013498266Sopenharmony_ci#else
56113498266Sopenharmony_ci#define get_localhost6(x,y) NULL
56213498266Sopenharmony_ci#endif
56313498266Sopenharmony_ci
56413498266Sopenharmony_ci/* return a static IPv4 127.0.0.1 for the given name */
56513498266Sopenharmony_cistatic struct Curl_addrinfo *get_localhost(int port, const char *name)
56613498266Sopenharmony_ci{
56713498266Sopenharmony_ci  struct Curl_addrinfo *ca;
56813498266Sopenharmony_ci  struct Curl_addrinfo *ca6;
56913498266Sopenharmony_ci  const size_t ss_size = sizeof(struct sockaddr_in);
57013498266Sopenharmony_ci  const size_t hostlen = strlen(name);
57113498266Sopenharmony_ci  struct sockaddr_in sa;
57213498266Sopenharmony_ci  unsigned int ipv4;
57313498266Sopenharmony_ci  unsigned short port16 = (unsigned short)(port & 0xffff);
57413498266Sopenharmony_ci
57513498266Sopenharmony_ci  /* memset to clear the sa.sin_zero field */
57613498266Sopenharmony_ci  memset(&sa, 0, sizeof(sa));
57713498266Sopenharmony_ci  sa.sin_family = AF_INET;
57813498266Sopenharmony_ci  sa.sin_port = htons(port16);
57913498266Sopenharmony_ci  if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
58013498266Sopenharmony_ci    return NULL;
58113498266Sopenharmony_ci  memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
58213498266Sopenharmony_ci
58313498266Sopenharmony_ci  ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
58413498266Sopenharmony_ci  if(!ca)
58513498266Sopenharmony_ci    return NULL;
58613498266Sopenharmony_ci  ca->ai_flags     = 0;
58713498266Sopenharmony_ci  ca->ai_family    = AF_INET;
58813498266Sopenharmony_ci  ca->ai_socktype  = SOCK_STREAM;
58913498266Sopenharmony_ci  ca->ai_protocol  = IPPROTO_TCP;
59013498266Sopenharmony_ci  ca->ai_addrlen   = (curl_socklen_t)ss_size;
59113498266Sopenharmony_ci  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
59213498266Sopenharmony_ci  memcpy(ca->ai_addr, &sa, ss_size);
59313498266Sopenharmony_ci  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
59413498266Sopenharmony_ci  strcpy(ca->ai_canonname, name);
59513498266Sopenharmony_ci
59613498266Sopenharmony_ci  ca6 = get_localhost6(port, name);
59713498266Sopenharmony_ci  if(!ca6)
59813498266Sopenharmony_ci    return ca;
59913498266Sopenharmony_ci  ca6->ai_next = ca;
60013498266Sopenharmony_ci  return ca6;
60113498266Sopenharmony_ci}
60213498266Sopenharmony_ci
60313498266Sopenharmony_ci#ifdef ENABLE_IPV6
60413498266Sopenharmony_ci/*
60513498266Sopenharmony_ci * Curl_ipv6works() returns TRUE if IPv6 seems to work.
60613498266Sopenharmony_ci */
60713498266Sopenharmony_cibool Curl_ipv6works(struct Curl_easy *data)
60813498266Sopenharmony_ci{
60913498266Sopenharmony_ci  if(data) {
61013498266Sopenharmony_ci    /* the nature of most system is that IPv6 status doesn't come and go
61113498266Sopenharmony_ci       during a program's lifetime so we only probe the first time and then we
61213498266Sopenharmony_ci       have the info kept for fast reuse */
61313498266Sopenharmony_ci    DEBUGASSERT(data);
61413498266Sopenharmony_ci    DEBUGASSERT(data->multi);
61513498266Sopenharmony_ci    if(data->multi->ipv6_up == IPV6_UNKNOWN) {
61613498266Sopenharmony_ci      bool works = Curl_ipv6works(NULL);
61713498266Sopenharmony_ci      data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD;
61813498266Sopenharmony_ci    }
61913498266Sopenharmony_ci    return data->multi->ipv6_up == IPV6_WORKS;
62013498266Sopenharmony_ci  }
62113498266Sopenharmony_ci  else {
62213498266Sopenharmony_ci    int ipv6_works = -1;
62313498266Sopenharmony_ci    /* probe to see if we have a working IPv6 stack */
62413498266Sopenharmony_ci    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
62513498266Sopenharmony_ci    if(s == CURL_SOCKET_BAD)
62613498266Sopenharmony_ci      /* an IPv6 address was requested but we can't get/use one */
62713498266Sopenharmony_ci      ipv6_works = 0;
62813498266Sopenharmony_ci    else {
62913498266Sopenharmony_ci      ipv6_works = 1;
63013498266Sopenharmony_ci      sclose(s);
63113498266Sopenharmony_ci    }
63213498266Sopenharmony_ci    return (ipv6_works>0)?TRUE:FALSE;
63313498266Sopenharmony_ci  }
63413498266Sopenharmony_ci}
63513498266Sopenharmony_ci#endif /* ENABLE_IPV6 */
63613498266Sopenharmony_ci
63713498266Sopenharmony_ci/*
63813498266Sopenharmony_ci * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
63913498266Sopenharmony_ci * (or IPv6 if supported) address.
64013498266Sopenharmony_ci */
64113498266Sopenharmony_cibool Curl_host_is_ipnum(const char *hostname)
64213498266Sopenharmony_ci{
64313498266Sopenharmony_ci  struct in_addr in;
64413498266Sopenharmony_ci#ifdef ENABLE_IPV6
64513498266Sopenharmony_ci  struct in6_addr in6;
64613498266Sopenharmony_ci#endif
64713498266Sopenharmony_ci  if(Curl_inet_pton(AF_INET, hostname, &in) > 0
64813498266Sopenharmony_ci#ifdef ENABLE_IPV6
64913498266Sopenharmony_ci     || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
65013498266Sopenharmony_ci#endif
65113498266Sopenharmony_ci    )
65213498266Sopenharmony_ci    return TRUE;
65313498266Sopenharmony_ci  return FALSE;
65413498266Sopenharmony_ci}
65513498266Sopenharmony_ci
65613498266Sopenharmony_ci
65713498266Sopenharmony_ci/* return TRUE if 'part' is a case insensitive tail of 'full' */
65813498266Sopenharmony_cistatic bool tailmatch(const char *full, const char *part)
65913498266Sopenharmony_ci{
66013498266Sopenharmony_ci  size_t plen = strlen(part);
66113498266Sopenharmony_ci  size_t flen = strlen(full);
66213498266Sopenharmony_ci  if(plen > flen)
66313498266Sopenharmony_ci    return FALSE;
66413498266Sopenharmony_ci  return strncasecompare(part, &full[flen - plen], plen);
66513498266Sopenharmony_ci}
66613498266Sopenharmony_ci
66713498266Sopenharmony_ci/*
66813498266Sopenharmony_ci * Curl_resolv() is the main name resolve function within libcurl. It resolves
66913498266Sopenharmony_ci * a name and returns a pointer to the entry in the 'entry' argument (if one
67013498266Sopenharmony_ci * is provided). This function might return immediately if we're using asynch
67113498266Sopenharmony_ci * resolves. See the return codes.
67213498266Sopenharmony_ci *
67313498266Sopenharmony_ci * The cache entry we return will get its 'inuse' counter increased when this
67413498266Sopenharmony_ci * function is used. You MUST call Curl_resolv_unlock() later (when you're
67513498266Sopenharmony_ci * done using this struct) to decrease the counter again.
67613498266Sopenharmony_ci *
67713498266Sopenharmony_ci * Return codes:
67813498266Sopenharmony_ci *
67913498266Sopenharmony_ci * CURLRESOLV_ERROR   (-1) = error, no pointer
68013498266Sopenharmony_ci * CURLRESOLV_RESOLVED (0) = OK, pointer provided
68113498266Sopenharmony_ci * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
68213498266Sopenharmony_ci */
68313498266Sopenharmony_ci
68413498266Sopenharmony_cienum resolve_t Curl_resolv(struct Curl_easy *data,
68513498266Sopenharmony_ci                           const char *hostname,
68613498266Sopenharmony_ci                           int port,
68713498266Sopenharmony_ci                           bool allowDOH,
68813498266Sopenharmony_ci                           struct Curl_dns_entry **entry)
68913498266Sopenharmony_ci{
69013498266Sopenharmony_ci  struct Curl_dns_entry *dns = NULL;
69113498266Sopenharmony_ci  CURLcode result;
69213498266Sopenharmony_ci  enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
69313498266Sopenharmony_ci  struct connectdata *conn = data->conn;
69413498266Sopenharmony_ci  /* We should intentionally error and not resolve .onion TLDs */
69513498266Sopenharmony_ci  size_t hostname_len = strlen(hostname);
69613498266Sopenharmony_ci  if(hostname_len >= 7 &&
69713498266Sopenharmony_ci     (curl_strequal(&hostname[hostname_len - 6], ".onion") ||
69813498266Sopenharmony_ci      curl_strequal(&hostname[hostname_len - 7], ".onion."))) {
69913498266Sopenharmony_ci    failf(data, "Not resolving .onion address (RFC 7686)");
70013498266Sopenharmony_ci    return CURLRESOLV_ERROR;
70113498266Sopenharmony_ci  }
70213498266Sopenharmony_ci  *entry = NULL;
70313498266Sopenharmony_ci#ifndef CURL_DISABLE_DOH
70413498266Sopenharmony_ci  conn->bits.doh = FALSE; /* default is not */
70513498266Sopenharmony_ci#else
70613498266Sopenharmony_ci  (void)allowDOH;
70713498266Sopenharmony_ci#endif
70813498266Sopenharmony_ci
70913498266Sopenharmony_ci  if(data->share)
71013498266Sopenharmony_ci    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
71113498266Sopenharmony_ci
71213498266Sopenharmony_ci  dns = fetch_addr(data, hostname, port);
71313498266Sopenharmony_ci
71413498266Sopenharmony_ci  if(dns) {
71513498266Sopenharmony_ci    infof(data, "Hostname %s was found in DNS cache", hostname);
71613498266Sopenharmony_ci    dns->inuse++; /* we use it! */
71713498266Sopenharmony_ci    rc = CURLRESOLV_RESOLVED;
71813498266Sopenharmony_ci  }
71913498266Sopenharmony_ci
72013498266Sopenharmony_ci  if(data->share)
72113498266Sopenharmony_ci    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
72213498266Sopenharmony_ci
72313498266Sopenharmony_ci  if(!dns) {
72413498266Sopenharmony_ci    /* The entry was not in the cache. Resolve it to IP address */
72513498266Sopenharmony_ci
72613498266Sopenharmony_ci    struct Curl_addrinfo *addr = NULL;
72713498266Sopenharmony_ci    int respwait = 0;
72813498266Sopenharmony_ci#if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS)
72913498266Sopenharmony_ci    struct in_addr in;
73013498266Sopenharmony_ci#endif
73113498266Sopenharmony_ci#ifndef CURL_DISABLE_DOH
73213498266Sopenharmony_ci#ifndef USE_RESOLVE_ON_IPS
73313498266Sopenharmony_ci    const
73413498266Sopenharmony_ci#endif
73513498266Sopenharmony_ci      bool ipnum = FALSE;
73613498266Sopenharmony_ci#endif
73713498266Sopenharmony_ci
73813498266Sopenharmony_ci    /* notify the resolver start callback */
73913498266Sopenharmony_ci    if(data->set.resolver_start) {
74013498266Sopenharmony_ci      int st;
74113498266Sopenharmony_ci      Curl_set_in_callback(data, true);
74213498266Sopenharmony_ci      st = data->set.resolver_start(
74313498266Sopenharmony_ci#ifdef USE_CURL_ASYNC
74413498266Sopenharmony_ci        data->state.async.resolver,
74513498266Sopenharmony_ci#else
74613498266Sopenharmony_ci        NULL,
74713498266Sopenharmony_ci#endif
74813498266Sopenharmony_ci        NULL,
74913498266Sopenharmony_ci        data->set.resolver_start_client);
75013498266Sopenharmony_ci      Curl_set_in_callback(data, false);
75113498266Sopenharmony_ci      if(st)
75213498266Sopenharmony_ci        return CURLRESOLV_ERROR;
75313498266Sopenharmony_ci    }
75413498266Sopenharmony_ci
75513498266Sopenharmony_ci#ifndef USE_RESOLVE_ON_IPS
75613498266Sopenharmony_ci    /* First check if this is an IPv4 address string */
75713498266Sopenharmony_ci    if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
75813498266Sopenharmony_ci      /* This is a dotted IP address 123.123.123.123-style */
75913498266Sopenharmony_ci      addr = Curl_ip2addr(AF_INET, &in, hostname, port);
76013498266Sopenharmony_ci      if(!addr)
76113498266Sopenharmony_ci        return CURLRESOLV_ERROR;
76213498266Sopenharmony_ci    }
76313498266Sopenharmony_ci#ifdef ENABLE_IPV6
76413498266Sopenharmony_ci    else {
76513498266Sopenharmony_ci      struct in6_addr in6;
76613498266Sopenharmony_ci      /* check if this is an IPv6 address string */
76713498266Sopenharmony_ci      if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
76813498266Sopenharmony_ci        /* This is an IPv6 address literal */
76913498266Sopenharmony_ci        addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
77013498266Sopenharmony_ci        if(!addr)
77113498266Sopenharmony_ci          return CURLRESOLV_ERROR;
77213498266Sopenharmony_ci      }
77313498266Sopenharmony_ci    }
77413498266Sopenharmony_ci#endif /* ENABLE_IPV6 */
77513498266Sopenharmony_ci
77613498266Sopenharmony_ci#else /* if USE_RESOLVE_ON_IPS */
77713498266Sopenharmony_ci#ifndef CURL_DISABLE_DOH
77813498266Sopenharmony_ci    /* First check if this is an IPv4 address string */
77913498266Sopenharmony_ci    if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
78013498266Sopenharmony_ci      /* This is a dotted IP address 123.123.123.123-style */
78113498266Sopenharmony_ci      ipnum = TRUE;
78213498266Sopenharmony_ci#ifdef ENABLE_IPV6
78313498266Sopenharmony_ci    else {
78413498266Sopenharmony_ci      struct in6_addr in6;
78513498266Sopenharmony_ci      /* check if this is an IPv6 address string */
78613498266Sopenharmony_ci      if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
78713498266Sopenharmony_ci        /* This is an IPv6 address literal */
78813498266Sopenharmony_ci        ipnum = TRUE;
78913498266Sopenharmony_ci    }
79013498266Sopenharmony_ci#endif /* ENABLE_IPV6 */
79113498266Sopenharmony_ci#endif /* CURL_DISABLE_DOH */
79213498266Sopenharmony_ci
79313498266Sopenharmony_ci#endif /* !USE_RESOLVE_ON_IPS */
79413498266Sopenharmony_ci
79513498266Sopenharmony_ci    if(!addr) {
79613498266Sopenharmony_ci      if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
79713498266Sopenharmony_ci        return CURLRESOLV_ERROR;
79813498266Sopenharmony_ci
79913498266Sopenharmony_ci      if(strcasecompare(hostname, "localhost") ||
80013498266Sopenharmony_ci         tailmatch(hostname, ".localhost"))
80113498266Sopenharmony_ci        addr = get_localhost(port, hostname);
80213498266Sopenharmony_ci#ifndef CURL_DISABLE_DOH
80313498266Sopenharmony_ci      else if(allowDOH && data->set.doh && !ipnum)
80413498266Sopenharmony_ci        addr = Curl_doh(data, hostname, port, &respwait);
80513498266Sopenharmony_ci#endif
80613498266Sopenharmony_ci      else {
80713498266Sopenharmony_ci        /* Check what IP specifics the app has requested and if we can provide
80813498266Sopenharmony_ci         * it. If not, bail out. */
80913498266Sopenharmony_ci        if(!Curl_ipvalid(data, conn))
81013498266Sopenharmony_ci          return CURLRESOLV_ERROR;
81113498266Sopenharmony_ci        /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
81213498266Sopenharmony_ci           non-zero value indicating that we need to wait for the response to
81313498266Sopenharmony_ci           the resolve call */
81413498266Sopenharmony_ci        addr = Curl_getaddrinfo(data, hostname, port, &respwait);
81513498266Sopenharmony_ci      }
81613498266Sopenharmony_ci    }
81713498266Sopenharmony_ci    if(!addr) {
81813498266Sopenharmony_ci      if(respwait) {
81913498266Sopenharmony_ci        /* the response to our resolve call will come asynchronously at
82013498266Sopenharmony_ci           a later time, good or bad */
82113498266Sopenharmony_ci        /* First, check that we haven't received the info by now */
82213498266Sopenharmony_ci        result = Curl_resolv_check(data, &dns);
82313498266Sopenharmony_ci        if(result) /* error detected */
82413498266Sopenharmony_ci          return CURLRESOLV_ERROR;
82513498266Sopenharmony_ci        if(dns)
82613498266Sopenharmony_ci          rc = CURLRESOLV_RESOLVED; /* pointer provided */
82713498266Sopenharmony_ci        else
82813498266Sopenharmony_ci          rc = CURLRESOLV_PENDING; /* no info yet */
82913498266Sopenharmony_ci      }
83013498266Sopenharmony_ci    }
83113498266Sopenharmony_ci    else {
83213498266Sopenharmony_ci      if(data->share)
83313498266Sopenharmony_ci        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
83413498266Sopenharmony_ci
83513498266Sopenharmony_ci      /* we got a response, store it in the cache */
83613498266Sopenharmony_ci      dns = Curl_cache_addr(data, addr, hostname, 0, port);
83713498266Sopenharmony_ci
83813498266Sopenharmony_ci      if(data->share)
83913498266Sopenharmony_ci        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
84013498266Sopenharmony_ci
84113498266Sopenharmony_ci      if(!dns)
84213498266Sopenharmony_ci        /* returned failure, bail out nicely */
84313498266Sopenharmony_ci        Curl_freeaddrinfo(addr);
84413498266Sopenharmony_ci      else {
84513498266Sopenharmony_ci        rc = CURLRESOLV_RESOLVED;
84613498266Sopenharmony_ci        show_resolve_info(data, dns);
84713498266Sopenharmony_ci      }
84813498266Sopenharmony_ci    }
84913498266Sopenharmony_ci  }
85013498266Sopenharmony_ci
85113498266Sopenharmony_ci  *entry = dns;
85213498266Sopenharmony_ci
85313498266Sopenharmony_ci  return rc;
85413498266Sopenharmony_ci}
85513498266Sopenharmony_ci
85613498266Sopenharmony_ci#ifdef USE_ALARM_TIMEOUT
85713498266Sopenharmony_ci/*
85813498266Sopenharmony_ci * This signal handler jumps back into the main libcurl code and continues
85913498266Sopenharmony_ci * execution.  This effectively causes the remainder of the application to run
86013498266Sopenharmony_ci * within a signal handler which is nonportable and could lead to problems.
86113498266Sopenharmony_ci */
86213498266Sopenharmony_ciCURL_NORETURN static
86313498266Sopenharmony_civoid alarmfunc(int sig)
86413498266Sopenharmony_ci{
86513498266Sopenharmony_ci  (void)sig;
86613498266Sopenharmony_ci  siglongjmp(curl_jmpenv, 1);
86713498266Sopenharmony_ci}
86813498266Sopenharmony_ci#endif /* USE_ALARM_TIMEOUT */
86913498266Sopenharmony_ci
87013498266Sopenharmony_ci/*
87113498266Sopenharmony_ci * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
87213498266Sopenharmony_ci * timeout.  This function might return immediately if we're using asynch
87313498266Sopenharmony_ci * resolves. See the return codes.
87413498266Sopenharmony_ci *
87513498266Sopenharmony_ci * The cache entry we return will get its 'inuse' counter increased when this
87613498266Sopenharmony_ci * function is used. You MUST call Curl_resolv_unlock() later (when you're
87713498266Sopenharmony_ci * done using this struct) to decrease the counter again.
87813498266Sopenharmony_ci *
87913498266Sopenharmony_ci * If built with a synchronous resolver and use of signals is not
88013498266Sopenharmony_ci * disabled by the application, then a nonzero timeout will cause a
88113498266Sopenharmony_ci * timeout after the specified number of milliseconds. Otherwise, timeout
88213498266Sopenharmony_ci * is ignored.
88313498266Sopenharmony_ci *
88413498266Sopenharmony_ci * Return codes:
88513498266Sopenharmony_ci *
88613498266Sopenharmony_ci * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
88713498266Sopenharmony_ci * CURLRESOLV_ERROR   (-1) = error, no pointer
88813498266Sopenharmony_ci * CURLRESOLV_RESOLVED (0) = OK, pointer provided
88913498266Sopenharmony_ci * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
89013498266Sopenharmony_ci */
89113498266Sopenharmony_ci
89213498266Sopenharmony_cienum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
89313498266Sopenharmony_ci                                   const char *hostname,
89413498266Sopenharmony_ci                                   int port,
89513498266Sopenharmony_ci                                   struct Curl_dns_entry **entry,
89613498266Sopenharmony_ci                                   timediff_t timeoutms)
89713498266Sopenharmony_ci{
89813498266Sopenharmony_ci#ifdef USE_ALARM_TIMEOUT
89913498266Sopenharmony_ci#ifdef HAVE_SIGACTION
90013498266Sopenharmony_ci  struct sigaction keep_sigact;   /* store the old struct here */
90113498266Sopenharmony_ci  volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
90213498266Sopenharmony_ci  struct sigaction sigact;
90313498266Sopenharmony_ci#else
90413498266Sopenharmony_ci#ifdef HAVE_SIGNAL
90513498266Sopenharmony_ci  void (*keep_sigact)(int);       /* store the old handler here */
90613498266Sopenharmony_ci#endif /* HAVE_SIGNAL */
90713498266Sopenharmony_ci#endif /* HAVE_SIGACTION */
90813498266Sopenharmony_ci  volatile long timeout;
90913498266Sopenharmony_ci  volatile unsigned int prev_alarm = 0;
91013498266Sopenharmony_ci#endif /* USE_ALARM_TIMEOUT */
91113498266Sopenharmony_ci  enum resolve_t rc;
91213498266Sopenharmony_ci
91313498266Sopenharmony_ci  *entry = NULL;
91413498266Sopenharmony_ci
91513498266Sopenharmony_ci  if(timeoutms < 0)
91613498266Sopenharmony_ci    /* got an already expired timeout */
91713498266Sopenharmony_ci    return CURLRESOLV_TIMEDOUT;
91813498266Sopenharmony_ci
91913498266Sopenharmony_ci#ifdef USE_ALARM_TIMEOUT
92013498266Sopenharmony_ci  if(data->set.no_signal)
92113498266Sopenharmony_ci    /* Ignore the timeout when signals are disabled */
92213498266Sopenharmony_ci    timeout = 0;
92313498266Sopenharmony_ci  else
92413498266Sopenharmony_ci    timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
92513498266Sopenharmony_ci
92613498266Sopenharmony_ci  if(!timeout)
92713498266Sopenharmony_ci    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
92813498266Sopenharmony_ci    return Curl_resolv(data, hostname, port, TRUE, entry);
92913498266Sopenharmony_ci
93013498266Sopenharmony_ci  if(timeout < 1000) {
93113498266Sopenharmony_ci    /* The alarm() function only provides integer second resolution, so if
93213498266Sopenharmony_ci       we want to wait less than one second we must bail out already now. */
93313498266Sopenharmony_ci    failf(data,
93413498266Sopenharmony_ci        "remaining timeout of %ld too small to resolve via SIGALRM method",
93513498266Sopenharmony_ci        timeout);
93613498266Sopenharmony_ci    return CURLRESOLV_TIMEDOUT;
93713498266Sopenharmony_ci  }
93813498266Sopenharmony_ci  /* This allows us to time-out from the name resolver, as the timeout
93913498266Sopenharmony_ci     will generate a signal and we will siglongjmp() from that here.
94013498266Sopenharmony_ci     This technique has problems (see alarmfunc).
94113498266Sopenharmony_ci     This should be the last thing we do before calling Curl_resolv(),
94213498266Sopenharmony_ci     as otherwise we'd have to worry about variables that get modified
94313498266Sopenharmony_ci     before we invoke Curl_resolv() (and thus use "volatile"). */
94413498266Sopenharmony_ci  curl_simple_lock_lock(&curl_jmpenv_lock);
94513498266Sopenharmony_ci
94613498266Sopenharmony_ci  if(sigsetjmp(curl_jmpenv, 1)) {
94713498266Sopenharmony_ci    /* this is coming from a siglongjmp() after an alarm signal */
94813498266Sopenharmony_ci    failf(data, "name lookup timed out");
94913498266Sopenharmony_ci    rc = CURLRESOLV_ERROR;
95013498266Sopenharmony_ci    goto clean_up;
95113498266Sopenharmony_ci  }
95213498266Sopenharmony_ci  else {
95313498266Sopenharmony_ci    /*************************************************************
95413498266Sopenharmony_ci     * Set signal handler to catch SIGALRM
95513498266Sopenharmony_ci     * Store the old value to be able to set it back later!
95613498266Sopenharmony_ci     *************************************************************/
95713498266Sopenharmony_ci#ifdef HAVE_SIGACTION
95813498266Sopenharmony_ci    sigaction(SIGALRM, NULL, &sigact);
95913498266Sopenharmony_ci    keep_sigact = sigact;
96013498266Sopenharmony_ci    keep_copysig = TRUE; /* yes, we have a copy */
96113498266Sopenharmony_ci    sigact.sa_handler = alarmfunc;
96213498266Sopenharmony_ci#ifdef SA_RESTART
96313498266Sopenharmony_ci    /* HPUX doesn't have SA_RESTART but defaults to that behavior! */
96413498266Sopenharmony_ci    sigact.sa_flags &= ~SA_RESTART;
96513498266Sopenharmony_ci#endif
96613498266Sopenharmony_ci    /* now set the new struct */
96713498266Sopenharmony_ci    sigaction(SIGALRM, &sigact, NULL);
96813498266Sopenharmony_ci#else /* HAVE_SIGACTION */
96913498266Sopenharmony_ci    /* no sigaction(), revert to the much lamer signal() */
97013498266Sopenharmony_ci#ifdef HAVE_SIGNAL
97113498266Sopenharmony_ci    keep_sigact = signal(SIGALRM, alarmfunc);
97213498266Sopenharmony_ci#endif
97313498266Sopenharmony_ci#endif /* HAVE_SIGACTION */
97413498266Sopenharmony_ci
97513498266Sopenharmony_ci    /* alarm() makes a signal get sent when the timeout fires off, and that
97613498266Sopenharmony_ci       will abort system calls */
97713498266Sopenharmony_ci    prev_alarm = alarm(curlx_sltoui(timeout/1000L));
97813498266Sopenharmony_ci  }
97913498266Sopenharmony_ci
98013498266Sopenharmony_ci#else
98113498266Sopenharmony_ci#ifndef CURLRES_ASYNCH
98213498266Sopenharmony_ci  if(timeoutms)
98313498266Sopenharmony_ci    infof(data, "timeout on name lookup is not supported");
98413498266Sopenharmony_ci#else
98513498266Sopenharmony_ci  (void)timeoutms; /* timeoutms not used with an async resolver */
98613498266Sopenharmony_ci#endif
98713498266Sopenharmony_ci#endif /* USE_ALARM_TIMEOUT */
98813498266Sopenharmony_ci
98913498266Sopenharmony_ci  /* Perform the actual name resolution. This might be interrupted by an
99013498266Sopenharmony_ci   * alarm if it takes too long.
99113498266Sopenharmony_ci   */
99213498266Sopenharmony_ci  rc = Curl_resolv(data, hostname, port, TRUE, entry);
99313498266Sopenharmony_ci
99413498266Sopenharmony_ci#ifdef USE_ALARM_TIMEOUT
99513498266Sopenharmony_ciclean_up:
99613498266Sopenharmony_ci
99713498266Sopenharmony_ci  if(!prev_alarm)
99813498266Sopenharmony_ci    /* deactivate a possibly active alarm before uninstalling the handler */
99913498266Sopenharmony_ci    alarm(0);
100013498266Sopenharmony_ci
100113498266Sopenharmony_ci#ifdef HAVE_SIGACTION
100213498266Sopenharmony_ci  if(keep_copysig) {
100313498266Sopenharmony_ci    /* we got a struct as it looked before, now put that one back nice
100413498266Sopenharmony_ci       and clean */
100513498266Sopenharmony_ci    sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
100613498266Sopenharmony_ci  }
100713498266Sopenharmony_ci#else
100813498266Sopenharmony_ci#ifdef HAVE_SIGNAL
100913498266Sopenharmony_ci  /* restore the previous SIGALRM handler */
101013498266Sopenharmony_ci  signal(SIGALRM, keep_sigact);
101113498266Sopenharmony_ci#endif
101213498266Sopenharmony_ci#endif /* HAVE_SIGACTION */
101313498266Sopenharmony_ci
101413498266Sopenharmony_ci  curl_simple_lock_unlock(&curl_jmpenv_lock);
101513498266Sopenharmony_ci
101613498266Sopenharmony_ci  /* switch back the alarm() to either zero or to what it was before minus
101713498266Sopenharmony_ci     the time we spent until now! */
101813498266Sopenharmony_ci  if(prev_alarm) {
101913498266Sopenharmony_ci    /* there was an alarm() set before us, now put it back */
102013498266Sopenharmony_ci    timediff_t elapsed_secs = Curl_timediff(Curl_now(),
102113498266Sopenharmony_ci                                            data->conn->created) / 1000;
102213498266Sopenharmony_ci
102313498266Sopenharmony_ci    /* the alarm period is counted in even number of seconds */
102413498266Sopenharmony_ci    unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs);
102513498266Sopenharmony_ci
102613498266Sopenharmony_ci    if(!alarm_set ||
102713498266Sopenharmony_ci       ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
102813498266Sopenharmony_ci      /* if the alarm time-left reached zero or turned "negative" (counted
102913498266Sopenharmony_ci         with unsigned values), we should fire off a SIGALRM here, but we
103013498266Sopenharmony_ci         won't, and zero would be to switch it off so we never set it to
103113498266Sopenharmony_ci         less than 1! */
103213498266Sopenharmony_ci      alarm(1);
103313498266Sopenharmony_ci      rc = CURLRESOLV_TIMEDOUT;
103413498266Sopenharmony_ci      failf(data, "Previous alarm fired off");
103513498266Sopenharmony_ci    }
103613498266Sopenharmony_ci    else
103713498266Sopenharmony_ci      alarm((unsigned int)alarm_set);
103813498266Sopenharmony_ci  }
103913498266Sopenharmony_ci#endif /* USE_ALARM_TIMEOUT */
104013498266Sopenharmony_ci
104113498266Sopenharmony_ci  return rc;
104213498266Sopenharmony_ci}
104313498266Sopenharmony_ci
104413498266Sopenharmony_ci/*
104513498266Sopenharmony_ci * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
104613498266Sopenharmony_ci * made, the struct may be destroyed due to pruning. It is important that only
104713498266Sopenharmony_ci * one unlock is made for each Curl_resolv() call.
104813498266Sopenharmony_ci *
104913498266Sopenharmony_ci * May be called with 'data' == NULL for global cache.
105013498266Sopenharmony_ci */
105113498266Sopenharmony_civoid Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
105213498266Sopenharmony_ci{
105313498266Sopenharmony_ci  if(data && data->share)
105413498266Sopenharmony_ci    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
105513498266Sopenharmony_ci
105613498266Sopenharmony_ci  freednsentry(dns);
105713498266Sopenharmony_ci
105813498266Sopenharmony_ci  if(data && data->share)
105913498266Sopenharmony_ci    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
106013498266Sopenharmony_ci}
106113498266Sopenharmony_ci
106213498266Sopenharmony_ci/*
106313498266Sopenharmony_ci * File-internal: release cache dns entry reference, free if inuse drops to 0
106413498266Sopenharmony_ci */
106513498266Sopenharmony_cistatic void freednsentry(void *freethis)
106613498266Sopenharmony_ci{
106713498266Sopenharmony_ci  struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
106813498266Sopenharmony_ci  DEBUGASSERT(dns && (dns->inuse>0));
106913498266Sopenharmony_ci
107013498266Sopenharmony_ci  dns->inuse--;
107113498266Sopenharmony_ci  if(dns->inuse == 0) {
107213498266Sopenharmony_ci    Curl_freeaddrinfo(dns->addr);
107313498266Sopenharmony_ci    free(dns);
107413498266Sopenharmony_ci  }
107513498266Sopenharmony_ci}
107613498266Sopenharmony_ci
107713498266Sopenharmony_ci/*
107813498266Sopenharmony_ci * Curl_init_dnscache() inits a new DNS cache.
107913498266Sopenharmony_ci */
108013498266Sopenharmony_civoid Curl_init_dnscache(struct Curl_hash *hash, int size)
108113498266Sopenharmony_ci{
108213498266Sopenharmony_ci  Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
108313498266Sopenharmony_ci                 freednsentry);
108413498266Sopenharmony_ci}
108513498266Sopenharmony_ci
108613498266Sopenharmony_ci/*
108713498266Sopenharmony_ci * Curl_hostcache_clean()
108813498266Sopenharmony_ci *
108913498266Sopenharmony_ci * This _can_ be called with 'data' == NULL but then of course no locking
109013498266Sopenharmony_ci * can be done!
109113498266Sopenharmony_ci */
109213498266Sopenharmony_ci
109313498266Sopenharmony_civoid Curl_hostcache_clean(struct Curl_easy *data,
109413498266Sopenharmony_ci                          struct Curl_hash *hash)
109513498266Sopenharmony_ci{
109613498266Sopenharmony_ci  if(data && data->share)
109713498266Sopenharmony_ci    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
109813498266Sopenharmony_ci
109913498266Sopenharmony_ci  Curl_hash_clean(hash);
110013498266Sopenharmony_ci
110113498266Sopenharmony_ci  if(data && data->share)
110213498266Sopenharmony_ci    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
110313498266Sopenharmony_ci}
110413498266Sopenharmony_ci
110513498266Sopenharmony_ci
110613498266Sopenharmony_ciCURLcode Curl_loadhostpairs(struct Curl_easy *data)
110713498266Sopenharmony_ci{
110813498266Sopenharmony_ci  struct curl_slist *hostp;
110913498266Sopenharmony_ci  char *host_end;
111013498266Sopenharmony_ci
111113498266Sopenharmony_ci  /* Default is no wildcard found */
111213498266Sopenharmony_ci  data->state.wildcard_resolve = false;
111313498266Sopenharmony_ci
111413498266Sopenharmony_ci  for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
111513498266Sopenharmony_ci    char entry_id[MAX_HOSTCACHE_LEN];
111613498266Sopenharmony_ci    if(!hostp->data)
111713498266Sopenharmony_ci      continue;
111813498266Sopenharmony_ci    if(hostp->data[0] == '-') {
111913498266Sopenharmony_ci      unsigned long num = 0;
112013498266Sopenharmony_ci      size_t entry_len;
112113498266Sopenharmony_ci      size_t hlen = 0;
112213498266Sopenharmony_ci      host_end = strchr(&hostp->data[1], ':');
112313498266Sopenharmony_ci
112413498266Sopenharmony_ci      if(host_end) {
112513498266Sopenharmony_ci        hlen = host_end - &hostp->data[1];
112613498266Sopenharmony_ci        num = strtoul(++host_end, NULL, 10);
112713498266Sopenharmony_ci        if(!hlen || (num > 0xffff))
112813498266Sopenharmony_ci          host_end = NULL;
112913498266Sopenharmony_ci      }
113013498266Sopenharmony_ci      if(!host_end) {
113113498266Sopenharmony_ci        infof(data, "Bad syntax CURLOPT_RESOLVE removal entry '%s'",
113213498266Sopenharmony_ci              hostp->data);
113313498266Sopenharmony_ci        continue;
113413498266Sopenharmony_ci      }
113513498266Sopenharmony_ci      /* Create an entry id, based upon the hostname and port */
113613498266Sopenharmony_ci      entry_len = create_hostcache_id(&hostp->data[1], hlen, (int)num,
113713498266Sopenharmony_ci                                      entry_id, sizeof(entry_id));
113813498266Sopenharmony_ci      if(data->share)
113913498266Sopenharmony_ci        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
114013498266Sopenharmony_ci
114113498266Sopenharmony_ci      /* delete entry, ignore if it didn't exist */
114213498266Sopenharmony_ci      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
114313498266Sopenharmony_ci
114413498266Sopenharmony_ci      if(data->share)
114513498266Sopenharmony_ci        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
114613498266Sopenharmony_ci    }
114713498266Sopenharmony_ci    else {
114813498266Sopenharmony_ci      struct Curl_dns_entry *dns;
114913498266Sopenharmony_ci      struct Curl_addrinfo *head = NULL, *tail = NULL;
115013498266Sopenharmony_ci      size_t entry_len;
115113498266Sopenharmony_ci      char address[64];
115213498266Sopenharmony_ci#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
115313498266Sopenharmony_ci      char *addresses = NULL;
115413498266Sopenharmony_ci#endif
115513498266Sopenharmony_ci      char *addr_begin;
115613498266Sopenharmony_ci      char *addr_end;
115713498266Sopenharmony_ci      char *port_ptr;
115813498266Sopenharmony_ci      int port = 0;
115913498266Sopenharmony_ci      char *end_ptr;
116013498266Sopenharmony_ci      bool permanent = TRUE;
116113498266Sopenharmony_ci      unsigned long tmp_port;
116213498266Sopenharmony_ci      bool error = true;
116313498266Sopenharmony_ci      char *host_begin = hostp->data;
116413498266Sopenharmony_ci      size_t hlen = 0;
116513498266Sopenharmony_ci
116613498266Sopenharmony_ci      if(host_begin[0] == '+') {
116713498266Sopenharmony_ci        host_begin++;
116813498266Sopenharmony_ci        permanent = FALSE;
116913498266Sopenharmony_ci      }
117013498266Sopenharmony_ci      host_end = strchr(host_begin, ':');
117113498266Sopenharmony_ci      if(!host_end)
117213498266Sopenharmony_ci        goto err;
117313498266Sopenharmony_ci      hlen = host_end - host_begin;
117413498266Sopenharmony_ci
117513498266Sopenharmony_ci      port_ptr = host_end + 1;
117613498266Sopenharmony_ci      tmp_port = strtoul(port_ptr, &end_ptr, 10);
117713498266Sopenharmony_ci      if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
117813498266Sopenharmony_ci        goto err;
117913498266Sopenharmony_ci
118013498266Sopenharmony_ci      port = (int)tmp_port;
118113498266Sopenharmony_ci#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
118213498266Sopenharmony_ci      addresses = end_ptr + 1;
118313498266Sopenharmony_ci#endif
118413498266Sopenharmony_ci
118513498266Sopenharmony_ci      while(*end_ptr) {
118613498266Sopenharmony_ci        size_t alen;
118713498266Sopenharmony_ci        struct Curl_addrinfo *ai;
118813498266Sopenharmony_ci
118913498266Sopenharmony_ci        addr_begin = end_ptr + 1;
119013498266Sopenharmony_ci        addr_end = strchr(addr_begin, ',');
119113498266Sopenharmony_ci        if(!addr_end)
119213498266Sopenharmony_ci          addr_end = addr_begin + strlen(addr_begin);
119313498266Sopenharmony_ci        end_ptr = addr_end;
119413498266Sopenharmony_ci
119513498266Sopenharmony_ci        /* allow IP(v6) address within [brackets] */
119613498266Sopenharmony_ci        if(*addr_begin == '[') {
119713498266Sopenharmony_ci          if(addr_end == addr_begin || *(addr_end - 1) != ']')
119813498266Sopenharmony_ci            goto err;
119913498266Sopenharmony_ci          ++addr_begin;
120013498266Sopenharmony_ci          --addr_end;
120113498266Sopenharmony_ci        }
120213498266Sopenharmony_ci
120313498266Sopenharmony_ci        alen = addr_end - addr_begin;
120413498266Sopenharmony_ci        if(!alen)
120513498266Sopenharmony_ci          continue;
120613498266Sopenharmony_ci
120713498266Sopenharmony_ci        if(alen >= sizeof(address))
120813498266Sopenharmony_ci          goto err;
120913498266Sopenharmony_ci
121013498266Sopenharmony_ci        memcpy(address, addr_begin, alen);
121113498266Sopenharmony_ci        address[alen] = '\0';
121213498266Sopenharmony_ci
121313498266Sopenharmony_ci#ifndef ENABLE_IPV6
121413498266Sopenharmony_ci        if(strchr(address, ':')) {
121513498266Sopenharmony_ci          infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
121613498266Sopenharmony_ci                address);
121713498266Sopenharmony_ci          continue;
121813498266Sopenharmony_ci        }
121913498266Sopenharmony_ci#endif
122013498266Sopenharmony_ci
122113498266Sopenharmony_ci        ai = Curl_str2addr(address, port);
122213498266Sopenharmony_ci        if(!ai) {
122313498266Sopenharmony_ci          infof(data, "Resolve address '%s' found illegal", address);
122413498266Sopenharmony_ci          goto err;
122513498266Sopenharmony_ci        }
122613498266Sopenharmony_ci
122713498266Sopenharmony_ci        if(tail) {
122813498266Sopenharmony_ci          tail->ai_next = ai;
122913498266Sopenharmony_ci          tail = tail->ai_next;
123013498266Sopenharmony_ci        }
123113498266Sopenharmony_ci        else {
123213498266Sopenharmony_ci          head = tail = ai;
123313498266Sopenharmony_ci        }
123413498266Sopenharmony_ci      }
123513498266Sopenharmony_ci
123613498266Sopenharmony_ci      if(!head)
123713498266Sopenharmony_ci        goto err;
123813498266Sopenharmony_ci
123913498266Sopenharmony_ci      error = false;
124013498266Sopenharmony_cierr:
124113498266Sopenharmony_ci      if(error) {
124213498266Sopenharmony_ci        failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'",
124313498266Sopenharmony_ci              hostp->data);
124413498266Sopenharmony_ci        Curl_freeaddrinfo(head);
124513498266Sopenharmony_ci        return CURLE_SETOPT_OPTION_SYNTAX;
124613498266Sopenharmony_ci      }
124713498266Sopenharmony_ci
124813498266Sopenharmony_ci      /* Create an entry id, based upon the hostname and port */
124913498266Sopenharmony_ci      entry_len = create_hostcache_id(host_begin, hlen, port,
125013498266Sopenharmony_ci                                      entry_id, sizeof(entry_id));
125113498266Sopenharmony_ci
125213498266Sopenharmony_ci      if(data->share)
125313498266Sopenharmony_ci        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
125413498266Sopenharmony_ci
125513498266Sopenharmony_ci      /* See if it's already in our dns cache */
125613498266Sopenharmony_ci      dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
125713498266Sopenharmony_ci
125813498266Sopenharmony_ci      if(dns) {
125913498266Sopenharmony_ci        infof(data, "RESOLVE %.*s:%d - old addresses discarded",
126013498266Sopenharmony_ci              (int)hlen, host_begin, port);
126113498266Sopenharmony_ci        /* delete old entry, there are two reasons for this
126213498266Sopenharmony_ci         1. old entry may have different addresses.
126313498266Sopenharmony_ci         2. even if entry with correct addresses is already in the cache,
126413498266Sopenharmony_ci            but if it is close to expire, then by the time next http
126513498266Sopenharmony_ci            request is made, it can get expired and pruned because old
126613498266Sopenharmony_ci            entry is not necessarily marked as permanent.
126713498266Sopenharmony_ci         3. when adding a non-permanent entry, we want it to remove and
126813498266Sopenharmony_ci            replace an existing permanent entry.
126913498266Sopenharmony_ci         4. when adding a non-permanent entry, we want it to get a "fresh"
127013498266Sopenharmony_ci            timeout that starts _now_. */
127113498266Sopenharmony_ci
127213498266Sopenharmony_ci        Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
127313498266Sopenharmony_ci      }
127413498266Sopenharmony_ci
127513498266Sopenharmony_ci      /* put this new host in the cache */
127613498266Sopenharmony_ci      dns = Curl_cache_addr(data, head, host_begin, hlen, port);
127713498266Sopenharmony_ci      if(dns) {
127813498266Sopenharmony_ci        if(permanent)
127913498266Sopenharmony_ci          dns->timestamp = 0; /* mark as permanent */
128013498266Sopenharmony_ci        /* release the returned reference; the cache itself will keep the
128113498266Sopenharmony_ci         * entry alive: */
128213498266Sopenharmony_ci        dns->inuse--;
128313498266Sopenharmony_ci      }
128413498266Sopenharmony_ci
128513498266Sopenharmony_ci      if(data->share)
128613498266Sopenharmony_ci        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
128713498266Sopenharmony_ci
128813498266Sopenharmony_ci      if(!dns) {
128913498266Sopenharmony_ci        Curl_freeaddrinfo(head);
129013498266Sopenharmony_ci        return CURLE_OUT_OF_MEMORY;
129113498266Sopenharmony_ci      }
129213498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
129313498266Sopenharmony_ci      infof(data, "Added %.*s:%d:%s to DNS cache%s",
129413498266Sopenharmony_ci            (int)hlen, host_begin, port, addresses,
129513498266Sopenharmony_ci            permanent ? "" : " (non-permanent)");
129613498266Sopenharmony_ci#endif
129713498266Sopenharmony_ci
129813498266Sopenharmony_ci      /* Wildcard hostname */
129913498266Sopenharmony_ci      if((hlen == 1) && (host_begin[0] == '*')) {
130013498266Sopenharmony_ci        infof(data, "RESOLVE *:%d using wildcard", port);
130113498266Sopenharmony_ci        data->state.wildcard_resolve = true;
130213498266Sopenharmony_ci      }
130313498266Sopenharmony_ci    }
130413498266Sopenharmony_ci  }
130513498266Sopenharmony_ci  data->state.resolve = NULL; /* dealt with now */
130613498266Sopenharmony_ci
130713498266Sopenharmony_ci  return CURLE_OK;
130813498266Sopenharmony_ci}
130913498266Sopenharmony_ci
131013498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
131113498266Sopenharmony_cistatic void show_resolve_info(struct Curl_easy *data,
131213498266Sopenharmony_ci                              struct Curl_dns_entry *dns)
131313498266Sopenharmony_ci{
131413498266Sopenharmony_ci  struct Curl_addrinfo *a;
131513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
131613498266Sopenharmony_ci#ifdef CURLRES_IPV6
131713498266Sopenharmony_ci  struct dynbuf out[2];
131813498266Sopenharmony_ci#else
131913498266Sopenharmony_ci  struct dynbuf out[1];
132013498266Sopenharmony_ci#endif
132113498266Sopenharmony_ci  DEBUGASSERT(data);
132213498266Sopenharmony_ci  DEBUGASSERT(dns);
132313498266Sopenharmony_ci
132413498266Sopenharmony_ci  if(!data->set.verbose ||
132513498266Sopenharmony_ci     /* ignore no name or numerical IP addresses */
132613498266Sopenharmony_ci     !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname))
132713498266Sopenharmony_ci    return;
132813498266Sopenharmony_ci
132913498266Sopenharmony_ci  a = dns->addr;
133013498266Sopenharmony_ci
133113498266Sopenharmony_ci  infof(data, "Host %s:%d was resolved.",
133213498266Sopenharmony_ci        (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport);
133313498266Sopenharmony_ci
133413498266Sopenharmony_ci  Curl_dyn_init(&out[0], 1024);
133513498266Sopenharmony_ci#ifdef CURLRES_IPV6
133613498266Sopenharmony_ci  Curl_dyn_init(&out[1], 1024);
133713498266Sopenharmony_ci#endif
133813498266Sopenharmony_ci
133913498266Sopenharmony_ci  while(a) {
134013498266Sopenharmony_ci    if(
134113498266Sopenharmony_ci#ifdef CURLRES_IPV6
134213498266Sopenharmony_ci       a->ai_family == PF_INET6 ||
134313498266Sopenharmony_ci#endif
134413498266Sopenharmony_ci       a->ai_family == PF_INET) {
134513498266Sopenharmony_ci      char buf[MAX_IPADR_LEN];
134613498266Sopenharmony_ci      struct dynbuf *d = &out[(a->ai_family != PF_INET)];
134713498266Sopenharmony_ci      Curl_printable_address(a, buf, sizeof(buf));
134813498266Sopenharmony_ci      if(Curl_dyn_len(d))
134913498266Sopenharmony_ci        result = Curl_dyn_addn(d, ", ", 2);
135013498266Sopenharmony_ci      if(!result)
135113498266Sopenharmony_ci        result = Curl_dyn_add(d, buf);
135213498266Sopenharmony_ci      if(result) {
135313498266Sopenharmony_ci        infof(data, "too many IP, can't show");
135413498266Sopenharmony_ci        goto fail;
135513498266Sopenharmony_ci      }
135613498266Sopenharmony_ci    }
135713498266Sopenharmony_ci    a = a->ai_next;
135813498266Sopenharmony_ci  }
135913498266Sopenharmony_ci
136013498266Sopenharmony_ci#ifdef CURLRES_IPV6
136113498266Sopenharmony_ci  infof(data, "IPv6: %s",
136213498266Sopenharmony_ci        (Curl_dyn_len(&out[1]) ? Curl_dyn_ptr(&out[1]) : "(none)"));
136313498266Sopenharmony_ci#endif
136413498266Sopenharmony_ci  infof(data, "IPv4: %s",
136513498266Sopenharmony_ci        (Curl_dyn_len(&out[0]) ? Curl_dyn_ptr(&out[0]) : "(none)"));
136613498266Sopenharmony_ci
136713498266Sopenharmony_cifail:
136813498266Sopenharmony_ci  Curl_dyn_free(&out[0]);
136913498266Sopenharmony_ci#ifdef CURLRES_IPV6
137013498266Sopenharmony_ci  Curl_dyn_free(&out[1]);
137113498266Sopenharmony_ci#endif
137213498266Sopenharmony_ci}
137313498266Sopenharmony_ci#endif
137413498266Sopenharmony_ci
137513498266Sopenharmony_ciCURLcode Curl_resolv_check(struct Curl_easy *data,
137613498266Sopenharmony_ci                           struct Curl_dns_entry **dns)
137713498266Sopenharmony_ci{
137813498266Sopenharmony_ci  CURLcode result;
137913498266Sopenharmony_ci#if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
138013498266Sopenharmony_ci  (void)data;
138113498266Sopenharmony_ci  (void)dns;
138213498266Sopenharmony_ci#endif
138313498266Sopenharmony_ci#ifndef CURL_DISABLE_DOH
138413498266Sopenharmony_ci  if(data->conn->bits.doh) {
138513498266Sopenharmony_ci    result = Curl_doh_is_resolved(data, dns);
138613498266Sopenharmony_ci  }
138713498266Sopenharmony_ci  else
138813498266Sopenharmony_ci#endif
138913498266Sopenharmony_ci  result = Curl_resolver_is_resolved(data, dns);
139013498266Sopenharmony_ci  if(*dns)
139113498266Sopenharmony_ci    show_resolve_info(data, *dns);
139213498266Sopenharmony_ci  return result;
139313498266Sopenharmony_ci}
139413498266Sopenharmony_ci
139513498266Sopenharmony_ciint Curl_resolv_getsock(struct Curl_easy *data,
139613498266Sopenharmony_ci                        curl_socket_t *socks)
139713498266Sopenharmony_ci{
139813498266Sopenharmony_ci#ifdef CURLRES_ASYNCH
139913498266Sopenharmony_ci#ifndef CURL_DISABLE_DOH
140013498266Sopenharmony_ci  if(data->conn->bits.doh)
140113498266Sopenharmony_ci    /* nothing to wait for during DoH resolve, those handles have their own
140213498266Sopenharmony_ci       sockets */
140313498266Sopenharmony_ci    return GETSOCK_BLANK;
140413498266Sopenharmony_ci#endif
140513498266Sopenharmony_ci  return Curl_resolver_getsock(data, socks);
140613498266Sopenharmony_ci#else
140713498266Sopenharmony_ci  (void)data;
140813498266Sopenharmony_ci  (void)socks;
140913498266Sopenharmony_ci  return GETSOCK_BLANK;
141013498266Sopenharmony_ci#endif
141113498266Sopenharmony_ci}
141213498266Sopenharmony_ci
141313498266Sopenharmony_ci/* Call this function after Curl_connect() has returned async=TRUE and
141413498266Sopenharmony_ci   then a successful name resolve has been received.
141513498266Sopenharmony_ci
141613498266Sopenharmony_ci   Note: this function disconnects and frees the conn data in case of
141713498266Sopenharmony_ci   resolve failure */
141813498266Sopenharmony_ciCURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
141913498266Sopenharmony_ci{
142013498266Sopenharmony_ci  CURLcode result;
142113498266Sopenharmony_ci  struct connectdata *conn = data->conn;
142213498266Sopenharmony_ci
142313498266Sopenharmony_ci#ifdef USE_CURL_ASYNC
142413498266Sopenharmony_ci  if(data->state.async.dns) {
142513498266Sopenharmony_ci    conn->dns_entry = data->state.async.dns;
142613498266Sopenharmony_ci    data->state.async.dns = NULL;
142713498266Sopenharmony_ci  }
142813498266Sopenharmony_ci#endif
142913498266Sopenharmony_ci
143013498266Sopenharmony_ci  result = Curl_setup_conn(data, protocol_done);
143113498266Sopenharmony_ci
143213498266Sopenharmony_ci  if(result) {
143313498266Sopenharmony_ci    Curl_detach_connection(data);
143413498266Sopenharmony_ci    Curl_conncache_remove_conn(data, conn, TRUE);
143513498266Sopenharmony_ci    Curl_disconnect(data, conn, TRUE);
143613498266Sopenharmony_ci  }
143713498266Sopenharmony_ci  return result;
143813498266Sopenharmony_ci}
143913498266Sopenharmony_ci
144013498266Sopenharmony_ci/*
144113498266Sopenharmony_ci * Curl_resolver_error() calls failf() with the appropriate message after a
144213498266Sopenharmony_ci * resolve error
144313498266Sopenharmony_ci */
144413498266Sopenharmony_ci
144513498266Sopenharmony_ci#ifdef USE_CURL_ASYNC
144613498266Sopenharmony_ciCURLcode Curl_resolver_error(struct Curl_easy *data)
144713498266Sopenharmony_ci{
144813498266Sopenharmony_ci  const char *host_or_proxy;
144913498266Sopenharmony_ci  CURLcode result;
145013498266Sopenharmony_ci
145113498266Sopenharmony_ci#ifndef CURL_DISABLE_PROXY
145213498266Sopenharmony_ci  struct connectdata *conn = data->conn;
145313498266Sopenharmony_ci  if(conn->bits.httpproxy) {
145413498266Sopenharmony_ci    host_or_proxy = "proxy";
145513498266Sopenharmony_ci    result = CURLE_COULDNT_RESOLVE_PROXY;
145613498266Sopenharmony_ci  }
145713498266Sopenharmony_ci  else
145813498266Sopenharmony_ci#endif
145913498266Sopenharmony_ci  {
146013498266Sopenharmony_ci    host_or_proxy = "host";
146113498266Sopenharmony_ci    result = CURLE_COULDNT_RESOLVE_HOST;
146213498266Sopenharmony_ci  }
146313498266Sopenharmony_ci
146413498266Sopenharmony_ci  failf(data, "Could not resolve %s: %s", host_or_proxy,
146513498266Sopenharmony_ci        data->state.async.hostname);
146613498266Sopenharmony_ci
146713498266Sopenharmony_ci  return result;
146813498266Sopenharmony_ci}
146913498266Sopenharmony_ci#endif /* USE_CURL_ASYNC */
1470