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_LIMITS_H 26#include <limits.h> 27#endif 28#ifdef HAVE_NETINET_IN_H 29#include <netinet/in.h> 30#endif 31#ifdef HAVE_NETDB_H 32#include <netdb.h> 33#endif 34#ifdef HAVE_ARPA_INET_H 35#include <arpa/inet.h> 36#endif 37#ifdef __VMS 38#include <in.h> 39#include <inet.h> 40#endif 41 42#ifdef HAVE_PROCESS_H 43#include <process.h> 44#endif 45 46#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) 47#undef in_addr_t 48#define in_addr_t unsigned long 49#endif 50 51/*********************************************************************** 52 * Only for ares-enabled builds 53 * And only for functions that fulfill the asynch resolver backend API 54 * as defined in asyn.h, nothing else belongs in this file! 55 **********************************************************************/ 56 57#ifdef CURLRES_ARES 58 59#include "urldata.h" 60#include "sendf.h" 61#include "hostip.h" 62#include "hash.h" 63#include "share.h" 64#include "strerror.h" 65#include "url.h" 66#include "multiif.h" 67#include "inet_pton.h" 68#include "connect.h" 69#include "select.h" 70#include "progress.h" 71 72#define _MPRINTF_REPLACE /* use our functions only */ 73#include <curl/mprintf.h> 74 75# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ 76 (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__)) 77# define CARES_STATICLIB 78# endif 79# include <ares.h> 80# include <ares_version.h> /* really old c-ares didn't include this by 81 itself */ 82 83#if ARES_VERSION >= 0x010500 84/* c-ares 1.5.0 or later, the callback proto is modified */ 85#define HAVE_CARES_CALLBACK_TIMEOUTS 1 86#endif 87 88#include "curl_memory.h" 89/* The last #include file should be: */ 90#include "memdebug.h" 91 92struct ResolverResults { 93 int num_pending; /* number of ares_gethostbyname() requests */ 94 Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */ 95 int last_status; 96}; 97 98/* 99 * Curl_resolver_global_init() - the generic low-level asynchronous name 100 * resolve API. Called from curl_global_init() to initialize global resolver 101 * environment. Initializes ares library. 102 */ 103int Curl_resolver_global_init(void) 104{ 105#ifdef CARES_HAVE_ARES_LIBRARY_INIT 106 if(ares_library_init(ARES_LIB_INIT_ALL)) { 107 return CURLE_FAILED_INIT; 108 } 109#endif 110 return CURLE_OK; 111} 112 113/* 114 * Curl_resolver_global_cleanup() 115 * 116 * Called from curl_global_cleanup() to destroy global resolver environment. 117 * Deinitializes ares library. 118 */ 119void Curl_resolver_global_cleanup(void) 120{ 121#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP 122 ares_library_cleanup(); 123#endif 124} 125 126/* 127 * Curl_resolver_init() 128 * 129 * Called from curl_easy_init() -> Curl_open() to initialize resolver 130 * URL-state specific environment ('resolver' member of the UrlState 131 * structure). Fills the passed pointer by the initialized ares_channel. 132 */ 133CURLcode Curl_resolver_init(void **resolver) 134{ 135 int status = ares_init((ares_channel*)resolver); 136 if(status != ARES_SUCCESS) { 137 if(status == ARES_ENOMEM) 138 return CURLE_OUT_OF_MEMORY; 139 else 140 return CURLE_FAILED_INIT; 141 } 142 return CURLE_OK; 143 /* make sure that all other returns from this function should destroy the 144 ares channel before returning error! */ 145} 146 147/* 148 * Curl_resolver_cleanup() 149 * 150 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver 151 * URL-state specific environment ('resolver' member of the UrlState 152 * structure). Destroys the ares channel. 153 */ 154void Curl_resolver_cleanup(void *resolver) 155{ 156 ares_destroy((ares_channel)resolver); 157} 158 159/* 160 * Curl_resolver_duphandle() 161 * 162 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific 163 * environment ('resolver' member of the UrlState structure). Duplicates the 164 * 'from' ares channel and passes the resulting channel to the 'to' pointer. 165 */ 166int Curl_resolver_duphandle(void **to, void *from) 167{ 168 /* Clone the ares channel for the new handle */ 169 if(ARES_SUCCESS != ares_dup((ares_channel*)to,(ares_channel)from)) 170 return CURLE_FAILED_INIT; 171 return CURLE_OK; 172} 173 174static void destroy_async_data (struct Curl_async *async); 175 176/* 177 * Cancel all possibly still on-going resolves for this connection. 178 */ 179void Curl_resolver_cancel(struct connectdata *conn) 180{ 181 if(conn && conn->data && conn->data->state.resolver) 182 ares_cancel((ares_channel)conn->data->state.resolver); 183 destroy_async_data(&conn->async); 184} 185 186/* 187 * destroy_async_data() cleans up async resolver data. 188 */ 189static void destroy_async_data (struct Curl_async *async) 190{ 191 if(async->hostname) 192 free(async->hostname); 193 194 if(async->os_specific) { 195 struct ResolverResults *res = (struct ResolverResults *)async->os_specific; 196 if(res) { 197 if(res->temp_ai) { 198 Curl_freeaddrinfo(res->temp_ai); 199 res->temp_ai = NULL; 200 } 201 free(res); 202 } 203 async->os_specific = NULL; 204 } 205 206 async->hostname = NULL; 207} 208 209/* 210 * Curl_resolver_getsock() is called when someone from the outside world 211 * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking 212 * with ares. The caller must make sure that this function is only called when 213 * we have a working ares channel. 214 * 215 * Returns: sockets-in-use-bitmap 216 */ 217 218int Curl_resolver_getsock(struct connectdata *conn, 219 curl_socket_t *socks, 220 int numsocks) 221 222{ 223 struct timeval maxtime; 224 struct timeval timebuf; 225 struct timeval *timeout; 226 long milli; 227 int max = ares_getsock((ares_channel)conn->data->state.resolver, 228 (ares_socket_t *)socks, numsocks); 229 230 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; 231 maxtime.tv_usec = 0; 232 233 timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime, 234 &timebuf); 235 milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000); 236 if(milli == 0) 237 milli += 10; 238 Curl_expire(conn->data, milli); 239 240 return max; 241} 242 243/* 244 * waitperform() 245 * 246 * 1) Ask ares what sockets it currently plays with, then 247 * 2) wait for the timeout period to check for action on ares' sockets. 248 * 3) tell ares to act on all the sockets marked as "with action" 249 * 250 * return number of sockets it worked on 251 */ 252 253static int waitperform(struct connectdata *conn, int timeout_ms) 254{ 255 struct SessionHandle *data = conn->data; 256 int nfds; 257 int bitmask; 258 ares_socket_t socks[ARES_GETSOCK_MAXNUM]; 259 struct pollfd pfd[ARES_GETSOCK_MAXNUM]; 260 int i; 261 int num = 0; 262 263 bitmask = ares_getsock((ares_channel)data->state.resolver, socks, 264 ARES_GETSOCK_MAXNUM); 265 266 for(i=0; i < ARES_GETSOCK_MAXNUM; i++) { 267 pfd[i].events = 0; 268 pfd[i].revents = 0; 269 if(ARES_GETSOCK_READABLE(bitmask, i)) { 270 pfd[i].fd = socks[i]; 271 pfd[i].events |= POLLRDNORM|POLLIN; 272 } 273 if(ARES_GETSOCK_WRITABLE(bitmask, i)) { 274 pfd[i].fd = socks[i]; 275 pfd[i].events |= POLLWRNORM|POLLOUT; 276 } 277 if(pfd[i].events != 0) 278 num++; 279 else 280 break; 281 } 282 283 if(num) 284 nfds = Curl_poll(pfd, num, timeout_ms); 285 else 286 nfds = 0; 287 288 if(!nfds) 289 /* Call ares_process() unconditonally here, even if we simply timed out 290 above, as otherwise the ares name resolve won't timeout! */ 291 ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD, 292 ARES_SOCKET_BAD); 293 else { 294 /* move through the descriptors and ask for processing on them */ 295 for(i=0; i < num; i++) 296 ares_process_fd((ares_channel)data->state.resolver, 297 pfd[i].revents & (POLLRDNORM|POLLIN)? 298 pfd[i].fd:ARES_SOCKET_BAD, 299 pfd[i].revents & (POLLWRNORM|POLLOUT)? 300 pfd[i].fd:ARES_SOCKET_BAD); 301 } 302 return nfds; 303} 304 305/* 306 * Curl_resolver_is_resolved() is called repeatedly to check if a previous 307 * name resolve request has completed. It should also make sure to time-out if 308 * the operation seems to take too long. 309 * 310 * Returns normal CURLcode errors. 311 */ 312CURLcode Curl_resolver_is_resolved(struct connectdata *conn, 313 struct Curl_dns_entry **dns) 314{ 315 struct SessionHandle *data = conn->data; 316 struct ResolverResults *res = (struct ResolverResults *) 317 conn->async.os_specific; 318 CURLcode rc = CURLE_OK; 319 320 *dns = NULL; 321 322 waitperform(conn, 0); 323 324 if(res && !res->num_pending) { 325 (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai); 326 /* temp_ai ownership is moved to the connection, so we need not free-up 327 them */ 328 res->temp_ai = NULL; 329 if(!conn->async.dns) { 330 failf(data, "Could not resolve: %s (%s)", 331 conn->async.hostname, ares_strerror(conn->async.status)); 332 rc = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: 333 CURLE_COULDNT_RESOLVE_HOST; 334 } 335 else 336 *dns = conn->async.dns; 337 338 destroy_async_data(&conn->async); 339 } 340 341 return rc; 342} 343 344/* 345 * Curl_resolver_wait_resolv() 346 * 347 * waits for a resolve to finish. This function should be avoided since using 348 * this risk getting the multi interface to "hang". 349 * 350 * If 'entry' is non-NULL, make it point to the resolved dns entry 351 * 352 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and 353 * CURLE_OPERATION_TIMEDOUT if a time-out occurred. 354 */ 355CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, 356 struct Curl_dns_entry **entry) 357{ 358 CURLcode rc=CURLE_OK; 359 struct SessionHandle *data = conn->data; 360 long timeout; 361 struct timeval now = Curl_tvnow(); 362 struct Curl_dns_entry *temp_entry; 363 364 timeout = Curl_timeleft(data, &now, TRUE); 365 if(!timeout) 366 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ 367 368 /* Wait for the name resolve query to complete. */ 369 for(;;) { 370 struct timeval *tvp, tv, store; 371 long timediff; 372 int itimeout; 373 int timeout_ms; 374 375 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout; 376 377 store.tv_sec = itimeout/1000; 378 store.tv_usec = (itimeout%1000)*1000; 379 380 tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv); 381 382 /* use the timeout period ares returned to us above if less than one 383 second is left, otherwise just use 1000ms to make sure the progress 384 callback gets called frequent enough */ 385 if(!tvp->tv_sec) 386 timeout_ms = (int)(tvp->tv_usec/1000); 387 else 388 timeout_ms = 1000; 389 390 waitperform(conn, timeout_ms); 391 Curl_resolver_is_resolved(conn,&temp_entry); 392 393 if(conn->async.done) 394 break; 395 396 if(Curl_pgrsUpdate(conn)) { 397 rc = CURLE_ABORTED_BY_CALLBACK; 398 timeout = -1; /* trigger the cancel below */ 399 } 400 else { 401 struct timeval now2 = Curl_tvnow(); 402 timediff = Curl_tvdiff(now2, now); /* spent time */ 403 timeout -= timediff?timediff:1; /* always deduct at least 1 */ 404 now = now2; /* for next loop */ 405 } 406 if(timeout < 0) { 407 /* our timeout, so we cancel the ares operation */ 408 ares_cancel((ares_channel)data->state.resolver); 409 break; 410 } 411 } 412 413 /* Operation complete, if the lookup was successful we now have the entry 414 in the cache. */ 415 416 if(entry) 417 *entry = conn->async.dns; 418 419 if(rc) 420 /* close the connection, since we can't return failure here without 421 cleaning up this connection properly. 422 TODO: remove this action from here, it is not a name resolver decision. 423 */ 424 connclose(conn, "c-ares resolve failed"); 425 426 return rc; 427} 428 429/* Connects results to the list */ 430static void compound_results(struct ResolverResults *res, 431 Curl_addrinfo *ai) 432{ 433 Curl_addrinfo *ai_tail; 434 if(!ai) 435 return; 436 ai_tail = ai; 437 438 while(ai_tail->ai_next) 439 ai_tail = ai_tail->ai_next; 440 441 /* Add the new results to the list of old results. */ 442 ai_tail->ai_next = res->temp_ai; 443 res->temp_ai = ai; 444} 445 446/* 447 * ares_query_completed_cb() is the callback that ares will call when 448 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(), 449 * when using ares, is completed either successfully or with failure. 450 */ 451static void query_completed_cb(void *arg, /* (struct connectdata *) */ 452 int status, 453#ifdef HAVE_CARES_CALLBACK_TIMEOUTS 454 int timeouts, 455#endif 456 struct hostent *hostent) 457{ 458 struct connectdata *conn = (struct connectdata *)arg; 459 struct ResolverResults *res; 460 461#ifdef HAVE_CARES_CALLBACK_TIMEOUTS 462 (void)timeouts; /* ignored */ 463#endif 464 465 if(ARES_EDESTRUCTION == status) 466 /* when this ares handle is getting destroyed, the 'arg' pointer may not 467 be valid so only defer it when we know the 'status' says its fine! */ 468 return; 469 470 res = (struct ResolverResults *)conn->async.os_specific; 471 res->num_pending--; 472 473 if(CURL_ASYNC_SUCCESS == status) { 474 Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port); 475 if(ai) { 476 compound_results(res, ai); 477 } 478 } 479 /* A successful result overwrites any previous error */ 480 if(res->last_status != ARES_SUCCESS) 481 res->last_status = status; 482} 483 484/* 485 * Curl_resolver_getaddrinfo() - when using ares 486 * 487 * Returns name information about the given hostname and port number. If 488 * successful, the 'hostent' is returned and the forth argument will point to 489 * memory we need to free after use. That memory *MUST* be freed with 490 * Curl_freeaddrinfo(), nothing else. 491 */ 492Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, 493 const char *hostname, 494 int port, 495 int *waitp) 496{ 497 char *bufp; 498 struct SessionHandle *data = conn->data; 499 struct in_addr in; 500 int family = PF_INET; 501#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ 502 struct in6_addr in6; 503#endif /* CURLRES_IPV6 */ 504 505 *waitp = 0; /* default to synchronous response */ 506 507 /* First check if this is an IPv4 address string */ 508 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) { 509 /* This is a dotted IP address 123.123.123.123-style */ 510 return Curl_ip2addr(AF_INET, &in, hostname, port); 511 } 512 513#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ 514 /* Otherwise, check if this is an IPv6 address string */ 515 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0) 516 /* This must be an IPv6 address literal. */ 517 return Curl_ip2addr(AF_INET6, &in6, hostname, port); 518 519 switch(conn->ip_version) { 520 default: 521#if ARES_VERSION >= 0x010601 522 family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older 523 c-ares versions this just falls through and defaults 524 to PF_INET */ 525 break; 526#endif 527 case CURL_IPRESOLVE_V4: 528 family = PF_INET; 529 break; 530 case CURL_IPRESOLVE_V6: 531 family = PF_INET6; 532 break; 533 } 534#endif /* CURLRES_IPV6 */ 535 536 bufp = strdup(hostname); 537 if(bufp) { 538 struct ResolverResults *res = NULL; 539 Curl_safefree(conn->async.hostname); 540 conn->async.hostname = bufp; 541 conn->async.port = port; 542 conn->async.done = FALSE; /* not done */ 543 conn->async.status = 0; /* clear */ 544 conn->async.dns = NULL; /* clear */ 545 res = calloc(sizeof(struct ResolverResults),1); 546 if(!res) { 547 Curl_safefree(conn->async.hostname); 548 conn->async.hostname = NULL; 549 return NULL; 550 } 551 conn->async.os_specific = res; 552 553 /* initial status - failed */ 554 res->last_status = ARES_ENOTFOUND; 555#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ 556 if(family == PF_UNSPEC) { 557 if(Curl_ipv6works()) { 558 res->num_pending = 2; 559 560 /* areschannel is already setup in the Curl_open() function */ 561 ares_gethostbyname((ares_channel)data->state.resolver, hostname, 562 PF_INET, query_completed_cb, conn); 563 ares_gethostbyname((ares_channel)data->state.resolver, hostname, 564 PF_INET6, query_completed_cb, conn); 565 } 566 else { 567 res->num_pending = 1; 568 569 /* areschannel is already setup in the Curl_open() function */ 570 ares_gethostbyname((ares_channel)data->state.resolver, hostname, 571 PF_INET, query_completed_cb, conn); 572 } 573 } 574 else 575#endif /* CURLRES_IPV6 */ 576 { 577 res->num_pending = 1; 578 579 /* areschannel is already setup in the Curl_open() function */ 580 ares_gethostbyname((ares_channel)data->state.resolver, hostname, family, 581 query_completed_cb, conn); 582 } 583 584 *waitp = 1; /* expect asynchronous response */ 585 } 586 return NULL; /* no struct yet */ 587} 588 589CURLcode Curl_set_dns_servers(struct SessionHandle *data, 590 char *servers) 591{ 592 CURLcode result = CURLE_NOT_BUILT_IN; 593 int ares_result; 594 595 /* If server is NULL or empty, this would purge all DNS servers 596 * from ares library, which will cause any and all queries to fail. 597 * So, just return OK if none are configured and don't actually make 598 * any changes to c-ares. This lets c-ares use it's defaults, which 599 * it gets from the OS (for instance from /etc/resolv.conf on Linux). 600 */ 601 if(!(servers && servers[0])) 602 return CURLE_OK; 603 604#if (ARES_VERSION >= 0x010704) 605 ares_result = ares_set_servers_csv(data->state.resolver, servers); 606 switch(ares_result) { 607 case ARES_SUCCESS: 608 result = CURLE_OK; 609 break; 610 case ARES_ENOMEM: 611 result = CURLE_OUT_OF_MEMORY; 612 break; 613 case ARES_ENOTINITIALIZED: 614 case ARES_ENODATA: 615 case ARES_EBADSTR: 616 default: 617 result = CURLE_BAD_FUNCTION_ARGUMENT; 618 break; 619 } 620#else /* too old c-ares version! */ 621 (void)data; 622 (void)(ares_result); 623#endif 624 return result; 625} 626 627CURLcode Curl_set_dns_interface(struct SessionHandle *data, 628 const char *interf) 629{ 630#if (ARES_VERSION >= 0x010704) 631 if(!interf) 632 interf = ""; 633 634 ares_set_local_dev((ares_channel)data->state.resolver, interf); 635 636 return CURLE_OK; 637#else /* c-ares version too old! */ 638 (void)data; 639 (void)interf; 640 return CURLE_NOT_BUILT_IN; 641#endif 642} 643 644CURLcode Curl_set_dns_local_ip4(struct SessionHandle *data, 645 const char *local_ip4) 646{ 647#if (ARES_VERSION >= 0x010704) 648 struct in_addr a4; 649 650 if((!local_ip4) || (local_ip4[0] == 0)) { 651 a4.s_addr = 0; /* disabled: do not bind to a specific address */ 652 } 653 else { 654 if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) { 655 return CURLE_BAD_FUNCTION_ARGUMENT; 656 } 657 } 658 659 ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr)); 660 661 return CURLE_OK; 662#else /* c-ares version too old! */ 663 (void)data; 664 (void)local_ip4; 665 return CURLE_NOT_BUILT_IN; 666#endif 667} 668 669CURLcode Curl_set_dns_local_ip6(struct SessionHandle *data, 670 const char *local_ip6) 671{ 672#if (ARES_VERSION >= 0x010704) 673 unsigned char a6[INET6_ADDRSTRLEN]; 674 675 if((!local_ip6) || (local_ip6[0] == 0)) { 676 /* disabled: do not bind to a specific address */ 677 memset(a6, 0, sizeof(a6)); 678 } 679 else { 680 if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) { 681 return CURLE_BAD_FUNCTION_ARGUMENT; 682 } 683 } 684 685 ares_set_local_ip6((ares_channel)data->state.resolver, a6); 686 687 return CURLE_OK; 688#else /* c-ares version too old! */ 689 (void)data; 690 (void)local_ip6; 691 return CURLE_NOT_BUILT_IN; 692#endif 693} 694#endif /* CURLRES_ARES */ 695