VirtualBox

source: vbox/trunk/src/libs/curl-7.87.0/lib/hostip.c@ 98326

Last change on this file since 98326 was 98326, checked in by vboxsync, 2 years ago

curl-7.87.0: Applied and adjusted our curl changes to 7.83.1. bugref:10356

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