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