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