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