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 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
118 static void hostcache_unlink_entry(void *entry);
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 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
178 struct 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 */
191 static int
hostcache_entry_is_stale(void * datap,void * hc)192 hostcache_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 >= (time_t)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 */
213 static time_t
hostcache_prune(struct Curl_hash * hostcache,int cache_timeout,time_t now)214 hostcache_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 */
Curl_hostcache_prune(struct Curl_easy * data)234 void 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. */
271 static sigjmp_buf curl_jmpenv;
272 static curl_simple_lock curl_jmpenv_lock;
273 #endif
274
275 /* lookup address, returns entry if found and not stale */
fetch_addr(struct Curl_easy * data,const char * hostname,int port)276 static 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 */
355 struct Curl_dns_entry *
Curl_fetch_addr(struct Curl_easy * data,const char * hostname,int port)356 Curl_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 */
num_addresses(const struct Curl_addrinfo * addr)380 static 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
390 UNITTEST 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 */
Curl_shuffle_addr(struct Curl_easy * data,struct Curl_addrinfo ** addr)404 UNITTEST 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 */
467 struct Curl_dns_entry *
Curl_cache_addr(struct Curl_easy * data,struct Curl_addrinfo * addr,const char * hostname,size_t hostlen,int port,bool permanent)468 Curl_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 */
get_localhost6(int port,const char * name)529 static 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 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
545 sa6.sin6_scope_id = 0;
546 #endif
547
548 (void)Curl_inet_pton(AF_INET6, "::1", ipv6);
549 memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
550
551 ca->ai_flags = 0;
552 ca->ai_family = AF_INET6;
553 ca->ai_socktype = SOCK_STREAM;
554 ca->ai_protocol = IPPROTO_TCP;
555 ca->ai_addrlen = (curl_socklen_t)ss_size;
556 ca->ai_next = NULL;
557 ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
558 memcpy(ca->ai_addr, &sa6, ss_size);
559 ca->ai_canonname = (char *)ca->ai_addr + ss_size;
560 strcpy(ca->ai_canonname, name);
561 return ca;
562 }
563 #else
564 #define get_localhost6(x,y) NULL
565 #endif
566
567 /* return a static IPv4 127.0.0.1 for the given name */
get_localhost(int port,const char * name)568 static struct Curl_addrinfo *get_localhost(int port, const char *name)
569 {
570 struct Curl_addrinfo *ca;
571 struct Curl_addrinfo *ca6;
572 const size_t ss_size = sizeof(struct sockaddr_in);
573 const size_t hostlen = strlen(name);
574 struct sockaddr_in sa;
575 unsigned int ipv4;
576 unsigned short port16 = (unsigned short)(port & 0xffff);
577
578 /* memset to clear the sa.sin_zero field */
579 memset(&sa, 0, sizeof(sa));
580 sa.sin_family = AF_INET;
581 sa.sin_port = htons(port16);
582 if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
583 return NULL;
584 memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
585
586 ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
587 if(!ca)
588 return NULL;
589 ca->ai_flags = 0;
590 ca->ai_family = AF_INET;
591 ca->ai_socktype = SOCK_STREAM;
592 ca->ai_protocol = IPPROTO_TCP;
593 ca->ai_addrlen = (curl_socklen_t)ss_size;
594 ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
595 memcpy(ca->ai_addr, &sa, ss_size);
596 ca->ai_canonname = (char *)ca->ai_addr + ss_size;
597 strcpy(ca->ai_canonname, name);
598
599 ca6 = get_localhost6(port, name);
600 if(!ca6)
601 return ca;
602 ca6->ai_next = ca;
603 return ca6;
604 }
605
606 #ifdef USE_IPV6
607 /*
608 * Curl_ipv6works() returns TRUE if IPv6 seems to work.
609 */
Curl_ipv6works(struct Curl_easy * data)610 bool Curl_ipv6works(struct Curl_easy *data)
611 {
612 if(data) {
613 /* the nature of most system is that IPv6 status does not come and go
614 during a program's lifetime so we only probe the first time and then we
615 have the info kept for fast reuse */
616 DEBUGASSERT(data);
617 DEBUGASSERT(data->multi);
618 if(data->multi->ipv6_up == IPV6_UNKNOWN) {
619 bool works = Curl_ipv6works(NULL);
620 data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD;
621 }
622 return data->multi->ipv6_up == IPV6_WORKS;
623 }
624 else {
625 int ipv6_works = -1;
626 /* probe to see if we have a working IPv6 stack */
627 curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
628 if(s == CURL_SOCKET_BAD)
629 /* an IPv6 address was requested but we cannot get/use one */
630 ipv6_works = 0;
631 else {
632 ipv6_works = 1;
633 sclose(s);
634 }
635 return ipv6_works > 0;
636 }
637 }
638 #endif /* USE_IPV6 */
639
640 /*
641 * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
642 * (or IPv6 if supported) address.
643 */
Curl_host_is_ipnum(const char * hostname)644 bool Curl_host_is_ipnum(const char *hostname)
645 {
646 struct in_addr in;
647 #ifdef USE_IPV6
648 struct in6_addr in6;
649 #endif
650 if(Curl_inet_pton(AF_INET, hostname, &in) > 0
651 #ifdef USE_IPV6
652 || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
653 #endif
654 )
655 return TRUE;
656 return FALSE;
657 }
658
659
660 /* return TRUE if 'part' is a case insensitive tail of 'full' */
tailmatch(const char * full,const char * part)661 static bool tailmatch(const char *full, const char *part)
662 {
663 size_t plen = strlen(part);
664 size_t flen = strlen(full);
665 if(plen > flen)
666 return FALSE;
667 return strncasecompare(part, &full[flen - plen], plen);
668 }
669
670 /*
671 * Curl_resolv() is the main name resolve function within libcurl. It resolves
672 * a name and returns a pointer to the entry in the 'entry' argument (if one
673 * is provided). This function might return immediately if we are using asynch
674 * resolves. See the return codes.
675 *
676 * The cache entry we return will get its 'inuse' counter increased when this
677 * function is used. You MUST call Curl_resolv_unlink() later (when you are
678 * done using this struct) to decrease the reference counter again.
679 *
680 * Return codes:
681 *
682 * CURLRESOLV_ERROR (-1) = error, no pointer
683 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
684 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
685 */
686
Curl_resolv(struct Curl_easy * data,const char * hostname,int port,bool allowDOH,struct Curl_dns_entry ** entry)687 enum resolve_t Curl_resolv(struct Curl_easy *data,
688 const char *hostname,
689 int port,
690 bool allowDOH,
691 struct Curl_dns_entry **entry)
692 {
693 struct Curl_dns_entry *dns = NULL;
694 CURLcode result;
695 enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
696 struct connectdata *conn = data->conn;
697 /* We should intentionally error and not resolve .onion TLDs */
698 size_t hostname_len = strlen(hostname);
699 if(hostname_len >= 7 &&
700 (curl_strequal(&hostname[hostname_len - 6], ".onion") ||
701 curl_strequal(&hostname[hostname_len - 7], ".onion."))) {
702 failf(data, "Not resolving .onion address (RFC 7686)");
703 return CURLRESOLV_ERROR;
704 }
705 *entry = NULL;
706 #ifndef CURL_DISABLE_DOH
707 conn->bits.doh = FALSE; /* default is not */
708 #else
709 (void)allowDOH;
710 #endif
711
712 if(data->share)
713 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
714
715 dns = fetch_addr(data, hostname, port);
716
717 if(dns) {
718 infof(data, "Hostname %s was found in DNS cache", hostname);
719 dns->refcount++; /* we use it! */
720 rc = CURLRESOLV_RESOLVED;
721 }
722
723 if(data->share)
724 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
725
726 if(!dns) {
727 /* The entry was not in the cache. Resolve it to IP address */
728
729 struct Curl_addrinfo *addr = NULL;
730 int respwait = 0;
731 #if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS)
732 struct in_addr in;
733 #endif
734 #ifndef CURL_DISABLE_DOH
735 #ifndef USE_RESOLVE_ON_IPS
736 const
737 #endif
738 bool ipnum = FALSE;
739 #endif
740
741 /* notify the resolver start callback */
742 if(data->set.resolver_start) {
743 int st;
744 Curl_set_in_callback(data, TRUE);
745 st = data->set.resolver_start(
746 #ifdef USE_CURL_ASYNC
747 data->state.async.resolver,
748 #else
749 NULL,
750 #endif
751 NULL,
752 data->set.resolver_start_client);
753 Curl_set_in_callback(data, FALSE);
754 if(st)
755 return CURLRESOLV_ERROR;
756 }
757
758 #ifndef USE_RESOLVE_ON_IPS
759 /* First check if this is an IPv4 address string */
760 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
761 /* This is a dotted IP address 123.123.123.123-style */
762 addr = Curl_ip2addr(AF_INET, &in, hostname, port);
763 if(!addr)
764 return CURLRESOLV_ERROR;
765 }
766 #ifdef USE_IPV6
767 else {
768 struct in6_addr in6;
769 /* check if this is an IPv6 address string */
770 if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
771 /* This is an IPv6 address literal */
772 addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
773 if(!addr)
774 return CURLRESOLV_ERROR;
775 }
776 }
777 #endif /* USE_IPV6 */
778
779 #else /* if USE_RESOLVE_ON_IPS */
780 #ifndef CURL_DISABLE_DOH
781 /* First check if this is an IPv4 address string */
782 if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
783 /* This is a dotted IP address 123.123.123.123-style */
784 ipnum = TRUE;
785 #ifdef USE_IPV6
786 else {
787 struct in6_addr in6;
788 /* check if this is an IPv6 address string */
789 if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
790 /* This is an IPv6 address literal */
791 ipnum = TRUE;
792 }
793 #endif /* USE_IPV6 */
794 #endif /* CURL_DISABLE_DOH */
795
796 #endif /* !USE_RESOLVE_ON_IPS */
797
798 if(!addr) {
799 if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
800 return CURLRESOLV_ERROR;
801
802 if(strcasecompare(hostname, "localhost") ||
803 strcasecompare(hostname, "localhost.") ||
804 tailmatch(hostname, ".localhost") ||
805 tailmatch(hostname, ".localhost."))
806 addr = get_localhost(port, hostname);
807 #ifndef CURL_DISABLE_DOH
808 else if(allowDOH && data->set.doh && !ipnum)
809 addr = Curl_doh(data, hostname, port, &respwait);
810 #endif
811 else {
812 /* Check what IP specifics the app has requested and if we can provide
813 * it. If not, bail out. */
814 if(!Curl_ipvalid(data, conn))
815 return CURLRESOLV_ERROR;
816 /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
817 non-zero value indicating that we need to wait for the response to
818 the resolve call */
819 addr = Curl_getaddrinfo(data, hostname, port, &respwait);
820 }
821 }
822 if(!addr) {
823 if(respwait) {
824 /* the response to our resolve call will come asynchronously at
825 a later time, good or bad */
826 /* First, check that we have not received the info by now */
827 result = Curl_resolv_check(data, &dns);
828 if(result) /* error detected */
829 return CURLRESOLV_ERROR;
830 if(dns)
831 rc = CURLRESOLV_RESOLVED; /* pointer provided */
832 else
833 rc = CURLRESOLV_PENDING; /* no info yet */
834 }
835 }
836 else {
837 if(data->share)
838 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
839
840 /* we got a response, store it in the cache */
841 dns = Curl_cache_addr(data, addr, hostname, 0, port, FALSE);
842
843 if(data->share)
844 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
845
846 if(!dns)
847 /* returned failure, bail out nicely */
848 Curl_freeaddrinfo(addr);
849 else {
850 rc = CURLRESOLV_RESOLVED;
851 show_resolve_info(data, dns);
852 }
853 }
854 }
855
856 *entry = dns;
857
858 return rc;
859 }
860
861 #ifdef USE_ALARM_TIMEOUT
862 /*
863 * This signal handler jumps back into the main libcurl code and continues
864 * execution. This effectively causes the remainder of the application to run
865 * within a signal handler which is nonportable and could lead to problems.
866 */
867 CURL_NORETURN static
alarmfunc(int sig)868 void alarmfunc(int sig)
869 {
870 (void)sig;
871 siglongjmp(curl_jmpenv, 1);
872 }
873 #endif /* USE_ALARM_TIMEOUT */
874
875 /*
876 * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
877 * timeout. This function might return immediately if we are using asynch
878 * resolves. See the return codes.
879 *
880 * The cache entry we return will get its 'inuse' counter increased when this
881 * function is used. You MUST call Curl_resolv_unlink() later (when you are
882 * done using this struct) to decrease the reference counter again.
883 *
884 * If built with a synchronous resolver and use of signals is not
885 * disabled by the application, then a nonzero timeout will cause a
886 * timeout after the specified number of milliseconds. Otherwise, timeout
887 * is ignored.
888 *
889 * Return codes:
890 *
891 * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
892 * CURLRESOLV_ERROR (-1) = error, no pointer
893 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
894 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
895 */
896
Curl_resolv_timeout(struct Curl_easy * data,const char * hostname,int port,struct Curl_dns_entry ** entry,timediff_t timeoutms)897 enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
898 const char *hostname,
899 int port,
900 struct Curl_dns_entry **entry,
901 timediff_t timeoutms)
902 {
903 #ifdef USE_ALARM_TIMEOUT
904 #ifdef HAVE_SIGACTION
905 struct sigaction keep_sigact; /* store the old struct here */
906 volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
907 struct sigaction sigact;
908 #else
909 #ifdef HAVE_SIGNAL
910 void (*keep_sigact)(int); /* store the old handler here */
911 #endif /* HAVE_SIGNAL */
912 #endif /* HAVE_SIGACTION */
913 volatile long timeout;
914 volatile unsigned int prev_alarm = 0;
915 #endif /* USE_ALARM_TIMEOUT */
916 enum resolve_t rc;
917
918 *entry = NULL;
919
920 if(timeoutms < 0)
921 /* got an already expired timeout */
922 return CURLRESOLV_TIMEDOUT;
923
924 #ifdef USE_ALARM_TIMEOUT
925 if(data->set.no_signal)
926 /* Ignore the timeout when signals are disabled */
927 timeout = 0;
928 else
929 timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
930
931 if(!timeout)
932 /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
933 return Curl_resolv(data, hostname, port, TRUE, entry);
934
935 if(timeout < 1000) {
936 /* The alarm() function only provides integer second resolution, so if
937 we want to wait less than one second we must bail out already now. */
938 failf(data,
939 "remaining timeout of %ld too small to resolve via SIGALRM method",
940 timeout);
941 return CURLRESOLV_TIMEDOUT;
942 }
943 /* This allows us to time-out from the name resolver, as the timeout
944 will generate a signal and we will siglongjmp() from that here.
945 This technique has problems (see alarmfunc).
946 This should be the last thing we do before calling Curl_resolv(),
947 as otherwise we would have to worry about variables that get modified
948 before we invoke Curl_resolv() (and thus use "volatile"). */
949 curl_simple_lock_lock(&curl_jmpenv_lock);
950
951 if(sigsetjmp(curl_jmpenv, 1)) {
952 /* this is coming from a siglongjmp() after an alarm signal */
953 failf(data, "name lookup timed out");
954 rc = CURLRESOLV_ERROR;
955 goto clean_up;
956 }
957 else {
958 /*************************************************************
959 * Set signal handler to catch SIGALRM
960 * Store the old value to be able to set it back later!
961 *************************************************************/
962 #ifdef HAVE_SIGACTION
963 sigaction(SIGALRM, NULL, &sigact);
964 keep_sigact = sigact;
965 keep_copysig = TRUE; /* yes, we have a copy */
966 sigact.sa_handler = alarmfunc;
967 #ifdef SA_RESTART
968 /* HP-UX does not have SA_RESTART but defaults to that behavior! */
969 sigact.sa_flags &= ~SA_RESTART;
970 #endif
971 /* now set the new struct */
972 sigaction(SIGALRM, &sigact, NULL);
973 #else /* HAVE_SIGACTION */
974 /* no sigaction(), revert to the much lamer signal() */
975 #ifdef HAVE_SIGNAL
976 keep_sigact = signal(SIGALRM, alarmfunc);
977 #endif
978 #endif /* HAVE_SIGACTION */
979
980 /* alarm() makes a signal get sent when the timeout fires off, and that
981 will abort system calls */
982 prev_alarm = alarm(curlx_sltoui(timeout/1000L));
983 }
984
985 #else
986 #ifndef CURLRES_ASYNCH
987 if(timeoutms)
988 infof(data, "timeout on name lookup is not supported");
989 #else
990 (void)timeoutms; /* timeoutms not used with an async resolver */
991 #endif
992 #endif /* USE_ALARM_TIMEOUT */
993
994 /* Perform the actual name resolution. This might be interrupted by an
995 * alarm if it takes too long.
996 */
997 rc = Curl_resolv(data, hostname, port, TRUE, entry);
998
999 #ifdef USE_ALARM_TIMEOUT
1000 clean_up:
1001
1002 if(!prev_alarm)
1003 /* deactivate a possibly active alarm before uninstalling the handler */
1004 alarm(0);
1005
1006 #ifdef HAVE_SIGACTION
1007 if(keep_copysig) {
1008 /* we got a struct as it looked before, now put that one back nice
1009 and clean */
1010 sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
1011 }
1012 #else
1013 #ifdef HAVE_SIGNAL
1014 /* restore the previous SIGALRM handler */
1015 signal(SIGALRM, keep_sigact);
1016 #endif
1017 #endif /* HAVE_SIGACTION */
1018
1019 curl_simple_lock_unlock(&curl_jmpenv_lock);
1020
1021 /* switch back the alarm() to either zero or to what it was before minus
1022 the time we spent until now! */
1023 if(prev_alarm) {
1024 /* there was an alarm() set before us, now put it back */
1025 timediff_t elapsed_secs = Curl_timediff(Curl_now(),
1026 data->conn->created) / 1000;
1027
1028 /* the alarm period is counted in even number of seconds */
1029 unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs);
1030
1031 if(!alarm_set ||
1032 ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
1033 /* if the alarm time-left reached zero or turned "negative" (counted
1034 with unsigned values), we should fire off a SIGALRM here, but we
1035 will not, and zero would be to switch it off so we never set it to
1036 less than 1! */
1037 alarm(1);
1038 rc = CURLRESOLV_TIMEDOUT;
1039 failf(data, "Previous alarm fired off");
1040 }
1041 else
1042 alarm((unsigned int)alarm_set);
1043 }
1044 #endif /* USE_ALARM_TIMEOUT */
1045
1046 return rc;
1047 }
1048
1049 /*
1050 * Curl_resolv_unlink() releases a reference to the given cached DNS entry.
1051 * When the reference count reaches 0, the entry is destroyed. It is important
1052 * that only one unlink is made for each Curl_resolv() call.
1053 *
1054 * May be called with 'data' == NULL for global cache.
1055 */
Curl_resolv_unlink(struct Curl_easy * data,struct Curl_dns_entry ** pdns)1056 void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns)
1057 {
1058 struct Curl_dns_entry *dns = *pdns;
1059 *pdns = NULL;
1060 if(data && data->share)
1061 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1062
1063 hostcache_unlink_entry(dns);
1064
1065 if(data && data->share)
1066 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1067 }
1068
1069 /*
1070 * File-internal: release cache dns entry reference, free if inuse drops to 0
1071 */
hostcache_unlink_entry(void * entry)1072 static void hostcache_unlink_entry(void *entry)
1073 {
1074 struct Curl_dns_entry *dns = (struct Curl_dns_entry *) entry;
1075 DEBUGASSERT(dns && (dns->refcount > 0));
1076
1077 dns->refcount--;
1078 if(dns->refcount == 0) {
1079 Curl_freeaddrinfo(dns->addr);
1080 #ifdef USE_HTTPSRR
1081 if(dns->hinfo) {
1082 free(dns->hinfo->target);
1083 free(dns->hinfo->ipv4hints);
1084 free(dns->hinfo->echconfiglist);
1085 free(dns->hinfo->ipv6hints);
1086 free(dns->hinfo);
1087 }
1088 #endif
1089 free(dns);
1090 }
1091 }
1092
1093 /*
1094 * Curl_init_dnscache() inits a new DNS cache.
1095 */
Curl_init_dnscache(struct Curl_hash * hash,size_t size)1096 void Curl_init_dnscache(struct Curl_hash *hash, size_t size)
1097 {
1098 Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
1099 hostcache_unlink_entry);
1100 }
1101
1102 /*
1103 * Curl_hostcache_clean()
1104 *
1105 * This _can_ be called with 'data' == NULL but then of course no locking
1106 * can be done!
1107 */
1108
Curl_hostcache_clean(struct Curl_easy * data,struct Curl_hash * hash)1109 void Curl_hostcache_clean(struct Curl_easy *data,
1110 struct Curl_hash *hash)
1111 {
1112 if(data && data->share)
1113 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1114
1115 Curl_hash_clean(hash);
1116
1117 if(data && data->share)
1118 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1119 }
1120
1121
Curl_loadhostpairs(struct Curl_easy * data)1122 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
1123 {
1124 struct curl_slist *hostp;
1125 char *host_end;
1126
1127 /* Default is no wildcard found */
1128 data->state.wildcard_resolve = FALSE;
1129
1130 for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
1131 char entry_id[MAX_HOSTCACHE_LEN];
1132 if(!hostp->data)
1133 continue;
1134 if(hostp->data[0] == '-') {
1135 unsigned long num = 0;
1136 size_t entry_len;
1137 size_t hlen = 0;
1138 host_end = strchr(&hostp->data[1], ':');
1139
1140 if(host_end) {
1141 hlen = host_end - &hostp->data[1];
1142 num = strtoul(++host_end, NULL, 10);
1143 if(!hlen || (num > 0xffff))
1144 host_end = NULL;
1145 }
1146 if(!host_end) {
1147 infof(data, "Bad syntax CURLOPT_RESOLVE removal entry '%s'",
1148 hostp->data);
1149 continue;
1150 }
1151 /* Create an entry id, based upon the hostname and port */
1152 entry_len = create_hostcache_id(&hostp->data[1], hlen, (int)num,
1153 entry_id, sizeof(entry_id));
1154 if(data->share)
1155 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1156
1157 /* delete entry, ignore if it did not exist */
1158 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
1159
1160 if(data->share)
1161 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1162 }
1163 else {
1164 struct Curl_dns_entry *dns;
1165 struct Curl_addrinfo *head = NULL, *tail = NULL;
1166 size_t entry_len;
1167 char address[64];
1168 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1169 char *addresses = NULL;
1170 #endif
1171 char *addr_begin;
1172 char *addr_end;
1173 char *port_ptr;
1174 int port = 0;
1175 char *end_ptr;
1176 bool permanent = TRUE;
1177 unsigned long tmp_port;
1178 bool error = TRUE;
1179 char *host_begin = hostp->data;
1180 size_t hlen = 0;
1181
1182 if(host_begin[0] == '+') {
1183 host_begin++;
1184 permanent = FALSE;
1185 }
1186 host_end = strchr(host_begin, ':');
1187 if(!host_end)
1188 goto err;
1189 hlen = host_end - host_begin;
1190
1191 port_ptr = host_end + 1;
1192 tmp_port = strtoul(port_ptr, &end_ptr, 10);
1193 if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
1194 goto err;
1195
1196 port = (int)tmp_port;
1197 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1198 addresses = end_ptr + 1;
1199 #endif
1200
1201 while(*end_ptr) {
1202 size_t alen;
1203 struct Curl_addrinfo *ai;
1204
1205 addr_begin = end_ptr + 1;
1206 addr_end = strchr(addr_begin, ',');
1207 if(!addr_end)
1208 addr_end = addr_begin + strlen(addr_begin);
1209 end_ptr = addr_end;
1210
1211 /* allow IP(v6) address within [brackets] */
1212 if(*addr_begin == '[') {
1213 if(addr_end == addr_begin || *(addr_end - 1) != ']')
1214 goto err;
1215 ++addr_begin;
1216 --addr_end;
1217 }
1218
1219 alen = addr_end - addr_begin;
1220 if(!alen)
1221 continue;
1222
1223 if(alen >= sizeof(address))
1224 goto err;
1225
1226 memcpy(address, addr_begin, alen);
1227 address[alen] = '\0';
1228
1229 #ifndef USE_IPV6
1230 if(strchr(address, ':')) {
1231 infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
1232 address);
1233 continue;
1234 }
1235 #endif
1236
1237 ai = Curl_str2addr(address, port);
1238 if(!ai) {
1239 infof(data, "Resolve address '%s' found illegal", address);
1240 goto err;
1241 }
1242
1243 if(tail) {
1244 tail->ai_next = ai;
1245 tail = tail->ai_next;
1246 }
1247 else {
1248 head = tail = ai;
1249 }
1250 }
1251
1252 if(!head)
1253 goto err;
1254
1255 error = FALSE;
1256 err:
1257 if(error) {
1258 failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'",
1259 hostp->data);
1260 Curl_freeaddrinfo(head);
1261 return CURLE_SETOPT_OPTION_SYNTAX;
1262 }
1263
1264 /* Create an entry id, based upon the hostname and port */
1265 entry_len = create_hostcache_id(host_begin, hlen, port,
1266 entry_id, sizeof(entry_id));
1267
1268 if(data->share)
1269 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1270
1271 /* See if it is already in our dns cache */
1272 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
1273
1274 if(dns) {
1275 infof(data, "RESOLVE %.*s:%d - old addresses discarded",
1276 (int)hlen, host_begin, port);
1277 /* delete old entry, there are two reasons for this
1278 1. old entry may have different addresses.
1279 2. even if entry with correct addresses is already in the cache,
1280 but if it is close to expire, then by the time next http
1281 request is made, it can get expired and pruned because old
1282 entry is not necessarily marked as permanent.
1283 3. when adding a non-permanent entry, we want it to remove and
1284 replace an existing permanent entry.
1285 4. when adding a non-permanent entry, we want it to get a "fresh"
1286 timeout that starts _now_. */
1287
1288 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
1289 }
1290
1291 /* put this new host in the cache */
1292 dns = Curl_cache_addr(data, head, host_begin, hlen, port, permanent);
1293 if(dns) {
1294 /* release the returned reference; the cache itself will keep the
1295 * entry alive: */
1296 dns->refcount--;
1297 }
1298
1299 if(data->share)
1300 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1301
1302 if(!dns) {
1303 Curl_freeaddrinfo(head);
1304 return CURLE_OUT_OF_MEMORY;
1305 }
1306 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1307 infof(data, "Added %.*s:%d:%s to DNS cache%s",
1308 (int)hlen, host_begin, port, addresses,
1309 permanent ? "" : " (non-permanent)");
1310 #endif
1311
1312 /* Wildcard hostname */
1313 if((hlen == 1) && (host_begin[0] == '*')) {
1314 infof(data, "RESOLVE *:%d using wildcard", port);
1315 data->state.wildcard_resolve = TRUE;
1316 }
1317 }
1318 }
1319 data->state.resolve = NULL; /* dealt with now */
1320
1321 return CURLE_OK;
1322 }
1323
1324 #ifndef CURL_DISABLE_VERBOSE_STRINGS
show_resolve_info(struct Curl_easy * data,struct Curl_dns_entry * dns)1325 static void show_resolve_info(struct Curl_easy *data,
1326 struct Curl_dns_entry *dns)
1327 {
1328 struct Curl_addrinfo *a;
1329 CURLcode result = CURLE_OK;
1330 #ifdef CURLRES_IPV6
1331 struct dynbuf out[2];
1332 #else
1333 struct dynbuf out[1];
1334 #endif
1335 DEBUGASSERT(data);
1336 DEBUGASSERT(dns);
1337
1338 if(!data->set.verbose ||
1339 /* ignore no name or numerical IP addresses */
1340 !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname))
1341 return;
1342
1343 a = dns->addr;
1344
1345 infof(data, "Host %s:%d was resolved.",
1346 (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport);
1347
1348 Curl_dyn_init(&out[0], 1024);
1349 #ifdef CURLRES_IPV6
1350 Curl_dyn_init(&out[1], 1024);
1351 #endif
1352
1353 while(a) {
1354 if(
1355 #ifdef CURLRES_IPV6
1356 a->ai_family == PF_INET6 ||
1357 #endif
1358 a->ai_family == PF_INET) {
1359 char buf[MAX_IPADR_LEN];
1360 struct dynbuf *d = &out[(a->ai_family != PF_INET)];
1361 Curl_printable_address(a, buf, sizeof(buf));
1362 if(Curl_dyn_len(d))
1363 result = Curl_dyn_addn(d, ", ", 2);
1364 if(!result)
1365 result = Curl_dyn_add(d, buf);
1366 if(result) {
1367 infof(data, "too many IP, cannot show");
1368 goto fail;
1369 }
1370 }
1371 a = a->ai_next;
1372 }
1373
1374 #ifdef CURLRES_IPV6
1375 infof(data, "IPv6: %s",
1376 (Curl_dyn_len(&out[1]) ? Curl_dyn_ptr(&out[1]) : "(none)"));
1377 #endif
1378 infof(data, "IPv4: %s",
1379 (Curl_dyn_len(&out[0]) ? Curl_dyn_ptr(&out[0]) : "(none)"));
1380
1381 fail:
1382 Curl_dyn_free(&out[0]);
1383 #ifdef CURLRES_IPV6
1384 Curl_dyn_free(&out[1]);
1385 #endif
1386 }
1387 #endif
1388
Curl_resolv_check(struct Curl_easy * data,struct Curl_dns_entry ** dns)1389 CURLcode Curl_resolv_check(struct Curl_easy *data,
1390 struct Curl_dns_entry **dns)
1391 {
1392 CURLcode result;
1393 #if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
1394 (void)data;
1395 (void)dns;
1396 #endif
1397 #ifndef CURL_DISABLE_DOH
1398 if(data->conn->bits.doh) {
1399 result = Curl_doh_is_resolved(data, dns);
1400 }
1401 else
1402 #endif
1403 result = Curl_resolver_is_resolved(data, dns);
1404 if(*dns)
1405 show_resolve_info(data, *dns);
1406 return result;
1407 }
1408
Curl_resolv_getsock(struct Curl_easy * data,curl_socket_t * socks)1409 int Curl_resolv_getsock(struct Curl_easy *data,
1410 curl_socket_t *socks)
1411 {
1412 #ifdef CURLRES_ASYNCH
1413 #ifndef CURL_DISABLE_DOH
1414 if(data->conn->bits.doh)
1415 /* nothing to wait for during DoH resolve, those handles have their own
1416 sockets */
1417 return GETSOCK_BLANK;
1418 #endif
1419 return Curl_resolver_getsock(data, socks);
1420 #else
1421 (void)data;
1422 (void)socks;
1423 return GETSOCK_BLANK;
1424 #endif
1425 }
1426
1427 /* Call this function after Curl_connect() has returned async=TRUE and
1428 then a successful name resolve has been received.
1429
1430 Note: this function disconnects and frees the conn data in case of
1431 resolve failure */
Curl_once_resolved(struct Curl_easy * data,bool * protocol_done)1432 CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
1433 {
1434 CURLcode result;
1435 struct connectdata *conn = data->conn;
1436
1437 #ifdef USE_CURL_ASYNC
1438 if(data->state.async.dns) {
1439 conn->dns_entry = data->state.async.dns;
1440 data->state.async.dns = NULL;
1441 }
1442 #endif
1443
1444 result = Curl_setup_conn(data, protocol_done);
1445
1446 if(result) {
1447 Curl_detach_connection(data);
1448 Curl_cpool_disconnect(data, conn, TRUE);
1449 }
1450 return result;
1451 }
1452
1453 /*
1454 * Curl_resolver_error() calls failf() with the appropriate message after a
1455 * resolve error
1456 */
1457
1458 #ifdef USE_CURL_ASYNC
Curl_resolver_error(struct Curl_easy * data)1459 CURLcode Curl_resolver_error(struct Curl_easy *data)
1460 {
1461 const char *host_or_proxy;
1462 CURLcode result;
1463
1464 #ifndef CURL_DISABLE_PROXY
1465 struct connectdata *conn = data->conn;
1466 if(conn->bits.httpproxy) {
1467 host_or_proxy = "proxy";
1468 result = CURLE_COULDNT_RESOLVE_PROXY;
1469 }
1470 else
1471 #endif
1472 {
1473 host_or_proxy = "host";
1474 result = CURLE_COULDNT_RESOLVE_HOST;
1475 }
1476
1477 failf(data, "Could not resolve %s: %s", host_or_proxy,
1478 data->state.async.hostname);
1479
1480 return result;
1481 }
1482 #endif /* USE_CURL_ASYNC */
1483