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