quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

ares_getaddrinfo.c (22294B)


      1 /* MIT License
      2  *
      3  * Copyright (c) 1998, 2011, 2013 Massachusetts Institute of Technology
      4  * Copyright (c) 2017 Christian Ammer
      5  * Copyright (c) 2019 Andrew Selivanov
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a copy
      8  * of this software and associated documentation files (the "Software"), to deal
      9  * in the Software without restriction, including without limitation the rights
     10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11  * copies of the Software, and to permit persons to whom the Software is
     12  * furnished to do so, subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the next
     15  * paragraph) shall be included in all copies or substantial portions of the
     16  * Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     24  * SOFTWARE.
     25  *
     26  * SPDX-License-Identifier: MIT
     27  */
     28 
     29 #include "ares_private.h"
     30 
     31 #ifdef HAVE_GETSERVBYNAME_R
     32 #  if !defined(GETSERVBYNAME_R_ARGS) || (GETSERVBYNAME_R_ARGS < 4) || \
     33     (GETSERVBYNAME_R_ARGS > 6)
     34 #    error "you MUST specify a valid number of arguments for getservbyname_r"
     35 #  endif
     36 #endif
     37 
     38 #ifdef HAVE_NETINET_IN_H
     39 #  include <netinet/in.h>
     40 #endif
     41 #ifdef HAVE_NETDB_H
     42 #  include <netdb.h>
     43 #endif
     44 #ifdef HAVE_ARPA_INET_H
     45 #  include <arpa/inet.h>
     46 #endif
     47 
     48 #include "ares_nameser.h"
     49 
     50 #ifdef HAVE_STRINGS_H
     51 #  include <strings.h>
     52 #endif
     53 #include <assert.h>
     54 
     55 #ifdef HAVE_LIMITS_H
     56 #  include <limits.h>
     57 #endif
     58 
     59 #include "ares_dns.h"
     60 
     61 struct host_query {
     62   ares_channel_t            *channel;
     63   char                      *name;
     64   unsigned short             port; /* in host order */
     65   ares_addrinfo_callback     callback;
     66   void                      *arg;
     67   struct ares_addrinfo_hints hints;
     68   int    sent_family; /* this family is what was is being used */
     69   size_t timeouts;    /* number of timeouts we saw for this request */
     70   char  *lookups; /* Duplicate memory from channel because of ares_reinit() */
     71   const char *remaining_lookups; /* types of lookup we need to perform ("fb" by
     72                                     default, file and dns respectively) */
     73 
     74   /* Search order for names */
     75   char      **names;
     76   size_t      names_cnt;
     77   size_t      next_name_idx;       /* next name index being attempted */
     78 
     79   struct ares_addrinfo *ai;        /* store results between lookups */
     80   unsigned short        qid_a;     /* qid for A request */
     81   unsigned short        qid_aaaa;  /* qid for AAAA request */
     82 
     83   size_t                remaining; /* number of DNS answers waiting for */
     84 
     85   /* Track nodata responses to possibly override final result */
     86   size_t                nodata_cnt;
     87 };
     88 
     89 static const struct ares_addrinfo_hints default_hints = {
     90   0,         /* ai_flags */
     91   AF_UNSPEC, /* ai_family */
     92   0,         /* ai_socktype */
     93   0,         /* ai_protocol */
     94 };
     95 
     96 /* forward declarations */
     97 static ares_bool_t next_dns_lookup(struct host_query *hquery);
     98 
     99 struct ares_addrinfo_cname *
    100   ares_append_addrinfo_cname(struct ares_addrinfo_cname **head)
    101 {
    102   struct ares_addrinfo_cname *tail = ares_malloc_zero(sizeof(*tail));
    103   struct ares_addrinfo_cname *last = *head;
    104 
    105   if (tail == NULL) {
    106     return NULL; /* LCOV_EXCL_LINE: OutOfMemory */
    107   }
    108 
    109   if (!last) {
    110     *head = tail;
    111     return tail;
    112   }
    113 
    114   while (last->next) {
    115     last = last->next;
    116   }
    117 
    118   last->next = tail;
    119   return tail;
    120 }
    121 
    122 void ares_addrinfo_cat_cnames(struct ares_addrinfo_cname **head,
    123                               struct ares_addrinfo_cname  *tail)
    124 {
    125   struct ares_addrinfo_cname *last = *head;
    126   if (!last) {
    127     *head = tail;
    128     return;
    129   }
    130 
    131   while (last->next) {
    132     last = last->next;
    133   }
    134 
    135   last->next = tail;
    136 }
    137 
    138 /* Allocate new addrinfo and append to the tail. */
    139 struct ares_addrinfo_node *
    140   ares_append_addrinfo_node(struct ares_addrinfo_node **head)
    141 {
    142   struct ares_addrinfo_node *tail = ares_malloc_zero(sizeof(*tail));
    143   struct ares_addrinfo_node *last = *head;
    144 
    145   if (tail == NULL) {
    146     return NULL; /* LCOV_EXCL_LINE: OutOfMemory */
    147   }
    148 
    149   if (!last) {
    150     *head = tail;
    151     return tail;
    152   }
    153 
    154   while (last->ai_next) {
    155     last = last->ai_next;
    156   }
    157 
    158   last->ai_next = tail;
    159   return tail;
    160 }
    161 
    162 void ares_addrinfo_cat_nodes(struct ares_addrinfo_node **head,
    163                              struct ares_addrinfo_node  *tail)
    164 {
    165   struct ares_addrinfo_node *last = *head;
    166   if (!last) {
    167     *head = tail;
    168     return;
    169   }
    170 
    171   while (last->ai_next) {
    172     last = last->ai_next;
    173   }
    174 
    175   last->ai_next = tail;
    176 }
    177 
    178 /* Resolve service name into port number given in host byte order.
    179  * If not resolved, return 0.
    180  */
    181 static unsigned short lookup_service(const char *service, int flags)
    182 {
    183   const char     *proto;
    184   struct servent *sep;
    185 #ifdef HAVE_GETSERVBYNAME_R
    186   struct servent se;
    187   char           tmpbuf[4096];
    188 #endif
    189 
    190   if (service) {
    191     if (flags & ARES_NI_UDP) {
    192       proto = "udp";
    193     } else if (flags & ARES_NI_SCTP) {
    194       proto = "sctp";
    195     } else if (flags & ARES_NI_DCCP) {
    196       proto = "dccp";
    197     } else {
    198       proto = "tcp";
    199     }
    200 #ifdef HAVE_GETSERVBYNAME_R
    201     memset(&se, 0, sizeof(se));
    202     sep = &se;
    203     memset(tmpbuf, 0, sizeof(tmpbuf));
    204 #  if GETSERVBYNAME_R_ARGS == 6
    205     if (getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf),
    206                         &sep) != 0) {
    207       sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */
    208     }
    209 #  elif GETSERVBYNAME_R_ARGS == 5
    210     sep = getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf));
    211 #  elif GETSERVBYNAME_R_ARGS == 4
    212     if (getservbyname_r(service, proto, &se, (void *)tmpbuf) != 0) {
    213       sep = NULL;
    214     }
    215 #  else
    216     /* Lets just hope the OS uses TLS! */
    217     sep = getservbyname(service, proto);
    218 #  endif
    219 #else
    220     /* Lets just hope the OS uses TLS! */
    221 #  if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
    222     sep = getservbyname(service, (char *)proto);
    223 #  else
    224     sep = getservbyname(service, proto);
    225 #  endif
    226 #endif
    227     return (sep ? ntohs((unsigned short)sep->s_port) : 0);
    228   }
    229   return 0;
    230 }
    231 
    232 /* If the name looks like an IP address or an error occurred,
    233  * fake up a host entry, end the query immediately, and return true.
    234  * Otherwise return false.
    235  */
    236 static ares_bool_t fake_addrinfo(const char *name, unsigned short port,
    237                                  const struct ares_addrinfo_hints *hints,
    238                                  struct ares_addrinfo             *ai,
    239                                  ares_addrinfo_callback callback, void *arg)
    240 {
    241   struct ares_addrinfo_cname *cname;
    242   ares_status_t               status = ARES_SUCCESS;
    243   ares_bool_t                 result = ARES_FALSE;
    244   int                         family = hints->ai_family;
    245   if (family == AF_INET || family == AF_INET6 || family == AF_UNSPEC) {
    246     /* It only looks like an IP address if it's all numbers and dots. */
    247     size_t      numdots = 0;
    248     ares_bool_t valid   = ARES_TRUE;
    249     const char *p;
    250     for (p = name; *p; p++) {
    251       if (!ares_isdigit(*p) && *p != '.') {
    252         valid = ARES_FALSE;
    253         break;
    254       } else if (*p == '.') {
    255         numdots++;
    256       }
    257     }
    258 
    259     /* if we don't have 3 dots, it is illegal
    260      * (although inet_pton doesn't think so).
    261      */
    262     if (numdots != 3 || !valid) {
    263       result = ARES_FALSE;
    264     } else {
    265       struct in_addr addr4;
    266       result =
    267         ares_inet_pton(AF_INET, name, &addr4) < 1 ? ARES_FALSE : ARES_TRUE;
    268       if (result) {
    269         status = ares_append_ai_node(AF_INET, port, 0, &addr4, &ai->nodes);
    270         if (status != ARES_SUCCESS) {
    271           callback(arg, (int)status, 0, NULL); /* LCOV_EXCL_LINE: OutOfMemory */
    272           return ARES_TRUE;                    /* LCOV_EXCL_LINE: OutOfMemory */
    273         }
    274       }
    275     }
    276   }
    277 
    278   if (!result && (family == AF_INET6 || family == AF_UNSPEC)) {
    279     struct ares_in6_addr addr6;
    280     result =
    281       ares_inet_pton(AF_INET6, name, &addr6) < 1 ? ARES_FALSE : ARES_TRUE;
    282     if (result) {
    283       status = ares_append_ai_node(AF_INET6, port, 0, &addr6, &ai->nodes);
    284       if (status != ARES_SUCCESS) {
    285         callback(arg, (int)status, 0, NULL); /* LCOV_EXCL_LINE: OutOfMemory */
    286         return ARES_TRUE;                    /* LCOV_EXCL_LINE: OutOfMemory */
    287       }
    288     }
    289   }
    290 
    291   if (!result) {
    292     return ARES_FALSE;
    293   }
    294 
    295   if (hints->ai_flags & ARES_AI_CANONNAME) {
    296     cname = ares_append_addrinfo_cname(&ai->cnames);
    297     if (!cname) {
    298       /* LCOV_EXCL_START: OutOfMemory */
    299       ares_freeaddrinfo(ai);
    300       callback(arg, ARES_ENOMEM, 0, NULL);
    301       return ARES_TRUE;
    302       /* LCOV_EXCL_STOP */
    303     }
    304 
    305     /* Duplicate the name, to avoid a constness violation. */
    306     cname->name = ares_strdup(name);
    307     if (!cname->name) {
    308       ares_freeaddrinfo(ai);
    309       callback(arg, ARES_ENOMEM, 0, NULL);
    310       return ARES_TRUE;
    311     }
    312   }
    313 
    314   ai->nodes->ai_socktype = hints->ai_socktype;
    315   ai->nodes->ai_protocol = hints->ai_protocol;
    316 
    317   callback(arg, ARES_SUCCESS, 0, ai);
    318   return ARES_TRUE;
    319 }
    320 
    321 static void hquery_free(struct host_query *hquery, ares_bool_t cleanup_ai)
    322 {
    323   if (cleanup_ai) {
    324     ares_freeaddrinfo(hquery->ai);
    325   }
    326   ares_strsplit_free(hquery->names, hquery->names_cnt);
    327   ares_free(hquery->name);
    328   ares_free(hquery->lookups);
    329   ares_free(hquery);
    330 }
    331 
    332 static void end_hquery(struct host_query *hquery, ares_status_t status)
    333 {
    334   struct ares_addrinfo_node  sentinel;
    335   struct ares_addrinfo_node *next;
    336 
    337   if (status == ARES_SUCCESS) {
    338     if (!(hquery->hints.ai_flags & ARES_AI_NOSORT) && hquery->ai->nodes) {
    339       sentinel.ai_next = hquery->ai->nodes;
    340       ares_sortaddrinfo(hquery->channel, &sentinel);
    341       hquery->ai->nodes = sentinel.ai_next;
    342     }
    343     next = hquery->ai->nodes;
    344 
    345     while (next) {
    346       next->ai_socktype = hquery->hints.ai_socktype;
    347       next->ai_protocol = hquery->hints.ai_protocol;
    348       next              = next->ai_next;
    349     }
    350   } else {
    351     /* Clean up what we have collected by so far. */
    352     ares_freeaddrinfo(hquery->ai);
    353     hquery->ai = NULL;
    354   }
    355 
    356   hquery->callback(hquery->arg, (int)status, (int)hquery->timeouts, hquery->ai);
    357   hquery_free(hquery, ARES_FALSE);
    358 }
    359 
    360 ares_bool_t ares_is_localhost(const char *name)
    361 {
    362   /* RFC6761 6.3 says : The domain "localhost." and any names falling within
    363    * ".localhost." */
    364   size_t len;
    365 
    366   if (name == NULL) {
    367     return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */
    368   }
    369 
    370   if (ares_strcaseeq(name, "localhost")) {
    371     return ARES_TRUE;
    372   }
    373 
    374   len = ares_strlen(name);
    375   if (len < 10 /* strlen(".localhost") */) {
    376     return ARES_FALSE;
    377   }
    378 
    379   if (ares_strcaseeq(name + (len - 10 /* strlen(".localhost") */),
    380                      ".localhost")) {
    381     return ARES_TRUE;
    382   }
    383 
    384   return ARES_FALSE;
    385 }
    386 
    387 static ares_status_t file_lookup(struct host_query *hquery)
    388 {
    389   const ares_hosts_entry_t *entry;
    390   ares_status_t             status;
    391 
    392   /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
    393   if (ares_is_onion_domain(hquery->name)) {
    394     return ARES_ENOTFOUND;
    395   }
    396 
    397   status = ares_hosts_search_host(
    398     hquery->channel,
    399     (hquery->hints.ai_flags & ARES_AI_ENVHOSTS) ? ARES_TRUE : ARES_FALSE,
    400     hquery->name, &entry);
    401 
    402   if (status != ARES_SUCCESS) {
    403     goto done;
    404   }
    405 
    406   status = ares_hosts_entry_to_addrinfo(
    407     entry, hquery->name, hquery->hints.ai_family, hquery->port,
    408     (hquery->hints.ai_flags & ARES_AI_CANONNAME) ? ARES_TRUE : ARES_FALSE,
    409     hquery->ai);
    410 
    411   if (status != ARES_SUCCESS) {
    412     goto done; /* LCOV_EXCL_LINE: OutOfMemory */
    413   }
    414 
    415 
    416 done:
    417   /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries
    418    * SHOULD recognize localhost names as special and SHOULD always return the
    419    * IP loopback address for address queries".
    420    * We will also ignore ALL errors when trying to resolve localhost, such
    421    * as permissions errors reading /etc/hosts or a malformed /etc/hosts.
    422    *
    423    * Also, just because the query itself returned success from /etc/hosts
    424    * lookup doesn't mean it returned everything it needed to for all requested
    425    * address families. As long as we're not on a critical out of memory
    426    * condition pass it through to fill in any other address classes. */
    427   if (status != ARES_ENOMEM && ares_is_localhost(hquery->name)) {
    428     return ares_addrinfo_localhost(hquery->name, hquery->port, &hquery->hints,
    429                                    hquery->ai);
    430   }
    431 
    432   return status;
    433 }
    434 
    435 static void next_lookup(struct host_query *hquery, ares_status_t status)
    436 {
    437   switch (*hquery->remaining_lookups) {
    438     case 'b':
    439       /* RFC6761 section 6.3 #3 says "Name resolution APIs SHOULD NOT send
    440        * queries for localhost names to their configured caching DNS
    441        * server(s)."
    442        * Otherwise, DNS lookup. */
    443       if (!ares_is_localhost(hquery->name) && next_dns_lookup(hquery)) {
    444         break;
    445       }
    446 
    447       hquery->remaining_lookups++;
    448       next_lookup(hquery, status);
    449       break;
    450 
    451     case 'f':
    452       /* Host file lookup */
    453       if (file_lookup(hquery) == ARES_SUCCESS) {
    454         end_hquery(hquery, ARES_SUCCESS);
    455         break;
    456       }
    457       hquery->remaining_lookups++;
    458       next_lookup(hquery, status);
    459       break;
    460     default:
    461       /* No lookup left */
    462       end_hquery(hquery, status);
    463       break;
    464   }
    465 }
    466 
    467 static void terminate_retries(const struct host_query *hquery,
    468                               unsigned short           qid)
    469 {
    470   unsigned short term_qid =
    471     (qid == hquery->qid_a) ? hquery->qid_aaaa : hquery->qid_a;
    472   const ares_channel_t *channel = hquery->channel;
    473   ares_query_t         *query   = NULL;
    474 
    475   /* No other outstanding queries, nothing to do */
    476   if (!hquery->remaining) {
    477     return;
    478   }
    479 
    480   query = ares_htable_szvp_get_direct(channel->queries_by_qid, term_qid);
    481   if (query == NULL) {
    482     return;
    483   }
    484 
    485   query->no_retries = ARES_TRUE;
    486 }
    487 
    488 static ares_bool_t ai_has_ipv4(struct ares_addrinfo *ai)
    489 {
    490   struct ares_addrinfo_node *node;
    491 
    492   for (node = ai->nodes; node != NULL; node = node->ai_next) {
    493     if (node->ai_family == AF_INET) {
    494       return ARES_TRUE;
    495     }
    496   }
    497   return ARES_FALSE;
    498 }
    499 
    500 static void host_callback(void *arg, ares_status_t status, size_t timeouts,
    501                           const ares_dns_record_t *dnsrec)
    502 {
    503   struct host_query *hquery         = (struct host_query *)arg;
    504   ares_status_t      addinfostatus  = ARES_SUCCESS;
    505   hquery->timeouts                 += timeouts;
    506   hquery->remaining--;
    507 
    508   if (status == ARES_SUCCESS) {
    509     if (dnsrec == NULL) {
    510       addinfostatus = ARES_EBADRESP; /* LCOV_EXCL_LINE: DefensiveCoding */
    511     } else {
    512       addinfostatus =
    513         ares_parse_into_addrinfo(dnsrec, ARES_TRUE, hquery->port, hquery->ai);
    514     }
    515 
    516     /* We sent out ipv4 and ipv6 requests simultaneously.  If we got a
    517      * successful ipv4 response, we want to go ahead and tell the ipv6 request
    518      * that if it fails or times out to not try again since we have the data
    519      * we need.
    520      *
    521      * Our initial implementation of this would terminate retries if we got any
    522      * successful response (ipv4 _or_ ipv6).  But we did get some user-reported
    523      * issues with this that had bad system configs and odd behavior:
    524      *  https://github.com/alpinelinux/docker-alpine/issues/366
    525      *
    526      * Essentially the ipv6 query succeeded but the ipv4 query failed or timed
    527      * out, and so we only returned the ipv6 address, but the host couldn't
    528      * use ipv6.  If we continued to allow ipv4 retries it would have found a
    529      * server that worked and returned both address classes (this is clearly
    530      * unexpected behavior).
    531      *
    532      * At some point down the road if ipv6 actually becomes required and
    533      * reliable we can drop this ipv4 check.
    534      */
    535     if (addinfostatus == ARES_SUCCESS && ai_has_ipv4(hquery->ai)) {
    536       terminate_retries(hquery, ares_dns_record_get_id(dnsrec));
    537     }
    538   }
    539 
    540   if (!hquery->remaining) {
    541     if (status == ARES_EDESTRUCTION || status == ARES_ECANCELLED) {
    542       /* must make sure we don't do next_lookup() on destroy or cancel,
    543        * and return the appropriate status.  We won't return a partial
    544        * result in this case. */
    545       end_hquery(hquery, status);
    546     } else if (addinfostatus != ARES_SUCCESS && addinfostatus != ARES_ENODATA) {
    547       /* error in parsing result e.g. no memory */
    548       if (addinfostatus == ARES_EBADRESP && hquery->ai->nodes) {
    549         /* We got a bad response from server, but at least one query
    550          * ended with ARES_SUCCESS */
    551         end_hquery(hquery, ARES_SUCCESS);
    552       } else {
    553         end_hquery(hquery, addinfostatus);
    554       }
    555     } else if (hquery->ai->nodes) {
    556       /* at least one query ended with ARES_SUCCESS */
    557       end_hquery(hquery, ARES_SUCCESS);
    558     } else if (status == ARES_ENOTFOUND || status == ARES_ENODATA ||
    559                addinfostatus == ARES_ENODATA) {
    560       if (status == ARES_ENODATA || addinfostatus == ARES_ENODATA) {
    561         hquery->nodata_cnt++;
    562       }
    563       next_lookup(hquery, hquery->nodata_cnt ? ARES_ENODATA : status);
    564     } else if ((status == ARES_ESERVFAIL || status == ARES_EREFUSED) &&
    565                ares_name_label_cnt(hquery->names[hquery->next_name_idx - 1]) ==
    566                  1) {
    567       /* Issue #852, systemd-resolved may return SERVFAIL or REFUSED on a
    568        * single label domain name. */
    569       next_lookup(hquery, hquery->nodata_cnt ? ARES_ENODATA : status);
    570     } else {
    571       end_hquery(hquery, status);
    572     }
    573   }
    574 
    575   /* at this point we keep on waiting for the next query to finish */
    576 }
    577 
    578 static void ares_getaddrinfo_int(ares_channel_t *channel, const char *name,
    579                                  const char                       *service,
    580                                  const struct ares_addrinfo_hints *hints,
    581                                  ares_addrinfo_callback callback, void *arg)
    582 {
    583   struct host_query    *hquery;
    584   unsigned short        port = 0;
    585   int                   family;
    586   struct ares_addrinfo *ai;
    587   ares_status_t         status;
    588 
    589   if (!hints) {
    590     hints = &default_hints;
    591   }
    592 
    593   family = hints->ai_family;
    594 
    595   /* Right now we only know how to look up Internet addresses
    596      and unspec means try both basically. */
    597   if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC) {
    598     callback(arg, ARES_ENOTIMP, 0, NULL);
    599     return;
    600   }
    601 
    602   if (ares_is_onion_domain(name)) {
    603     callback(arg, ARES_ENOTFOUND, 0, NULL);
    604     return;
    605   }
    606 
    607   if (service) {
    608     if (hints->ai_flags & ARES_AI_NUMERICSERV) {
    609       unsigned long val;
    610       errno = 0;
    611       val   = strtoul(service, NULL, 0);
    612       if ((val == 0 && errno != 0) || val > 65535) {
    613         callback(arg, ARES_ESERVICE, 0, NULL);
    614         return;
    615       }
    616       port = (unsigned short)val;
    617     } else {
    618       port = lookup_service(service, 0);
    619       if (!port) {
    620         unsigned long val;
    621         errno = 0;
    622         val   = strtoul(service, NULL, 0);
    623         if ((val == 0 && errno != 0) || val > 65535) {
    624           callback(arg, ARES_ESERVICE, 0, NULL);
    625           return;
    626         }
    627         port = (unsigned short)val;
    628       }
    629     }
    630   }
    631 
    632   ai = ares_malloc_zero(sizeof(*ai));
    633   if (!ai) {
    634     callback(arg, ARES_ENOMEM, 0, NULL);
    635     return;
    636   }
    637 
    638   if (fake_addrinfo(name, port, hints, ai, callback, arg)) {
    639     return;
    640   }
    641 
    642   /* Allocate and fill in the host query structure. */
    643   hquery = ares_malloc_zero(sizeof(*hquery));
    644   if (!hquery) {
    645     ares_freeaddrinfo(ai);
    646     callback(arg, ARES_ENOMEM, 0, NULL);
    647     return;
    648   }
    649 
    650   hquery->port        = port;
    651   hquery->channel     = channel;
    652   hquery->hints       = *hints;
    653   hquery->sent_family = -1; /* nothing is sent yet */
    654   hquery->callback    = callback;
    655   hquery->arg         = arg;
    656   hquery->ai          = ai;
    657   hquery->name        = ares_strdup(name);
    658   if (hquery->name == NULL) {
    659     hquery_free(hquery, ARES_TRUE);
    660     callback(arg, ARES_ENOMEM, 0, NULL);
    661     return;
    662   }
    663 
    664   status =
    665     ares_search_name_list(channel, name, &hquery->names, &hquery->names_cnt);
    666   if (status != ARES_SUCCESS) {
    667     hquery_free(hquery, ARES_TRUE);
    668     callback(arg, (int)status, 0, NULL);
    669     return;
    670   }
    671   hquery->next_name_idx = 0;
    672 
    673 
    674   hquery->lookups = ares_strdup(channel->lookups);
    675   if (hquery->lookups == NULL) {
    676     hquery_free(hquery, ARES_TRUE);
    677     callback(arg, ARES_ENOMEM, 0, NULL);
    678     return;
    679   }
    680   hquery->remaining_lookups = hquery->lookups;
    681 
    682   /* Start performing lookups according to channel->lookups. */
    683   next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */);
    684 }
    685 
    686 void ares_getaddrinfo(ares_channel_t *channel, const char *name,
    687                       const char                       *service,
    688                       const struct ares_addrinfo_hints *hints,
    689                       ares_addrinfo_callback callback, void *arg)
    690 {
    691   if (channel == NULL) {
    692     return;
    693   }
    694   ares_channel_lock(channel);
    695   ares_getaddrinfo_int(channel, name, service, hints, callback, arg);
    696   ares_channel_unlock(channel);
    697 }
    698 
    699 static ares_bool_t next_dns_lookup(struct host_query *hquery)
    700 {
    701   const char *name = NULL;
    702 
    703   if (hquery->next_name_idx >= hquery->names_cnt) {
    704     return ARES_FALSE;
    705   }
    706 
    707   name = hquery->names[hquery->next_name_idx++];
    708 
    709   /* NOTE: hquery may be invalidated during the call to ares_query_qid(),
    710    *       so should not be referenced after this point */
    711   switch (hquery->hints.ai_family) {
    712     case AF_INET:
    713       hquery->remaining += 1;
    714       ares_query_nolock(hquery->channel, name, ARES_CLASS_IN, ARES_REC_TYPE_A,
    715                         host_callback, hquery, &hquery->qid_a);
    716       break;
    717     case AF_INET6:
    718       hquery->remaining += 1;
    719       ares_query_nolock(hquery->channel, name, ARES_CLASS_IN,
    720                         ARES_REC_TYPE_AAAA, host_callback, hquery,
    721                         &hquery->qid_aaaa);
    722       break;
    723     case AF_UNSPEC:
    724       hquery->remaining += 2;
    725       ares_query_nolock(hquery->channel, name, ARES_CLASS_IN, ARES_REC_TYPE_A,
    726                         host_callback, hquery, &hquery->qid_a);
    727       ares_query_nolock(hquery->channel, name, ARES_CLASS_IN,
    728                         ARES_REC_TYPE_AAAA, host_callback, hquery,
    729                         &hquery->qid_aaaa);
    730       break;
    731     default:
    732       break;
    733   }
    734 
    735   return ARES_TRUE;
    736 }