1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2017, 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 /*********************************************************************** 26 * Only for threaded name resolves builds 27 **********************************************************************/ 28 #ifdef CURLRES_THREADED 29 30 #ifdef HAVE_NETINET_IN_H 31 #include <netinet/in.h> 32 #endif 33 #ifdef HAVE_NETDB_H 34 #include <netdb.h> 35 #endif 36 #ifdef HAVE_ARPA_INET_H 37 #include <arpa/inet.h> 38 #endif 39 #ifdef __VMS 40 #include <in.h> 41 #include <inet.h> 42 #endif 43 44 #if defined(USE_THREADS_POSIX) 45 # ifdef HAVE_PTHREAD_H 46 # include <pthread.h> 47 # endif 48 #elif defined(USE_THREADS_WIN32) 49 # ifdef HAVE_PROCESS_H 50 # include <process.h> 51 # endif 52 #endif 53 54 #if (defined(NETWARE) && defined(__NOVELL_LIBC__)) 55 #undef in_addr_t 56 #define in_addr_t unsigned long 57 #endif 58 59 #ifdef HAVE_GETADDRINFO 60 # define RESOLVER_ENOMEM EAI_MEMORY 61 #else 62 # define RESOLVER_ENOMEM ENOMEM 63 #endif 64 65 #include "urldata.h" 66 #include "sendf.h" 67 #include "hostip.h" 68 #include "hash.h" 69 #include "share.h" 70 #include "strerror.h" 71 #include "url.h" 72 #include "multiif.h" 73 #include "inet_pton.h" 74 #include "inet_ntop.h" 75 #include "curl_threads.h" 76 #include "connect.h" 77 /* The last 3 #include files should be in this order */ 78 #include "curl_printf.h" 79 #include "curl_memory.h" 80 #include "memdebug.h" 81 82 /* 83 * Curl_resolver_global_init() 84 * Called from curl_global_init() to initialize global resolver environment. 85 * Does nothing here. 86 */ 87 int Curl_resolver_global_init(void) 88 { 89 return CURLE_OK; 90 } 91 92 /* 93 * Curl_resolver_global_cleanup() 94 * Called from curl_global_cleanup() to destroy global resolver environment. 95 * Does nothing here. 96 */ 97 void Curl_resolver_global_cleanup(void) 98 { 99 } 100 101 /* 102 * Curl_resolver_init() 103 * Called from curl_easy_init() -> Curl_open() to initialize resolver 104 * URL-state specific environment ('resolver' member of the UrlState 105 * structure). Does nothing here. 106 */ 107 CURLcode Curl_resolver_init(void **resolver) 108 { 109 (void)resolver; 110 return CURLE_OK; 111 } 112 113 /* 114 * Curl_resolver_cleanup() 115 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver 116 * URL-state specific environment ('resolver' member of the UrlState 117 * structure). Does nothing here. 118 */ 119 void Curl_resolver_cleanup(void *resolver) 120 { 121 (void)resolver; 122 } 123 124 /* 125 * Curl_resolver_duphandle() 126 * Called from curl_easy_duphandle() to duplicate resolver URL state-specific 127 * environment ('resolver' member of the UrlState structure). Does nothing 128 * here. 129 */ 130 int Curl_resolver_duphandle(void **to, void *from) 131 { 132 (void)to; 133 (void)from; 134 return CURLE_OK; 135 } 136 137 static void destroy_async_data(struct Curl_async *); 138 139 /* 140 * Cancel all possibly still on-going resolves for this connection. 141 */ 142 void Curl_resolver_cancel(struct connectdata *conn) 143 { 144 destroy_async_data(&conn->async); 145 } 146 147 /* This function is used to init a threaded resolve */ 148 static bool init_resolve_thread(struct connectdata *conn, 149 const char *hostname, int port, 150 const struct addrinfo *hints); 151 152 153 /* Data for synchronization between resolver thread and its parent */ 154 struct thread_sync_data { 155 curl_mutex_t * mtx; 156 int done; 157 158 char *hostname; /* hostname to resolve, Curl_async.hostname 159 duplicate */ 160 int port; 161 int sock_error; 162 Curl_addrinfo *res; 163 #ifdef HAVE_GETADDRINFO 164 struct addrinfo hints; 165 #endif 166 struct thread_data *td; /* for thread-self cleanup */ 167 }; 168 169 struct thread_data { 170 curl_thread_t thread_hnd; 171 unsigned int poll_interval; 172 time_t interval_end; 173 struct thread_sync_data tsd; 174 }; 175 176 static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn) 177 { 178 return &(((struct thread_data *)conn->async.os_specific)->tsd); 179 } 180 181 #define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd); 182 183 /* Destroy resolver thread synchronization data */ 184 static 185 void destroy_thread_sync_data(struct thread_sync_data * tsd) 186 { 187 if(tsd->mtx) { 188 Curl_mutex_destroy(tsd->mtx); 189 free(tsd->mtx); 190 } 191 192 free(tsd->hostname); 193 194 if(tsd->res) 195 Curl_freeaddrinfo(tsd->res); 196 197 memset(tsd, 0, sizeof(*tsd)); 198 } 199 200 /* Initialize resolver thread synchronization data */ 201 static 202 int init_thread_sync_data(struct thread_data * td, 203 const char *hostname, 204 int port, 205 const struct addrinfo *hints) 206 { 207 struct thread_sync_data *tsd = &td->tsd; 208 209 memset(tsd, 0, sizeof(*tsd)); 210 211 tsd->td = td; 212 tsd->port = port; 213 /* Treat the request as done until the thread actually starts so any early 214 * cleanup gets done properly. 215 */ 216 tsd->done = 1; 217 #ifdef HAVE_GETADDRINFO 218 DEBUGASSERT(hints); 219 tsd->hints = *hints; 220 #else 221 (void) hints; 222 #endif 223 224 tsd->mtx = malloc(sizeof(curl_mutex_t)); 225 if(tsd->mtx == NULL) 226 goto err_exit; 227 228 Curl_mutex_init(tsd->mtx); 229 230 tsd->sock_error = CURL_ASYNC_SUCCESS; 231 232 /* Copying hostname string because original can be destroyed by parent 233 * thread during gethostbyname execution. 234 */ 235 tsd->hostname = strdup(hostname); 236 if(!tsd->hostname) 237 goto err_exit; 238 239 return 1; 240 241 err_exit: 242 /* Memory allocation failed */ 243 destroy_thread_sync_data(tsd); 244 return 0; 245 } 246 247 static int getaddrinfo_complete(struct connectdata *conn) 248 { 249 struct thread_sync_data *tsd = conn_thread_sync_data(conn); 250 int rc; 251 252 rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res); 253 /* The tsd->res structure has been copied to async.dns and perhaps the DNS 254 cache. Set our copy to NULL so destroy_thread_sync_data doesn't free it. 255 */ 256 tsd->res = NULL; 257 258 return rc; 259 } 260 261 262 #ifdef HAVE_GETADDRINFO 263 264 /* 265 * getaddrinfo_thread() resolves a name and then exits. 266 * 267 * For builds without ARES, but with ENABLE_IPV6, create a resolver thread 268 * and wait on it. 269 */ 270 static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) 271 { 272 struct thread_sync_data *tsd = (struct thread_sync_data*)arg; 273 struct thread_data *td = tsd->td; 274 char service[12]; 275 int rc; 276 277 snprintf(service, sizeof(service), "%d", tsd->port); 278 279 rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res); 280 281 if(rc != 0) { 282 tsd->sock_error = SOCKERRNO?SOCKERRNO:rc; 283 if(tsd->sock_error == 0) 284 tsd->sock_error = RESOLVER_ENOMEM; 285 } 286 else { 287 Curl_addrinfo_set_port(tsd->res, tsd->port); 288 } 289 290 Curl_mutex_acquire(tsd->mtx); 291 if(tsd->done) { 292 /* too late, gotta clean up the mess */ 293 Curl_mutex_release(tsd->mtx); 294 destroy_thread_sync_data(tsd); 295 free(td); 296 } 297 else { 298 tsd->done = 1; 299 Curl_mutex_release(tsd->mtx); 300 } 301 302 return 0; 303 } 304 305 #else /* HAVE_GETADDRINFO */ 306 307 /* 308 * gethostbyname_thread() resolves a name and then exits. 309 */ 310 static unsigned int CURL_STDCALL gethostbyname_thread(void *arg) 311 { 312 struct thread_sync_data *tsd = (struct thread_sync_data *)arg; 313 struct thread_data *td = tsd->td; 314 315 tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port); 316 317 if(!tsd->res) { 318 tsd->sock_error = SOCKERRNO; 319 if(tsd->sock_error == 0) 320 tsd->sock_error = RESOLVER_ENOMEM; 321 } 322 323 Curl_mutex_acquire(tsd->mtx); 324 if(tsd->done) { 325 /* too late, gotta clean up the mess */ 326 Curl_mutex_release(tsd->mtx); 327 destroy_thread_sync_data(tsd); 328 free(td); 329 } 330 else { 331 tsd->done = 1; 332 Curl_mutex_release(tsd->mtx); 333 } 334 335 return 0; 336 } 337 338 #endif /* HAVE_GETADDRINFO */ 339 340 /* 341 * destroy_async_data() cleans up async resolver data and thread handle. 342 */ 343 static void destroy_async_data(struct Curl_async *async) 344 { 345 if(async->os_specific) { 346 struct thread_data *td = (struct thread_data*) async->os_specific; 347 int done; 348 349 /* 350 * if the thread is still blocking in the resolve syscall, detach it and 351 * let the thread do the cleanup... 352 */ 353 Curl_mutex_acquire(td->tsd.mtx); 354 done = td->tsd.done; 355 td->tsd.done = 1; 356 Curl_mutex_release(td->tsd.mtx); 357 358 if(!done) { 359 Curl_thread_destroy(td->thread_hnd); 360 } 361 else { 362 if(td->thread_hnd != curl_thread_t_null) 363 Curl_thread_join(&td->thread_hnd); 364 365 destroy_thread_sync_data(&td->tsd); 366 367 free(async->os_specific); 368 } 369 } 370 async->os_specific = NULL; 371 372 free(async->hostname); 373 async->hostname = NULL; 374 } 375 376 /* 377 * init_resolve_thread() starts a new thread that performs the actual 378 * resolve. This function returns before the resolve is done. 379 * 380 * Returns FALSE in case of failure, otherwise TRUE. 381 */ 382 static bool init_resolve_thread(struct connectdata *conn, 383 const char *hostname, int port, 384 const struct addrinfo *hints) 385 { 386 struct thread_data *td = calloc(1, sizeof(struct thread_data)); 387 int err = ENOMEM; 388 389 conn->async.os_specific = (void *)td; 390 if(!td) 391 goto errno_exit; 392 393 conn->async.port = port; 394 conn->async.done = FALSE; 395 conn->async.status = 0; 396 conn->async.dns = NULL; 397 td->thread_hnd = curl_thread_t_null; 398 399 if(!init_thread_sync_data(td, hostname, port, hints)) { 400 conn->async.os_specific = NULL; 401 free(td); 402 goto errno_exit; 403 } 404 405 free(conn->async.hostname); 406 conn->async.hostname = strdup(hostname); 407 if(!conn->async.hostname) 408 goto err_exit; 409 410 /* The thread will set this to 1 when complete. */ 411 td->tsd.done = 0; 412 413 #ifdef HAVE_GETADDRINFO 414 td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd); 415 #else 416 td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd); 417 #endif 418 419 if(!td->thread_hnd) { 420 /* The thread never started, so mark it as done here for proper cleanup. */ 421 td->tsd.done = 1; 422 err = errno; 423 goto err_exit; 424 } 425 426 return TRUE; 427 428 err_exit: 429 destroy_async_data(&conn->async); 430 431 errno_exit: 432 errno = err; 433 return FALSE; 434 } 435 436 /* 437 * resolver_error() calls failf() with the appropriate message after a resolve 438 * error 439 */ 440 441 static CURLcode resolver_error(struct connectdata *conn) 442 { 443 const char *host_or_proxy; 444 CURLcode result; 445 446 if(conn->bits.httpproxy) { 447 host_or_proxy = "proxy"; 448 result = CURLE_COULDNT_RESOLVE_PROXY; 449 } 450 else { 451 host_or_proxy = "host"; 452 result = CURLE_COULDNT_RESOLVE_HOST; 453 } 454 455 failf(conn->data, "Could not resolve %s: %s", host_or_proxy, 456 conn->async.hostname); 457 458 return result; 459 } 460 461 /* 462 * Curl_resolver_wait_resolv() 463 * 464 * waits for a resolve to finish. This function should be avoided since using 465 * this risk getting the multi interface to "hang". 466 * 467 * If 'entry' is non-NULL, make it point to the resolved dns entry 468 * 469 * This is the version for resolves-in-a-thread. 470 */ 471 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, 472 struct Curl_dns_entry **entry) 473 { 474 struct thread_data *td = (struct thread_data*) conn->async.os_specific; 475 CURLcode result = CURLE_OK; 476 477 DEBUGASSERT(conn && td); 478 479 /* wait for the thread to resolve the name */ 480 if(Curl_thread_join(&td->thread_hnd)) 481 result = getaddrinfo_complete(conn); 482 else 483 DEBUGASSERT(0); 484 485 conn->async.done = TRUE; 486 487 if(entry) 488 *entry = conn->async.dns; 489 490 if(!conn->async.dns) 491 /* a name was not resolved, report error */ 492 result = resolver_error(conn); 493 494 destroy_async_data(&conn->async); 495 496 if(!conn->async.dns) 497 connclose(conn, "asynch resolve failed"); 498 499 return result; 500 } 501 502 /* 503 * Curl_resolver_is_resolved() is called repeatedly to check if a previous 504 * name resolve request has completed. It should also make sure to time-out if 505 * the operation seems to take too long. 506 */ 507 CURLcode Curl_resolver_is_resolved(struct connectdata *conn, 508 struct Curl_dns_entry **entry) 509 { 510 struct Curl_easy *data = conn->data; 511 struct thread_data *td = (struct thread_data*) conn->async.os_specific; 512 int done = 0; 513 514 *entry = NULL; 515 516 if(!td) { 517 DEBUGASSERT(td); 518 return CURLE_COULDNT_RESOLVE_HOST; 519 } 520 521 Curl_mutex_acquire(td->tsd.mtx); 522 done = td->tsd.done; 523 Curl_mutex_release(td->tsd.mtx); 524 525 if(done) { 526 getaddrinfo_complete(conn); 527 528 if(!conn->async.dns) { 529 CURLcode result = resolver_error(conn); 530 destroy_async_data(&conn->async); 531 return result; 532 } 533 destroy_async_data(&conn->async); 534 *entry = conn->async.dns; 535 } 536 else { 537 /* poll for name lookup done with exponential backoff up to 250ms */ 538 timediff_t elapsed = Curl_timediff(Curl_now(), 539 data->progress.t_startsingle); 540 if(elapsed < 0) 541 elapsed = 0; 542 543 if(td->poll_interval == 0) 544 /* Start at 1ms poll interval */ 545 td->poll_interval = 1; 546 else if(elapsed >= td->interval_end) 547 /* Back-off exponentially if last interval expired */ 548 td->poll_interval *= 2; 549 550 if(td->poll_interval > 250) 551 td->poll_interval = 250; 552 553 td->interval_end = elapsed + td->poll_interval; 554 Curl_expire(conn->data, td->poll_interval, EXPIRE_ASYNC_NAME); 555 } 556 557 return CURLE_OK; 558 } 559 560 int Curl_resolver_getsock(struct connectdata *conn, 561 curl_socket_t *socks, 562 int numsocks) 563 { 564 (void)conn; 565 (void)socks; 566 (void)numsocks; 567 return 0; 568 } 569 570 #ifndef HAVE_GETADDRINFO 571 /* 572 * Curl_getaddrinfo() - for platforms without getaddrinfo 573 */ 574 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, 575 const char *hostname, 576 int port, 577 int *waitp) 578 { 579 struct in_addr in; 580 581 *waitp = 0; /* default to synchronous response */ 582 583 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) 584 /* This is a dotted IP address 123.123.123.123-style */ 585 return Curl_ip2addr(AF_INET, &in, hostname, port); 586 587 /* fire up a new resolver thread! */ 588 if(init_resolve_thread(conn, hostname, port, NULL)) { 589 *waitp = 1; /* expect asynchronous response */ 590 return NULL; 591 } 592 593 /* fall-back to blocking version */ 594 return Curl_ipv4_resolve_r(hostname, port); 595 } 596 597 #else /* !HAVE_GETADDRINFO */ 598 599 /* 600 * Curl_resolver_getaddrinfo() - for getaddrinfo 601 */ 602 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, 603 const char *hostname, 604 int port, 605 int *waitp) 606 { 607 struct addrinfo hints; 608 Curl_addrinfo *res; 609 int error; 610 char sbuf[12]; 611 int pf = PF_INET; 612 613 *waitp = 0; /* default to synchronous response */ 614 615 #ifndef USE_RESOLVE_ON_IPS 616 { 617 struct in_addr in; 618 /* First check if this is an IPv4 address string */ 619 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) 620 /* This is a dotted IP address 123.123.123.123-style */ 621 return Curl_ip2addr(AF_INET, &in, hostname, port); 622 } 623 #ifdef CURLRES_IPV6 624 { 625 struct in6_addr in6; 626 /* check if this is an IPv6 address string */ 627 if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) 628 /* This is an IPv6 address literal */ 629 return Curl_ip2addr(AF_INET6, &in6, hostname, port); 630 } 631 #endif /* CURLRES_IPV6 */ 632 #endif /* !USE_RESOLVE_ON_IPS */ 633 634 #ifdef CURLRES_IPV6 635 /* 636 * Check if a limited name resolve has been requested. 637 */ 638 switch(conn->ip_version) { 639 case CURL_IPRESOLVE_V4: 640 pf = PF_INET; 641 break; 642 case CURL_IPRESOLVE_V6: 643 pf = PF_INET6; 644 break; 645 default: 646 pf = PF_UNSPEC; 647 break; 648 } 649 650 if((pf != PF_INET) && !Curl_ipv6works()) 651 /* The stack seems to be a non-IPv6 one */ 652 pf = PF_INET; 653 #endif /* CURLRES_IPV6 */ 654 655 memset(&hints, 0, sizeof(hints)); 656 hints.ai_family = pf; 657 hints.ai_socktype = conn->socktype; 658 659 snprintf(sbuf, sizeof(sbuf), "%d", port); 660 661 /* fire up a new resolver thread! */ 662 if(init_resolve_thread(conn, hostname, port, &hints)) { 663 *waitp = 1; /* expect asynchronous response */ 664 return NULL; 665 } 666 667 /* fall-back to blocking version */ 668 infof(conn->data, "init_resolve_thread() failed for %s; %s\n", 669 hostname, Curl_strerror(conn, errno)); 670 671 error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res); 672 if(error) { 673 infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n", 674 hostname, port, Curl_strerror(conn, SOCKERRNO)); 675 return NULL; 676 } 677 else { 678 Curl_addrinfo_set_port(res, port); 679 } 680 681 return res; 682 } 683 684 #endif /* !HAVE_GETADDRINFO */ 685 686 CURLcode Curl_set_dns_servers(struct Curl_easy *data, 687 char *servers) 688 { 689 (void)data; 690 (void)servers; 691 return CURLE_NOT_BUILT_IN; 692 693 } 694 695 CURLcode Curl_set_dns_interface(struct Curl_easy *data, 696 const char *interf) 697 { 698 (void)data; 699 (void)interf; 700 return CURLE_NOT_BUILT_IN; 701 } 702 703 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, 704 const char *local_ip4) 705 { 706 (void)data; 707 (void)local_ip4; 708 return CURLE_NOT_BUILT_IN; 709 } 710 711 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, 712 const char *local_ip6) 713 { 714 (void)data; 715 (void)local_ip6; 716 return CURLE_NOT_BUILT_IN; 717 } 718 719 #endif /* CURLRES_THREADED */ 720