Annotation of embedaddon/curl/lib/asyn-ares.c, revision 1.1.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>