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