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