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