VirtualBox

source: vbox/trunk/src/libs/curl-7.64.0/lib/hostip.c@ 95219

Last change on this file since 95219 was 85671, checked in by vboxsync, 4 years ago

Export out internal curl copy to make it a lot simpler to build VBox (OSE) on Windows. bugref:9814

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette