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