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_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 319 *dns = NULL; 320 321 waitperform(conn, 0); 322 323 if(res && !res->num_pending) { 324 (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai); 325 /* temp_ai ownership is moved to the connection, so we need not free-up 326 them */ 327 res->temp_ai = NULL; 328 destroy_async_data(&conn->async); 329 if(!conn->async.dns) { 330 failf(data, "Could not resolve %s: %s (%s)", 331 conn->bits.proxy?"proxy":"host", 332 conn->host.dispname, 333 ares_strerror(conn->async.status)); 334 return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY: 335 CURLE_COULDNT_RESOLVE_HOST; 336 } 337 *dns = conn->async.dns; 338 } 339 340 return CURLE_OK; 341} 342 343/* 344 * Curl_resolver_wait_resolv() 345 * 346 * waits for a resolve to finish. This function should be avoided since using 347 * this risk getting the multi interface to "hang". 348 * 349 * If 'entry' is non-NULL, make it point to the resolved dns entry 350 * 351 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and 352 * CURLE_OPERATION_TIMEDOUT if a time-out occurred. 353 */ 354CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, 355 struct Curl_dns_entry **entry) 356{ 357 CURLcode rc=CURLE_OK; 358 struct SessionHandle *data = conn->data; 359 long timeout; 360 struct timeval now = Curl_tvnow(); 361 struct Curl_dns_entry *temp_entry; 362 363 timeout = Curl_timeleft(data, &now, TRUE); 364 if(!timeout) 365 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ 366 367 /* Wait for the name resolve query to complete. */ 368 for(;;) { 369 struct timeval *tvp, tv, store; 370 long timediff; 371 int itimeout; 372 int timeout_ms; 373 374 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout; 375 376 store.tv_sec = itimeout/1000; 377 store.tv_usec = (itimeout%1000)*1000; 378 379 tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv); 380 381 /* use the timeout period ares returned to us above if less than one 382 second is left, otherwise just use 1000ms to make sure the progress 383 callback gets called frequent enough */ 384 if(!tvp->tv_sec) 385 timeout_ms = (int)(tvp->tv_usec/1000); 386 else 387 timeout_ms = 1000; 388 389 waitperform(conn, timeout_ms); 390 Curl_resolver_is_resolved(conn,&temp_entry); 391 392 if(conn->async.done) 393 break; 394 395 if(Curl_pgrsUpdate(conn)) { 396 rc = CURLE_ABORTED_BY_CALLBACK; 397 timeout = -1; /* trigger the cancel below */ 398 } 399 else { 400 struct timeval now2 = Curl_tvnow(); 401 timediff = Curl_tvdiff(now2, now); /* spent time */ 402 timeout -= timediff?timediff:1; /* always deduct at least 1 */ 403 now = now2; /* for next loop */ 404 } 405 if(timeout < 0) { 406 /* our timeout, so we cancel the ares operation */ 407 ares_cancel((ares_channel)data->state.resolver); 408 break; 409 } 410 } 411 412 /* Operation complete, if the lookup was successful we now have the entry 413 in the cache. */ 414 415 if(entry) 416 *entry = conn->async.dns; 417 418 if(!conn->async.dns) { 419 /* a name was not resolved */ 420 if((timeout < 0) || (conn->async.status == ARES_ETIMEOUT)) { 421 if(conn->bits.proxy) { 422 failf(data, "Resolving proxy timed out: %s", conn->proxy.dispname); 423 rc = CURLE_COULDNT_RESOLVE_PROXY; 424 } 425 else { 426 failf(data, "Resolving host timed out: %s", conn->host.dispname); 427 rc = CURLE_COULDNT_RESOLVE_HOST; 428 } 429 } 430 else if(conn->async.done) { 431 if(conn->bits.proxy) { 432 failf(data, "Could not resolve proxy: %s (%s)", conn->proxy.dispname, 433 ares_strerror(conn->async.status)); 434 rc = CURLE_COULDNT_RESOLVE_PROXY; 435 } 436 else { 437 failf(data, "Could not resolve host: %s (%s)", conn->host.dispname, 438 ares_strerror(conn->async.status)); 439 rc = CURLE_COULDNT_RESOLVE_HOST; 440 } 441 } 442 else 443 rc = CURLE_OPERATION_TIMEDOUT; 444 445 /* close the connection, since we can't return failure here without 446 cleaning up this connection properly */ 447 conn->bits.close = TRUE; 448 } 449 450 return rc; 451} 452 453/* Connects results to the list */ 454static void compound_results(struct ResolverResults *res, 455 Curl_addrinfo *ai) 456{ 457 Curl_addrinfo *ai_tail; 458 if(!ai) 459 return; 460 ai_tail = ai; 461 462 while(ai_tail->ai_next) 463 ai_tail = ai_tail->ai_next; 464 465 /* Add the new results to the list of old results. */ 466 ai_tail->ai_next = res->temp_ai; 467 res->temp_ai = ai; 468} 469 470/* 471 * ares_query_completed_cb() is the callback that ares will call when 472 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(), 473 * when using ares, is completed either successfully or with failure. 474 */ 475static void query_completed_cb(void *arg, /* (struct connectdata *) */ 476 int status, 477#ifdef HAVE_CARES_CALLBACK_TIMEOUTS 478 int timeouts, 479#endif 480 struct hostent *hostent) 481{ 482 struct connectdata *conn = (struct connectdata *)arg; 483 struct ResolverResults *res; 484 485#ifdef HAVE_CARES_CALLBACK_TIMEOUTS 486 (void)timeouts; /* ignored */ 487#endif 488 489 if(ARES_EDESTRUCTION == status) 490 /* when this ares handle is getting destroyed, the 'arg' pointer may not 491 be valid so only defer it when we know the 'status' says its fine! */ 492 return; 493 494 res = (struct ResolverResults *)conn->async.os_specific; 495 res->num_pending--; 496 497 if(CURL_ASYNC_SUCCESS == status) { 498 Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port); 499 if(ai) { 500 compound_results(res, ai); 501 } 502 } 503 /* A successful result overwrites any previous error */ 504 if(res->last_status != ARES_SUCCESS) 505 res->last_status = status; 506} 507 508/* 509 * Curl_resolver_getaddrinfo() - when using ares 510 * 511 * Returns name information about the given hostname and port number. If 512 * successful, the 'hostent' is returned and the forth argument will point to 513 * memory we need to free after use. That memory *MUST* be freed with 514 * Curl_freeaddrinfo(), nothing else. 515 */ 516Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, 517 const char *hostname, 518 int port, 519 int *waitp) 520{ 521 char *bufp; 522 struct SessionHandle *data = conn->data; 523 struct in_addr in; 524 int family = PF_INET; 525#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ 526 struct in6_addr in6; 527#endif /* CURLRES_IPV6 */ 528 529 *waitp = 0; /* default to synchronous response */ 530 531 /* First check if this is an IPv4 address string */ 532 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) { 533 /* This is a dotted IP address 123.123.123.123-style */ 534 return Curl_ip2addr(AF_INET, &in, hostname, port); 535 } 536 537#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ 538 /* Otherwise, check if this is an IPv6 address string */ 539 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0) 540 /* This must be an IPv6 address literal. */ 541 return Curl_ip2addr(AF_INET6, &in6, hostname, port); 542 543 switch(conn->ip_version) { 544 default: 545#if ARES_VERSION >= 0x010601 546 family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older 547 c-ares versions this just falls through and defaults 548 to PF_INET */ 549 break; 550#endif 551 case CURL_IPRESOLVE_V4: 552 family = PF_INET; 553 break; 554 case CURL_IPRESOLVE_V6: 555 family = PF_INET6; 556 break; 557 } 558#endif /* CURLRES_IPV6 */ 559 560 bufp = strdup(hostname); 561 if(bufp) { 562 struct ResolverResults *res = NULL; 563 Curl_safefree(conn->async.hostname); 564 conn->async.hostname = bufp; 565 conn->async.port = port; 566 conn->async.done = FALSE; /* not done */ 567 conn->async.status = 0; /* clear */ 568 conn->async.dns = NULL; /* clear */ 569 res = calloc(sizeof(struct ResolverResults),1); 570 if(!res) { 571 Curl_safefree(conn->async.hostname); 572 conn->async.hostname = NULL; 573 return NULL; 574 } 575 conn->async.os_specific = res; 576 577 /* initial status - failed */ 578 res->last_status = ARES_ENOTFOUND; 579#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ 580 if(family == PF_UNSPEC) { 581 if(Curl_ipv6works()) { 582 res->num_pending = 2; 583 584 /* areschannel is already setup in the Curl_open() function */ 585 ares_gethostbyname((ares_channel)data->state.resolver, hostname, 586 PF_INET, query_completed_cb, conn); 587 ares_gethostbyname((ares_channel)data->state.resolver, hostname, 588 PF_INET6, query_completed_cb, conn); 589 } 590 else { 591 res->num_pending = 1; 592 593 /* areschannel is already setup in the Curl_open() function */ 594 ares_gethostbyname((ares_channel)data->state.resolver, hostname, 595 PF_INET, query_completed_cb, conn); 596 } 597 } 598 else 599#endif /* CURLRES_IPV6 */ 600 { 601 res->num_pending = 1; 602 603 /* areschannel is already setup in the Curl_open() function */ 604 ares_gethostbyname((ares_channel)data->state.resolver, hostname, family, 605 query_completed_cb, conn); 606 } 607 608 *waitp = 1; /* expect asynchronous response */ 609 } 610 return NULL; /* no struct yet */ 611} 612 613CURLcode Curl_set_dns_servers(struct SessionHandle *data, 614 char *servers) 615{ 616 CURLcode result = CURLE_NOT_BUILT_IN; 617#if (ARES_VERSION >= 0x010704) 618 int ares_result = ares_set_servers_csv(data->state.resolver, servers); 619 switch(ares_result) { 620 case ARES_SUCCESS: 621 result = CURLE_OK; 622 break; 623 case ARES_ENOMEM: 624 result = CURLE_OUT_OF_MEMORY; 625 break; 626 case ARES_ENOTINITIALIZED: 627 case ARES_ENODATA: 628 case ARES_EBADSTR: 629 default: 630 result = CURLE_BAD_FUNCTION_ARGUMENT; 631 break; 632 } 633#else /* too old c-ares version! */ 634 (void)data; 635 (void)servers; 636#endif 637 return result; 638} 639#endif /* CURLRES_ARES */ 640