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