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