asyn-thrdd.c (21160B)
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 #include "socketpair.h" 27 28 /*********************************************************************** 29 * Only for threaded name resolves builds 30 **********************************************************************/ 31 #ifdef CURLRES_THREADED 32 33 #ifdef HAVE_NETINET_IN_H 34 #include <netinet/in.h> 35 #endif 36 #ifdef HAVE_NETDB_H 37 #include <netdb.h> 38 #endif 39 #ifdef HAVE_ARPA_INET_H 40 #include <arpa/inet.h> 41 #endif 42 #ifdef __VMS 43 #include <in.h> 44 #include <inet.h> 45 #endif 46 47 #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) 48 # include <pthread.h> 49 #endif 50 51 #ifdef HAVE_GETADDRINFO 52 # define RESOLVER_ENOMEM EAI_MEMORY /* = WSA_NOT_ENOUGH_MEMORY on Windows */ 53 #else 54 # define RESOLVER_ENOMEM SOCKENOMEM 55 #endif 56 57 #include "urldata.h" 58 #include "cfilters.h" 59 #include "sendf.h" 60 #include "hostip.h" 61 #include "hash.h" 62 #include "share.h" 63 #include "url.h" 64 #include "multiif.h" 65 #include "curl_threads.h" 66 #include "strdup.h" 67 68 #ifdef USE_ARES 69 #include <ares.h> 70 #ifdef USE_HTTPSRR 71 #define USE_HTTPSRR_ARES /* the combo */ 72 #endif 73 #endif 74 75 /* The last 3 #include files should be in this order */ 76 #include "curl_printf.h" 77 #include "curl_memory.h" 78 #include "memdebug.h" 79 80 81 /* 82 * Curl_async_global_init() 83 * Called from curl_global_init() to initialize global resolver environment. 84 * Does nothing here. 85 */ 86 int Curl_async_global_init(void) 87 { 88 #if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT) 89 if(ares_library_init(ARES_LIB_INIT_ALL)) { 90 return CURLE_FAILED_INIT; 91 } 92 #endif 93 return CURLE_OK; 94 } 95 96 /* 97 * Curl_async_global_cleanup() 98 * Called from curl_global_cleanup() to destroy global resolver environment. 99 * Does nothing here. 100 */ 101 void Curl_async_global_cleanup(void) 102 { 103 #if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT) 104 ares_library_cleanup(); 105 #endif 106 } 107 108 static void async_thrdd_destroy(struct Curl_easy *); 109 110 CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl) 111 { 112 (void)data; 113 *impl = NULL; 114 return CURLE_OK; 115 } 116 117 /* Destroy context of threaded resolver */ 118 static void addr_ctx_destroy(struct async_thrdd_addr_ctx *addr_ctx) 119 { 120 if(addr_ctx) { 121 DEBUGASSERT(!addr_ctx->ref_count); 122 Curl_mutex_destroy(&addr_ctx->mutx); 123 free(addr_ctx->hostname); 124 if(addr_ctx->res) 125 Curl_freeaddrinfo(addr_ctx->res); 126 #ifndef CURL_DISABLE_SOCKETPAIR 127 /* 128 * close one end of the socket pair (may be done in resolver thread); 129 * the other end (for reading) is always closed in the parent thread. 130 */ 131 #ifndef USE_EVENTFD 132 if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) { 133 wakeup_close(addr_ctx->sock_pair[1]); 134 } 135 #endif 136 #endif 137 free(addr_ctx); 138 } 139 } 140 141 /* Initialize context for threaded resolver */ 142 static struct async_thrdd_addr_ctx * 143 addr_ctx_create(const char *hostname, int port, 144 const struct addrinfo *hints) 145 { 146 struct async_thrdd_addr_ctx *addr_ctx = calloc(1, sizeof(*addr_ctx)); 147 if(!addr_ctx) 148 return NULL; 149 150 addr_ctx->thread_hnd = curl_thread_t_null; 151 addr_ctx->port = port; 152 #ifndef CURL_DISABLE_SOCKETPAIR 153 addr_ctx->sock_pair[0] = CURL_SOCKET_BAD; 154 addr_ctx->sock_pair[1] = CURL_SOCKET_BAD; 155 #endif 156 addr_ctx->ref_count = 0; 157 158 #ifdef HAVE_GETADDRINFO 159 DEBUGASSERT(hints); 160 addr_ctx->hints = *hints; 161 #else 162 (void) hints; 163 #endif 164 165 Curl_mutex_init(&addr_ctx->mutx); 166 167 #ifndef CURL_DISABLE_SOCKETPAIR 168 /* create socket pair or pipe */ 169 if(wakeup_create(addr_ctx->sock_pair, FALSE) < 0) { 170 addr_ctx->sock_pair[0] = CURL_SOCKET_BAD; 171 addr_ctx->sock_pair[1] = CURL_SOCKET_BAD; 172 goto err_exit; 173 } 174 #endif 175 addr_ctx->sock_error = CURL_ASYNC_SUCCESS; 176 177 /* Copying hostname string because original can be destroyed by parent 178 * thread during gethostbyname execution. 179 */ 180 addr_ctx->hostname = strdup(hostname); 181 if(!addr_ctx->hostname) 182 goto err_exit; 183 184 addr_ctx->ref_count = 1; 185 return addr_ctx; 186 187 err_exit: 188 #ifndef CURL_DISABLE_SOCKETPAIR 189 if(addr_ctx->sock_pair[0] != CURL_SOCKET_BAD) { 190 wakeup_close(addr_ctx->sock_pair[0]); 191 addr_ctx->sock_pair[0] = CURL_SOCKET_BAD; 192 } 193 #endif 194 addr_ctx_destroy(addr_ctx); 195 return NULL; 196 } 197 198 #ifdef HAVE_GETADDRINFO 199 200 /* 201 * getaddrinfo_thread() resolves a name and then exits. 202 * 203 * For builds without ARES, but with USE_IPV6, create a resolver thread 204 * and wait on it. 205 */ 206 static 207 #if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE) 208 DWORD 209 #else 210 unsigned int 211 #endif 212 CURL_STDCALL getaddrinfo_thread(void *arg) 213 { 214 struct async_thrdd_addr_ctx *addr_ctx = arg; 215 char service[12]; 216 int rc; 217 bool all_gone; 218 219 msnprintf(service, sizeof(service), "%d", addr_ctx->port); 220 221 rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service, 222 &addr_ctx->hints, &addr_ctx->res); 223 224 if(rc) { 225 addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc; 226 if(addr_ctx->sock_error == 0) 227 addr_ctx->sock_error = RESOLVER_ENOMEM; 228 } 229 else { 230 Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port); 231 } 232 233 Curl_mutex_acquire(&addr_ctx->mutx); 234 if(addr_ctx->ref_count > 1) { 235 /* Someone still waiting on our results. */ 236 #ifndef CURL_DISABLE_SOCKETPAIR 237 if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) { 238 #ifdef USE_EVENTFD 239 const uint64_t buf[1] = { 1 }; 240 #else 241 const char buf[1] = { 1 }; 242 #endif 243 /* DNS has been resolved, signal client task */ 244 if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) { 245 /* update sock_erro to errno */ 246 addr_ctx->sock_error = SOCKERRNO; 247 } 248 } 249 #endif 250 } 251 /* thread gives up its reference to the shared data now. */ 252 --addr_ctx->ref_count; 253 all_gone = !addr_ctx->ref_count; 254 Curl_mutex_release(&addr_ctx->mutx); 255 if(all_gone) 256 addr_ctx_destroy(addr_ctx); 257 258 return 0; 259 } 260 261 #else /* HAVE_GETADDRINFO */ 262 263 /* 264 * gethostbyname_thread() resolves a name and then exits. 265 */ 266 static 267 #if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE) 268 DWORD 269 #else 270 unsigned int 271 #endif 272 CURL_STDCALL gethostbyname_thread(void *arg) 273 { 274 struct async_thrdd_addr_ctx *addr_ctx = arg; 275 bool all_gone; 276 277 addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port); 278 279 if(!addr_ctx->res) { 280 addr_ctx->sock_error = SOCKERRNO; 281 if(addr_ctx->sock_error == 0) 282 addr_ctx->sock_error = RESOLVER_ENOMEM; 283 } 284 285 Curl_mutex_acquire(&addr_ctx->mutx); 286 /* thread gives up its reference to the shared data now. */ 287 --addr_ctx->ref_count; 288 all_gone = !addr_ctx->ref_count;; 289 Curl_mutex_release(&addr_ctx->mutx); 290 if(all_gone) 291 addr_ctx_destroy(addr_ctx); 292 293 return 0; 294 } 295 296 #endif /* HAVE_GETADDRINFO */ 297 298 /* 299 * async_thrdd_destroy() cleans up async resolver data and thread handle. 300 */ 301 static void async_thrdd_destroy(struct Curl_easy *data) 302 { 303 struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; 304 struct async_thrdd_addr_ctx *addr = thrdd->addr; 305 #ifdef USE_HTTPSRR_ARES 306 if(thrdd->rr.channel) { 307 ares_destroy(thrdd->rr.channel); 308 thrdd->rr.channel = NULL; 309 } 310 Curl_httpsrr_cleanup(&thrdd->rr.hinfo); 311 #endif 312 313 if(addr) { 314 #ifndef CURL_DISABLE_SOCKETPAIR 315 curl_socket_t sock_rd = addr->sock_pair[0]; 316 #endif 317 bool done; 318 319 /* Release our reference to the data shared with the thread. */ 320 Curl_mutex_acquire(&addr->mutx); 321 --addr->ref_count; 322 CURL_TRC_DNS(data, "resolve, destroy async data, shared ref=%d", 323 addr->ref_count); 324 done = !addr->ref_count; 325 /* we give up our reference to `addr`, so NULL our pointer. 326 * coverity analyses this as being a potential unsynched write, 327 * assuming two calls to this function could be invoked concurrently. 328 * Which they never are, as the transfer's side runs single-threaded. */ 329 thrdd->addr = NULL; 330 if(!done) { 331 /* thread is still running. Detach the thread while mutexed, it will 332 * trigger the cleanup when it releases its reference. */ 333 Curl_thread_destroy(&addr->thread_hnd); 334 } 335 Curl_mutex_release(&addr->mutx); 336 337 if(done) { 338 /* thread has released its reference, join it and 339 * release the memory we shared with it. */ 340 if(addr->thread_hnd != curl_thread_t_null) 341 Curl_thread_join(&addr->thread_hnd); 342 addr_ctx_destroy(addr); 343 } 344 #ifndef CURL_DISABLE_SOCKETPAIR 345 /* 346 * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE 347 * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL 348 */ 349 Curl_multi_will_close(data, sock_rd); 350 wakeup_close(sock_rd); 351 #endif 352 } 353 } 354 355 #ifdef USE_HTTPSRR_ARES 356 357 static void async_thrdd_rr_done(void *user_data, ares_status_t status, 358 size_t timeouts, 359 const ares_dns_record_t *dnsrec) 360 { 361 struct Curl_easy *data = user_data; 362 struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; 363 364 (void)timeouts; 365 thrdd->rr.done = TRUE; 366 if((ARES_SUCCESS != status) || !dnsrec) 367 return; 368 thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo); 369 } 370 371 static CURLcode async_rr_start(struct Curl_easy *data) 372 { 373 struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; 374 int status; 375 376 DEBUGASSERT(!thrdd->rr.channel); 377 status = ares_init_options(&thrdd->rr.channel, NULL, 0); 378 if(status != ARES_SUCCESS) { 379 thrdd->rr.channel = NULL; 380 return CURLE_FAILED_INIT; 381 } 382 383 memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo)); 384 thrdd->rr.hinfo.port = -1; 385 ares_query_dnsrec(thrdd->rr.channel, 386 data->conn->host.name, ARES_CLASS_IN, 387 ARES_REC_TYPE_HTTPS, 388 async_thrdd_rr_done, data, NULL); 389 return CURLE_OK; 390 } 391 #endif 392 393 /* 394 * async_thrdd_init() starts a new thread that performs the actual 395 * resolve. This function returns before the resolve is done. 396 * 397 * Returns FALSE in case of failure, otherwise TRUE. 398 */ 399 static bool async_thrdd_init(struct Curl_easy *data, 400 const char *hostname, int port, int ip_version, 401 const struct addrinfo *hints) 402 { 403 struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; 404 struct async_thrdd_addr_ctx *addr_ctx; 405 406 /* !checksrc! disable ERRNOVAR 1 */ 407 int err = ENOMEM; 408 409 if(thrdd->addr 410 #ifdef USE_HTTPSRR_ARES 411 || thrdd->rr.channel 412 #endif 413 ) { 414 CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up"); 415 async_thrdd_destroy(data); 416 DEBUGASSERT(!thrdd->addr); 417 #ifdef USE_HTTPSRR_ARES 418 DEBUGASSERT(!thrdd->rr.channel); 419 #endif 420 } 421 422 data->state.async.dns = NULL; 423 data->state.async.done = FALSE; 424 data->state.async.port = port; 425 data->state.async.ip_version = ip_version; 426 free(data->state.async.hostname); 427 data->state.async.hostname = strdup(hostname); 428 if(!data->state.async.hostname) 429 goto err_exit; 430 431 addr_ctx = addr_ctx_create(hostname, port, hints); 432 if(!addr_ctx) 433 goto err_exit; 434 thrdd->addr = addr_ctx; 435 436 Curl_mutex_acquire(&addr_ctx->mutx); 437 DEBUGASSERT(addr_ctx->ref_count == 1); 438 /* passing addr_ctx to the thread adds a reference */ 439 addr_ctx->start = curlx_now(); 440 ++addr_ctx->ref_count; 441 #ifdef HAVE_GETADDRINFO 442 addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx); 443 #else 444 addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx); 445 #endif 446 if(addr_ctx->thread_hnd == curl_thread_t_null) { 447 /* The thread never started, remove its reference that never happened. */ 448 --addr_ctx->ref_count; 449 err = errno; 450 Curl_mutex_release(&addr_ctx->mutx); 451 goto err_exit; 452 } 453 Curl_mutex_release(&addr_ctx->mutx); 454 455 #ifdef USE_HTTPSRR_ARES 456 if(async_rr_start(data)) 457 infof(data, "Failed HTTPS RR operation"); 458 #endif 459 CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port); 460 return TRUE; 461 462 err_exit: 463 CURL_TRC_DNS(data, "resolve thread failed init: %d", err); 464 async_thrdd_destroy(data); 465 CURL_SETERRNO(err); 466 return FALSE; 467 } 468 469 /* 470 * 'entry' may be NULL and then no data is returned 471 */ 472 static CURLcode asyn_thrdd_await(struct Curl_easy *data, 473 struct async_thrdd_addr_ctx *addr_ctx, 474 struct Curl_dns_entry **entry) 475 { 476 CURLcode result = CURLE_OK; 477 478 DEBUGASSERT(addr_ctx->thread_hnd != curl_thread_t_null); 479 480 CURL_TRC_DNS(data, "resolve, wait for thread to finish"); 481 /* wait for the thread to resolve the name */ 482 if(Curl_thread_join(&addr_ctx->thread_hnd)) { 483 if(entry) 484 result = Curl_async_is_resolved(data, entry); 485 } 486 else 487 DEBUGASSERT(0); 488 489 data->state.async.done = TRUE; 490 if(entry) 491 *entry = data->state.async.dns; 492 493 async_thrdd_destroy(data); 494 return result; 495 } 496 497 498 /* 499 * Until we gain a way to signal the resolver threads to stop early, we must 500 * simply wait for them and ignore their results. 501 */ 502 void Curl_async_thrdd_shutdown(struct Curl_easy *data) 503 { 504 struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; 505 506 /* If we are still resolving, we must wait for the threads to fully clean up, 507 unfortunately. Otherwise, we can simply cancel to clean up any resolver 508 data. */ 509 if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null) && 510 !data->set.quick_exit) 511 (void)asyn_thrdd_await(data, thrdd->addr, NULL); 512 else 513 async_thrdd_destroy(data); 514 } 515 516 void Curl_async_thrdd_destroy(struct Curl_easy *data) 517 { 518 Curl_async_thrdd_shutdown(data); 519 } 520 521 /* 522 * Curl_async_await() 523 * 524 * Waits for a resolve to finish. This function should be avoided since using 525 * this risk getting the multi interface to "hang". 526 * 527 * If 'entry' is non-NULL, make it point to the resolved dns entry 528 * 529 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, 530 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. 531 * 532 * This is the version for resolves-in-a-thread. 533 */ 534 CURLcode Curl_async_await(struct Curl_easy *data, 535 struct Curl_dns_entry **entry) 536 { 537 struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; 538 if(thrdd->addr) 539 return asyn_thrdd_await(data, thrdd->addr, entry); 540 return CURLE_FAILED_INIT; 541 } 542 543 /* 544 * Curl_async_is_resolved() is called repeatedly to check if a previous 545 * name resolve request has completed. It should also make sure to time-out if 546 * the operation seems to take too long. 547 */ 548 CURLcode Curl_async_is_resolved(struct Curl_easy *data, 549 struct Curl_dns_entry **dns) 550 { 551 struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; 552 bool done = FALSE; 553 554 DEBUGASSERT(dns); 555 *dns = NULL; 556 557 if(data->state.async.done) { 558 *dns = data->state.async.dns; 559 CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound", 560 *dns ? "" : "not "); 561 return CURLE_OK; 562 } 563 564 #ifdef USE_HTTPSRR_ARES 565 /* best effort, ignore errors */ 566 if(thrdd->rr.channel) 567 (void)Curl_ares_perform(thrdd->rr.channel, 0); 568 #endif 569 570 DEBUGASSERT(thrdd->addr); 571 if(!thrdd->addr) 572 return CURLE_FAILED_INIT; 573 574 Curl_mutex_acquire(&thrdd->addr->mutx); 575 done = (thrdd->addr->ref_count == 1); 576 Curl_mutex_release(&thrdd->addr->mutx); 577 578 if(done) { 579 CURLcode result = CURLE_OK; 580 581 data->state.async.done = TRUE; 582 Curl_resolv_unlink(data, &data->state.async.dns); 583 584 if(thrdd->addr->res) { 585 data->state.async.dns = 586 Curl_dnscache_mk_entry(data, thrdd->addr->res, 587 data->state.async.hostname, 0, 588 data->state.async.port, FALSE); 589 thrdd->addr->res = NULL; 590 if(!data->state.async.dns) 591 result = CURLE_OUT_OF_MEMORY; 592 593 #ifdef USE_HTTPSRR_ARES 594 if(thrdd->rr.channel) { 595 result = thrdd->rr.result; 596 if(!result) { 597 struct Curl_https_rrinfo *lhrr; 598 lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo); 599 if(!lhrr) 600 result = CURLE_OUT_OF_MEMORY; 601 else 602 data->state.async.dns->hinfo = lhrr; 603 } 604 } 605 #endif 606 if(!result && data->state.async.dns) 607 result = Curl_dnscache_add(data, data->state.async.dns); 608 } 609 610 if(!result && !data->state.async.dns) 611 result = Curl_resolver_error(data); 612 if(result) 613 Curl_resolv_unlink(data, &data->state.async.dns); 614 *dns = data->state.async.dns; 615 CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound", 616 result, *dns ? "" : "not "); 617 async_thrdd_destroy(data); 618 return result; 619 } 620 else { 621 /* poll for name lookup done with exponential backoff up to 250ms */ 622 /* should be fine even if this converts to 32-bit */ 623 timediff_t elapsed = curlx_timediff(curlx_now(), 624 data->progress.t_startsingle); 625 if(elapsed < 0) 626 elapsed = 0; 627 628 if(thrdd->addr->poll_interval == 0) 629 /* Start at 1ms poll interval */ 630 thrdd->addr->poll_interval = 1; 631 else if(elapsed >= thrdd->addr->interval_end) 632 /* Back-off exponentially if last interval expired */ 633 thrdd->addr->poll_interval *= 2; 634 635 if(thrdd->addr->poll_interval > 250) 636 thrdd->addr->poll_interval = 250; 637 638 thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval; 639 Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME); 640 return CURLE_OK; 641 } 642 } 643 644 int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks) 645 { 646 struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; 647 int ret_val = 0; 648 #if !defined(CURL_DISABLE_SOCKETPAIR) || defined(USE_HTTPSRR_ARES) 649 int socketi = 0; 650 #else 651 (void)socks; 652 #endif 653 654 #ifdef USE_HTTPSRR_ARES 655 if(thrdd->rr.channel) { 656 ret_val = Curl_ares_getsock(data, thrdd->rr.channel, socks); 657 for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++) 658 if(!ARES_GETSOCK_READABLE(ret_val, socketi) && 659 !ARES_GETSOCK_WRITABLE(ret_val, socketi)) 660 break; 661 } 662 #endif 663 if(!thrdd->addr) 664 return ret_val; 665 666 #ifndef CURL_DISABLE_SOCKETPAIR 667 if(thrdd->addr) { 668 /* return read fd to client for polling the DNS resolution status */ 669 socks[socketi] = thrdd->addr->sock_pair[0]; 670 ret_val |= GETSOCK_READSOCK(socketi); 671 } 672 else 673 #endif 674 { 675 timediff_t milli; 676 timediff_t ms = curlx_timediff(curlx_now(), thrdd->addr->start); 677 if(ms < 3) 678 milli = 0; 679 else if(ms <= 50) 680 milli = ms/3; 681 else if(ms <= 250) 682 milli = 50; 683 else 684 milli = 200; 685 Curl_expire(data, milli, EXPIRE_ASYNC_NAME); 686 } 687 688 return ret_val; 689 } 690 691 #ifndef HAVE_GETADDRINFO 692 /* 693 * Curl_async_getaddrinfo() - for platforms without getaddrinfo 694 */ 695 struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, 696 const char *hostname, 697 int port, 698 int ip_version, 699 int *waitp) 700 { 701 (void)ip_version; 702 *waitp = 0; /* default to synchronous response */ 703 704 /* fire up a new resolver thread! */ 705 if(async_thrdd_init(data, hostname, port, ip_version, NULL)) { 706 *waitp = 1; /* expect asynchronous response */ 707 return NULL; 708 } 709 710 failf(data, "getaddrinfo() thread failed"); 711 712 return NULL; 713 } 714 715 #else /* !HAVE_GETADDRINFO */ 716 717 /* 718 * Curl_async_getaddrinfo() - for getaddrinfo 719 */ 720 struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, 721 const char *hostname, 722 int port, 723 int ip_version, 724 int *waitp) 725 { 726 struct addrinfo hints; 727 int pf = PF_INET; 728 *waitp = 0; /* default to synchronous response */ 729 730 CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port); 731 #ifdef CURLRES_IPV6 732 if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { 733 /* The stack seems to be IPv6-enabled */ 734 if(ip_version == CURL_IPRESOLVE_V6) 735 pf = PF_INET6; 736 else 737 pf = PF_UNSPEC; 738 } 739 #else 740 (void)ip_version; 741 #endif /* CURLRES_IPV6 */ 742 743 memset(&hints, 0, sizeof(hints)); 744 hints.ai_family = pf; 745 hints.ai_socktype = 746 (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? 747 SOCK_STREAM : SOCK_DGRAM; 748 749 /* fire up a new resolver thread! */ 750 if(async_thrdd_init(data, hostname, port, ip_version, &hints)) { 751 *waitp = 1; /* expect asynchronous response */ 752 return NULL; 753 } 754 755 failf(data, "getaddrinfo() thread failed to start"); 756 return NULL; 757 758 } 759 760 #endif /* !HAVE_GETADDRINFO */ 761 762 #endif /* CURLRES_THREADED */