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#ifdef HAVE_LIMITS_H 26#include <limits.h> 27#endif 28#ifdef HAVE_SYS_SOCKET_H 29#include <sys/socket.h> 30#endif 31#ifdef HAVE_NETINET_IN_H 32#include <netinet/in.h> 33#endif 34#ifdef HAVE_NETDB_H 35#include <netdb.h> 36#endif 37#ifdef HAVE_ARPA_INET_H 38#include <arpa/inet.h> 39#endif 40#ifdef HAVE_UNISTD_H 41#include <unistd.h> /* for the close() proto */ 42#endif 43#ifdef __VMS 44#include <in.h> 45#include <inet.h> 46#endif 47 48#ifdef HAVE_PROCESS_H 49#include <process.h> 50#endif 51 52#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) 53#undef in_addr_t 54#define in_addr_t unsigned long 55#endif 56 57/*********************************************************************** 58 * Only for ares-enabled builds 59 * And only for functions that fulfill the asynch resolver backend API 60 * as defined in asyn.h, nothing else belongs in this file! 61 **********************************************************************/ 62 63#ifdef CURLRES_ARES 64 65#include "urldata.h" 66#include "sendf.h" 67#include "hostip.h" 68#include "hash.h" 69#include "share.h" 70#include "strerror.h" 71#include "url.h" 72#include "multiif.h" 73#include "inet_pton.h" 74#include "connect.h" 75#include "select.h" 76#include "progress.h" 77 78#define _MPRINTF_REPLACE /* use our functions only */ 79#include <curl/mprintf.h> 80 81# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ 82 (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__)) 83# define CARES_STATICLIB 84# endif 85# include <ares.h> 86 87#if ARES_VERSION >= 0x010500 88/* c-ares 1.5.0 or later, the callback proto is modified */ 89#define HAVE_CARES_CALLBACK_TIMEOUTS 1 90#endif 91 92#include "curl_memory.h" 93/* The last #include file should be: */ 94#include "memdebug.h" 95 96struct ResolverResults { 97 int num_pending; /* number of ares_gethostbyname() requests */ 98 Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */ 99 int last_status; 100}; 101 102/* 103 * Curl_resolver_global_init() - the generic low-level asynchronous name 104 * resolve API. Called from curl_global_init() to initialize global resolver 105 * environment. Initializes ares library. 106 */ 107int Curl_resolver_global_init(void) 108{ 109#ifdef CARES_HAVE_ARES_LIBRARY_INIT 110 if(ares_library_init(ARES_LIB_INIT_ALL)) { 111 return CURLE_FAILED_INIT; 112 } 113#endif 114 return CURLE_OK; 115} 116 117/* 118 * Curl_resolver_global_cleanup() 119 * 120 * Called from curl_global_cleanup() to destroy global resolver environment. 121 * Deinitializes ares library. 122 */ 123void Curl_resolver_global_cleanup(void) 124{ 125#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP 126 ares_library_cleanup(); 127#endif 128} 129 130/* 131 * Curl_resolver_init() 132 * 133 * Called from curl_easy_init() -> Curl_open() to initialize resolver 134 * URL-state specific environment ('resolver' member of the UrlState 135 * structure). Fills the passed pointer by the initialized ares_channel. 136 */ 137CURLcode Curl_resolver_init(void **resolver) 138{ 139 int status = ares_init((ares_channel*)resolver); 140 if(status != ARES_SUCCESS) { 141 if(status == ARES_ENOMEM) 142 return CURLE_OUT_OF_MEMORY; 143 else 144 return CURLE_FAILED_INIT; 145 } 146 return CURLE_OK; 147 /* make sure that all other returns from this function should destroy the 148 ares channel before returning error! */ 149} 150 151/* 152 * Curl_resolver_cleanup() 153 * 154 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver 155 * URL-state specific environment ('resolver' member of the UrlState 156 * structure). Destroys the ares channel. 157 */ 158void Curl_resolver_cleanup(void *resolver) 159{ 160 ares_destroy((ares_channel)resolver); 161} 162 163/* 164 * Curl_resolver_duphandle() 165 * 166 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific 167 * environment ('resolver' member of the UrlState structure). Duplicates the 168 * 'from' ares channel and passes the resulting channel to the 'to' pointer. 169 */ 170int Curl_resolver_duphandle(void **to, void *from) 171{ 172 /* Clone the ares channel for the new handle */ 173 if(ARES_SUCCESS != ares_dup((ares_channel*)to,(ares_channel)from)) 174 return CURLE_FAILED_INIT; 175 return CURLE_OK; 176} 177 178static void destroy_async_data (struct Curl_async *async); 179 180/* 181 * Cancel all possibly still on-going resolves for this connection. 182 */ 183void Curl_resolver_cancel(struct connectdata *conn) 184{ 185 if(conn && conn->data && conn->data->state.resolver) 186 ares_cancel((ares_channel)conn->data->state.resolver); 187 destroy_async_data(&conn->async); 188} 189 190/* 191 * destroy_async_data() cleans up async resolver data. 192 */ 193static void destroy_async_data (struct Curl_async *async) 194{ 195 if(async->hostname) 196 free(async->hostname); 197 198 if(async->os_specific) { 199 struct ResolverResults *res = (struct ResolverResults *)async->os_specific; 200 if(res) { 201 if(res->temp_ai) { 202 Curl_freeaddrinfo(res->temp_ai); 203 res->temp_ai = NULL; 204 } 205 free(res); 206 } 207 async->os_specific = NULL; 208 } 209 210 async->hostname = NULL; 211} 212 213/* 214 * Curl_resolver_fdset() is called when someone from the outside world (using 215 * curl_multi_fdset()) wants to get our fd_set setup and we're talking with 216 * ares. The caller must make sure that this function is only called when we 217 * have a working ares channel. 218 * 219 * Returns: CURLE_OK always! 220 */ 221 222int Curl_resolver_getsock(struct connectdata *conn, 223 curl_socket_t *socks, 224 int numsocks) 225 226{ 227 struct timeval maxtime; 228 struct timeval timebuf; 229 struct timeval *timeout; 230 int max = ares_getsock((ares_channel)conn->data->state.resolver, 231 (ares_socket_t *)socks, numsocks); 232 233 234 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; 235 maxtime.tv_usec = 0; 236 237 timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime, 238 &timebuf); 239 240 Curl_expire(conn->data, 241 (timeout->tv_sec * 1000) + (timeout->tv_usec/1000)); 242 243 return max; 244} 245 246/* 247 * waitperform() 248 * 249 * 1) Ask ares what sockets it currently plays with, then 250 * 2) wait for the timeout period to check for action on ares' sockets. 251 * 3) tell ares to act on all the sockets marked as "with action" 252 * 253 * return number of sockets it worked on 254 */ 255 256static int waitperform(struct connectdata *conn, int timeout_ms) 257{ 258 struct SessionHandle *data = conn->data; 259 int nfds; 260 int bitmask; 261 ares_socket_t socks[ARES_GETSOCK_MAXNUM]; 262 struct pollfd pfd[ARES_GETSOCK_MAXNUM]; 263 int i; 264 int num = 0; 265 266 bitmask = ares_getsock((ares_channel)data->state.resolver, socks, 267 ARES_GETSOCK_MAXNUM); 268 269 for(i=0; i < ARES_GETSOCK_MAXNUM; i++) { 270 pfd[i].events = 0; 271 pfd[i].revents = 0; 272 if(ARES_GETSOCK_READABLE(bitmask, i)) { 273 pfd[i].fd = socks[i]; 274 pfd[i].events |= POLLRDNORM|POLLIN; 275 } 276 if(ARES_GETSOCK_WRITABLE(bitmask, i)) { 277 pfd[i].fd = socks[i]; 278 pfd[i].events |= POLLWRNORM|POLLOUT; 279 } 280 if(pfd[i].events != 0) 281 num++; 282 else 283 break; 284 } 285 286 if(num) 287 nfds = Curl_poll(pfd, num, timeout_ms); 288 else 289 nfds = 0; 290 291 if(!nfds) 292 /* Call ares_process() unconditonally here, even if we simply timed out 293 above, as otherwise the ares name resolve won't timeout! */ 294 ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD, 295 ARES_SOCKET_BAD); 296 else { 297 /* move through the descriptors and ask for processing on them */ 298 for(i=0; i < num; i++) 299 ares_process_fd((ares_channel)data->state.resolver, 300 pfd[i].revents & (POLLRDNORM|POLLIN)? 301 pfd[i].fd:ARES_SOCKET_BAD, 302 pfd[i].revents & (POLLWRNORM|POLLOUT)? 303 pfd[i].fd:ARES_SOCKET_BAD); 304 } 305 return nfds; 306} 307 308/* 309 * Curl_resolver_is_resolved() is called repeatedly to check if a previous 310 * name resolve request has completed. It should also make sure to time-out if 311 * the operation seems to take too long. 312 * 313 * Returns normal CURLcode errors. 314 */ 315CURLcode Curl_resolver_is_resolved(struct connectdata *conn, 316 struct Curl_dns_entry **dns) 317{ 318 struct SessionHandle *data = conn->data; 319 struct ResolverResults *res = (struct ResolverResults *) 320 conn->async.os_specific; 321 322 *dns = NULL; 323 324 waitperform(conn, 0); 325 326 if(res && !res->num_pending) { 327 (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai); 328 /* temp_ai ownership is moved to the connection, so we need not free-up 329 them */ 330 res->temp_ai = NULL; 331 destroy_async_data(&conn->async); 332 if(!conn->async.dns) { 333 failf(data, "Could not resolve host: %s (%s)", conn->host.dispname, 334 ares_strerror(conn->async.status)); 335 return 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.httpproxy) { 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.httpproxy) { 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 res->num_pending = 2; 582 583 /* areschannel is already setup in the Curl_open() function */ 584 ares_gethostbyname((ares_channel)data->state.resolver, hostname, 585 PF_INET, query_completed_cb, conn); 586 ares_gethostbyname((ares_channel)data->state.resolver, hostname, 587 PF_INET6, query_completed_cb, conn); 588 } 589 else 590#endif /* CURLRES_IPV6 */ 591 { 592 res->num_pending = 1; 593 594 /* areschannel is already setup in the Curl_open() function */ 595 ares_gethostbyname((ares_channel)data->state.resolver, hostname, family, 596 query_completed_cb, conn); 597 } 598 599 *waitp = 1; /* expect asynchronous response */ 600 } 601 return NULL; /* no struct yet */ 602} 603#endif /* CURLRES_ARES */ 604