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