asyn-ares.c (30051B)
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 CURLRES_ARES 28 29 /*********************************************************************** 30 * Only for ares-enabled builds 31 * And only for functions that fulfill the asynch resolver backend API 32 * as defined in asyn.h, nothing else belongs in this file! 33 **********************************************************************/ 34 35 #include <limits.h> 36 #ifdef HAVE_NETINET_IN_H 37 #include <netinet/in.h> 38 #endif 39 #ifdef HAVE_NETDB_H 40 #include <netdb.h> 41 #endif 42 #ifdef HAVE_ARPA_INET_H 43 #include <arpa/inet.h> 44 #endif 45 #ifdef __VMS 46 #include <in.h> 47 #include <inet.h> 48 #endif 49 50 #include "urldata.h" 51 #include "cfilters.h" 52 #include "sendf.h" 53 #include "hostip.h" 54 #include "hash.h" 55 #include "share.h" 56 #include "url.h" 57 #include "multiif.h" 58 #include "curlx/inet_pton.h" 59 #include "connect.h" 60 #include "select.h" 61 #include "progress.h" 62 #include "curlx/timediff.h" 63 #include "httpsrr.h" 64 #include "strdup.h" 65 66 #include <ares.h> 67 #include <ares_version.h> /* really old c-ares did not include this by 68 itself */ 69 70 #if ARES_VERSION >= 0x010601 71 /* IPv6 supported since 1.6.1 */ 72 #define HAVE_CARES_IPV6 1 73 #endif 74 75 #if ARES_VERSION >= 0x010704 76 #define HAVE_CARES_SERVERS_CSV 1 77 #define HAVE_CARES_LOCAL_DEV 1 78 #define HAVE_CARES_SET_LOCAL 1 79 #endif 80 81 #if ARES_VERSION >= 0x010b00 82 #define HAVE_CARES_PORTS_CSV 1 83 #endif 84 85 #if ARES_VERSION >= 0x011000 86 /* 1.16.0 or later has ares_getaddrinfo */ 87 #define HAVE_CARES_GETADDRINFO 1 88 #endif 89 90 #ifdef USE_HTTPSRR 91 #if ARES_VERSION < 0x011c00 92 #error "requires c-ares 1.28.0 or newer for HTTPSRR" 93 #endif 94 #define HTTPSRR_WORKS 95 #endif 96 97 /* The last 3 #include files should be in this order */ 98 #include "curl_printf.h" 99 #include "curl_memory.h" 100 #include "memdebug.h" 101 102 /* How long we are willing to wait for additional parallel responses after 103 obtaining a "definitive" one. For old c-ares without getaddrinfo. 104 105 This is intended to equal the c-ares default timeout. cURL always uses that 106 default value. Unfortunately, c-ares does not expose its default timeout in 107 its API, but it is officially documented as 5 seconds. 108 109 See query_completed_cb() for an explanation of how this is used. 110 */ 111 #define HAPPY_EYEBALLS_DNS_TIMEOUT 5000 112 113 #define CARES_TIMEOUT_PER_ATTEMPT 2000 114 115 static int ares_ver = 0; 116 117 static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, 118 bool reset_on_null); 119 120 /* 121 * Curl_async_global_init() - the generic low-level asynchronous name 122 * resolve API. Called from curl_global_init() to initialize global resolver 123 * environment. Initializes ares library. 124 */ 125 int Curl_async_global_init(void) 126 { 127 #ifdef CARES_HAVE_ARES_LIBRARY_INIT 128 if(ares_library_init(ARES_LIB_INIT_ALL)) { 129 return CURLE_FAILED_INIT; 130 } 131 #endif 132 ares_version(&ares_ver); 133 return CURLE_OK; 134 } 135 136 /* 137 * Curl_async_global_cleanup() 138 * 139 * Called from curl_global_cleanup() to destroy global resolver environment. 140 * Deinitializes ares library. 141 */ 142 void Curl_async_global_cleanup(void) 143 { 144 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP 145 ares_library_cleanup(); 146 #endif 147 } 148 149 150 static void sock_state_cb(void *data, ares_socket_t socket_fd, 151 int readable, int writable) 152 { 153 struct Curl_easy *easy = data; 154 if(!readable && !writable) { 155 DEBUGASSERT(easy); 156 Curl_multi_will_close(easy, socket_fd); 157 } 158 } 159 160 static CURLcode async_ares_init(struct Curl_easy *data) 161 { 162 struct async_ares_ctx *ares = &data->state.async.ares; 163 int status; 164 struct ares_options options; 165 int optmask = ARES_OPT_SOCK_STATE_CB; 166 CURLcode rc = CURLE_OK; 167 168 options.sock_state_cb = sock_state_cb; 169 options.sock_state_cb_data = data; 170 171 DEBUGASSERT(!ares->channel); 172 /* 173 if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s) 174 175 if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need 176 to set the timeout value; 177 178 if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to 179 overwrite c-ares' timeout. 180 */ 181 DEBUGASSERT(ares_ver); 182 if(ares_ver < 0x011400) { 183 options.timeout = CARES_TIMEOUT_PER_ATTEMPT; 184 optmask |= ARES_OPT_TIMEOUTMS; 185 } 186 187 status = ares_init_options(&ares->channel, &options, optmask); 188 if(status != ARES_SUCCESS) { 189 ares->channel = NULL; 190 rc = (status == ARES_ENOMEM) ? 191 CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT; 192 goto out; 193 } 194 195 rc = async_ares_set_dns_servers(data, FALSE); 196 if(rc && rc != CURLE_NOT_BUILT_IN) 197 goto out; 198 199 rc = Curl_async_ares_set_dns_interface(data); 200 if(rc && rc != CURLE_NOT_BUILT_IN) 201 goto out; 202 203 rc = Curl_async_ares_set_dns_local_ip4(data); 204 if(rc && rc != CURLE_NOT_BUILT_IN) 205 goto out; 206 207 rc = Curl_async_ares_set_dns_local_ip6(data); 208 if(rc && rc != CURLE_NOT_BUILT_IN) 209 goto out; 210 211 rc = CURLE_OK; 212 213 out: 214 if(rc && ares->channel) { 215 ares_destroy(ares->channel); 216 ares->channel = NULL; 217 } 218 return rc; 219 } 220 221 static CURLcode async_ares_init_lazy(struct Curl_easy *data) 222 { 223 struct async_ares_ctx *ares = &data->state.async.ares; 224 if(!ares->channel) 225 return async_ares_init(data); 226 return CURLE_OK; 227 } 228 229 CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl) 230 { 231 struct async_ares_ctx *ares = &data->state.async.ares; 232 CURLcode result = CURLE_OK; 233 if(!ares->channel) { 234 result = async_ares_init(data); 235 } 236 *impl = ares->channel; 237 return result; 238 } 239 240 static void async_ares_cleanup(struct Curl_easy *data); 241 242 void Curl_async_ares_shutdown(struct Curl_easy *data) 243 { 244 struct async_ares_ctx *ares = &data->state.async.ares; 245 if(ares->channel) 246 ares_cancel(ares->channel); 247 async_ares_cleanup(data); 248 } 249 250 void Curl_async_ares_destroy(struct Curl_easy *data) 251 { 252 struct async_ares_ctx *ares = &data->state.async.ares; 253 Curl_async_ares_shutdown(data); 254 if(ares->channel) { 255 ares_destroy(ares->channel); 256 ares->channel = NULL; 257 } 258 } 259 260 /* 261 * async_ares_cleanup() cleans up async resolver data. 262 */ 263 static void async_ares_cleanup(struct Curl_easy *data) 264 { 265 struct async_ares_ctx *ares = &data->state.async.ares; 266 if(ares->temp_ai) { 267 Curl_freeaddrinfo(ares->temp_ai); 268 ares->temp_ai = NULL; 269 } 270 #ifdef USE_HTTPSRR 271 Curl_httpsrr_cleanup(&ares->hinfo); 272 #endif 273 } 274 275 /* 276 * Curl_async_getsock() is called when someone from the outside world 277 * (using curl_multi_fdset()) wants to get our fd_set setup. 278 */ 279 280 int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks) 281 { 282 struct async_ares_ctx *ares = &data->state.async.ares; 283 DEBUGASSERT(ares->channel); 284 return Curl_ares_getsock(data, ares->channel, socks); 285 } 286 287 /* 288 * Curl_async_is_resolved() is called repeatedly to check if a previous 289 * name resolve request has completed. It should also make sure to time-out if 290 * the operation seems to take too long. 291 * 292 * Returns normal CURLcode errors. 293 */ 294 CURLcode Curl_async_is_resolved(struct Curl_easy *data, 295 struct Curl_dns_entry **dns) 296 { 297 struct async_ares_ctx *ares = &data->state.async.ares; 298 CURLcode result = CURLE_OK; 299 300 DEBUGASSERT(dns); 301 *dns = NULL; 302 303 if(data->state.async.done) { 304 *dns = data->state.async.dns; 305 return CURLE_OK; 306 } 307 308 if(Curl_ares_perform(ares->channel, 0) < 0) 309 return CURLE_UNRECOVERABLE_POLL; 310 311 #ifndef HAVE_CARES_GETADDRINFO 312 /* Now that we have checked for any last minute results above, see if there 313 are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer 314 expires. */ 315 if(ares->num_pending 316 /* This is only set to non-zero if the timer was started. */ 317 && (ares->happy_eyeballs_dns_time.tv_sec 318 || ares->happy_eyeballs_dns_time.tv_usec) 319 && (curlx_timediff(curlx_now(), ares->happy_eyeballs_dns_time) 320 >= HAPPY_EYEBALLS_DNS_TIMEOUT)) { 321 /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer 322 running. */ 323 memset(&ares->happy_eyeballs_dns_time, 0, 324 sizeof(ares->happy_eyeballs_dns_time)); 325 326 /* Cancel the raw c-ares request, which will fire query_completed_cb() with 327 ARES_ECANCELLED synchronously for all pending responses. This will 328 leave us with res->num_pending == 0, which is perfect for the next 329 block. */ 330 ares_cancel(ares->channel); 331 DEBUGASSERT(ares->num_pending == 0); 332 } 333 #endif 334 335 if(!ares->num_pending) { 336 /* all c-ares operations done, what is the result to report? */ 337 Curl_resolv_unlink(data, &data->state.async.dns); 338 data->state.async.done = TRUE; 339 result = ares->result; 340 if(ares->last_status == CURL_ASYNC_SUCCESS && !result) { 341 data->state.async.dns = 342 Curl_dnscache_mk_entry(data, ares->temp_ai, 343 data->state.async.hostname, 0, 344 data->state.async.port, FALSE); 345 ares->temp_ai = NULL; /* temp_ai now owned by entry */ 346 #ifdef HTTPSRR_WORKS 347 if(data->state.async.dns) { 348 struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo); 349 if(!lhrr) 350 result = CURLE_OUT_OF_MEMORY; 351 else 352 data->state.async.dns->hinfo = lhrr; 353 } 354 #endif 355 if(!result && data->state.async.dns) 356 result = Curl_dnscache_add(data, data->state.async.dns); 357 } 358 /* if we have not found anything, report the proper 359 * CURLE_COULDNT_RESOLVE_* code */ 360 if(!result && !data->state.async.dns) 361 result = Curl_resolver_error(data); 362 if(result) 363 Curl_resolv_unlink(data, &data->state.async.dns); 364 *dns = data->state.async.dns; 365 CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound", 366 result, *dns ? "" : "not "); 367 async_ares_cleanup(data); 368 } 369 return result; 370 } 371 372 /* 373 * Curl_async_await() 374 * 375 * Waits for a resolve to finish. This function should be avoided since using 376 * this risk getting the multi interface to "hang". 377 * 378 * 'entry' MUST be non-NULL. 379 * 380 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, 381 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. 382 */ 383 CURLcode Curl_async_await(struct Curl_easy *data, 384 struct Curl_dns_entry **entry) 385 { 386 struct async_ares_ctx *ares = &data->state.async.ares; 387 CURLcode result = CURLE_OK; 388 timediff_t timeout; 389 struct curltime now = curlx_now(); 390 391 DEBUGASSERT(entry); 392 *entry = NULL; /* clear on entry */ 393 394 timeout = Curl_timeleft(data, &now, TRUE); 395 if(timeout < 0) { 396 /* already expired! */ 397 connclose(data->conn, "Timed out before name resolve started"); 398 return CURLE_OPERATION_TIMEDOUT; 399 } 400 if(!timeout) 401 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ 402 403 /* Wait for the name resolve query to complete. */ 404 while(!result) { 405 struct timeval *tvp, tv, store; 406 int itimeout; 407 timediff_t timeout_ms; 408 409 #if TIMEDIFF_T_MAX > INT_MAX 410 itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout; 411 #else 412 itimeout = (int)timeout; 413 #endif 414 415 store.tv_sec = itimeout/1000; 416 store.tv_usec = (itimeout%1000)*1000; 417 418 tvp = ares_timeout(ares->channel, &store, &tv); 419 420 /* use the timeout period ares returned to us above if less than one 421 second is left, otherwise just use 1000ms to make sure the progress 422 callback gets called frequent enough */ 423 if(!tvp->tv_sec) 424 timeout_ms = (timediff_t)(tvp->tv_usec/1000); 425 else 426 timeout_ms = 1000; 427 428 if(Curl_ares_perform(ares->channel, timeout_ms) < 0) 429 return CURLE_UNRECOVERABLE_POLL; 430 431 result = Curl_async_is_resolved(data, entry); 432 if(result || data->state.async.done) 433 break; 434 435 if(Curl_pgrsUpdate(data)) 436 result = CURLE_ABORTED_BY_CALLBACK; 437 else { 438 struct curltime now2 = curlx_now(); 439 timediff_t timediff = curlx_timediff(now2, now); /* spent time */ 440 if(timediff <= 0) 441 timeout -= 1; /* always deduct at least 1 */ 442 else if(timediff > timeout) 443 timeout = -1; 444 else 445 timeout -= timediff; 446 now = now2; /* for next loop */ 447 } 448 if(timeout < 0) 449 result = CURLE_OPERATION_TIMEDOUT; 450 } 451 452 /* Operation complete, if the lookup was successful we now have the entry 453 in the cache. */ 454 data->state.async.done = TRUE; 455 *entry = data->state.async.dns; 456 457 if(result) 458 ares_cancel(ares->channel); 459 return result; 460 } 461 462 #ifndef HAVE_CARES_GETADDRINFO 463 464 /* Connects results to the list */ 465 static void async_addr_concat(struct Curl_addrinfo **pbase, 466 struct Curl_addrinfo *ai) 467 { 468 if(!ai) 469 return; 470 471 /* When adding `ai` to an existing address list, we prefer ipv6 472 * to be in front. */ 473 #ifdef USE_IPV6 /* CURLRES_IPV6 */ 474 if(*pbase && (*pbase)->ai_family == PF_INET6) { 475 /* ipv6 already in front, append `ai` */ 476 struct Curl_addrinfo *tail = *pbase; 477 while(tail->ai_next) 478 tail = tail->ai_next; 479 tail->ai_next = ai; 480 } 481 else 482 #endif /* CURLRES_IPV6 */ 483 { 484 /* prepend to the (possibly) existing list. */ 485 struct Curl_addrinfo *tail = ai; 486 while(tail->ai_next) 487 tail = tail->ai_next; 488 tail->ai_next = *pbase; 489 *pbase = ai; 490 } 491 } 492 493 /* 494 * ares_query_completed_cb() is the callback that ares will call when 495 * the host query initiated by ares_gethostbyname() from 496 * Curl_async_getaddrinfo(), when using ares, is completed either 497 * successfully or with failure. 498 */ 499 static void async_ares_hostbyname_cb(void *user_data, 500 int status, 501 int timeouts, 502 struct hostent *hostent) 503 { 504 struct Curl_easy *data = (struct Curl_easy *)user_data; 505 struct async_ares_ctx *ares = &data->state.async.ares; 506 507 (void)timeouts; /* ignored */ 508 509 if(ARES_EDESTRUCTION == status) 510 /* when this ares handle is getting destroyed, the 'arg' pointer may not 511 be valid so only defer it when we know the 'status' says its fine! */ 512 return; 513 514 if(CURL_ASYNC_SUCCESS == status) { 515 ares->last_status = status; /* one success overrules any error */ 516 async_addr_concat(&ares->temp_ai, 517 Curl_he2ai(hostent, data->state.async.port)); 518 } 519 else if(ares->last_status != ARES_SUCCESS) { 520 /* no success so far, remember error */ 521 ares->last_status = status; 522 } 523 524 ares->num_pending--; 525 526 CURL_TRC_DNS(data, "ares: hostbyname done, status=%d, pending=%d, " 527 "addr=%sfound", 528 status, ares->num_pending, ares->temp_ai ? "" : "not "); 529 /* If there are responses still pending, we presume they must be the 530 complementary IPv4 or IPv6 lookups that we started in parallel in 531 Curl_async_getaddrinfo() (for Happy Eyeballs). If we have got a 532 "definitive" response from one of a set of parallel queries, we need to 533 think about how long we are willing to wait for more responses. */ 534 if(ares->num_pending 535 /* Only these c-ares status values count as "definitive" for these 536 purposes. For example, ARES_ENODATA is what we expect when there is 537 no IPv6 entry for a domain name, and that is not a reason to get more 538 aggressive in our timeouts for the other response. Other errors are 539 either a result of bad input (which should affect all parallel 540 requests), local or network conditions, non-definitive server 541 responses, or us cancelling the request. */ 542 && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) { 543 /* Right now, there can only be up to two parallel queries, so do not 544 bother handling any other cases. */ 545 DEBUGASSERT(ares->num_pending == 1); 546 547 /* it is possible that one of these parallel queries could succeed 548 quickly, but the other could always fail or timeout (when we are 549 talking to a pool of DNS servers that can only successfully resolve 550 IPv4 address, for example). 551 552 it is also possible that the other request could always just take 553 longer because it needs more time or only the second DNS server can 554 fulfill it successfully. But, to align with the philosophy of Happy 555 Eyeballs, we do not want to wait _too_ long or users will think 556 requests are slow when IPv6 lookups do not actually work (but IPv4 557 ones do). 558 559 So, now that we have a usable answer (some IPv4 addresses, some IPv6 560 addresses, or "no such domain"), we start a timeout for the remaining 561 pending responses. Even though it is typical that this resolved 562 request came back quickly, that needn't be the case. It might be that 563 this completing request did not get a result from the first DNS 564 server or even the first round of the whole DNS server pool. So it 565 could already be quite some time after we issued the DNS queries in 566 the first place. Without modifying c-ares, we cannot know exactly 567 where in its retry cycle we are. We could guess based on how much 568 time has gone by, but it does not really matter. Happy Eyeballs tells 569 us that, given usable information in hand, we simply do not want to 570 wait "too much longer" after we get a result. 571 572 We simply wait an additional amount of time equal to the default 573 c-ares query timeout. That is enough time for a typical parallel 574 response to arrive without being "too long". Even on a network 575 where one of the two types of queries is failing or timing out 576 constantly, this will usually mean we wait a total of the default 577 c-ares timeout (5 seconds) plus the round trip time for the successful 578 request, which seems bearable. The downside is that c-ares might race 579 with us to issue one more retry just before we give up, but it seems 580 better to "waste" that request instead of trying to guess the perfect 581 timeout to prevent it. After all, we do not even know where in the 582 c-ares retry cycle each request is. 583 */ 584 ares->happy_eyeballs_dns_time = curlx_now(); 585 Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT, 586 EXPIRE_HAPPY_EYEBALLS_DNS); 587 } 588 } 589 590 #else 591 /* c-ares 1.16.0 or later */ 592 593 /* 594 * async_ares_node2addr() converts an address list provided by c-ares 595 * to an internal libcurl compatible list. 596 */ 597 static struct Curl_addrinfo * 598 async_ares_node2addr(struct ares_addrinfo_node *node) 599 { 600 /* traverse the ares_addrinfo_node list */ 601 struct ares_addrinfo_node *ai; 602 struct Curl_addrinfo *cafirst = NULL; 603 struct Curl_addrinfo *calast = NULL; 604 int error = 0; 605 606 for(ai = node; ai != NULL; ai = ai->ai_next) { 607 size_t ss_size; 608 struct Curl_addrinfo *ca; 609 /* ignore elements with unsupported address family, */ 610 /* settle family-specific sockaddr structure size. */ 611 if(ai->ai_family == AF_INET) 612 ss_size = sizeof(struct sockaddr_in); 613 #ifdef USE_IPV6 614 else if(ai->ai_family == AF_INET6) 615 ss_size = sizeof(struct sockaddr_in6); 616 #endif 617 else 618 continue; 619 620 /* ignore elements without required address info */ 621 if(!ai->ai_addr || !(ai->ai_addrlen > 0)) 622 continue; 623 624 /* ignore elements with bogus address size */ 625 if((size_t)ai->ai_addrlen < ss_size) 626 continue; 627 628 ca = malloc(sizeof(struct Curl_addrinfo) + ss_size); 629 if(!ca) { 630 error = EAI_MEMORY; 631 break; 632 } 633 634 /* copy each structure member individually, member ordering, */ 635 /* size, or padding might be different for each platform. */ 636 637 ca->ai_flags = ai->ai_flags; 638 ca->ai_family = ai->ai_family; 639 ca->ai_socktype = ai->ai_socktype; 640 ca->ai_protocol = ai->ai_protocol; 641 ca->ai_addrlen = (curl_socklen_t)ss_size; 642 ca->ai_addr = NULL; 643 ca->ai_canonname = NULL; 644 ca->ai_next = NULL; 645 646 ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); 647 memcpy(ca->ai_addr, ai->ai_addr, ss_size); 648 649 /* if the return list is empty, this becomes the first element */ 650 if(!cafirst) 651 cafirst = ca; 652 653 /* add this element last in the return list */ 654 if(calast) 655 calast->ai_next = ca; 656 calast = ca; 657 } 658 659 /* if we failed, destroy the Curl_addrinfo list */ 660 if(error) { 661 Curl_freeaddrinfo(cafirst); 662 cafirst = NULL; 663 } 664 665 return cafirst; 666 } 667 668 static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts, 669 struct ares_addrinfo *result) 670 { 671 struct Curl_easy *data = (struct Curl_easy *)user_data; 672 struct async_ares_ctx *ares = &data->state.async.ares; 673 (void)timeouts; 674 CURL_TRC_DNS(data, "asyn-ares: addrinfo callback, status=%d", status); 675 if(ARES_SUCCESS == status) { 676 ares->temp_ai = async_ares_node2addr(result->nodes); 677 ares->last_status = CURL_ASYNC_SUCCESS; 678 ares_freeaddrinfo(result); 679 } 680 ares->num_pending--; 681 CURL_TRC_DNS(data, "ares: addrinfo done, status=%d, pending=%d, " 682 "addr=%sfound", 683 status, ares->num_pending, ares->temp_ai ? "" : "not "); 684 } 685 686 #endif 687 688 #ifdef USE_HTTPSRR 689 static void async_ares_rr_done(void *user_data, ares_status_t status, 690 size_t timeouts, 691 const ares_dns_record_t *dnsrec) 692 { 693 struct Curl_easy *data = user_data; 694 struct async_ares_ctx *ares = &data->state.async.ares; 695 696 (void)timeouts; 697 --ares->num_pending; 698 CURL_TRC_DNS(data, "ares: httpsrr done, status=%d, pending=%d, " 699 "dnsres=%sfound", 700 status, ares->num_pending, 701 (dnsrec && 702 ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)) ? 703 "" : "not "); 704 if((ARES_SUCCESS != status) || !dnsrec) 705 return; 706 ares->result = Curl_httpsrr_from_ares(data, dnsrec, &ares->hinfo); 707 } 708 #endif /* USE_HTTPSRR */ 709 710 /* 711 * Curl_async_getaddrinfo() - when using ares 712 * 713 * Returns name information about the given hostname and port number. If 714 * successful, the 'hostent' is returned and the fourth argument will point to 715 * memory we need to free after use. That memory *MUST* be freed with 716 * Curl_freeaddrinfo(), nothing else. 717 */ 718 struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, 719 const char *hostname, 720 int port, 721 int ip_version, 722 int *waitp) 723 { 724 struct async_ares_ctx *ares = &data->state.async.ares; 725 *waitp = 0; /* default to synchronous response */ 726 727 if(async_ares_init_lazy(data)) 728 return NULL; 729 730 data->state.async.done = FALSE; /* not done */ 731 data->state.async.dns = NULL; /* clear */ 732 data->state.async.port = port; 733 data->state.async.ip_version = ip_version; 734 data->state.async.hostname = strdup(hostname); 735 if(!data->state.async.hostname) 736 return NULL; 737 738 /* initial status - failed */ 739 ares->last_status = ARES_ENOTFOUND; 740 741 #ifdef HAVE_CARES_GETADDRINFO 742 { 743 struct ares_addrinfo_hints hints; 744 char service[12]; 745 int pf = PF_INET; 746 memset(&hints, 0, sizeof(hints)); 747 #ifdef CURLRES_IPV6 748 if((ip_version != CURL_IPRESOLVE_V4) && 749 Curl_ipv6works(data)) { 750 /* The stack seems to be IPv6-enabled */ 751 if(ip_version == CURL_IPRESOLVE_V6) 752 pf = PF_INET6; 753 else 754 pf = PF_UNSPEC; 755 } 756 #endif /* CURLRES_IPV6 */ 757 CURL_TRC_DNS(data, "asyn-ares: fire off getaddrinfo for %s", 758 (pf == PF_UNSPEC) ? "A+AAAA" : 759 ((pf == PF_INET) ? "A" : "AAAA")); 760 hints.ai_family = pf; 761 hints.ai_socktype = 762 (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? 763 SOCK_STREAM : SOCK_DGRAM; 764 /* Since the service is a numerical one, set the hint flags 765 * accordingly to save a call to getservbyname in inside C-Ares 766 */ 767 hints.ai_flags = ARES_AI_NUMERICSERV; 768 msnprintf(service, sizeof(service), "%d", port); 769 ares->num_pending = 1; 770 ares_getaddrinfo(ares->channel, data->state.async.hostname, 771 service, &hints, async_ares_addrinfo_cb, data); 772 } 773 #else 774 775 #ifdef HAVE_CARES_IPV6 776 if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { 777 /* The stack seems to be IPv6-enabled */ 778 /* areschannel is already setup in the Curl_open() function */ 779 CURL_TRC_DNS(data, "asyn-ares: fire off query for A"); 780 ares_gethostbyname(ares->channel, hostname, PF_INET, 781 async_ares_hostbyname_cb, data); 782 CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA"); 783 ares->num_pending = 2; 784 ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET6, 785 async_ares_hostbyname_cb, data); 786 } 787 else 788 #endif 789 { 790 /* areschannel is already setup in the Curl_open() function */ 791 CURL_TRC_DNS(data, "asyn-ares: fire off query for A"); 792 ares->num_pending = 1; 793 ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET, 794 async_ares_hostbyname_cb, data); 795 } 796 #endif 797 #ifdef USE_HTTPSRR 798 { 799 CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR"); 800 memset(&ares->hinfo, 0, sizeof(ares->hinfo)); 801 ares->hinfo.port = -1; 802 ares->num_pending++; /* one more */ 803 ares_query_dnsrec(ares->channel, data->state.async.hostname, 804 ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, 805 async_ares_rr_done, data, NULL); 806 } 807 #endif 808 *waitp = 1; /* expect asynchronous response */ 809 810 return NULL; /* no struct yet */ 811 } 812 813 /* Set what DNS server are is to use. This is called in 2 situations: 814 * 1. when the application does 'CURLOPT_DNS_SERVERS' and passing NULL 815 * means any previous set value should be unset. Which means 816 * we need to destroy and create the are channel anew, if there is one. 817 * 2. When we lazy init the ares channel and NULL means that there 818 * are no preferences and we do not reset any existing channel. */ 819 static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, 820 bool reset_on_null) 821 { 822 struct async_ares_ctx *ares = &data->state.async.ares; 823 CURLcode result = CURLE_NOT_BUILT_IN; 824 const char *servers = data->set.str[STRING_DNS_SERVERS]; 825 int ares_result = ARES_SUCCESS; 826 827 #if defined(CURLDEBUG) && defined(HAVE_CARES_SERVERS_CSV) 828 if(getenv("CURL_DNS_SERVER")) 829 servers = getenv("CURL_DNS_SERVER"); 830 #endif 831 832 if(!servers) { 833 if(reset_on_null) { 834 Curl_async_destroy(data); 835 } 836 return CURLE_OK; 837 } 838 839 #ifdef HAVE_CARES_SERVERS_CSV 840 /* if channel is not there, this is just a parameter check */ 841 if(ares->channel) 842 #ifdef HAVE_CARES_PORTS_CSV 843 ares_result = ares_set_servers_ports_csv(ares->channel, servers); 844 #else 845 ares_result = ares_set_servers_csv(ares->channel, servers); 846 #endif 847 switch(ares_result) { 848 case ARES_SUCCESS: 849 result = CURLE_OK; 850 break; 851 case ARES_ENOMEM: 852 result = CURLE_OUT_OF_MEMORY; 853 break; 854 case ARES_ENOTINITIALIZED: 855 case ARES_ENODATA: 856 case ARES_EBADSTR: 857 default: 858 DEBUGF(infof(data, "bad servers set")); 859 result = CURLE_BAD_FUNCTION_ARGUMENT; 860 break; 861 } 862 #else /* too old c-ares version! */ 863 (void)data; 864 (void)(ares_result); 865 #endif 866 return result; 867 } 868 869 CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data) 870 { 871 return async_ares_set_dns_servers(data, TRUE); 872 } 873 874 CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data) 875 { 876 #ifdef HAVE_CARES_LOCAL_DEV 877 struct async_ares_ctx *ares = &data->state.async.ares; 878 const char *interf = data->set.str[STRING_DNS_INTERFACE]; 879 880 if(!interf) 881 interf = ""; 882 883 /* if channel is not there, this is just a parameter check */ 884 if(ares->channel) 885 ares_set_local_dev(ares->channel, interf); 886 887 return CURLE_OK; 888 #else /* c-ares version too old! */ 889 (void)data; 890 (void)interf; 891 return CURLE_NOT_BUILT_IN; 892 #endif 893 } 894 895 CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data) 896 { 897 #ifdef HAVE_CARES_SET_LOCAL 898 struct async_ares_ctx *ares = &data->state.async.ares; 899 struct in_addr a4; 900 const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4]; 901 902 if((!local_ip4) || (local_ip4[0] == 0)) { 903 a4.s_addr = 0; /* disabled: do not bind to a specific address */ 904 } 905 else { 906 if(curlx_inet_pton(AF_INET, local_ip4, &a4) != 1) { 907 DEBUGF(infof(data, "bad DNS IPv4 address")); 908 return CURLE_BAD_FUNCTION_ARGUMENT; 909 } 910 } 911 912 /* if channel is not there yet, this is just a parameter check */ 913 if(ares->channel) 914 ares_set_local_ip4(ares->channel, ntohl(a4.s_addr)); 915 916 return CURLE_OK; 917 #else /* c-ares version too old! */ 918 (void)data; 919 (void)local_ip4; 920 return CURLE_NOT_BUILT_IN; 921 #endif 922 } 923 924 CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data) 925 { 926 #if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6) 927 struct async_ares_ctx *ares = &data->state.async.ares; 928 unsigned char a6[INET6_ADDRSTRLEN]; 929 const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6]; 930 931 if((!local_ip6) || (local_ip6[0] == 0)) { 932 /* disabled: do not bind to a specific address */ 933 memset(a6, 0, sizeof(a6)); 934 } 935 else { 936 if(curlx_inet_pton(AF_INET6, local_ip6, a6) != 1) { 937 DEBUGF(infof(data, "bad DNS IPv6 address")); 938 return CURLE_BAD_FUNCTION_ARGUMENT; 939 } 940 } 941 942 /* if channel is not there, this is just a parameter check */ 943 if(ares->channel) 944 ares_set_local_ip6(ares->channel, a6); 945 946 return CURLE_OK; 947 #else /* c-ares version too old! */ 948 (void)data; 949 return CURLE_NOT_BUILT_IN; 950 #endif 951 } 952 953 #endif /* CURLRES_ARES */