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