File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / asyn-ares.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:15 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    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>