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