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