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