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