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