1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2014, 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 http://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 73#define _MPRINTF_REPLACE /* use our functions only */ 74#include <curl/mprintf.h> 75 76#include "curl_memory.h" 77/* The last #include file should be: */ 78#include "memdebug.h" 79 80/*********************************************************************** 81 * Only for threaded name resolves builds 82 **********************************************************************/ 83#ifdef CURLRES_THREADED 84 85/* 86 * Curl_resolver_global_init() 87 * Called from curl_global_init() to initialize global resolver environment. 88 * Does nothing here. 89 */ 90int Curl_resolver_global_init(void) 91{ 92 return CURLE_OK; 93} 94 95/* 96 * Curl_resolver_global_cleanup() 97 * Called from curl_global_cleanup() to destroy global resolver environment. 98 * Does nothing here. 99 */ 100void Curl_resolver_global_cleanup(void) 101{ 102} 103 104/* 105 * Curl_resolver_init() 106 * Called from curl_easy_init() -> Curl_open() to initialize resolver 107 * URL-state specific environment ('resolver' member of the UrlState 108 * structure). Does nothing here. 109 */ 110CURLcode Curl_resolver_init(void **resolver) 111{ 112 (void)resolver; 113 return CURLE_OK; 114} 115 116/* 117 * Curl_resolver_cleanup() 118 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver 119 * URL-state specific environment ('resolver' member of the UrlState 120 * structure). Does nothing here. 121 */ 122void Curl_resolver_cleanup(void *resolver) 123{ 124 (void)resolver; 125} 126 127/* 128 * Curl_resolver_duphandle() 129 * Called from curl_easy_duphandle() to duplicate resolver URL state-specific 130 * environment ('resolver' member of the UrlState structure). Does nothing 131 * here. 132 */ 133int Curl_resolver_duphandle(void **to, void *from) 134{ 135 (void)to; 136 (void)from; 137 return CURLE_OK; 138} 139 140static void destroy_async_data(struct Curl_async *); 141 142/* 143 * Cancel all possibly still on-going resolves for this connection. 144 */ 145void Curl_resolver_cancel(struct connectdata *conn) 146{ 147 destroy_async_data(&conn->async); 148} 149 150/* This function is used to init a threaded resolve */ 151static bool init_resolve_thread(struct connectdata *conn, 152 const char *hostname, int port, 153 const struct addrinfo *hints); 154 155 156/* Data for synchronization between resolver thread and its parent */ 157struct thread_sync_data { 158 curl_mutex_t * mtx; 159 int done; 160 161 char * hostname; /* hostname to resolve, Curl_async.hostname 162 duplicate */ 163 int port; 164 int sock_error; 165 Curl_addrinfo *res; 166#ifdef HAVE_GETADDRINFO 167 struct addrinfo hints; 168#endif 169}; 170 171struct thread_data { 172 curl_thread_t thread_hnd; 173 unsigned int poll_interval; 174 long interval_end; 175 struct thread_sync_data tsd; 176}; 177 178static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn) 179{ 180 return &(((struct thread_data *)conn->async.os_specific)->tsd); 181} 182 183#define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd); 184 185/* Destroy resolver thread synchronization data */ 186static 187void destroy_thread_sync_data(struct thread_sync_data * tsd) 188{ 189 if(tsd->mtx) { 190 Curl_mutex_destroy(tsd->mtx); 191 free(tsd->mtx); 192 } 193 194 if(tsd->hostname) 195 free(tsd->hostname); 196 197 if(tsd->res) 198 Curl_freeaddrinfo(tsd->res); 199 200 memset(tsd,0,sizeof(*tsd)); 201} 202 203/* Initialize resolver thread synchronization data */ 204static 205int init_thread_sync_data(struct thread_sync_data * tsd, 206 const char * hostname, 207 int port, 208 const struct addrinfo *hints) 209{ 210 memset(tsd, 0, sizeof(*tsd)); 211 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 char service[12]; 270 int rc; 271 272 snprintf(service, sizeof(service), "%d", tsd->port); 273 274 rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res); 275 276 if(rc != 0) { 277 tsd->sock_error = SOCKERRNO?SOCKERRNO:rc; 278 if(tsd->sock_error == 0) 279 tsd->sock_error = RESOLVER_ENOMEM; 280 } 281 282 Curl_mutex_acquire(tsd->mtx); 283 tsd->done = 1; 284 Curl_mutex_release(tsd->mtx); 285 286 return 0; 287} 288 289#else /* HAVE_GETADDRINFO */ 290 291/* 292 * gethostbyname_thread() resolves a name and then exits. 293 */ 294static unsigned int CURL_STDCALL gethostbyname_thread (void *arg) 295{ 296 struct thread_sync_data *tsd = (struct thread_sync_data *)arg; 297 298 tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port); 299 300 if(!tsd->res) { 301 tsd->sock_error = SOCKERRNO; 302 if(tsd->sock_error == 0) 303 tsd->sock_error = RESOLVER_ENOMEM; 304 } 305 306 Curl_mutex_acquire(tsd->mtx); 307 tsd->done = 1; 308 Curl_mutex_release(tsd->mtx); 309 310 return 0; 311} 312 313#endif /* HAVE_GETADDRINFO */ 314 315/* 316 * destroy_async_data() cleans up async resolver data and thread handle. 317 */ 318static void destroy_async_data (struct Curl_async *async) 319{ 320 if(async->hostname) 321 free(async->hostname); 322 323 if(async->os_specific) { 324 struct thread_data *td = (struct thread_data*) async->os_specific; 325 326 if(td->thread_hnd != curl_thread_t_null) 327 Curl_thread_join(&td->thread_hnd); 328 329 destroy_thread_sync_data(&td->tsd); 330 331 free(async->os_specific); 332 } 333 async->hostname = NULL; 334 async->os_specific = NULL; 335} 336 337/* 338 * init_resolve_thread() starts a new thread that performs the actual 339 * resolve. This function returns before the resolve is done. 340 * 341 * Returns FALSE in case of failure, otherwise TRUE. 342 */ 343static bool init_resolve_thread (struct connectdata *conn, 344 const char *hostname, int port, 345 const struct addrinfo *hints) 346{ 347 struct thread_data *td = calloc(1, sizeof(struct thread_data)); 348 int err = RESOLVER_ENOMEM; 349 350 conn->async.os_specific = (void*) td; 351 if(!td) 352 goto err_exit; 353 354 conn->async.port = port; 355 conn->async.done = FALSE; 356 conn->async.status = 0; 357 conn->async.dns = NULL; 358 td->thread_hnd = curl_thread_t_null; 359 360 if(!init_thread_sync_data(&td->tsd, hostname, port, hints)) 361 goto err_exit; 362 363 Curl_safefree(conn->async.hostname); 364 conn->async.hostname = strdup(hostname); 365 if(!conn->async.hostname) 366 goto err_exit; 367 368#ifdef HAVE_GETADDRINFO 369 td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd); 370#else 371 td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd); 372#endif 373 374 if(!td->thread_hnd) { 375#ifndef _WIN32_WCE 376 err = errno; 377#endif 378 goto err_exit; 379 } 380 381 return TRUE; 382 383 err_exit: 384 destroy_async_data(&conn->async); 385 386 SET_ERRNO(err); 387 388 return FALSE; 389} 390 391/* 392 * resolver_error() calls failf() with the appropriate message after a resolve 393 * error 394 */ 395 396static CURLcode resolver_error(struct connectdata *conn) 397{ 398 const char *host_or_proxy; 399 CURLcode rc; 400 if(conn->bits.httpproxy) { 401 host_or_proxy = "proxy"; 402 rc = CURLE_COULDNT_RESOLVE_PROXY; 403 } 404 else { 405 host_or_proxy = "host"; 406 rc = CURLE_COULDNT_RESOLVE_HOST; 407 } 408 409 failf(conn->data, "Could not resolve %s: %s", host_or_proxy, 410 conn->async.hostname); 411 return rc; 412} 413 414/* 415 * Curl_resolver_wait_resolv() 416 * 417 * waits for a resolve to finish. This function should be avoided since using 418 * this risk getting the multi interface to "hang". 419 * 420 * If 'entry' is non-NULL, make it point to the resolved dns entry 421 * 422 * This is the version for resolves-in-a-thread. 423 */ 424CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, 425 struct Curl_dns_entry **entry) 426{ 427 struct thread_data *td = (struct thread_data*) conn->async.os_specific; 428 CURLcode rc = CURLE_OK; 429 430 DEBUGASSERT(conn && td); 431 432 /* wait for the thread to resolve the name */ 433 if(Curl_thread_join(&td->thread_hnd)) 434 rc = getaddrinfo_complete(conn); 435 else 436 DEBUGASSERT(0); 437 438 conn->async.done = TRUE; 439 440 if(entry) 441 *entry = conn->async.dns; 442 443 if(!conn->async.dns) 444 /* a name was not resolved, report error */ 445 rc = resolver_error(conn); 446 447 destroy_async_data(&conn->async); 448 449 if(!conn->async.dns) 450 connclose(conn, "asynch resolve failed"); 451 452 return (rc); 453} 454 455/* 456 * Curl_resolver_is_resolved() is called repeatedly to check if a previous 457 * name resolve request has completed. It should also make sure to time-out if 458 * the operation seems to take too long. 459 */ 460CURLcode Curl_resolver_is_resolved(struct connectdata *conn, 461 struct Curl_dns_entry **entry) 462{ 463 struct SessionHandle *data = conn->data; 464 struct thread_data *td = (struct thread_data*) conn->async.os_specific; 465 int done = 0; 466 467 *entry = NULL; 468 469 if(!td) { 470 DEBUGASSERT(td); 471 return CURLE_COULDNT_RESOLVE_HOST; 472 } 473 474 Curl_mutex_acquire(td->tsd.mtx); 475 done = td->tsd.done; 476 Curl_mutex_release(td->tsd.mtx); 477 478 if(done) { 479 getaddrinfo_complete(conn); 480 481 if(!conn->async.dns) { 482 CURLcode rc = resolver_error(conn); 483 destroy_async_data(&conn->async); 484 return rc; 485 } 486 destroy_async_data(&conn->async); 487 *entry = conn->async.dns; 488 } 489 else { 490 /* poll for name lookup done with exponential backoff up to 250ms */ 491 long elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); 492 if(elapsed < 0) 493 elapsed = 0; 494 495 if(td->poll_interval == 0) 496 /* Start at 1ms poll interval */ 497 td->poll_interval = 1; 498 else if(elapsed >= td->interval_end) 499 /* Back-off exponentially if last interval expired */ 500 td->poll_interval *= 2; 501 502 if(td->poll_interval > 250) 503 td->poll_interval = 250; 504 505 td->interval_end = elapsed + td->poll_interval; 506 Curl_expire(conn->data, td->poll_interval); 507 } 508 509 return CURLE_OK; 510} 511 512int Curl_resolver_getsock(struct connectdata *conn, 513 curl_socket_t *socks, 514 int numsocks) 515{ 516 (void)conn; 517 (void)socks; 518 (void)numsocks; 519 return 0; 520} 521 522#ifndef HAVE_GETADDRINFO 523/* 524 * Curl_getaddrinfo() - for platforms without getaddrinfo 525 */ 526Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, 527 const char *hostname, 528 int port, 529 int *waitp) 530{ 531 struct in_addr in; 532 533 *waitp = 0; /* default to synchronous response */ 534 535 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) 536 /* This is a dotted IP address 123.123.123.123-style */ 537 return Curl_ip2addr(AF_INET, &in, hostname, port); 538 539 /* fire up a new resolver thread! */ 540 if(init_resolve_thread(conn, hostname, port, NULL)) { 541 *waitp = 1; /* expect asynchronous response */ 542 return NULL; 543 } 544 545 /* fall-back to blocking version */ 546 return Curl_ipv4_resolve_r(hostname, port); 547} 548 549#else /* !HAVE_GETADDRINFO */ 550 551/* 552 * Curl_resolver_getaddrinfo() - for getaddrinfo 553 */ 554Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, 555 const char *hostname, 556 int port, 557 int *waitp) 558{ 559 struct addrinfo hints; 560 struct in_addr in; 561 Curl_addrinfo *res; 562 int error; 563 char sbuf[12]; 564 int pf = PF_INET; 565#ifdef CURLRES_IPV6 566 struct in6_addr in6; 567#endif /* CURLRES_IPV6 */ 568 569 *waitp = 0; /* default to synchronous response */ 570 571 /* First check if this is an IPv4 address string */ 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#ifdef CURLRES_IPV6 577 /* check if this is an IPv6 address string */ 578 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0) 579 /* This is an IPv6 address literal */ 580 return Curl_ip2addr(AF_INET6, &in6, hostname, port); 581 582 /* 583 * Check if a limited name resolve has been requested. 584 */ 585 switch(conn->ip_version) { 586 case CURL_IPRESOLVE_V4: 587 pf = PF_INET; 588 break; 589 case CURL_IPRESOLVE_V6: 590 pf = PF_INET6; 591 break; 592 default: 593 pf = PF_UNSPEC; 594 break; 595 } 596 597 if((pf != PF_INET) && !Curl_ipv6works()) 598 /* the stack seems to be a non-ipv6 one */ 599 pf = PF_INET; 600 601#endif /* CURLRES_IPV6 */ 602 603 memset(&hints, 0, sizeof(hints)); 604 hints.ai_family = pf; 605 hints.ai_socktype = conn->socktype; 606 607 snprintf(sbuf, sizeof(sbuf), "%d", port); 608 609 /* fire up a new resolver thread! */ 610 if(init_resolve_thread(conn, hostname, port, &hints)) { 611 *waitp = 1; /* expect asynchronous response */ 612 return NULL; 613 } 614 615 /* fall-back to blocking version */ 616 infof(conn->data, "init_resolve_thread() failed for %s; %s\n", 617 hostname, Curl_strerror(conn, ERRNO)); 618 619 error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res); 620 if(error) { 621 infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n", 622 hostname, port, Curl_strerror(conn, SOCKERRNO)); 623 return NULL; 624 } 625 return res; 626} 627 628#endif /* !HAVE_GETADDRINFO */ 629 630CURLcode Curl_set_dns_servers(struct SessionHandle *data, 631 char *servers) 632{ 633 (void)data; 634 (void)servers; 635 return CURLE_NOT_BUILT_IN; 636 637} 638 639CURLcode Curl_set_dns_interface(struct SessionHandle *data, 640 const char *interf) 641{ 642 (void)data; 643 (void)interf; 644 return CURLE_NOT_BUILT_IN; 645} 646 647CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data, 648 const char *local_ip4) 649{ 650 (void)data; 651 (void)local_ip4; 652 return CURLE_NOT_BUILT_IN; 653} 654 655CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data, 656 const char *local_ip6) 657{ 658 (void)data; 659 (void)local_ip6; 660 return CURLE_NOT_BUILT_IN; 661} 662 663#endif /* CURLRES_THREADED */ 664