quickjs-tart

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

ares_hosts_file.c (25888B)


      1 /* MIT License
      2  *
      3  * Copyright (c) 2023 Brad House
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining a copy
      6  * of this software and associated documentation files (the "Software"), to deal
      7  * in the Software without restriction, including without limitation the rights
      8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      9  * copies of the Software, and to permit persons to whom the Software is
     10  * furnished to do so, subject to the following conditions:
     11  *
     12  * The above copyright notice and this permission notice (including the next
     13  * paragraph) shall be included in all copies or substantial portions of the
     14  * Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     22  * SOFTWARE.
     23  *
     24  * SPDX-License-Identifier: MIT
     25  */
     26 #include "ares_private.h"
     27 #ifdef HAVE_SYS_TYPES_H
     28 #  include <sys/types.h>
     29 #endif
     30 #ifdef HAVE_SYS_STAT_H
     31 #  include <sys/stat.h>
     32 #endif
     33 #ifdef HAVE_NETINET_IN_H
     34 #  include <netinet/in.h>
     35 #endif
     36 #ifdef HAVE_NETDB_H
     37 #  include <netdb.h>
     38 #endif
     39 #ifdef HAVE_ARPA_INET_H
     40 #  include <arpa/inet.h>
     41 #endif
     42 #include <time.h>
     43 
     44 /* HOSTS FILE PROCESSING OVERVIEW
     45  * ==============================
     46  * The hosts file on the system contains static entries to be processed locally
     47  * rather than querying the nameserver.  Each row is an IP address followed by
     48  * a list of space delimited hostnames that match the ip address.  This is used
     49  * for both forward and reverse lookups.
     50  *
     51  * We are caching the entire parsed hosts file for performance reasons.  Some
     52  * files may be quite sizable and as per Issue #458 can approach 1/2MB in size,
     53  * and the parse overhead on a rapid succession of queries can be quite large.
     54  * The entries are stored in forwards and backwards hashtables so we can get
     55  * O(1) performance on lookup.  The file is cached until the file modification
     56  * timestamp changes.
     57  *
     58  * The hosts file processing is quite unique. It has to merge all related hosts
     59  * and ips into a single entry due to file formatting requirements.  For
     60  * instance take the below:
     61  *
     62  * 127.0.0.1    localhost.localdomain localhost
     63  * ::1          localhost.localdomain localhost
     64  * 192.168.1.1  host.example.com host
     65  * 192.168.1.5  host.example.com host
     66  * 2620:1234::1 host.example.com host6.example.com host6 host
     67  *
     68  * This will yield 2 entries.
     69  *  1) ips: 127.0.0.1,::1
     70  *     hosts: localhost.localdomain,localhost
     71  *  2) ips: 192.168.1.1,192.168.1.5,2620:1234::1
     72  *     hosts: host.example.com,host,host6.example.com,host6
     73  *
     74  * It could be argued that if searching for 192.168.1.1 that the 'host6'
     75  * hostnames should not be returned, but this implementation will return them
     76  * since they are related.  It is unlikely this will matter in the real world.
     77  */
     78 
     79 struct ares_hosts_file {
     80   time_t               ts;
     81   /*! cache the filename so we know if the filename changes it automatically
     82    *  invalidates the cache */
     83   char                *filename;
     84   /*! iphash is the owner of the 'entry' object as there is only ever a single
     85    *  match to the object. */
     86   ares_htable_strvp_t *iphash;
     87   /*! hosthash does not own the entry so won't free on destruction */
     88   ares_htable_strvp_t *hosthash;
     89 };
     90 
     91 struct ares_hosts_entry {
     92   size_t        refcnt; /*! If the entry is stored multiple times in the
     93                          *  ip address hash, we have to reference count it */
     94   ares_llist_t *ips;
     95   ares_llist_t *hosts;
     96 };
     97 
     98 const void *ares_dns_pton(const char *ipaddr, struct ares_addr *addr,
     99                           size_t *out_len)
    100 {
    101   const void *ptr     = NULL;
    102   size_t      ptr_len = 0;
    103 
    104   if (ipaddr == NULL || addr == NULL || out_len == NULL) {
    105     return NULL; /* LCOV_EXCL_LINE: DefensiveCoding */
    106   }
    107 
    108   *out_len = 0;
    109 
    110   if (addr->family == AF_INET &&
    111       ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) {
    112     ptr     = &addr->addr.addr4;
    113     ptr_len = sizeof(addr->addr.addr4);
    114   } else if (addr->family == AF_INET6 &&
    115              ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) {
    116     ptr     = &addr->addr.addr6;
    117     ptr_len = sizeof(addr->addr.addr6);
    118   } else if (addr->family == AF_UNSPEC) {
    119     if (ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) {
    120       addr->family = AF_INET;
    121       ptr          = &addr->addr.addr4;
    122       ptr_len      = sizeof(addr->addr.addr4);
    123     } else if (ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) {
    124       addr->family = AF_INET6;
    125       ptr          = &addr->addr.addr6;
    126       ptr_len      = sizeof(addr->addr.addr6);
    127     }
    128   }
    129 
    130   *out_len = ptr_len;
    131   return ptr;
    132 }
    133 
    134 static ares_bool_t ares_normalize_ipaddr(const char *ipaddr, char *out,
    135                                          size_t out_len)
    136 {
    137   struct ares_addr data;
    138   const void      *addr;
    139   size_t           addr_len = 0;
    140 
    141   memset(&data, 0, sizeof(data));
    142   data.family = AF_UNSPEC;
    143 
    144   addr = ares_dns_pton(ipaddr, &data, &addr_len);
    145   if (addr == NULL) {
    146     return ARES_FALSE;
    147   }
    148 
    149   if (!ares_inet_ntop(data.family, addr, out, (ares_socklen_t)out_len)) {
    150     return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */
    151   }
    152 
    153   return ARES_TRUE;
    154 }
    155 
    156 static void ares_hosts_entry_destroy(ares_hosts_entry_t *entry)
    157 {
    158   if (entry == NULL) {
    159     return;
    160   }
    161 
    162   /* Honor reference counting */
    163   if (entry->refcnt != 0) {
    164     entry->refcnt--;
    165   }
    166 
    167   if (entry->refcnt > 0) {
    168     return;
    169   }
    170 
    171   ares_llist_destroy(entry->hosts);
    172   ares_llist_destroy(entry->ips);
    173   ares_free(entry);
    174 }
    175 
    176 static void ares_hosts_entry_destroy_cb(void *entry)
    177 {
    178   ares_hosts_entry_destroy(entry);
    179 }
    180 
    181 void ares_hosts_file_destroy(ares_hosts_file_t *hf)
    182 {
    183   if (hf == NULL) {
    184     return;
    185   }
    186 
    187   ares_free(hf->filename);
    188   ares_htable_strvp_destroy(hf->hosthash);
    189   ares_htable_strvp_destroy(hf->iphash);
    190   ares_free(hf);
    191 }
    192 
    193 static ares_hosts_file_t *ares_hosts_file_create(const char *filename)
    194 {
    195   ares_hosts_file_t *hf = ares_malloc_zero(sizeof(*hf));
    196   if (hf == NULL) {
    197     goto fail;
    198   }
    199 
    200   hf->ts = time(NULL);
    201 
    202   hf->filename = ares_strdup(filename);
    203   if (hf->filename == NULL) {
    204     goto fail;
    205   }
    206 
    207   hf->iphash = ares_htable_strvp_create(ares_hosts_entry_destroy_cb);
    208   if (hf->iphash == NULL) {
    209     goto fail;
    210   }
    211 
    212   hf->hosthash = ares_htable_strvp_create(NULL);
    213   if (hf->hosthash == NULL) {
    214     goto fail;
    215   }
    216 
    217   return hf;
    218 
    219 fail:
    220   ares_hosts_file_destroy(hf);
    221   return NULL;
    222 }
    223 
    224 typedef enum {
    225   ARES_MATCH_NONE   = 0,
    226   ARES_MATCH_IPADDR = 1,
    227   ARES_MATCH_HOST   = 2
    228 } ares_hosts_file_match_t;
    229 
    230 static ares_status_t ares_hosts_file_merge_entry(
    231   const ares_hosts_file_t *hf, ares_hosts_entry_t *existing,
    232   ares_hosts_entry_t *entry, ares_hosts_file_match_t matchtype)
    233 {
    234   ares_llist_node_t *node;
    235 
    236   /* If we matched on IP address, we know there can only be 1, so there's no
    237    * reason to do anything */
    238   if (matchtype != ARES_MATCH_IPADDR) {
    239     while ((node = ares_llist_node_first(entry->ips)) != NULL) {
    240       const char *ipaddr = ares_llist_node_val(node);
    241 
    242       if (ares_htable_strvp_get_direct(hf->iphash, ipaddr) != NULL) {
    243         ares_llist_node_destroy(node);
    244         continue;
    245       }
    246 
    247       ares_llist_node_mvparent_last(node, existing->ips);
    248     }
    249   }
    250 
    251 
    252   while ((node = ares_llist_node_first(entry->hosts)) != NULL) {
    253     const char *hostname = ares_llist_node_val(node);
    254 
    255     if (ares_htable_strvp_get_direct(hf->hosthash, hostname) != NULL) {
    256       ares_llist_node_destroy(node);
    257       continue;
    258     }
    259 
    260     ares_llist_node_mvparent_last(node, existing->hosts);
    261   }
    262 
    263   ares_hosts_entry_destroy(entry);
    264   return ARES_SUCCESS;
    265 }
    266 
    267 static ares_hosts_file_match_t
    268   ares_hosts_file_match(const ares_hosts_file_t *hf, ares_hosts_entry_t *entry,
    269                         ares_hosts_entry_t **match)
    270 {
    271   ares_llist_node_t *node;
    272   *match = NULL;
    273 
    274   for (node = ares_llist_node_first(entry->ips); node != NULL;
    275        node = ares_llist_node_next(node)) {
    276     const char *ipaddr = ares_llist_node_val(node);
    277     *match             = ares_htable_strvp_get_direct(hf->iphash, ipaddr);
    278     if (*match != NULL) {
    279       return ARES_MATCH_IPADDR;
    280     }
    281   }
    282 
    283   for (node = ares_llist_node_first(entry->hosts); node != NULL;
    284        node = ares_llist_node_next(node)) {
    285     const char *host = ares_llist_node_val(node);
    286     *match           = ares_htable_strvp_get_direct(hf->hosthash, host);
    287     if (*match != NULL) {
    288       return ARES_MATCH_HOST;
    289     }
    290   }
    291 
    292   return ARES_MATCH_NONE;
    293 }
    294 
    295 /*! entry is invalidated upon calling this function, always, even on error */
    296 static ares_status_t ares_hosts_file_add(ares_hosts_file_t  *hosts,
    297                                          ares_hosts_entry_t *entry)
    298 {
    299   ares_hosts_entry_t     *match  = NULL;
    300   ares_status_t           status = ARES_SUCCESS;
    301   ares_llist_node_t      *node;
    302   ares_hosts_file_match_t matchtype;
    303   size_t                  num_hostnames;
    304 
    305   /* Record the number of hostnames in this entry file.  If we merge into an
    306    * existing record, these will be *appended* to the entry, so we'll count
    307    * backwards when adding to the hosts hashtable */
    308   num_hostnames = ares_llist_len(entry->hosts);
    309 
    310   matchtype = ares_hosts_file_match(hosts, entry, &match);
    311 
    312   if (matchtype != ARES_MATCH_NONE) {
    313     status = ares_hosts_file_merge_entry(hosts, match, entry, matchtype);
    314     if (status != ARES_SUCCESS) {
    315       ares_hosts_entry_destroy(entry); /* LCOV_EXCL_LINE: DefensiveCoding */
    316       return status;                   /* LCOV_EXCL_LINE: DefensiveCoding */
    317     }
    318     /* entry was invalidated above by merging */
    319     entry = match;
    320   }
    321 
    322   if (matchtype != ARES_MATCH_IPADDR) {
    323     const char *ipaddr = ares_llist_last_val(entry->ips);
    324 
    325     if (!ares_htable_strvp_get(hosts->iphash, ipaddr, NULL)) {
    326       if (!ares_htable_strvp_insert(hosts->iphash, ipaddr, entry)) {
    327         ares_hosts_entry_destroy(entry);
    328         return ARES_ENOMEM;
    329       }
    330       entry->refcnt++;
    331     }
    332   }
    333 
    334   /* Go backwards, on a merge, hostnames are appended.  Breakout once we've
    335    * consumed all the hosts that we appended */
    336   for (node = ares_llist_node_last(entry->hosts); node != NULL;
    337        node = ares_llist_node_prev(node)) {
    338     const char *val = ares_llist_node_val(node);
    339 
    340     if (num_hostnames == 0) {
    341       break;
    342     }
    343 
    344     num_hostnames--;
    345 
    346     /* first hostname match wins.  If we detect a duplicate hostname for another
    347      * ip it will automatically be added to the same entry */
    348     if (ares_htable_strvp_get(hosts->hosthash, val, NULL)) {
    349       continue;
    350     }
    351 
    352     if (!ares_htable_strvp_insert(hosts->hosthash, val, entry)) {
    353       return ARES_ENOMEM;
    354     }
    355   }
    356 
    357   return ARES_SUCCESS;
    358 }
    359 
    360 static ares_bool_t ares_hosts_entry_isdup(ares_hosts_entry_t *entry,
    361                                           const char         *host)
    362 {
    363   ares_llist_node_t *node;
    364 
    365   for (node = ares_llist_node_first(entry->ips); node != NULL;
    366        node = ares_llist_node_next(node)) {
    367     const char *myhost = ares_llist_node_val(node);
    368     if (ares_strcaseeq(myhost, host)) {
    369       return ARES_TRUE;
    370     }
    371   }
    372 
    373   return ARES_FALSE;
    374 }
    375 
    376 static ares_status_t ares_parse_hosts_hostnames(ares_buf_t         *buf,
    377                                                 ares_hosts_entry_t *entry)
    378 {
    379   entry->hosts = ares_llist_create(ares_free);
    380   if (entry->hosts == NULL) {
    381     return ARES_ENOMEM;
    382   }
    383 
    384   /* Parse hostnames and aliases */
    385   while (ares_buf_len(buf)) {
    386     char          hostname[256];
    387     char         *temp;
    388     ares_status_t status;
    389     unsigned char comment = '#';
    390 
    391     ares_buf_consume_whitespace(buf, ARES_FALSE);
    392 
    393     if (ares_buf_len(buf) == 0) {
    394       break;
    395     }
    396 
    397     /* See if it is a comment, if so stop processing */
    398     if (ares_buf_begins_with(buf, &comment, 1)) {
    399       break;
    400     }
    401 
    402     ares_buf_tag(buf);
    403 
    404     /* Must be at end of line */
    405     if (ares_buf_consume_nonwhitespace(buf) == 0) {
    406       break;
    407     }
    408 
    409     status = ares_buf_tag_fetch_string(buf, hostname, sizeof(hostname));
    410     if (status != ARES_SUCCESS) {
    411       /* Bad entry, just ignore as long as its not the first.  If its the first,
    412        * it must be valid */
    413       if (ares_llist_len(entry->hosts) == 0) {
    414         return ARES_EBADSTR;
    415       }
    416 
    417       continue;
    418     }
    419 
    420     /* Validate it is a valid hostname characterset */
    421     if (!ares_is_hostname(hostname)) {
    422       continue;
    423     }
    424 
    425     /* Don't add a duplicate to the same entry */
    426     if (ares_hosts_entry_isdup(entry, hostname)) {
    427       continue;
    428     }
    429 
    430     /* Add to list */
    431     temp = ares_strdup(hostname);
    432     if (temp == NULL) {
    433       return ARES_ENOMEM;
    434     }
    435 
    436     if (ares_llist_insert_last(entry->hosts, temp) == NULL) {
    437       ares_free(temp);
    438       return ARES_ENOMEM;
    439     }
    440   }
    441 
    442   /* Must have at least 1 entry */
    443   if (ares_llist_len(entry->hosts) == 0) {
    444     return ARES_EBADSTR;
    445   }
    446 
    447   return ARES_SUCCESS;
    448 }
    449 
    450 static ares_status_t ares_parse_hosts_ipaddr(ares_buf_t          *buf,
    451                                              ares_hosts_entry_t **entry_out)
    452 {
    453   char                addr[INET6_ADDRSTRLEN];
    454   char               *temp;
    455   ares_hosts_entry_t *entry = NULL;
    456   ares_status_t       status;
    457 
    458   *entry_out = NULL;
    459 
    460   ares_buf_tag(buf);
    461   ares_buf_consume_nonwhitespace(buf);
    462   status = ares_buf_tag_fetch_string(buf, addr, sizeof(addr));
    463   if (status != ARES_SUCCESS) {
    464     return status;
    465   }
    466 
    467   /* Validate and normalize the ip address format */
    468   if (!ares_normalize_ipaddr(addr, addr, sizeof(addr))) {
    469     return ARES_EBADSTR;
    470   }
    471 
    472   entry = ares_malloc_zero(sizeof(*entry));
    473   if (entry == NULL) {
    474     return ARES_ENOMEM;
    475   }
    476 
    477   entry->ips = ares_llist_create(ares_free);
    478   if (entry->ips == NULL) {
    479     ares_hosts_entry_destroy(entry);
    480     return ARES_ENOMEM;
    481   }
    482 
    483   temp = ares_strdup(addr);
    484   if (temp == NULL) {
    485     ares_hosts_entry_destroy(entry);
    486     return ARES_ENOMEM;
    487   }
    488 
    489   if (ares_llist_insert_first(entry->ips, temp) == NULL) {
    490     ares_free(temp);
    491     ares_hosts_entry_destroy(entry);
    492     return ARES_ENOMEM;
    493   }
    494 
    495   *entry_out = entry;
    496 
    497   return ARES_SUCCESS;
    498 }
    499 
    500 static ares_status_t ares_parse_hosts(const char         *filename,
    501                                       ares_hosts_file_t **out)
    502 {
    503   ares_buf_t         *buf    = NULL;
    504   ares_status_t       status = ARES_EBADRESP;
    505   ares_hosts_file_t  *hf     = NULL;
    506   ares_hosts_entry_t *entry  = NULL;
    507 
    508   *out = NULL;
    509 
    510   buf = ares_buf_create();
    511   if (buf == NULL) {
    512     status = ARES_ENOMEM;
    513     goto done;
    514   }
    515 
    516   status = ares_buf_load_file(filename, buf);
    517   if (status != ARES_SUCCESS) {
    518     goto done;
    519   }
    520 
    521   hf = ares_hosts_file_create(filename);
    522   if (hf == NULL) {
    523     status = ARES_ENOMEM;
    524     goto done;
    525   }
    526 
    527   while (ares_buf_len(buf)) {
    528     unsigned char comment = '#';
    529 
    530     /* -- Start of new line here -- */
    531 
    532     /* Consume any leading whitespace */
    533     ares_buf_consume_whitespace(buf, ARES_FALSE);
    534 
    535     if (ares_buf_len(buf) == 0) {
    536       break;
    537     }
    538 
    539     /* See if it is a comment, if so, consume remaining line */
    540     if (ares_buf_begins_with(buf, &comment, 1)) {
    541       ares_buf_consume_line(buf, ARES_TRUE);
    542       continue;
    543     }
    544 
    545     /* Pull off ip address */
    546     status = ares_parse_hosts_ipaddr(buf, &entry);
    547     if (status == ARES_ENOMEM) {
    548       goto done;
    549     }
    550     if (status != ARES_SUCCESS) {
    551       /* Bad line, consume and go onto next */
    552       ares_buf_consume_line(buf, ARES_TRUE);
    553       continue;
    554     }
    555 
    556     /* Parse of the hostnames */
    557     status = ares_parse_hosts_hostnames(buf, entry);
    558     if (status == ARES_ENOMEM) {
    559       goto done;
    560     } else if (status != ARES_SUCCESS) {
    561       /* Bad line, consume and go onto next */
    562       ares_hosts_entry_destroy(entry);
    563       entry = NULL;
    564       ares_buf_consume_line(buf, ARES_TRUE);
    565       continue;
    566     }
    567 
    568     /* Append the successful entry to the hosts file */
    569     status = ares_hosts_file_add(hf, entry);
    570     entry  = NULL; /* is always invalidated by this function, even on error */
    571     if (status != ARES_SUCCESS) {
    572       goto done;
    573     }
    574 
    575     /* Go to next line */
    576     ares_buf_consume_line(buf, ARES_TRUE);
    577   }
    578 
    579   status = ARES_SUCCESS;
    580 
    581 done:
    582   ares_hosts_entry_destroy(entry);
    583   ares_buf_destroy(buf);
    584   if (status != ARES_SUCCESS) {
    585     ares_hosts_file_destroy(hf);
    586   } else {
    587     *out = hf;
    588   }
    589   return status;
    590 }
    591 
    592 static ares_bool_t ares_hosts_expired(const char              *filename,
    593                                       const ares_hosts_file_t *hf)
    594 {
    595   time_t mod_ts = 0;
    596 
    597 #ifdef HAVE_STAT
    598   struct stat st;
    599   if (stat(filename, &st) == 0) {
    600     mod_ts = st.st_mtime;
    601   }
    602 #elif defined(_WIN32)
    603   struct _stat st;
    604   if (_stat(filename, &st) == 0) {
    605     mod_ts = st.st_mtime;
    606   }
    607 #else
    608   (void)filename;
    609 #endif
    610 
    611   if (hf == NULL) {
    612     return ARES_TRUE;
    613   }
    614 
    615   /* Expire every 60s if we can't get a time */
    616   if (mod_ts == 0) {
    617     mod_ts =
    618       time(NULL) - 60; /* LCOV_EXCL_LINE: only on systems without stat() */
    619   }
    620 
    621   /* If filenames are different, its expired */
    622   if (!ares_strcaseeq(hf->filename, filename)) {
    623     return ARES_TRUE;
    624   }
    625 
    626   if (hf->ts <= mod_ts) {
    627     return ARES_TRUE;
    628   }
    629 
    630   return ARES_FALSE;
    631 }
    632 
    633 static ares_status_t ares_hosts_path(const ares_channel_t *channel,
    634                                      ares_bool_t use_env, char **path)
    635 {
    636   char *path_hosts = NULL;
    637 
    638   *path = NULL;
    639 
    640   if (channel->hosts_path) {
    641     path_hosts = ares_strdup(channel->hosts_path);
    642     if (!path_hosts) {
    643       return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    644     }
    645   }
    646 
    647   if (use_env) {
    648     if (path_hosts) {
    649       ares_free(path_hosts);
    650     }
    651 
    652     path_hosts = ares_strdup(getenv("CARES_HOSTS"));
    653     if (!path_hosts) {
    654       return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    655     }
    656   }
    657 
    658   if (!path_hosts) {
    659 #if defined(USE_WINSOCK)
    660     char  PATH_HOSTS[MAX_PATH] = "";
    661     char  tmp[MAX_PATH];
    662     HKEY  hkeyHosts;
    663     DWORD dwLength = sizeof(tmp);
    664     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ,
    665                       &hkeyHosts) != ERROR_SUCCESS) {
    666       return ARES_ENOTFOUND;
    667     }
    668     RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp,
    669                      &dwLength);
    670     ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH);
    671     RegCloseKey(hkeyHosts);
    672     strcat(PATH_HOSTS, WIN_PATH_HOSTS);
    673 #elif defined(WATT32)
    674     const char *PATH_HOSTS = _w32_GetHostsFile();
    675 
    676     if (!PATH_HOSTS) {
    677       return ARES_ENOTFOUND;
    678     }
    679 #endif
    680     path_hosts = ares_strdup(PATH_HOSTS);
    681     if (!path_hosts) {
    682       return ARES_ENOMEM;
    683     }
    684   }
    685 
    686   *path = path_hosts;
    687   return ARES_SUCCESS;
    688 }
    689 
    690 static ares_status_t ares_hosts_update(ares_channel_t *channel,
    691                                        ares_bool_t     use_env)
    692 {
    693   ares_status_t status;
    694   char         *filename = NULL;
    695 
    696   status = ares_hosts_path(channel, use_env, &filename);
    697   if (status != ARES_SUCCESS) {
    698     return status;
    699   }
    700 
    701   if (!ares_hosts_expired(filename, channel->hf)) {
    702     ares_free(filename);
    703     return ARES_SUCCESS;
    704   }
    705 
    706   ares_hosts_file_destroy(channel->hf);
    707   channel->hf = NULL;
    708 
    709   status = ares_parse_hosts(filename, &channel->hf);
    710   ares_free(filename);
    711   return status;
    712 }
    713 
    714 ares_status_t ares_hosts_search_ipaddr(ares_channel_t *channel,
    715                                        ares_bool_t use_env, const char *ipaddr,
    716                                        const ares_hosts_entry_t **entry)
    717 {
    718   ares_status_t status;
    719   char          addr[INET6_ADDRSTRLEN];
    720 
    721   *entry = NULL;
    722 
    723   status = ares_hosts_update(channel, use_env);
    724   if (status != ARES_SUCCESS) {
    725     return status;
    726   }
    727 
    728   if (channel->hf == NULL) {
    729     return ARES_ENOTFOUND; /* LCOV_EXCL_LINE: DefensiveCoding */
    730   }
    731 
    732   if (!ares_normalize_ipaddr(ipaddr, addr, sizeof(addr))) {
    733     return ARES_EBADNAME;
    734   }
    735 
    736   *entry = ares_htable_strvp_get_direct(channel->hf->iphash, addr);
    737   if (*entry == NULL) {
    738     return ARES_ENOTFOUND;
    739   }
    740 
    741   return ARES_SUCCESS;
    742 }
    743 
    744 ares_status_t ares_hosts_search_host(ares_channel_t *channel,
    745                                      ares_bool_t use_env, const char *host,
    746                                      const ares_hosts_entry_t **entry)
    747 {
    748   ares_status_t status;
    749 
    750   *entry = NULL;
    751 
    752   status = ares_hosts_update(channel, use_env);
    753   if (status != ARES_SUCCESS) {
    754     return status;
    755   }
    756 
    757   if (channel->hf == NULL) {
    758     return ARES_ENOTFOUND; /* LCOV_EXCL_LINE: DefensiveCoding */
    759   }
    760 
    761   *entry = ares_htable_strvp_get_direct(channel->hf->hosthash, host);
    762   if (*entry == NULL) {
    763     return ARES_ENOTFOUND;
    764   }
    765 
    766   return ARES_SUCCESS;
    767 }
    768 
    769 static ares_status_t
    770   ares_hosts_ai_append_cnames(const ares_hosts_entry_t    *entry,
    771                               struct ares_addrinfo_cname **cnames_out)
    772 {
    773   struct ares_addrinfo_cname *cname  = NULL;
    774   struct ares_addrinfo_cname *cnames = NULL;
    775   const char                 *primaryhost;
    776   ares_llist_node_t          *node;
    777   ares_status_t               status;
    778   size_t                      cnt = 0;
    779 
    780   node        = ares_llist_node_first(entry->hosts);
    781   primaryhost = ares_llist_node_val(node);
    782   /* Skip to next node to start with aliases */
    783   node = ares_llist_node_next(node);
    784 
    785   while (node != NULL) {
    786     const char *host = ares_llist_node_val(node);
    787 
    788     /* Cap at 100 entries. , some people use
    789      * https://github.com/StevenBlack/hosts and we don't need 200k+ aliases */
    790     cnt++;
    791     if (cnt > 100) {
    792       break; /* LCOV_EXCL_LINE: DefensiveCoding */
    793     }
    794 
    795     cname = ares_append_addrinfo_cname(&cnames);
    796     if (cname == NULL) {
    797       status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    798       goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
    799     }
    800 
    801     cname->alias = ares_strdup(host);
    802     if (cname->alias == NULL) {
    803       status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    804       goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
    805     }
    806 
    807     cname->name = ares_strdup(primaryhost);
    808     if (cname->name == NULL) {
    809       status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    810       goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
    811     }
    812 
    813     node = ares_llist_node_next(node);
    814   }
    815 
    816   /* No entries, add only primary */
    817   if (cnames == NULL) {
    818     cname = ares_append_addrinfo_cname(&cnames);
    819     if (cname == NULL) {
    820       status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    821       goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
    822     }
    823 
    824     cname->name = ares_strdup(primaryhost);
    825     if (cname->name == NULL) {
    826       status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    827       goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
    828     }
    829   }
    830   status = ARES_SUCCESS;
    831 
    832 done:
    833   if (status != ARES_SUCCESS) {
    834     ares_freeaddrinfo_cnames(cnames); /* LCOV_EXCL_LINE: DefensiveCoding */
    835     return status;                    /* LCOV_EXCL_LINE: DefensiveCoding */
    836   }
    837 
    838   *cnames_out = cnames;
    839   return ARES_SUCCESS;
    840 }
    841 
    842 ares_status_t ares_hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry,
    843                                            const char *name, int family,
    844                                            unsigned short        port,
    845                                            ares_bool_t           want_cnames,
    846                                            struct ares_addrinfo *ai)
    847 {
    848   ares_status_t               status  = ARES_ENOTFOUND;
    849   struct ares_addrinfo_cname *cnames  = NULL;
    850   struct ares_addrinfo_node  *ainodes = NULL;
    851   ares_llist_node_t          *node;
    852 
    853   switch (family) {
    854     case AF_INET:
    855     case AF_INET6:
    856     case AF_UNSPEC:
    857       break;
    858     default:                  /* LCOV_EXCL_LINE: DefensiveCoding */
    859       return ARES_EBADFAMILY; /* LCOV_EXCL_LINE: DefensiveCoding */
    860   }
    861 
    862   if (name != NULL) {
    863     ares_free(ai->name);
    864     ai->name = ares_strdup(name);
    865     if (ai->name == NULL) {
    866       status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
    867       goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
    868     }
    869   }
    870 
    871   for (node = ares_llist_node_first(entry->ips); node != NULL;
    872        node = ares_llist_node_next(node)) {
    873     struct ares_addr addr;
    874     const void      *ptr     = NULL;
    875     size_t           ptr_len = 0;
    876     const char      *ipaddr  = ares_llist_node_val(node);
    877 
    878     memset(&addr, 0, sizeof(addr));
    879     addr.family = family;
    880     ptr         = ares_dns_pton(ipaddr, &addr, &ptr_len);
    881 
    882     if (ptr == NULL) {
    883       continue;
    884     }
    885 
    886     status = ares_append_ai_node(addr.family, port, 0, ptr, &ainodes);
    887     if (status != ARES_SUCCESS) {
    888       goto done; /* LCOV_EXCL_LINE: DefensiveCoding */
    889     }
    890   }
    891 
    892   /* Might be ARES_ENOTFOUND here if no ips matched requested address family */
    893   if (status != ARES_SUCCESS) {
    894     goto done;
    895   }
    896 
    897   if (want_cnames) {
    898     status = ares_hosts_ai_append_cnames(entry, &cnames);
    899     if (status != ARES_SUCCESS) {
    900       goto done; /* LCOV_EXCL_LINE: DefensiveCoding */
    901     }
    902   }
    903 
    904   status = ARES_SUCCESS;
    905 
    906 done:
    907   if (status != ARES_SUCCESS) {
    908     /* LCOV_EXCL_START: defensive coding */
    909     ares_freeaddrinfo_cnames(cnames);
    910     ares_freeaddrinfo_nodes(ainodes);
    911     ares_free(ai->name);
    912     ai->name = NULL;
    913     return status;
    914     /* LCOV_EXCL_STOP */
    915   }
    916   ares_addrinfo_cat_cnames(&ai->cnames, cnames);
    917   ares_addrinfo_cat_nodes(&ai->nodes, ainodes);
    918 
    919   return status;
    920 }
    921 
    922 ares_status_t ares_hosts_entry_to_hostent(const ares_hosts_entry_t *entry,
    923                                           int family, struct hostent **hostent)
    924 {
    925   ares_status_t         status;
    926   struct ares_addrinfo *ai = ares_malloc_zero(sizeof(*ai));
    927 
    928   *hostent = NULL;
    929 
    930   if (ai == NULL) {
    931     return ARES_ENOMEM;
    932   }
    933 
    934   status = ares_hosts_entry_to_addrinfo(entry, NULL, family, 0, ARES_TRUE, ai);
    935   if (status != ARES_SUCCESS) {
    936     goto done;
    937   }
    938 
    939   status = ares_addrinfo2hostent(ai, family, hostent);
    940   if (status != ARES_SUCCESS) {
    941     goto done;
    942   }
    943 
    944 done:
    945   ares_freeaddrinfo(ai);
    946   if (status != ARES_SUCCESS) {
    947     ares_free_hostent(*hostent);
    948     *hostent = NULL;
    949   }
    950 
    951   return status;
    952 }