Annotation of embedaddon/curl/lib/asyn-ares.c, revision 1.1
1.1 ! misho 1: /***************************************************************************
! 2: * _ _ ____ _
! 3: * Project ___| | | | _ \| |
! 4: * / __| | | | |_) | |
! 5: * | (__| |_| | _ <| |___
! 6: * \___|\___/|_| \_\_____|
! 7: *
! 8: * Copyright (C) 1998 - 2020, 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 https://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: /***********************************************************************
! 26: * Only for ares-enabled builds
! 27: * And only for functions that fulfill the asynch resolver backend API
! 28: * as defined in asyn.h, nothing else belongs in this file!
! 29: **********************************************************************/
! 30:
! 31: #ifdef CURLRES_ARES
! 32:
! 33: #include <limits.h>
! 34: #ifdef HAVE_NETINET_IN_H
! 35: #include <netinet/in.h>
! 36: #endif
! 37: #ifdef HAVE_NETDB_H
! 38: #include <netdb.h>
! 39: #endif
! 40: #ifdef HAVE_ARPA_INET_H
! 41: #include <arpa/inet.h>
! 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: #include "urldata.h"
! 58: #include "sendf.h"
! 59: #include "hostip.h"
! 60: #include "hash.h"
! 61: #include "share.h"
! 62: #include "strerror.h"
! 63: #include "url.h"
! 64: #include "multiif.h"
! 65: #include "inet_pton.h"
! 66: #include "connect.h"
! 67: #include "select.h"
! 68: #include "progress.h"
! 69:
! 70: # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
! 71: (defined(WIN32) || defined(__SYMBIAN32__))
! 72: # define CARES_STATICLIB
! 73: # endif
! 74: # include <ares.h>
! 75: # include <ares_version.h> /* really old c-ares didn't include this by
! 76: itself */
! 77:
! 78: #if ARES_VERSION >= 0x010500
! 79: /* c-ares 1.5.0 or later, the callback proto is modified */
! 80: #define HAVE_CARES_CALLBACK_TIMEOUTS 1
! 81: #endif
! 82:
! 83: /* The last 3 #include files should be in this order */
! 84: #include "curl_printf.h"
! 85: #include "curl_memory.h"
! 86: #include "memdebug.h"
! 87:
! 88: struct ResolverResults {
! 89: int num_pending; /* number of ares_gethostbyname() requests */
! 90: Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
! 91: int last_status;
! 92: struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
! 93: };
! 94:
! 95: /* How long we are willing to wait for additional parallel responses after
! 96: obtaining a "definitive" one.
! 97:
! 98: This is intended to equal the c-ares default timeout. cURL always uses that
! 99: default value. Unfortunately, c-ares doesn't expose its default timeout in
! 100: its API, but it is officially documented as 5 seconds.
! 101:
! 102: See query_completed_cb() for an explanation of how this is used.
! 103: */
! 104: #define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
! 105:
! 106: /*
! 107: * Curl_resolver_global_init() - the generic low-level asynchronous name
! 108: * resolve API. Called from curl_global_init() to initialize global resolver
! 109: * environment. Initializes ares library.
! 110: */
! 111: int Curl_resolver_global_init(void)
! 112: {
! 113: #ifdef CARES_HAVE_ARES_LIBRARY_INIT
! 114: if(ares_library_init(ARES_LIB_INIT_ALL)) {
! 115: return CURLE_FAILED_INIT;
! 116: }
! 117: #endif
! 118: return CURLE_OK;
! 119: }
! 120:
! 121: /*
! 122: * Curl_resolver_global_cleanup()
! 123: *
! 124: * Called from curl_global_cleanup() to destroy global resolver environment.
! 125: * Deinitializes ares library.
! 126: */
! 127: void Curl_resolver_global_cleanup(void)
! 128: {
! 129: #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
! 130: ares_library_cleanup();
! 131: #endif
! 132: }
! 133:
! 134:
! 135: static void Curl_ares_sock_state_cb(void *data, ares_socket_t socket_fd,
! 136: int readable, int writable)
! 137: {
! 138: struct Curl_easy *easy = data;
! 139: if(!readable && !writable) {
! 140: DEBUGASSERT(easy);
! 141: Curl_multi_closed(easy, socket_fd);
! 142: }
! 143: }
! 144:
! 145: /*
! 146: * Curl_resolver_init()
! 147: *
! 148: * Called from curl_easy_init() -> Curl_open() to initialize resolver
! 149: * URL-state specific environment ('resolver' member of the UrlState
! 150: * structure). Fills the passed pointer by the initialized ares_channel.
! 151: */
! 152: CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
! 153: {
! 154: int status;
! 155: struct ares_options options;
! 156: int optmask = ARES_OPT_SOCK_STATE_CB;
! 157: options.sock_state_cb = Curl_ares_sock_state_cb;
! 158: options.sock_state_cb_data = easy;
! 159: status = ares_init_options((ares_channel*)resolver, &options, optmask);
! 160: if(status != ARES_SUCCESS) {
! 161: if(status == ARES_ENOMEM)
! 162: return CURLE_OUT_OF_MEMORY;
! 163: else
! 164: return CURLE_FAILED_INIT;
! 165: }
! 166: return CURLE_OK;
! 167: /* make sure that all other returns from this function should destroy the
! 168: ares channel before returning error! */
! 169: }
! 170:
! 171: /*
! 172: * Curl_resolver_cleanup()
! 173: *
! 174: * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
! 175: * URL-state specific environment ('resolver' member of the UrlState
! 176: * structure). Destroys the ares channel.
! 177: */
! 178: void Curl_resolver_cleanup(void *resolver)
! 179: {
! 180: ares_destroy((ares_channel)resolver);
! 181: }
! 182:
! 183: /*
! 184: * Curl_resolver_duphandle()
! 185: *
! 186: * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
! 187: * environment ('resolver' member of the UrlState structure). Duplicates the
! 188: * 'from' ares channel and passes the resulting channel to the 'to' pointer.
! 189: */
! 190: CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
! 191: {
! 192: (void)from;
! 193: /*
! 194: * it would be better to call ares_dup instead, but right now
! 195: * it is not possible to set 'sock_state_cb_data' outside of
! 196: * ares_init_options
! 197: */
! 198: return Curl_resolver_init(easy, to);
! 199: }
! 200:
! 201: static void destroy_async_data(struct Curl_async *async);
! 202:
! 203: /*
! 204: * Cancel all possibly still on-going resolves for this connection.
! 205: */
! 206: void Curl_resolver_cancel(struct connectdata *conn)
! 207: {
! 208: if(conn->data && conn->data->state.resolver)
! 209: ares_cancel((ares_channel)conn->data->state.resolver);
! 210: destroy_async_data(&conn->async);
! 211: }
! 212:
! 213: /*
! 214: * We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We
! 215: * never block.
! 216: */
! 217: void Curl_resolver_kill(struct connectdata *conn)
! 218: {
! 219: /* We don't need to check the resolver state because we can be called safely
! 220: at any time and we always do the same thing. */
! 221: Curl_resolver_cancel(conn);
! 222: }
! 223:
! 224: /*
! 225: * destroy_async_data() cleans up async resolver data.
! 226: */
! 227: static void destroy_async_data(struct Curl_async *async)
! 228: {
! 229: free(async->hostname);
! 230:
! 231: if(async->os_specific) {
! 232: struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
! 233: if(res) {
! 234: if(res->temp_ai) {
! 235: Curl_freeaddrinfo(res->temp_ai);
! 236: res->temp_ai = NULL;
! 237: }
! 238: free(res);
! 239: }
! 240: async->os_specific = NULL;
! 241: }
! 242:
! 243: async->hostname = NULL;
! 244: }
! 245:
! 246: /*
! 247: * Curl_resolver_getsock() is called when someone from the outside world
! 248: * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
! 249: * with ares. The caller must make sure that this function is only called when
! 250: * we have a working ares channel.
! 251: *
! 252: * Returns: sockets-in-use-bitmap
! 253: */
! 254:
! 255: int Curl_resolver_getsock(struct connectdata *conn,
! 256: curl_socket_t *socks)
! 257: {
! 258: struct timeval maxtime;
! 259: struct timeval timebuf;
! 260: struct timeval *timeout;
! 261: long milli;
! 262: int max = ares_getsock((ares_channel)conn->data->state.resolver,
! 263: (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
! 264:
! 265: maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
! 266: maxtime.tv_usec = 0;
! 267:
! 268: timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
! 269: &timebuf);
! 270: milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
! 271: if(milli == 0)
! 272: milli += 10;
! 273: Curl_expire(conn->data, milli, EXPIRE_ASYNC_NAME);
! 274:
! 275: return max;
! 276: }
! 277:
! 278: /*
! 279: * waitperform()
! 280: *
! 281: * 1) Ask ares what sockets it currently plays with, then
! 282: * 2) wait for the timeout period to check for action on ares' sockets.
! 283: * 3) tell ares to act on all the sockets marked as "with action"
! 284: *
! 285: * return number of sockets it worked on
! 286: */
! 287:
! 288: static int waitperform(struct connectdata *conn, int timeout_ms)
! 289: {
! 290: struct Curl_easy *data = conn->data;
! 291: int nfds;
! 292: int bitmask;
! 293: ares_socket_t socks[ARES_GETSOCK_MAXNUM];
! 294: struct pollfd pfd[ARES_GETSOCK_MAXNUM];
! 295: int i;
! 296: int num = 0;
! 297:
! 298: bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
! 299: ARES_GETSOCK_MAXNUM);
! 300:
! 301: for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
! 302: pfd[i].events = 0;
! 303: pfd[i].revents = 0;
! 304: if(ARES_GETSOCK_READABLE(bitmask, i)) {
! 305: pfd[i].fd = socks[i];
! 306: pfd[i].events |= POLLRDNORM|POLLIN;
! 307: }
! 308: if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
! 309: pfd[i].fd = socks[i];
! 310: pfd[i].events |= POLLWRNORM|POLLOUT;
! 311: }
! 312: if(pfd[i].events != 0)
! 313: num++;
! 314: else
! 315: break;
! 316: }
! 317:
! 318: if(num)
! 319: nfds = Curl_poll(pfd, num, timeout_ms);
! 320: else
! 321: nfds = 0;
! 322:
! 323: if(!nfds)
! 324: /* Call ares_process() unconditonally here, even if we simply timed out
! 325: above, as otherwise the ares name resolve won't timeout! */
! 326: ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
! 327: ARES_SOCKET_BAD);
! 328: else {
! 329: /* move through the descriptors and ask for processing on them */
! 330: for(i = 0; i < num; i++)
! 331: ares_process_fd((ares_channel)data->state.resolver,
! 332: (pfd[i].revents & (POLLRDNORM|POLLIN))?
! 333: pfd[i].fd:ARES_SOCKET_BAD,
! 334: (pfd[i].revents & (POLLWRNORM|POLLOUT))?
! 335: pfd[i].fd:ARES_SOCKET_BAD);
! 336: }
! 337: return nfds;
! 338: }
! 339:
! 340: /*
! 341: * Curl_resolver_is_resolved() is called repeatedly to check if a previous
! 342: * name resolve request has completed. It should also make sure to time-out if
! 343: * the operation seems to take too long.
! 344: *
! 345: * Returns normal CURLcode errors.
! 346: */
! 347: CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
! 348: struct Curl_dns_entry **dns)
! 349: {
! 350: struct Curl_easy *data = conn->data;
! 351: struct ResolverResults *res = (struct ResolverResults *)
! 352: conn->async.os_specific;
! 353: CURLcode result = CURLE_OK;
! 354:
! 355: if(dns)
! 356: *dns = NULL;
! 357:
! 358: waitperform(conn, 0);
! 359:
! 360: /* Now that we've checked for any last minute results above, see if there are
! 361: any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
! 362: expires. */
! 363: if(res
! 364: && res->num_pending
! 365: /* This is only set to non-zero if the timer was started. */
! 366: && (res->happy_eyeballs_dns_time.tv_sec
! 367: || res->happy_eyeballs_dns_time.tv_usec)
! 368: && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
! 369: >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
! 370: /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
! 371: running. */
! 372: memset(
! 373: &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time));
! 374:
! 375: /* Cancel the raw c-ares request, which will fire query_completed_cb() with
! 376: ARES_ECANCELLED synchronously for all pending responses. This will
! 377: leave us with res->num_pending == 0, which is perfect for the next
! 378: block. */
! 379: ares_cancel((ares_channel)data->state.resolver);
! 380: DEBUGASSERT(res->num_pending == 0);
! 381: }
! 382:
! 383: if(res && !res->num_pending) {
! 384: if(dns) {
! 385: (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
! 386: /* temp_ai ownership is moved to the connection, so we need not free-up
! 387: them */
! 388: res->temp_ai = NULL;
! 389: }
! 390: if(!conn->async.dns) {
! 391: failf(data, "Could not resolve: %s (%s)",
! 392: conn->async.hostname, ares_strerror(conn->async.status));
! 393: result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
! 394: CURLE_COULDNT_RESOLVE_HOST;
! 395: }
! 396: else if(dns)
! 397: *dns = conn->async.dns;
! 398:
! 399: destroy_async_data(&conn->async);
! 400: }
! 401:
! 402: return result;
! 403: }
! 404:
! 405: /*
! 406: * Curl_resolver_wait_resolv()
! 407: *
! 408: * Waits for a resolve to finish. This function should be avoided since using
! 409: * this risk getting the multi interface to "hang".
! 410: *
! 411: * If 'entry' is non-NULL, make it point to the resolved dns entry
! 412: *
! 413: * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
! 414: * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
! 415: */
! 416: CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
! 417: struct Curl_dns_entry **entry)
! 418: {
! 419: CURLcode result = CURLE_OK;
! 420: struct Curl_easy *data = conn->data;
! 421: timediff_t timeout;
! 422: struct curltime now = Curl_now();
! 423: struct Curl_dns_entry *temp_entry;
! 424:
! 425: if(entry)
! 426: *entry = NULL; /* clear on entry */
! 427:
! 428: timeout = Curl_timeleft(data, &now, TRUE);
! 429: if(timeout < 0) {
! 430: /* already expired! */
! 431: connclose(conn, "Timed out before name resolve started");
! 432: return CURLE_OPERATION_TIMEDOUT;
! 433: }
! 434: if(!timeout)
! 435: timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
! 436:
! 437: /* Wait for the name resolve query to complete. */
! 438: while(!result) {
! 439: struct timeval *tvp, tv, store;
! 440: int itimeout;
! 441: int timeout_ms;
! 442:
! 443: itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
! 444:
! 445: store.tv_sec = itimeout/1000;
! 446: store.tv_usec = (itimeout%1000)*1000;
! 447:
! 448: tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
! 449:
! 450: /* use the timeout period ares returned to us above if less than one
! 451: second is left, otherwise just use 1000ms to make sure the progress
! 452: callback gets called frequent enough */
! 453: if(!tvp->tv_sec)
! 454: timeout_ms = (int)(tvp->tv_usec/1000);
! 455: else
! 456: timeout_ms = 1000;
! 457:
! 458: waitperform(conn, timeout_ms);
! 459: result = Curl_resolver_is_resolved(conn, entry?&temp_entry:NULL);
! 460:
! 461: if(result || conn->async.done)
! 462: break;
! 463:
! 464: if(Curl_pgrsUpdate(conn))
! 465: result = CURLE_ABORTED_BY_CALLBACK;
! 466: else {
! 467: struct curltime now2 = Curl_now();
! 468: timediff_t timediff = Curl_timediff(now2, now); /* spent time */
! 469: if(timediff <= 0)
! 470: timeout -= 1; /* always deduct at least 1 */
! 471: else if(timediff > timeout)
! 472: timeout = -1;
! 473: else
! 474: timeout -= (long)timediff;
! 475: now = now2; /* for next loop */
! 476: }
! 477: if(timeout < 0)
! 478: result = CURLE_OPERATION_TIMEDOUT;
! 479: }
! 480: if(result)
! 481: /* failure, so we cancel the ares operation */
! 482: ares_cancel((ares_channel)data->state.resolver);
! 483:
! 484: /* Operation complete, if the lookup was successful we now have the entry
! 485: in the cache. */
! 486: if(entry)
! 487: *entry = conn->async.dns;
! 488:
! 489: if(result)
! 490: /* close the connection, since we can't return failure here without
! 491: cleaning up this connection properly. */
! 492: connclose(conn, "c-ares resolve failed");
! 493:
! 494: return result;
! 495: }
! 496:
! 497: /* Connects results to the list */
! 498: static void compound_results(struct ResolverResults *res,
! 499: Curl_addrinfo *ai)
! 500: {
! 501: Curl_addrinfo *ai_tail;
! 502: if(!ai)
! 503: return;
! 504: ai_tail = ai;
! 505:
! 506: while(ai_tail->ai_next)
! 507: ai_tail = ai_tail->ai_next;
! 508:
! 509: /* Add the new results to the list of old results. */
! 510: ai_tail->ai_next = res->temp_ai;
! 511: res->temp_ai = ai;
! 512: }
! 513:
! 514: /*
! 515: * ares_query_completed_cb() is the callback that ares will call when
! 516: * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
! 517: * when using ares, is completed either successfully or with failure.
! 518: */
! 519: static void query_completed_cb(void *arg, /* (struct connectdata *) */
! 520: int status,
! 521: #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
! 522: int timeouts,
! 523: #endif
! 524: struct hostent *hostent)
! 525: {
! 526: struct connectdata *conn = (struct connectdata *)arg;
! 527: struct ResolverResults *res;
! 528:
! 529: #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
! 530: (void)timeouts; /* ignored */
! 531: #endif
! 532:
! 533: if(ARES_EDESTRUCTION == status)
! 534: /* when this ares handle is getting destroyed, the 'arg' pointer may not
! 535: be valid so only defer it when we know the 'status' says its fine! */
! 536: return;
! 537:
! 538: res = (struct ResolverResults *)conn->async.os_specific;
! 539: if(res) {
! 540: res->num_pending--;
! 541:
! 542: if(CURL_ASYNC_SUCCESS == status) {
! 543: Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
! 544: if(ai) {
! 545: compound_results(res, ai);
! 546: }
! 547: }
! 548: /* A successful result overwrites any previous error */
! 549: if(res->last_status != ARES_SUCCESS)
! 550: res->last_status = status;
! 551:
! 552: /* If there are responses still pending, we presume they must be the
! 553: complementary IPv4 or IPv6 lookups that we started in parallel in
! 554: Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we've got a
! 555: "definitive" response from one of a set of parallel queries, we need to
! 556: think about how long we're willing to wait for more responses. */
! 557: if(res->num_pending
! 558: /* Only these c-ares status values count as "definitive" for these
! 559: purposes. For example, ARES_ENODATA is what we expect when there is
! 560: no IPv6 entry for a domain name, and that's not a reason to get more
! 561: aggressive in our timeouts for the other response. Other errors are
! 562: either a result of bad input (which should affect all parallel
! 563: requests), local or network conditions, non-definitive server
! 564: responses, or us cancelling the request. */
! 565: && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
! 566: /* Right now, there can only be up to two parallel queries, so don't
! 567: bother handling any other cases. */
! 568: DEBUGASSERT(res->num_pending == 1);
! 569:
! 570: /* It's possible that one of these parallel queries could succeed
! 571: quickly, but the other could always fail or timeout (when we're
! 572: talking to a pool of DNS servers that can only successfully resolve
! 573: IPv4 address, for example).
! 574:
! 575: It's also possible that the other request could always just take
! 576: longer because it needs more time or only the second DNS server can
! 577: fulfill it successfully. But, to align with the philosophy of Happy
! 578: Eyeballs, we don't want to wait _too_ long or users will think
! 579: requests are slow when IPv6 lookups don't actually work (but IPv4 ones
! 580: do).
! 581:
! 582: So, now that we have a usable answer (some IPv4 addresses, some IPv6
! 583: addresses, or "no such domain"), we start a timeout for the remaining
! 584: pending responses. Even though it is typical that this resolved
! 585: request came back quickly, that needn't be the case. It might be that
! 586: this completing request didn't get a result from the first DNS server
! 587: or even the first round of the whole DNS server pool. So it could
! 588: already be quite some time after we issued the DNS queries in the
! 589: first place. Without modifying c-ares, we can't know exactly where in
! 590: its retry cycle we are. We could guess based on how much time has
! 591: gone by, but it doesn't really matter. Happy Eyeballs tells us that,
! 592: given usable information in hand, we simply don't want to wait "too
! 593: much longer" after we get a result.
! 594:
! 595: We simply wait an additional amount of time equal to the default
! 596: c-ares query timeout. That is enough time for a typical parallel
! 597: response to arrive without being "too long". Even on a network
! 598: where one of the two types of queries is failing or timing out
! 599: constantly, this will usually mean we wait a total of the default
! 600: c-ares timeout (5 seconds) plus the round trip time for the successful
! 601: request, which seems bearable. The downside is that c-ares might race
! 602: with us to issue one more retry just before we give up, but it seems
! 603: better to "waste" that request instead of trying to guess the perfect
! 604: timeout to prevent it. After all, we don't even know where in the
! 605: c-ares retry cycle each request is.
! 606: */
! 607: res->happy_eyeballs_dns_time = Curl_now();
! 608: Curl_expire(
! 609: conn->data, HAPPY_EYEBALLS_DNS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS_DNS);
! 610: }
! 611: }
! 612: }
! 613:
! 614: /*
! 615: * Curl_resolver_getaddrinfo() - when using ares
! 616: *
! 617: * Returns name information about the given hostname and port number. If
! 618: * successful, the 'hostent' is returned and the forth argument will point to
! 619: * memory we need to free after use. That memory *MUST* be freed with
! 620: * Curl_freeaddrinfo(), nothing else.
! 621: */
! 622: Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
! 623: const char *hostname,
! 624: int port,
! 625: int *waitp)
! 626: {
! 627: char *bufp;
! 628: struct Curl_easy *data = conn->data;
! 629: int family = PF_INET;
! 630:
! 631: *waitp = 0; /* default to synchronous response */
! 632:
! 633: #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
! 634: switch(conn->ip_version) {
! 635: default:
! 636: #if ARES_VERSION >= 0x010601
! 637: family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
! 638: c-ares versions this just falls through and defaults
! 639: to PF_INET */
! 640: break;
! 641: #endif
! 642: case CURL_IPRESOLVE_V4:
! 643: family = PF_INET;
! 644: break;
! 645: case CURL_IPRESOLVE_V6:
! 646: family = PF_INET6;
! 647: break;
! 648: }
! 649: #endif /* CURLRES_IPV6 */
! 650:
! 651: bufp = strdup(hostname);
! 652: if(bufp) {
! 653: struct ResolverResults *res = NULL;
! 654: free(conn->async.hostname);
! 655: conn->async.hostname = bufp;
! 656: conn->async.port = port;
! 657: conn->async.done = FALSE; /* not done */
! 658: conn->async.status = 0; /* clear */
! 659: conn->async.dns = NULL; /* clear */
! 660: res = calloc(sizeof(struct ResolverResults), 1);
! 661: if(!res) {
! 662: free(conn->async.hostname);
! 663: conn->async.hostname = NULL;
! 664: return NULL;
! 665: }
! 666: conn->async.os_specific = res;
! 667:
! 668: /* initial status - failed */
! 669: res->last_status = ARES_ENOTFOUND;
! 670: #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
! 671: if(family == PF_UNSPEC) {
! 672: if(Curl_ipv6works(conn)) {
! 673: res->num_pending = 2;
! 674:
! 675: /* areschannel is already setup in the Curl_open() function */
! 676: ares_gethostbyname((ares_channel)data->state.resolver, hostname,
! 677: PF_INET, query_completed_cb, conn);
! 678: ares_gethostbyname((ares_channel)data->state.resolver, hostname,
! 679: PF_INET6, query_completed_cb, conn);
! 680: }
! 681: else {
! 682: res->num_pending = 1;
! 683:
! 684: /* areschannel is already setup in the Curl_open() function */
! 685: ares_gethostbyname((ares_channel)data->state.resolver, hostname,
! 686: PF_INET, query_completed_cb, conn);
! 687: }
! 688: }
! 689: else
! 690: #endif /* CURLRES_IPV6 */
! 691: {
! 692: res->num_pending = 1;
! 693:
! 694: /* areschannel is already setup in the Curl_open() function */
! 695: ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
! 696: query_completed_cb, conn);
! 697: }
! 698:
! 699: *waitp = 1; /* expect asynchronous response */
! 700: }
! 701: return NULL; /* no struct yet */
! 702: }
! 703:
! 704: CURLcode Curl_set_dns_servers(struct Curl_easy *data,
! 705: char *servers)
! 706: {
! 707: CURLcode result = CURLE_NOT_BUILT_IN;
! 708: int ares_result;
! 709:
! 710: /* If server is NULL or empty, this would purge all DNS servers
! 711: * from ares library, which will cause any and all queries to fail.
! 712: * So, just return OK if none are configured and don't actually make
! 713: * any changes to c-ares. This lets c-ares use it's defaults, which
! 714: * it gets from the OS (for instance from /etc/resolv.conf on Linux).
! 715: */
! 716: if(!(servers && servers[0]))
! 717: return CURLE_OK;
! 718:
! 719: #if (ARES_VERSION >= 0x010704)
! 720: #if (ARES_VERSION >= 0x010b00)
! 721: ares_result = ares_set_servers_ports_csv(data->state.resolver, servers);
! 722: #else
! 723: ares_result = ares_set_servers_csv(data->state.resolver, servers);
! 724: #endif
! 725: switch(ares_result) {
! 726: case ARES_SUCCESS:
! 727: result = CURLE_OK;
! 728: break;
! 729: case ARES_ENOMEM:
! 730: result = CURLE_OUT_OF_MEMORY;
! 731: break;
! 732: case ARES_ENOTINITIALIZED:
! 733: case ARES_ENODATA:
! 734: case ARES_EBADSTR:
! 735: default:
! 736: result = CURLE_BAD_FUNCTION_ARGUMENT;
! 737: break;
! 738: }
! 739: #else /* too old c-ares version! */
! 740: (void)data;
! 741: (void)(ares_result);
! 742: #endif
! 743: return result;
! 744: }
! 745:
! 746: CURLcode Curl_set_dns_interface(struct Curl_easy *data,
! 747: const char *interf)
! 748: {
! 749: #if (ARES_VERSION >= 0x010704)
! 750: if(!interf)
! 751: interf = "";
! 752:
! 753: ares_set_local_dev((ares_channel)data->state.resolver, interf);
! 754:
! 755: return CURLE_OK;
! 756: #else /* c-ares version too old! */
! 757: (void)data;
! 758: (void)interf;
! 759: return CURLE_NOT_BUILT_IN;
! 760: #endif
! 761: }
! 762:
! 763: CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
! 764: const char *local_ip4)
! 765: {
! 766: #if (ARES_VERSION >= 0x010704)
! 767: struct in_addr a4;
! 768:
! 769: if((!local_ip4) || (local_ip4[0] == 0)) {
! 770: a4.s_addr = 0; /* disabled: do not bind to a specific address */
! 771: }
! 772: else {
! 773: if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
! 774: return CURLE_BAD_FUNCTION_ARGUMENT;
! 775: }
! 776: }
! 777:
! 778: ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr));
! 779:
! 780: return CURLE_OK;
! 781: #else /* c-ares version too old! */
! 782: (void)data;
! 783: (void)local_ip4;
! 784: return CURLE_NOT_BUILT_IN;
! 785: #endif
! 786: }
! 787:
! 788: CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
! 789: const char *local_ip6)
! 790: {
! 791: #if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6)
! 792: unsigned char a6[INET6_ADDRSTRLEN];
! 793:
! 794: if((!local_ip6) || (local_ip6[0] == 0)) {
! 795: /* disabled: do not bind to a specific address */
! 796: memset(a6, 0, sizeof(a6));
! 797: }
! 798: else {
! 799: if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
! 800: return CURLE_BAD_FUNCTION_ARGUMENT;
! 801: }
! 802: }
! 803:
! 804: ares_set_local_ip6((ares_channel)data->state.resolver, a6);
! 805:
! 806: return CURLE_OK;
! 807: #else /* c-ares version too old! */
! 808: (void)data;
! 809: (void)local_ip6;
! 810: return CURLE_NOT_BUILT_IN;
! 811: #endif
! 812: }
! 813: #endif /* CURLRES_ARES */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>