1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel (at) 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.haxx.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 ***************************************************************************/ 22 23 #include "curl_setup.h" 24 25 #ifdef HAVE_NETINET_IN_H 26 #include <netinet/in.h> 27 #endif 28 #ifdef HAVE_NETINET_IN6_H 29 #include <netinet/in6.h> 30 #endif 31 #ifdef HAVE_NETDB_H 32 #include <netdb.h> 33 #endif 34 #ifdef HAVE_ARPA_INET_H 35 #include <arpa/inet.h> 36 #endif 37 #ifdef __VMS 38 #include <in.h> 39 #include <inet.h> 40 #endif 41 42 #ifdef HAVE_SETJMP_H 43 #include <setjmp.h> 44 #endif 45 #ifdef HAVE_SIGNAL_H 46 #include <signal.h> 47 #endif 48 49 #ifdef HAVE_PROCESS_H 50 #include <process.h> 51 #endif 52 53 #include "urldata.h" 54 #include "sendf.h" 55 #include "hostip.h" 56 #include "hash.h" 57 #include "rand.h" 58 #include "share.h" 59 #include "strerror.h" 60 #include "url.h" 61 #include "inet_ntop.h" 62 #include "multiif.h" 63 #include "doh.h" 64 #include "warnless.h" 65 /* The last 3 #include files should be in this order */ 66 #include "curl_printf.h" 67 #include "curl_memory.h" 68 #include "memdebug.h" 69 70 #if defined(CURLRES_SYNCH) && \ 71 defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP) 72 /* alarm-based timeouts can only be used with all the dependencies satisfied */ 73 #define USE_ALARM_TIMEOUT 74 #endif 75 76 #define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */ 77 78 /* 79 * hostip.c explained 80 * ================== 81 * 82 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c 83 * source file are these: 84 * 85 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use 86 * that. The host may not be able to resolve IPv6, but we don't really have to 87 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4 88 * defined. 89 * 90 * CURLRES_ARES - is defined if libcurl is built to use c-ares for 91 * asynchronous name resolves. This can be Windows or *nix. 92 * 93 * CURLRES_THREADED - is defined if libcurl is built to run under (native) 94 * Windows, and then the name resolve will be done in a new thread, and the 95 * supported API will be the same as for ares-builds. 96 * 97 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If 98 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is 99 * defined. 100 * 101 * The host*.c sources files are split up like this: 102 * 103 * hostip.c - method-independent resolver functions and utility functions 104 * hostasyn.c - functions for asynchronous name resolves 105 * hostsyn.c - functions for synchronous name resolves 106 * hostip4.c - IPv4 specific functions 107 * hostip6.c - IPv6 specific functions 108 * 109 * The two asynchronous name resolver backends are implemented in: 110 * asyn-ares.c - functions for ares-using name resolves 111 * asyn-thread.c - functions for threaded name resolves 112 113 * The hostip.h is the united header file for all this. It defines the 114 * CURLRES_* defines based on the config*.h and curl_setup.h defines. 115 */ 116 117 /* These two symbols are for the global DNS cache */ 118 static struct curl_hash hostname_cache; 119 static int host_cache_initialized; 120 121 static void freednsentry(void *freethis); 122 123 /* 124 * Curl_global_host_cache_init() initializes and sets up a global DNS cache. 125 * Global DNS cache is general badness. Do not use. This will be removed in 126 * a future version. Use the share interface instead! 127 * 128 * Returns a struct curl_hash pointer on success, NULL on failure. 129 */ 130 struct curl_hash *Curl_global_host_cache_init(void) 131 { 132 int rc = 0; 133 if(!host_cache_initialized) { 134 rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str, 135 Curl_str_key_compare, freednsentry); 136 if(!rc) 137 host_cache_initialized = 1; 138 } 139 return rc?NULL:&hostname_cache; 140 } 141 142 /* 143 * Destroy and cleanup the global DNS cache 144 */ 145 void Curl_global_host_cache_dtor(void) 146 { 147 if(host_cache_initialized) { 148 Curl_hash_destroy(&hostname_cache); 149 host_cache_initialized = 0; 150 } 151 } 152 153 /* 154 * Return # of addresses in a Curl_addrinfo struct 155 */ 156 int Curl_num_addresses(const Curl_addrinfo *addr) 157 { 158 int i = 0; 159 while(addr) { 160 addr = addr->ai_next; 161 i++; 162 } 163 return i; 164 } 165 166 /* 167 * Curl_printable_address() returns a printable version of the 1st address 168 * given in the 'ai' argument. The result will be stored in the buf that is 169 * bufsize bytes big. 170 * 171 * If the conversion fails, it returns NULL. 172 */ 173 const char * 174 Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize) 175 { 176 const struct sockaddr_in *sa4; 177 const struct in_addr *ipaddr4; 178 #ifdef ENABLE_IPV6 179 const struct sockaddr_in6 *sa6; 180 const struct in6_addr *ipaddr6; 181 #endif 182 183 switch(ai->ai_family) { 184 case AF_INET: 185 sa4 = (const void *)ai->ai_addr; 186 ipaddr4 = &sa4->sin_addr; 187 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, 188 bufsize); 189 #ifdef ENABLE_IPV6 190 case AF_INET6: 191 sa6 = (const void *)ai->ai_addr; 192 ipaddr6 = &sa6->sin6_addr; 193 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, 194 bufsize); 195 #endif 196 default: 197 break; 198 } 199 return NULL; 200 } 201 202 /* 203 * Create a hostcache id string for the provided host + port, to be used by 204 * the DNS caching. Without alloc. 205 */ 206 static void 207 create_hostcache_id(const char *name, int port, char *ptr, size_t buflen) 208 { 209 size_t len = strlen(name); 210 if(len > (buflen - 7)) 211 len = buflen - 7; 212 /* store and lower case the name */ 213 while(len--) 214 *ptr++ = (char)TOLOWER(*name++); 215 msnprintf(ptr, 7, ":%u", port); 216 } 217 218 struct hostcache_prune_data { 219 long cache_timeout; 220 time_t now; 221 }; 222 223 /* 224 * This function is set as a callback to be called for every entry in the DNS 225 * cache when we want to prune old unused entries. 226 * 227 * Returning non-zero means remove the entry, return 0 to keep it in the 228 * cache. 229 */ 230 static int 231 hostcache_timestamp_remove(void *datap, void *hc) 232 { 233 struct hostcache_prune_data *data = 234 (struct hostcache_prune_data *) datap; 235 struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc; 236 237 return (0 != c->timestamp) 238 && (data->now - c->timestamp >= data->cache_timeout); 239 } 240 241 /* 242 * Prune the DNS cache. This assumes that a lock has already been taken. 243 */ 244 static void 245 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now) 246 { 247 struct hostcache_prune_data user; 248 249 user.cache_timeout = cache_timeout; 250 user.now = now; 251 252 Curl_hash_clean_with_criterium(hostcache, 253 (void *) &user, 254 hostcache_timestamp_remove); 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_hostcache_prune(struct Curl_easy *data) 262 { 263 time_t now; 264 265 if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache) 266 /* cache forever means never prune, and NULL hostcache means 267 we can't do it */ 268 return; 269 270 if(data->share) 271 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); 272 273 time(&now); 274 275 /* Remove outdated and unused entries from the hostcache */ 276 hostcache_prune(data->dns.hostcache, 277 data->set.dns_cache_timeout, 278 now); 279 280 if(data->share) 281 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); 282 } 283 284 #ifdef HAVE_SIGSETJMP 285 /* Beware this is a global and unique instance. This is used to store the 286 return address that we can jump back to from inside a signal handler. This 287 is not thread-safe stuff. */ 288 sigjmp_buf curl_jmpenv; 289 #endif 290 291 /* lookup address, returns entry if found and not stale */ 292 static struct Curl_dns_entry * 293 fetch_addr(struct connectdata *conn, 294 const char *hostname, 295 int port) 296 { 297 struct Curl_dns_entry *dns = NULL; 298 size_t entry_len; 299 struct Curl_easy *data = conn->data; 300 char entry_id[MAX_HOSTCACHE_LEN]; 301 302 /* Create an entry id, based upon the hostname and port */ 303 create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); 304 entry_len = strlen(entry_id); 305 306 /* See if its already in our dns cache */ 307 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); 308 309 /* No entry found in cache, check if we might have a wildcard entry */ 310 if(!dns && data->change.wildcard_resolve) { 311 create_hostcache_id("*", port, entry_id, sizeof(entry_id)); 312 entry_len = strlen(entry_id); 313 314 /* See if it's already in our dns cache */ 315 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); 316 } 317 318 if(dns && (data->set.dns_cache_timeout != -1)) { 319 /* See whether the returned entry is stale. Done before we release lock */ 320 struct hostcache_prune_data user; 321 322 time(&user.now); 323 user.cache_timeout = data->set.dns_cache_timeout; 324 325 if(hostcache_timestamp_remove(&user, dns)) { 326 infof(data, "Hostname in DNS cache was stale, zapped\n"); 327 dns = NULL; /* the memory deallocation is being handled by the hash */ 328 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); 329 } 330 } 331 332 return dns; 333 } 334 335 /* 336 * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache. 337 * 338 * Curl_resolv() checks initially and multi_runsingle() checks each time 339 * it discovers the handle in the state WAITRESOLVE whether the hostname 340 * has already been resolved and the address has already been stored in 341 * the DNS cache. This short circuits waiting for a lot of pending 342 * lookups for the same hostname requested by different handles. 343 * 344 * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. 345 * 346 * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after 347 * use, or we'll leak memory! 348 */ 349 struct Curl_dns_entry * 350 Curl_fetch_addr(struct connectdata *conn, 351 const char *hostname, 352 int port) 353 { 354 struct Curl_easy *data = conn->data; 355 struct Curl_dns_entry *dns = NULL; 356 357 if(data->share) 358 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); 359 360 dns = fetch_addr(conn, hostname, port); 361 362 if(dns) 363 dns->inuse++; /* we use it! */ 364 365 if(data->share) 366 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); 367 368 return dns; 369 } 370 371 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, 372 Curl_addrinfo **addr); 373 374 /* 375 * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' 376 * struct by re-linking its linked list. 377 * 378 * The addr argument should be the address of a pointer to the head node of a 379 * `Curl_addrinfo` list and it will be modified to point to the new head after 380 * shuffling. 381 * 382 * Not declared static only to make it easy to use in a unit test! 383 * 384 * @unittest: 1608 385 */ 386 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, 387 Curl_addrinfo **addr) 388 { 389 CURLcode result = CURLE_OK; 390 const int num_addrs = Curl_num_addresses(*addr); 391 392 if(num_addrs > 1) { 393 Curl_addrinfo **nodes; 394 infof(data, "Shuffling %i addresses", num_addrs); 395 396 nodes = malloc(num_addrs*sizeof(*nodes)); 397 if(nodes) { 398 int i; 399 unsigned int *rnd; 400 const size_t rnd_size = num_addrs * sizeof(*rnd); 401 402 /* build a plain array of Curl_addrinfo pointers */ 403 nodes[0] = *addr; 404 for(i = 1; i < num_addrs; i++) { 405 nodes[i] = nodes[i-1]->ai_next; 406 } 407 408 rnd = malloc(rnd_size); 409 if(rnd) { 410 /* Fisher-Yates shuffle */ 411 if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { 412 Curl_addrinfo *swap_tmp; 413 for(i = num_addrs - 1; i > 0; i--) { 414 swap_tmp = nodes[rnd[i] % (i + 1)]; 415 nodes[rnd[i] % (i + 1)] = nodes[i]; 416 nodes[i] = swap_tmp; 417 } 418 419 /* relink list in the new order */ 420 for(i = 1; i < num_addrs; i++) { 421 nodes[i-1]->ai_next = nodes[i]; 422 } 423 424 nodes[num_addrs-1]->ai_next = NULL; 425 *addr = nodes[0]; 426 } 427 free(rnd); 428 } 429 else 430 result = CURLE_OUT_OF_MEMORY; 431 free(nodes); 432 } 433 else 434 result = CURLE_OUT_OF_MEMORY; 435 } 436 return result; 437 } 438 439 /* 440 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. 441 * 442 * When calling Curl_resolv() has resulted in a response with a returned 443 * address, we call this function to store the information in the dns 444 * cache etc 445 * 446 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. 447 */ 448 struct Curl_dns_entry * 449 Curl_cache_addr(struct Curl_easy *data, 450 Curl_addrinfo *addr, 451 const char *hostname, 452 int port) 453 { 454 char entry_id[MAX_HOSTCACHE_LEN]; 455 size_t entry_len; 456 struct Curl_dns_entry *dns; 457 struct Curl_dns_entry *dns2; 458 459 /* shuffle addresses if requested */ 460 if(data->set.dns_shuffle_addresses) { 461 CURLcode result = Curl_shuffle_addr(data, &addr); 462 if(result) 463 return NULL; 464 } 465 466 /* Create a new cache entry */ 467 dns = calloc(1, sizeof(struct Curl_dns_entry)); 468 if(!dns) { 469 return NULL; 470 } 471 472 /* Create an entry id, based upon the hostname and port */ 473 create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); 474 entry_len = strlen(entry_id); 475 476 dns->inuse = 1; /* the cache has the first reference */ 477 dns->addr = addr; /* this is the address(es) */ 478 time(&dns->timestamp); 479 if(dns->timestamp == 0) 480 dns->timestamp = 1; /* zero indicates CURLOPT_RESOLVE entry */ 481 482 /* Store the resolved data in our DNS cache. */ 483 dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1, 484 (void *)dns); 485 if(!dns2) { 486 free(dns); 487 return NULL; 488 } 489 490 dns = dns2; 491 dns->inuse++; /* mark entry as in-use */ 492 return dns; 493 } 494 495 /* 496 * Curl_resolv() is the main name resolve function within libcurl. It resolves 497 * a name and returns a pointer to the entry in the 'entry' argument (if one 498 * is provided). This function might return immediately if we're using asynch 499 * resolves. See the return codes. 500 * 501 * The cache entry we return will get its 'inuse' counter increased when this 502 * function is used. You MUST call Curl_resolv_unlock() later (when you're 503 * done using this struct) to decrease the counter again. 504 * 505 * In debug mode, we specifically test for an interface name "LocalHost" 506 * and resolve "localhost" instead as a means to permit test cases 507 * to connect to a local test server with any host name. 508 * 509 * Return codes: 510 * 511 * CURLRESOLV_ERROR (-1) = error, no pointer 512 * CURLRESOLV_RESOLVED (0) = OK, pointer provided 513 * CURLRESOLV_PENDING (1) = waiting for response, no pointer 514 */ 515 516 int Curl_resolv(struct connectdata *conn, 517 const char *hostname, 518 int port, 519 struct Curl_dns_entry **entry) 520 { 521 struct Curl_dns_entry *dns = NULL; 522 struct Curl_easy *data = conn->data; 523 CURLcode result; 524 int rc = CURLRESOLV_ERROR; /* default to failure */ 525 526 *entry = NULL; 527 528 if(data->share) 529 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); 530 531 dns = fetch_addr(conn, hostname, port); 532 533 if(dns) { 534 infof(data, "Hostname %s was found in DNS cache\n", hostname); 535 dns->inuse++; /* we use it! */ 536 rc = CURLRESOLV_RESOLVED; 537 } 538 539 if(data->share) 540 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); 541 542 if(!dns) { 543 /* The entry was not in the cache. Resolve it to IP address */ 544 545 Curl_addrinfo *addr; 546 int respwait = 0; 547 548 /* Check what IP specifics the app has requested and if we can provide it. 549 * If not, bail out. */ 550 if(!Curl_ipvalid(conn)) 551 return CURLRESOLV_ERROR; 552 553 /* notify the resolver start callback */ 554 if(data->set.resolver_start) { 555 int st; 556 Curl_set_in_callback(data, true); 557 st = data->set.resolver_start(data->state.resolver, NULL, 558 data->set.resolver_start_client); 559 Curl_set_in_callback(data, false); 560 if(st) 561 return CURLRESOLV_ERROR; 562 } 563 564 if(data->set.doh) { 565 addr = Curl_doh(conn, hostname, port, &respwait); 566 } 567 else { 568 /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a 569 non-zero value indicating that we need to wait for the response to the 570 resolve call */ 571 addr = Curl_getaddrinfo(conn, 572 #ifdef DEBUGBUILD 573 (data->set.str[STRING_DEVICE] 574 && !strcmp(data->set.str[STRING_DEVICE], 575 "LocalHost"))?"localhost": 576 #endif 577 hostname, port, &respwait); 578 } 579 if(!addr) { 580 if(respwait) { 581 /* the response to our resolve call will come asynchronously at 582 a later time, good or bad */ 583 /* First, check that we haven't received the info by now */ 584 result = Curl_resolv_check(conn, &dns); 585 if(result) /* error detected */ 586 return CURLRESOLV_ERROR; 587 if(dns) 588 rc = CURLRESOLV_RESOLVED; /* pointer provided */ 589 else 590 rc = CURLRESOLV_PENDING; /* no info yet */ 591 } 592 } 593 else { 594 if(data->share) 595 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); 596 597 /* we got a response, store it in the cache */ 598 dns = Curl_cache_addr(data, addr, hostname, port); 599 600 if(data->share) 601 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); 602 603 if(!dns) 604 /* returned failure, bail out nicely */ 605 Curl_freeaddrinfo(addr); 606 else 607 rc = CURLRESOLV_RESOLVED; 608 } 609 } 610 611 *entry = dns; 612 613 return rc; 614 } 615 616 #ifdef USE_ALARM_TIMEOUT 617 /* 618 * This signal handler jumps back into the main libcurl code and continues 619 * execution. This effectively causes the remainder of the application to run 620 * within a signal handler which is nonportable and could lead to problems. 621 */ 622 static 623 RETSIGTYPE alarmfunc(int sig) 624 { 625 /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */ 626 (void)sig; 627 siglongjmp(curl_jmpenv, 1); 628 } 629 #endif /* USE_ALARM_TIMEOUT */ 630 631 /* 632 * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a 633 * timeout. This function might return immediately if we're using asynch 634 * resolves. See the return codes. 635 * 636 * The cache entry we return will get its 'inuse' counter increased when this 637 * function is used. You MUST call Curl_resolv_unlock() later (when you're 638 * done using this struct) to decrease the counter again. 639 * 640 * If built with a synchronous resolver and use of signals is not 641 * disabled by the application, then a nonzero timeout will cause a 642 * timeout after the specified number of milliseconds. Otherwise, timeout 643 * is ignored. 644 * 645 * Return codes: 646 * 647 * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired 648 * CURLRESOLV_ERROR (-1) = error, no pointer 649 * CURLRESOLV_RESOLVED (0) = OK, pointer provided 650 * CURLRESOLV_PENDING (1) = waiting for response, no pointer 651 */ 652 653 int Curl_resolv_timeout(struct connectdata *conn, 654 const char *hostname, 655 int port, 656 struct Curl_dns_entry **entry, 657 time_t timeoutms) 658 { 659 #ifdef USE_ALARM_TIMEOUT 660 #ifdef HAVE_SIGACTION 661 struct sigaction keep_sigact; /* store the old struct here */ 662 volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */ 663 struct sigaction sigact; 664 #else 665 #ifdef HAVE_SIGNAL 666 void (*keep_sigact)(int); /* store the old handler here */ 667 #endif /* HAVE_SIGNAL */ 668 #endif /* HAVE_SIGACTION */ 669 volatile long timeout; 670 volatile unsigned int prev_alarm = 0; 671 struct Curl_easy *data = conn->data; 672 #endif /* USE_ALARM_TIMEOUT */ 673 int rc; 674 675 *entry = NULL; 676 677 if(timeoutms < 0) 678 /* got an already expired timeout */ 679 return CURLRESOLV_TIMEDOUT; 680 681 #ifdef USE_ALARM_TIMEOUT 682 if(data->set.no_signal) 683 /* Ignore the timeout when signals are disabled */ 684 timeout = 0; 685 else 686 timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms; 687 688 if(!timeout) 689 /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */ 690 return Curl_resolv(conn, hostname, port, entry); 691 692 if(timeout < 1000) { 693 /* The alarm() function only provides integer second resolution, so if 694 we want to wait less than one second we must bail out already now. */ 695 failf(data, 696 "remaining timeout of %ld too small to resolve via SIGALRM method", 697 timeout); 698 return CURLRESOLV_TIMEDOUT; 699 } 700 /* This allows us to time-out from the name resolver, as the timeout 701 will generate a signal and we will siglongjmp() from that here. 702 This technique has problems (see alarmfunc). 703 This should be the last thing we do before calling Curl_resolv(), 704 as otherwise we'd have to worry about variables that get modified 705 before we invoke Curl_resolv() (and thus use "volatile"). */ 706 if(sigsetjmp(curl_jmpenv, 1)) { 707 /* this is coming from a siglongjmp() after an alarm signal */ 708 failf(data, "name lookup timed out"); 709 rc = CURLRESOLV_ERROR; 710 goto clean_up; 711 } 712 else { 713 /************************************************************* 714 * Set signal handler to catch SIGALRM 715 * Store the old value to be able to set it back later! 716 *************************************************************/ 717 #ifdef HAVE_SIGACTION 718 sigaction(SIGALRM, NULL, &sigact); 719 keep_sigact = sigact; 720 keep_copysig = TRUE; /* yes, we have a copy */ 721 sigact.sa_handler = alarmfunc; 722 #ifdef SA_RESTART 723 /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */ 724 sigact.sa_flags &= ~SA_RESTART; 725 #endif 726 /* now set the new struct */ 727 sigaction(SIGALRM, &sigact, NULL); 728 #else /* HAVE_SIGACTION */ 729 /* no sigaction(), revert to the much lamer signal() */ 730 #ifdef HAVE_SIGNAL 731 keep_sigact = signal(SIGALRM, alarmfunc); 732 #endif 733 #endif /* HAVE_SIGACTION */ 734 735 /* alarm() makes a signal get sent when the timeout fires off, and that 736 will abort system calls */ 737 prev_alarm = alarm(curlx_sltoui(timeout/1000L)); 738 } 739 740 #else 741 #ifndef CURLRES_ASYNCH 742 if(timeoutms) 743 infof(conn->data, "timeout on name lookup is not supported\n"); 744 #else 745 (void)timeoutms; /* timeoutms not used with an async resolver */ 746 #endif 747 #endif /* USE_ALARM_TIMEOUT */ 748 749 /* Perform the actual name resolution. This might be interrupted by an 750 * alarm if it takes too long. 751 */ 752 rc = Curl_resolv(conn, hostname, port, entry); 753 754 #ifdef USE_ALARM_TIMEOUT 755 clean_up: 756 757 if(!prev_alarm) 758 /* deactivate a possibly active alarm before uninstalling the handler */ 759 alarm(0); 760 761 #ifdef HAVE_SIGACTION 762 if(keep_copysig) { 763 /* we got a struct as it looked before, now put that one back nice 764 and clean */ 765 sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */ 766 } 767 #else 768 #ifdef HAVE_SIGNAL 769 /* restore the previous SIGALRM handler */ 770 signal(SIGALRM, keep_sigact); 771 #endif 772 #endif /* HAVE_SIGACTION */ 773 774 /* switch back the alarm() to either zero or to what it was before minus 775 the time we spent until now! */ 776 if(prev_alarm) { 777 /* there was an alarm() set before us, now put it back */ 778 timediff_t elapsed_secs = Curl_timediff(Curl_now(), 779 conn->created) / 1000; 780 781 /* the alarm period is counted in even number of seconds */ 782 unsigned long alarm_set = prev_alarm - elapsed_secs; 783 784 if(!alarm_set || 785 ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) { 786 /* if the alarm time-left reached zero or turned "negative" (counted 787 with unsigned values), we should fire off a SIGALRM here, but we 788 won't, and zero would be to switch it off so we never set it to 789 less than 1! */ 790 alarm(1); 791 rc = CURLRESOLV_TIMEDOUT; 792 failf(data, "Previous alarm fired off!"); 793 } 794 else 795 alarm((unsigned int)alarm_set); 796 } 797 #endif /* USE_ALARM_TIMEOUT */ 798 799 return rc; 800 } 801 802 /* 803 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been 804 * made, the struct may be destroyed due to pruning. It is important that only 805 * one unlock is made for each Curl_resolv() call. 806 * 807 * May be called with 'data' == NULL for global cache. 808 */ 809 void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns) 810 { 811 if(data && data->share) 812 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); 813 814 freednsentry(dns); 815 816 if(data && data->share) 817 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); 818 } 819 820 /* 821 * File-internal: release cache dns entry reference, free if inuse drops to 0 822 */ 823 static void freednsentry(void *freethis) 824 { 825 struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis; 826 DEBUGASSERT(dns && (dns->inuse>0)); 827 828 dns->inuse--; 829 if(dns->inuse == 0) { 830 Curl_freeaddrinfo(dns->addr); 831 free(dns); 832 } 833 } 834 835 /* 836 * Curl_mk_dnscache() inits a new DNS cache and returns success/failure. 837 */ 838 int Curl_mk_dnscache(struct curl_hash *hash) 839 { 840 return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare, 841 freednsentry); 842 } 843 844 /* 845 * Curl_hostcache_clean() 846 * 847 * This _can_ be called with 'data' == NULL but then of course no locking 848 * can be done! 849 */ 850 851 void Curl_hostcache_clean(struct Curl_easy *data, 852 struct curl_hash *hash) 853 { 854 if(data && data->share) 855 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); 856 857 Curl_hash_clean(hash); 858 859 if(data && data->share) 860 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); 861 } 862 863 864 CURLcode Curl_loadhostpairs(struct Curl_easy *data) 865 { 866 struct curl_slist *hostp; 867 char hostname[256]; 868 int port = 0; 869 870 /* Default is no wildcard found */ 871 data->change.wildcard_resolve = false; 872 873 for(hostp = data->change.resolve; hostp; hostp = hostp->next) { 874 char entry_id[MAX_HOSTCACHE_LEN]; 875 if(!hostp->data) 876 continue; 877 if(hostp->data[0] == '-') { 878 size_t entry_len; 879 880 if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) { 881 infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n", 882 hostp->data); 883 continue; 884 } 885 886 /* Create an entry id, based upon the hostname and port */ 887 create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); 888 entry_len = strlen(entry_id); 889 890 if(data->share) 891 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); 892 893 /* delete entry, ignore if it didn't exist */ 894 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); 895 896 if(data->share) 897 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); 898 } 899 else { 900 struct Curl_dns_entry *dns; 901 Curl_addrinfo *head = NULL, *tail = NULL; 902 size_t entry_len; 903 char address[64]; 904 #if !defined(CURL_DISABLE_VERBOSE_STRINGS) 905 char *addresses = NULL; 906 #endif 907 char *addr_begin; 908 char *addr_end; 909 char *port_ptr; 910 char *end_ptr; 911 char *host_end; 912 unsigned long tmp_port; 913 bool error = true; 914 915 host_end = strchr(hostp->data, ':'); 916 if(!host_end || 917 ((host_end - hostp->data) >= (ptrdiff_t)sizeof(hostname))) 918 goto err; 919 920 memcpy(hostname, hostp->data, host_end - hostp->data); 921 hostname[host_end - hostp->data] = '\0'; 922 923 port_ptr = host_end + 1; 924 tmp_port = strtoul(port_ptr, &end_ptr, 10); 925 if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':') 926 goto err; 927 928 port = (int)tmp_port; 929 #if !defined(CURL_DISABLE_VERBOSE_STRINGS) 930 addresses = end_ptr + 1; 931 #endif 932 933 while(*end_ptr) { 934 size_t alen; 935 Curl_addrinfo *ai; 936 937 addr_begin = end_ptr + 1; 938 addr_end = strchr(addr_begin, ','); 939 if(!addr_end) 940 addr_end = addr_begin + strlen(addr_begin); 941 end_ptr = addr_end; 942 943 /* allow IP(v6) address within [brackets] */ 944 if(*addr_begin == '[') { 945 if(addr_end == addr_begin || *(addr_end - 1) != ']') 946 goto err; 947 ++addr_begin; 948 --addr_end; 949 } 950 951 alen = addr_end - addr_begin; 952 if(!alen) 953 continue; 954 955 if(alen >= sizeof(address)) 956 goto err; 957 958 memcpy(address, addr_begin, alen); 959 address[alen] = '\0'; 960 961 #ifndef ENABLE_IPV6 962 if(strchr(address, ':')) { 963 infof(data, "Ignoring resolve address '%s', missing IPv6 support.\n", 964 address); 965 continue; 966 } 967 #endif 968 969 ai = Curl_str2addr(address, port); 970 if(!ai) { 971 infof(data, "Resolve address '%s' found illegal!\n", address); 972 goto err; 973 } 974 975 if(tail) { 976 tail->ai_next = ai; 977 tail = tail->ai_next; 978 } 979 else { 980 head = tail = ai; 981 } 982 } 983 984 if(!head) 985 goto err; 986 987 error = false; 988 err: 989 if(error) { 990 infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n", 991 hostp->data); 992 Curl_freeaddrinfo(head); 993 continue; 994 } 995 996 /* Create an entry id, based upon the hostname and port */ 997 create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); 998 entry_len = strlen(entry_id); 999 1000 if(data->share) 1001 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); 1002 1003 /* See if its already in our dns cache */ 1004 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); 1005 1006 if(dns) { 1007 infof(data, "RESOLVE %s:%d is - old addresses discarded!\n", 1008 hostname, port); 1009 /* delete old entry entry, there are two reasons for this 1010 1. old entry may have different addresses. 1011 2. even if entry with correct addresses is already in the cache, 1012 but if it is close to expire, then by the time next http 1013 request is made, it can get expired and pruned because old 1014 entry is not necessarily marked as added by CURLOPT_RESOLVE. */ 1015 1016 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); 1017 } 1018 1019 /* put this new host in the cache */ 1020 dns = Curl_cache_addr(data, head, hostname, port); 1021 if(dns) { 1022 dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */ 1023 /* release the returned reference; the cache itself will keep the 1024 * entry alive: */ 1025 dns->inuse--; 1026 } 1027 1028 if(data->share) 1029 Curl_share_unlock(data, CURL_LOCK_DATA_DNS); 1030 1031 if(!dns) { 1032 Curl_freeaddrinfo(head); 1033 return CURLE_OUT_OF_MEMORY; 1034 } 1035 infof(data, "Added %s:%d:%s to DNS cache\n", 1036 hostname, port, addresses); 1037 1038 /* Wildcard hostname */ 1039 if(hostname[0] == '*' && hostname[1] == '\0') { 1040 infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks\n", 1041 hostname, port); 1042 data->change.wildcard_resolve = true; 1043 } 1044 } 1045 } 1046 data->change.resolve = NULL; /* dealt with now */ 1047 1048 return CURLE_OK; 1049 } 1050 1051 CURLcode Curl_resolv_check(struct connectdata *conn, 1052 struct Curl_dns_entry **dns) 1053 { 1054 if(conn->data->set.doh) 1055 return Curl_doh_is_resolved(conn, dns); 1056 return Curl_resolver_is_resolved(conn, dns); 1057 } 1058 1059 int Curl_resolv_getsock(struct connectdata *conn, 1060 curl_socket_t *socks, 1061 int numsocks) 1062 { 1063 #ifdef CURLRES_ASYNCH 1064 if(conn->data->set.doh) 1065 /* nothing to wait for during DOH resolve, those handles have their own 1066 sockets */ 1067 return GETSOCK_BLANK; 1068 return Curl_resolver_getsock(conn, socks, numsocks); 1069 #else 1070 (void)conn; 1071 (void)socks; 1072 (void)numsocks; 1073 return GETSOCK_BLANK; 1074 #endif 1075 } 1076 1077 /* Call this function after Curl_connect() has returned async=TRUE and 1078 then a successful name resolve has been received. 1079 1080 Note: this function disconnects and frees the conn data in case of 1081 resolve failure */ 1082 CURLcode Curl_once_resolved(struct connectdata *conn, 1083 bool *protocol_done) 1084 { 1085 CURLcode result; 1086 1087 if(conn->async.dns) { 1088 conn->dns_entry = conn->async.dns; 1089 conn->async.dns = NULL; 1090 } 1091 1092 result = Curl_setup_conn(conn, protocol_done); 1093 1094 if(result) 1095 /* We're not allowed to return failure with memory left allocated 1096 in the connectdata struct, free those here */ 1097 Curl_disconnect(conn->data, conn, TRUE); /* close the connection */ 1098 1099 return result; 1100 } 1101