quickjs-tart

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

ares_gethostbyname.c (10887B)


      1 /* MIT License
      2  *
      3  * Copyright (c) 1998, 2011, 2013 Massachusetts Institute of Technology
      4  * Copyright (c) The c-ares project and its contributors
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a copy
      7  * of this software and associated documentation files (the "Software"), to deal
      8  * in the Software without restriction, including without limitation the rights
      9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10  * copies of the Software, and to permit persons to whom the Software is
     11  * furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice (including the next
     14  * paragraph) shall be included in all copies or substantial portions of the
     15  * Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     23  * SOFTWARE.
     24  *
     25  * SPDX-License-Identifier: MIT
     26  */
     27 
     28 #include "ares_private.h"
     29 
     30 #ifdef HAVE_NETINET_IN_H
     31 #  include <netinet/in.h>
     32 #endif
     33 #ifdef HAVE_NETDB_H
     34 #  include <netdb.h>
     35 #endif
     36 #ifdef HAVE_ARPA_INET_H
     37 #  include <arpa/inet.h>
     38 #endif
     39 
     40 #include "ares_nameser.h"
     41 
     42 #ifdef HAVE_STRINGS_H
     43 #  include <strings.h>
     44 #endif
     45 
     46 #include "ares_inet_net_pton.h"
     47 
     48 static void   sort_addresses(const struct hostent  *host,
     49                              const struct apattern *sortlist, size_t nsort);
     50 static void   sort6_addresses(const struct hostent  *host,
     51                               const struct apattern *sortlist, size_t nsort);
     52 static size_t get_address_index(const struct in_addr  *addr,
     53                                 const struct apattern *sortlist, size_t nsort);
     54 static size_t get6_address_index(const struct ares_in6_addr *addr,
     55                                  const struct apattern *sortlist, size_t nsort);
     56 
     57 struct host_query {
     58   ares_host_callback callback;
     59   void              *arg;
     60   ares_channel_t    *channel;
     61 };
     62 
     63 static void ares_gethostbyname_callback(void *arg, int status, int timeouts,
     64                                         struct ares_addrinfo *result)
     65 {
     66   struct hostent    *hostent  = NULL;
     67   struct host_query *ghbn_arg = arg;
     68 
     69   if (status == ARES_SUCCESS) {
     70     status = (int)ares_addrinfo2hostent(result, AF_UNSPEC, &hostent);
     71   }
     72 
     73   /* addrinfo2hostent will only return ENODATA if there are no addresses _and_
     74    * no cname/aliases.  However, gethostbyname will return ENODATA even if there
     75    * is cname/alias data */
     76   if (status == ARES_SUCCESS && hostent &&
     77       (!hostent->h_addr_list || !hostent->h_addr_list[0])) {
     78     status = ARES_ENODATA;
     79   }
     80 
     81   if (status == ARES_SUCCESS && ghbn_arg->channel->nsort && hostent) {
     82     if (hostent->h_addrtype == AF_INET6) {
     83       sort6_addresses(hostent, ghbn_arg->channel->sortlist,
     84                       ghbn_arg->channel->nsort);
     85     }
     86     if (hostent->h_addrtype == AF_INET) {
     87       sort_addresses(hostent, ghbn_arg->channel->sortlist,
     88                      ghbn_arg->channel->nsort);
     89     }
     90   }
     91 
     92   ghbn_arg->callback(ghbn_arg->arg, status, timeouts, hostent);
     93 
     94   ares_freeaddrinfo(result);
     95   ares_free(ghbn_arg);
     96   ares_free_hostent(hostent);
     97 }
     98 
     99 void ares_gethostbyname(ares_channel_t *channel, const char *name, int family,
    100                         ares_host_callback callback, void *arg)
    101 {
    102   struct ares_addrinfo_hints hints;
    103   struct host_query         *ghbn_arg;
    104 
    105   if (!callback) {
    106     return;
    107   }
    108 
    109   memset(&hints, 0, sizeof(hints));
    110   hints.ai_flags  = ARES_AI_CANONNAME;
    111   hints.ai_family = family;
    112 
    113   ghbn_arg = ares_malloc(sizeof(*ghbn_arg));
    114   if (!ghbn_arg) {
    115     callback(arg, ARES_ENOMEM, 0, NULL);
    116     return;
    117   }
    118 
    119   ghbn_arg->callback = callback;
    120   ghbn_arg->arg      = arg;
    121   ghbn_arg->channel  = channel;
    122 
    123   /* NOTE: ares_getaddrinfo() locks the channel, we don't use the channel
    124    *       outside so no need to lock */
    125   ares_getaddrinfo(channel, name, NULL, &hints, ares_gethostbyname_callback,
    126                    ghbn_arg);
    127 }
    128 
    129 static void sort_addresses(const struct hostent  *host,
    130                            const struct apattern *sortlist, size_t nsort)
    131 {
    132   struct in_addr a1;
    133   struct in_addr a2;
    134   int            i1;
    135   int            i2;
    136   size_t         ind1;
    137   size_t         ind2;
    138 
    139   /* This is a simple insertion sort, not optimized at all.  i1 walks
    140    * through the address list, with the loop invariant that everything
    141    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
    142    * back through the list (via i2) until it is in sorted order.
    143    */
    144   for (i1 = 0; host->h_addr_list[i1]; i1++) {
    145     memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr));
    146     ind1 = get_address_index(&a1, sortlist, nsort);
    147     for (i2 = i1 - 1; i2 >= 0; i2--) {
    148       memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr));
    149       ind2 = get_address_index(&a2, sortlist, nsort);
    150       if (ind2 <= ind1) {
    151         break;
    152       }
    153       memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr));
    154     }
    155     memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr));
    156   }
    157 }
    158 
    159 /* Find the first entry in sortlist which matches addr.  Return nsort
    160  * if none of them match.
    161  */
    162 static size_t get_address_index(const struct in_addr  *addr,
    163                                 const struct apattern *sortlist, size_t nsort)
    164 {
    165   size_t           i;
    166   struct ares_addr aaddr;
    167 
    168   memset(&aaddr, 0, sizeof(aaddr));
    169   aaddr.family = AF_INET;
    170   memcpy(&aaddr.addr.addr4, addr, 4);
    171 
    172   for (i = 0; i < nsort; i++) {
    173     if (sortlist[i].addr.family != AF_INET) {
    174       continue;
    175     }
    176 
    177     if (ares_subnet_match(&aaddr, &sortlist[i].addr, sortlist[i].mask)) {
    178       break;
    179     }
    180   }
    181 
    182   return i;
    183 }
    184 
    185 static void sort6_addresses(const struct hostent  *host,
    186                             const struct apattern *sortlist, size_t nsort)
    187 {
    188   struct ares_in6_addr a1;
    189   struct ares_in6_addr a2;
    190   int                  i1;
    191   int                  i2;
    192   size_t               ind1;
    193   size_t               ind2;
    194 
    195   /* This is a simple insertion sort, not optimized at all.  i1 walks
    196    * through the address list, with the loop invariant that everything
    197    * to the left of i1 is sorted.  In the loop body, the value at i1 is moved
    198    * back through the list (via i2) until it is in sorted order.
    199    */
    200   for (i1 = 0; host->h_addr_list[i1]; i1++) {
    201     memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr));
    202     ind1 = get6_address_index(&a1, sortlist, nsort);
    203     for (i2 = i1 - 1; i2 >= 0; i2--) {
    204       memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr));
    205       ind2 = get6_address_index(&a2, sortlist, nsort);
    206       if (ind2 <= ind1) {
    207         break;
    208       }
    209       memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr));
    210     }
    211     memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr));
    212   }
    213 }
    214 
    215 /* Find the first entry in sortlist which matches addr.  Return nsort
    216  * if none of them match.
    217  */
    218 static size_t get6_address_index(const struct ares_in6_addr *addr,
    219                                  const struct apattern *sortlist, size_t nsort)
    220 {
    221   size_t           i;
    222   struct ares_addr aaddr;
    223 
    224   memset(&aaddr, 0, sizeof(aaddr));
    225   aaddr.family = AF_INET6;
    226   memcpy(&aaddr.addr.addr6, addr, 16);
    227 
    228   for (i = 0; i < nsort; i++) {
    229     if (sortlist[i].addr.family != AF_INET6) {
    230       continue;
    231     }
    232 
    233     if (ares_subnet_match(&aaddr, &sortlist[i].addr, sortlist[i].mask)) {
    234       break;
    235     }
    236   }
    237   return i;
    238 }
    239 
    240 static ares_status_t ares_hostent_localhost(const char *name, int family,
    241                                             struct hostent **host_out)
    242 {
    243   ares_status_t              status;
    244   struct ares_addrinfo      *ai = NULL;
    245   struct ares_addrinfo_hints hints;
    246 
    247   memset(&hints, 0, sizeof(hints));
    248   hints.ai_family = family;
    249 
    250   ai = ares_malloc_zero(sizeof(*ai));
    251   if (ai == NULL) {
    252     status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    253     goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
    254   }
    255 
    256   status = ares_addrinfo_localhost(name, 0, &hints, ai);
    257   if (status != ARES_SUCCESS) {
    258     goto done; /* LCOV_EXCL_LINE: OutOfMemory */
    259   }
    260 
    261   status = ares_addrinfo2hostent(ai, family, host_out);
    262   if (status != ARES_SUCCESS) {
    263     goto done; /* LCOV_EXCL_LINE: OutOfMemory */
    264   }
    265 
    266 done:
    267   ares_freeaddrinfo(ai);
    268   return status;
    269 }
    270 
    271 /* I really have no idea why this is exposed as a public function, but since
    272  * it is, we can't kill this legacy function. */
    273 static ares_status_t ares_gethostbyname_file_int(ares_channel_t *channel,
    274                                                  const char *name, int family,
    275                                                  struct hostent **host)
    276 {
    277   const ares_hosts_entry_t *entry;
    278   ares_status_t             status;
    279 
    280   /* We only take the channel to ensure that ares_init() been called. */
    281   if (channel == NULL || name == NULL || host == NULL) {
    282     /* Anything will do, really.  This seems fine, and is consistent with
    283        other error cases. */
    284     if (host != NULL) {
    285       *host = NULL;
    286     }
    287     return ARES_ENOTFOUND;
    288   }
    289 
    290   *host  = NULL;
    291 
    292   /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
    293   if (ares_is_onion_domain(name)) {
    294     return ARES_ENOTFOUND;
    295   }
    296 
    297   status = ares_hosts_search_host(channel, ARES_FALSE, name, &entry);
    298   if (status != ARES_SUCCESS) {
    299     goto done;
    300   }
    301 
    302   status = ares_hosts_entry_to_hostent(entry, family, host);
    303   if (status != ARES_SUCCESS) {
    304     goto done; /* LCOV_EXCL_LINE: OutOfMemory */
    305   }
    306 
    307 done:
    308   /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries
    309    * SHOULD recognize localhost names as special and SHOULD always return the
    310    * IP loopback address for address queries".
    311    * We will also ignore ALL errors when trying to resolve localhost, such
    312    * as permissions errors reading /etc/hosts or a malformed /etc/hosts.
    313    *
    314    * Also, just because the query itself returned success from /etc/hosts
    315    * lookup doesn't mean it returned everything it needed to for all requested
    316    * address families. As long as we're not on a critical out of memory
    317    * condition pass it through to fill in any other address classes. */
    318   if (status != ARES_ENOMEM && ares_is_localhost(name)) {
    319     return ares_hostent_localhost(name, family, host);
    320   }
    321 
    322   return status;
    323 }
    324 
    325 int ares_gethostbyname_file(ares_channel_t *channel, const char *name,
    326                             int family, struct hostent **host)
    327 {
    328   ares_status_t status;
    329   if (channel == NULL) {
    330     return ARES_ENOTFOUND;
    331   }
    332 
    333   ares_channel_lock(channel);
    334   status = ares_gethostbyname_file_int(channel, name, family, host);
    335   ares_channel_unlock(channel);
    336   return (int)status;
    337 }