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 53#else 54# define RESOLVER_ENOMEM ENOMEM 55#endif 56 57#include "system_win32.h" 58#include "urldata.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 "inet_ntop.h" 66#include "curl_threads.h" 67#include "connect.h" 68/* The last 3 #include files should be in this order */ 69#include "curl_printf.h" 70#include "curl_memory.h" 71#include "memdebug.h" 72 73struct resdata { 74 struct curltime start; 75}; 76 77/* 78 * Curl_resolver_global_init() 79 * Called from curl_global_init() to initialize global resolver environment. 80 * Does nothing here. 81 */ 82int Curl_resolver_global_init(void) 83{ 84 return CURLE_OK; 85} 86 87/* 88 * Curl_resolver_global_cleanup() 89 * Called from curl_global_cleanup() to destroy global resolver environment. 90 * Does nothing here. 91 */ 92void Curl_resolver_global_cleanup(void) 93{ 94} 95 96/* 97 * Curl_resolver_init() 98 * Called from curl_easy_init() -> Curl_open() to initialize resolver 99 * URL-state specific environment ('resolver' member of the UrlState 100 * structure). 101 */ 102CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver) 103{ 104 (void)easy; 105 *resolver = calloc(1, sizeof(struct resdata)); 106 if(!*resolver) 107 return CURLE_OUT_OF_MEMORY; 108 return CURLE_OK; 109} 110 111/* 112 * Curl_resolver_cleanup() 113 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver 114 * URL-state specific environment ('resolver' member of the UrlState 115 * structure). 116 */ 117void Curl_resolver_cleanup(void *resolver) 118{ 119 free(resolver); 120} 121 122/* 123 * Curl_resolver_duphandle() 124 * Called from curl_easy_duphandle() to duplicate resolver URL state-specific 125 * environment ('resolver' member of the UrlState structure). 126 */ 127CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from) 128{ 129 (void)from; 130 return Curl_resolver_init(easy, to); 131} 132 133static void destroy_async_data(struct Curl_async *); 134 135/* 136 * Cancel all possibly still on-going resolves for this connection. 137 */ 138void Curl_resolver_cancel(struct Curl_easy *data) 139{ 140 destroy_async_data(&data->state.async); 141} 142 143/* This function is used to init a threaded resolve */ 144static bool init_resolve_thread(struct Curl_easy *data, 145 const char *hostname, int port, 146 const struct addrinfo *hints); 147 148#ifdef _WIN32 149/* Thread sync data used by GetAddrInfoExW for win8+ */ 150struct thread_sync_data_w8 151{ 152 OVERLAPPED overlapped; 153 ADDRINFOEXW_ *res; 154 HANDLE cancel_ev; 155 ADDRINFOEXW_ hints; 156}; 157#endif 158 159/* Data for synchronization between resolver thread and its parent */ 160struct thread_sync_data { 161#ifdef _WIN32 162 struct thread_sync_data_w8 w8; 163#endif 164 curl_mutex_t *mtx; 165 int done; 166 int port; 167 char *hostname; /* hostname to resolve, Curl_async.hostname 168 duplicate */ 169#ifndef CURL_DISABLE_SOCKETPAIR 170 struct Curl_easy *data; 171 curl_socket_t sock_pair[2]; /* socket pair */ 172#endif 173 int sock_error; 174 struct Curl_addrinfo *res; 175#ifdef HAVE_GETADDRINFO 176 struct addrinfo hints; 177#endif 178 struct thread_data *td; /* for thread-self cleanup */ 179}; 180 181struct thread_data { 182#ifdef _WIN32 183 HANDLE complete_ev; 184#endif 185 curl_thread_t thread_hnd; 186 unsigned int poll_interval; 187 timediff_t interval_end; 188 struct thread_sync_data tsd; 189}; 190 191static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data) 192{ 193 return &(data->state.async.tdata->tsd); 194} 195 196/* Destroy resolver thread synchronization data */ 197static 198void destroy_thread_sync_data(struct thread_sync_data *tsd) 199{ 200 if(tsd->mtx) { 201 Curl_mutex_destroy(tsd->mtx); 202 free(tsd->mtx); 203 } 204 205 free(tsd->hostname); 206 207 if(tsd->res) 208 Curl_freeaddrinfo(tsd->res); 209 210#ifndef CURL_DISABLE_SOCKETPAIR 211 /* 212 * close one end of the socket pair (may be done in resolver thread); 213 * the other end (for reading) is always closed in the parent thread. 214 */ 215 if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { 216 wakeup_close(tsd->sock_pair[1]); 217 } 218#endif 219 memset(tsd, 0, sizeof(*tsd)); 220} 221 222/* Initialize resolver thread synchronization data */ 223static 224int init_thread_sync_data(struct thread_data *td, 225 const char *hostname, 226 int port, 227 const struct addrinfo *hints) 228{ 229 struct thread_sync_data *tsd = &td->tsd; 230 231 memset(tsd, 0, sizeof(*tsd)); 232 233 tsd->td = td; 234 tsd->port = port; 235 /* Treat the request as done until the thread actually starts so any early 236 * cleanup gets done properly. 237 */ 238 tsd->done = 1; 239#ifdef HAVE_GETADDRINFO 240 DEBUGASSERT(hints); 241 tsd->hints = *hints; 242#else 243 (void) hints; 244#endif 245 246 tsd->mtx = malloc(sizeof(curl_mutex_t)); 247 if(!tsd->mtx) 248 goto err_exit; 249 250 Curl_mutex_init(tsd->mtx); 251 252#ifndef CURL_DISABLE_SOCKETPAIR 253 /* create socket pair or pipe */ 254 if(wakeup_create(&tsd->sock_pair[0]) < 0) { 255 tsd->sock_pair[0] = CURL_SOCKET_BAD; 256 tsd->sock_pair[1] = CURL_SOCKET_BAD; 257 goto err_exit; 258 } 259#endif 260 tsd->sock_error = CURL_ASYNC_SUCCESS; 261 262 /* Copying hostname string because original can be destroyed by parent 263 * thread during gethostbyname execution. 264 */ 265 tsd->hostname = strdup(hostname); 266 if(!tsd->hostname) 267 goto err_exit; 268 269 return 1; 270 271err_exit: 272#ifndef CURL_DISABLE_SOCKETPAIR 273 if(tsd->sock_pair[0] != CURL_SOCKET_BAD) { 274 wakeup_close(tsd->sock_pair[0]); 275 tsd->sock_pair[0] = CURL_SOCKET_BAD; 276 } 277#endif 278 destroy_thread_sync_data(tsd); 279 return 0; 280} 281 282static CURLcode getaddrinfo_complete(struct Curl_easy *data) 283{ 284 struct thread_sync_data *tsd = conn_thread_sync_data(data); 285 CURLcode result; 286 287 result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res); 288 /* The tsd->res structure has been copied to async.dns and perhaps the DNS 289 cache. Set our copy to NULL so destroy_thread_sync_data doesn't free it. 290 */ 291 tsd->res = NULL; 292 293 return result; 294} 295 296#ifdef _WIN32 297static VOID WINAPI 298query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped) 299{ 300 size_t ss_size; 301 const ADDRINFOEXW_ *ai; 302 struct Curl_addrinfo *ca; 303 struct Curl_addrinfo *cafirst = NULL; 304 struct Curl_addrinfo *calast = NULL; 305#ifdef __clang__ 306#pragma clang diagnostic push 307#pragma clang diagnostic ignored "-Wcast-align" 308#endif 309 struct thread_sync_data *tsd = 310 CONTAINING_RECORD(overlapped, struct thread_sync_data, w8.overlapped); 311#ifdef __clang__ 312#pragma clang diagnostic pop 313#endif 314 struct thread_data *td = tsd->td; 315 const ADDRINFOEXW_ *res = tsd->w8.res; 316 int error = (int)err; 317 (void)bytes; 318 319 if(error == ERROR_SUCCESS) { 320 /* traverse the addrinfo list */ 321 322 for(ai = res; ai != NULL; ai = ai->ai_next) { 323 size_t namelen = ai->ai_canonname ? wcslen(ai->ai_canonname) + 1 : 0; 324 /* ignore elements with unsupported address family, */ 325 /* settle family-specific sockaddr structure size. */ 326 if(ai->ai_family == AF_INET) 327 ss_size = sizeof(struct sockaddr_in); 328#ifdef ENABLE_IPV6 329 else if(ai->ai_family == AF_INET6) 330 ss_size = sizeof(struct sockaddr_in6); 331#endif 332 else 333 continue; 334 335 /* ignore elements without required address info */ 336 if(!ai->ai_addr || !(ai->ai_addrlen > 0)) 337 continue; 338 339 /* ignore elements with bogus address size */ 340 if((size_t)ai->ai_addrlen < ss_size) 341 continue; 342 343 ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen); 344 if(!ca) { 345 error = EAI_MEMORY; 346 break; 347 } 348 349 /* copy each structure member individually, member ordering, */ 350 /* size, or padding might be different for each platform. */ 351 ca->ai_flags = ai->ai_flags; 352 ca->ai_family = ai->ai_family; 353 ca->ai_socktype = ai->ai_socktype; 354 ca->ai_protocol = ai->ai_protocol; 355 ca->ai_addrlen = (curl_socklen_t)ss_size; 356 ca->ai_addr = NULL; 357 ca->ai_canonname = NULL; 358 ca->ai_next = NULL; 359 360 ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); 361 memcpy(ca->ai_addr, ai->ai_addr, ss_size); 362 363 if(namelen) { 364 size_t i; 365 ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size); 366 for(i = 0; i < namelen; ++i) /* convert wide string to ascii */ 367 ca->ai_canonname[i] = (char)ai->ai_canonname[i]; 368 ca->ai_canonname[namelen] = '\0'; 369 } 370 371 /* if the return list is empty, this becomes the first element */ 372 if(!cafirst) 373 cafirst = ca; 374 375 /* add this element last in the return list */ 376 if(calast) 377 calast->ai_next = ca; 378 calast = ca; 379 } 380 381 /* if we failed, also destroy the Curl_addrinfo list */ 382 if(error) { 383 Curl_freeaddrinfo(cafirst); 384 cafirst = NULL; 385 } 386 else if(!cafirst) { 387#ifdef EAI_NONAME 388 /* rfc3493 conformant */ 389 error = EAI_NONAME; 390#else 391 /* rfc3493 obsoleted */ 392 error = EAI_NODATA; 393#endif 394#ifdef USE_WINSOCK 395 SET_SOCKERRNO(error); 396#endif 397 } 398 tsd->res = cafirst; 399 } 400 401 if(tsd->w8.res) { 402 Curl_FreeAddrInfoExW(tsd->w8.res); 403 tsd->w8.res = NULL; 404 } 405 406 if(error) { 407 tsd->sock_error = SOCKERRNO?SOCKERRNO:error; 408 if(tsd->sock_error == 0) 409 tsd->sock_error = RESOLVER_ENOMEM; 410 } 411 else { 412 Curl_addrinfo_set_port(tsd->res, tsd->port); 413 } 414 415 Curl_mutex_acquire(tsd->mtx); 416 if(tsd->done) { 417 /* too late, gotta clean up the mess */ 418 Curl_mutex_release(tsd->mtx); 419 destroy_thread_sync_data(tsd); 420 free(td); 421 } 422 else { 423#ifndef CURL_DISABLE_SOCKETPAIR 424 char buf[1]; 425 if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { 426 /* DNS has been resolved, signal client task */ 427 buf[0] = 1; 428 if(swrite(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { 429 /* update sock_erro to errno */ 430 tsd->sock_error = SOCKERRNO; 431 } 432 } 433#endif 434 tsd->done = 1; 435 Curl_mutex_release(tsd->mtx); 436 if(td->complete_ev) 437 SetEvent(td->complete_ev); /* Notify caller that the query completed */ 438 } 439} 440#endif 441 442#ifdef HAVE_GETADDRINFO 443 444/* 445 * getaddrinfo_thread() resolves a name and then exits. 446 * 447 * For builds without ARES, but with ENABLE_IPV6, create a resolver thread 448 * and wait on it. 449 */ 450static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) 451{ 452 struct thread_sync_data *tsd = (struct thread_sync_data *)arg; 453 struct thread_data *td = tsd->td; 454 char service[12]; 455 int rc; 456#ifndef CURL_DISABLE_SOCKETPAIR 457 char buf[1]; 458#endif 459 460 msnprintf(service, sizeof(service), "%d", tsd->port); 461 462 rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res); 463 464 if(rc) { 465 tsd->sock_error = SOCKERRNO?SOCKERRNO:rc; 466 if(tsd->sock_error == 0) 467 tsd->sock_error = RESOLVER_ENOMEM; 468 } 469 else { 470 Curl_addrinfo_set_port(tsd->res, tsd->port); 471 } 472 473 Curl_mutex_acquire(tsd->mtx); 474 if(tsd->done) { 475 /* too late, gotta clean up the mess */ 476 Curl_mutex_release(tsd->mtx); 477 destroy_thread_sync_data(tsd); 478 free(td); 479 } 480 else { 481#ifndef CURL_DISABLE_SOCKETPAIR 482 if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { 483 /* DNS has been resolved, signal client task */ 484 buf[0] = 1; 485 if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { 486 /* update sock_erro to errno */ 487 tsd->sock_error = SOCKERRNO; 488 } 489 } 490#endif 491 tsd->done = 1; 492 Curl_mutex_release(tsd->mtx); 493 } 494 495 return 0; 496} 497 498#else /* HAVE_GETADDRINFO */ 499 500/* 501 * gethostbyname_thread() resolves a name and then exits. 502 */ 503static unsigned int CURL_STDCALL gethostbyname_thread(void *arg) 504{ 505 struct thread_sync_data *tsd = (struct thread_sync_data *)arg; 506 struct thread_data *td = tsd->td; 507 508 tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port); 509 510 if(!tsd->res) { 511 tsd->sock_error = SOCKERRNO; 512 if(tsd->sock_error == 0) 513 tsd->sock_error = RESOLVER_ENOMEM; 514 } 515 516 Curl_mutex_acquire(tsd->mtx); 517 if(tsd->done) { 518 /* too late, gotta clean up the mess */ 519 Curl_mutex_release(tsd->mtx); 520 destroy_thread_sync_data(tsd); 521 free(td); 522 } 523 else { 524 tsd->done = 1; 525 Curl_mutex_release(tsd->mtx); 526 } 527 528 return 0; 529} 530 531#endif /* HAVE_GETADDRINFO */ 532 533/* 534 * destroy_async_data() cleans up async resolver data and thread handle. 535 */ 536static void destroy_async_data(struct Curl_async *async) 537{ 538 if(async->tdata) { 539 struct thread_data *td = async->tdata; 540 int done; 541#ifndef CURL_DISABLE_SOCKETPAIR 542 curl_socket_t sock_rd = td->tsd.sock_pair[0]; 543 struct Curl_easy *data = td->tsd.data; 544#endif 545 546 /* 547 * if the thread is still blocking in the resolve syscall, detach it and 548 * let the thread do the cleanup... 549 */ 550 Curl_mutex_acquire(td->tsd.mtx); 551 done = td->tsd.done; 552 td->tsd.done = 1; 553 Curl_mutex_release(td->tsd.mtx); 554 555 if(!done) { 556#ifdef _WIN32 557 if(td->complete_ev) 558 CloseHandle(td->complete_ev); 559 else 560#endif 561 Curl_thread_destroy(td->thread_hnd); 562 } 563 else { 564#ifdef _WIN32 565 if(td->complete_ev) { 566 Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev); 567 WaitForSingleObject(td->complete_ev, INFINITE); 568 CloseHandle(td->complete_ev); 569 } 570#endif 571 if(td->thread_hnd != curl_thread_t_null) 572 Curl_thread_join(&td->thread_hnd); 573 574 destroy_thread_sync_data(&td->tsd); 575 576 free(async->tdata); 577 } 578#ifndef CURL_DISABLE_SOCKETPAIR 579 /* 580 * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE 581 * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL 582 */ 583 Curl_multi_closed(data, sock_rd); 584 sclose(sock_rd); 585#endif 586 } 587 async->tdata = NULL; 588 589 free(async->hostname); 590 async->hostname = NULL; 591} 592 593/* 594 * init_resolve_thread() starts a new thread that performs the actual 595 * resolve. This function returns before the resolve is done. 596 * 597 * Returns FALSE in case of failure, otherwise TRUE. 598 */ 599static bool init_resolve_thread(struct Curl_easy *data, 600 const char *hostname, int port, 601 const struct addrinfo *hints) 602{ 603 struct thread_data *td = calloc(1, sizeof(struct thread_data)); 604 int err = ENOMEM; 605 struct Curl_async *asp = &data->state.async; 606 607 data->state.async.tdata = td; 608 if(!td) 609 goto errno_exit; 610 611 asp->port = port; 612 asp->done = FALSE; 613 asp->status = 0; 614 asp->dns = NULL; 615 td->thread_hnd = curl_thread_t_null; 616#ifdef _WIN32 617 td->complete_ev = NULL; 618#endif 619 620 if(!init_thread_sync_data(td, hostname, port, hints)) { 621 asp->tdata = NULL; 622 free(td); 623 goto errno_exit; 624 } 625 626 free(asp->hostname); 627 asp->hostname = strdup(hostname); 628 if(!asp->hostname) 629 goto err_exit; 630 631 /* The thread will set this to 1 when complete. */ 632 td->tsd.done = 0; 633 634#ifdef _WIN32 635 if(Curl_isWindows8OrGreater && Curl_FreeAddrInfoExW && 636 Curl_GetAddrInfoExCancel && Curl_GetAddrInfoExW) { 637#define MAX_NAME_LEN 256 /* max domain name is 253 chars */ 638#define MAX_PORT_LEN 8 639 WCHAR namebuf[MAX_NAME_LEN]; 640 WCHAR portbuf[MAX_PORT_LEN]; 641 /* calculate required length */ 642 int w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, hostname, 643 -1, NULL, 0); 644 if((w_len > 0) && (w_len < MAX_NAME_LEN)) { 645 /* do utf8 conversion */ 646 w_len = MultiByteToWideChar(CP_UTF8, 0, hostname, -1, namebuf, w_len); 647 if((w_len > 0) && (w_len < MAX_NAME_LEN)) { 648 swprintf(portbuf, MAX_PORT_LEN, L"%d", port); 649 td->tsd.w8.hints.ai_family = hints->ai_family; 650 td->tsd.w8.hints.ai_socktype = hints->ai_socktype; 651 td->complete_ev = CreateEvent(NULL, TRUE, FALSE, NULL); 652 if(!td->complete_ev) { 653 /* failed to start, mark it as done here for proper cleanup. */ 654 td->tsd.done = 1; 655 goto err_exit; 656 } 657 err = Curl_GetAddrInfoExW(namebuf, portbuf, NS_DNS, 658 NULL, &td->tsd.w8.hints, &td->tsd.w8.res, 659 NULL, &td->tsd.w8.overlapped, 660 &query_complete, &td->tsd.w8.cancel_ev); 661 if(err != WSA_IO_PENDING) 662 query_complete(err, 0, &td->tsd.w8.overlapped); 663 return TRUE; 664 } 665 } 666 } 667#endif 668 669#ifdef HAVE_GETADDRINFO 670 td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd); 671#else 672 td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd); 673#endif 674 675 if(!td->thread_hnd) { 676 /* The thread never started, so mark it as done here for proper cleanup. */ 677 td->tsd.done = 1; 678 err = errno; 679 goto err_exit; 680 } 681 682 return TRUE; 683 684err_exit: 685 destroy_async_data(asp); 686 687errno_exit: 688 errno = err; 689 return FALSE; 690} 691 692/* 693 * 'entry' may be NULL and then no data is returned 694 */ 695static CURLcode thread_wait_resolv(struct Curl_easy *data, 696 struct Curl_dns_entry **entry, 697 bool report) 698{ 699 struct thread_data *td; 700 CURLcode result = CURLE_OK; 701 702 DEBUGASSERT(data); 703 td = data->state.async.tdata; 704 DEBUGASSERT(td); 705#ifdef _WIN32 706 DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null); 707#else 708 DEBUGASSERT(td->thread_hnd != curl_thread_t_null); 709#endif 710 711 /* wait for the thread to resolve the name */ 712#ifdef _WIN32 713 if(td->complete_ev) { 714 WaitForSingleObject(td->complete_ev, INFINITE); 715 CloseHandle(td->complete_ev); 716 if(entry) 717 result = getaddrinfo_complete(data); 718 } 719 else 720#endif 721 if(Curl_thread_join(&td->thread_hnd)) { 722 if(entry) 723 result = getaddrinfo_complete(data); 724 } 725 else 726 DEBUGASSERT(0); 727 728 data->state.async.done = TRUE; 729 730 if(entry) 731 *entry = data->state.async.dns; 732 733 if(!data->state.async.dns && report) 734 /* a name was not resolved, report error */ 735 result = Curl_resolver_error(data); 736 737 destroy_async_data(&data->state.async); 738 739 if(!data->state.async.dns && report) 740 connclose(data->conn, "asynch resolve failed"); 741 742 return result; 743} 744 745 746/* 747 * Until we gain a way to signal the resolver threads to stop early, we must 748 * simply wait for them and ignore their results. 749 */ 750void Curl_resolver_kill(struct Curl_easy *data) 751{ 752 struct thread_data *td = data->state.async.tdata; 753 754 /* If we're still resolving, we must wait for the threads to fully clean up, 755 unfortunately. Otherwise, we can simply cancel to clean up any resolver 756 data. */ 757 if(td && td->thread_hnd != curl_thread_t_null 758 && (data->set.quick_exit != 1L)) 759 (void)thread_wait_resolv(data, NULL, FALSE); 760 else 761 Curl_resolver_cancel(data); 762} 763 764/* 765 * Curl_resolver_wait_resolv() 766 * 767 * Waits for a resolve to finish. This function should be avoided since using 768 * this risk getting the multi interface to "hang". 769 * 770 * If 'entry' is non-NULL, make it point to the resolved dns entry 771 * 772 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, 773 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. 774 * 775 * This is the version for resolves-in-a-thread. 776 */ 777CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, 778 struct Curl_dns_entry **entry) 779{ 780 return thread_wait_resolv(data, entry, TRUE); 781} 782 783/* 784 * Curl_resolver_is_resolved() is called repeatedly to check if a previous 785 * name resolve request has completed. It should also make sure to time-out if 786 * the operation seems to take too long. 787 */ 788CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, 789 struct Curl_dns_entry **entry) 790{ 791 struct thread_data *td = data->state.async.tdata; 792 int done = 0; 793 794 DEBUGASSERT(entry); 795 *entry = NULL; 796 797 if(!td) { 798 DEBUGASSERT(td); 799 return CURLE_COULDNT_RESOLVE_HOST; 800 } 801 802 Curl_mutex_acquire(td->tsd.mtx); 803 done = td->tsd.done; 804 Curl_mutex_release(td->tsd.mtx); 805 806 if(done) { 807 getaddrinfo_complete(data); 808 809 if(!data->state.async.dns) { 810 CURLcode result = Curl_resolver_error(data); 811 destroy_async_data(&data->state.async); 812 return result; 813 } 814 destroy_async_data(&data->state.async); 815 *entry = data->state.async.dns; 816 } 817 else { 818 /* poll for name lookup done with exponential backoff up to 250ms */ 819 /* should be fine even if this converts to 32 bit */ 820 timediff_t elapsed = Curl_timediff(Curl_now(), 821 data->progress.t_startsingle); 822 if(elapsed < 0) 823 elapsed = 0; 824 825 if(td->poll_interval == 0) 826 /* Start at 1ms poll interval */ 827 td->poll_interval = 1; 828 else if(elapsed >= td->interval_end) 829 /* Back-off exponentially if last interval expired */ 830 td->poll_interval *= 2; 831 832 if(td->poll_interval > 250) 833 td->poll_interval = 250; 834 835 td->interval_end = elapsed + td->poll_interval; 836 Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME); 837 } 838 839 return CURLE_OK; 840} 841 842int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks) 843{ 844 int ret_val = 0; 845 timediff_t milli; 846 timediff_t ms; 847 struct resdata *reslv = (struct resdata *)data->state.async.resolver; 848#ifndef CURL_DISABLE_SOCKETPAIR 849 struct thread_data *td = data->state.async.tdata; 850#else 851 (void)socks; 852#endif 853 854#ifndef CURL_DISABLE_SOCKETPAIR 855 if(td) { 856 /* return read fd to client for polling the DNS resolution status */ 857 socks[0] = td->tsd.sock_pair[0]; 858 td->tsd.data = data; 859 ret_val = GETSOCK_READSOCK(0); 860 } 861 else { 862#endif 863 ms = Curl_timediff(Curl_now(), reslv->start); 864 if(ms < 3) 865 milli = 0; 866 else if(ms <= 50) 867 milli = ms/3; 868 else if(ms <= 250) 869 milli = 50; 870 else 871 milli = 200; 872 Curl_expire(data, milli, EXPIRE_ASYNC_NAME); 873#ifndef CURL_DISABLE_SOCKETPAIR 874 } 875#endif 876 877 878 return ret_val; 879} 880 881#ifndef HAVE_GETADDRINFO 882/* 883 * Curl_getaddrinfo() - for platforms without getaddrinfo 884 */ 885struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, 886 const char *hostname, 887 int port, 888 int *waitp) 889{ 890 struct resdata *reslv = (struct resdata *)data->state.async.resolver; 891 892 *waitp = 0; /* default to synchronous response */ 893 894 reslv->start = Curl_now(); 895 896 /* fire up a new resolver thread! */ 897 if(init_resolve_thread(data, hostname, port, NULL)) { 898 *waitp = 1; /* expect asynchronous response */ 899 return NULL; 900 } 901 902 failf(data, "getaddrinfo() thread failed"); 903 904 return NULL; 905} 906 907#else /* !HAVE_GETADDRINFO */ 908 909/* 910 * Curl_resolver_getaddrinfo() - for getaddrinfo 911 */ 912struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, 913 const char *hostname, 914 int port, 915 int *waitp) 916{ 917 struct addrinfo hints; 918 int pf = PF_INET; 919 struct resdata *reslv = (struct resdata *)data->state.async.resolver; 920 921 *waitp = 0; /* default to synchronous response */ 922 923#ifdef CURLRES_IPV6 924 if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { 925 /* The stack seems to be IPv6-enabled */ 926 if(data->conn->ip_version == CURL_IPRESOLVE_V6) 927 pf = PF_INET6; 928 else 929 pf = PF_UNSPEC; 930 } 931#endif /* CURLRES_IPV6 */ 932 933 memset(&hints, 0, sizeof(hints)); 934 hints.ai_family = pf; 935 hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)? 936 SOCK_STREAM : SOCK_DGRAM; 937 938 reslv->start = Curl_now(); 939 /* fire up a new resolver thread! */ 940 if(init_resolve_thread(data, hostname, port, &hints)) { 941 *waitp = 1; /* expect asynchronous response */ 942 return NULL; 943 } 944 945 failf(data, "getaddrinfo() thread failed to start"); 946 return NULL; 947 948} 949 950#endif /* !HAVE_GETADDRINFO */ 951 952CURLcode Curl_set_dns_servers(struct Curl_easy *data, 953 char *servers) 954{ 955 (void)data; 956 (void)servers; 957 return CURLE_NOT_BUILT_IN; 958 959} 960 961CURLcode Curl_set_dns_interface(struct Curl_easy *data, 962 const char *interf) 963{ 964 (void)data; 965 (void)interf; 966 return CURLE_NOT_BUILT_IN; 967} 968 969CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, 970 const char *local_ip4) 971{ 972 (void)data; 973 (void)local_ip4; 974 return CURLE_NOT_BUILT_IN; 975} 976 977CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, 978 const char *local_ip6) 979{ 980 (void)data; 981 (void)local_ip6; 982 return CURLE_NOT_BUILT_IN; 983} 984 985#endif /* CURLRES_THREADED */ 986