VirtualBox

source: vbox/trunk/src/libs/curl-8.11.1/lib/hostip.c@ 108257

Last change on this file since 108257 was 108048, checked in by vboxsync, 3 months ago

curl-8.11.1: Applied and adjusted our curl changes to 8.7.1. jiraref:VBP-1535

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

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