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