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_setup.h"
27#include "ares.h"
28#include "ares_private.h"
29#ifdef HAVE_SYS_TYPES_H
30#  include <sys/types.h>
31#endif
32#ifdef HAVE_SYS_STAT_H
33#  include <sys/stat.h>
34#endif
35#ifdef HAVE_NETINET_IN_H
36#  include <netinet/in.h>
37#endif
38#ifdef HAVE_NETDB_H
39#  include <netdb.h>
40#endif
41#ifdef HAVE_ARPA_INET_H
42#  include <arpa/inet.h>
43#endif
44#include <time.h>
45#include "ares_platform.h"
46
47/* HOSTS FILE PROCESSING OVERVIEW
48 * ==============================
49 * The hosts file on the system contains static entries to be processed locally
50 * rather than querying the nameserver.  Each row is an IP address followed by
51 * a list of space delimited hostnames that match the ip address.  This is used
52 * for both forward and reverse lookups.
53 *
54 * We are caching the entire parsed hosts file for performance reasons.  Some
55 * files may be quite sizable and as per Issue #458 can approach 1/2MB in size,
56 * and the parse overhead on a rapid succession of queries can be quite large.
57 * The entries are stored in forwards and backwards hashtables so we can get
58 * O(1) performance on lookup.  The file is cached until the file modification
59 * timestamp changes.
60 *
61 * The hosts file processing is quite unique. It has to merge all related hosts
62 * and ips into a single entry due to file formatting requirements.  For
63 * instance take the below:
64 *
65 * 127.0.0.1    localhost.localdomain localhost
66 * ::1          localhost.localdomain localhost
67 * 192.168.1.1  host.example.com host
68 * 192.168.1.5  host.example.com host
69 * 2620:1234::1 host.example.com host6.example.com host6 host
70 *
71 * This will yield 2 entries.
72 *  1) ips: 127.0.0.1,::1
73 *     hosts: localhost.localdomain,localhost
74 *  2) ips: 192.168.1.1,192.168.1.5,2620:1234::1
75 *     hosts: host.example.com,host,host6.example.com,host6
76 *
77 * It could be argued that if searching for 192.168.1.1 that the 'host6'
78 * hostnames should not be returned, but this implementation will return them
79 * since they are related.  It is unlikely this will matter in the real world.
80 */
81
82struct ares_hosts_file {
83  time_t                ts;
84  /*! cache the filename so we know if the filename changes it automatically
85   *  invalidates the cache */
86  char                 *filename;
87  /*! iphash is the owner of the 'entry' object as there is only ever a single
88   *  match to the object. */
89  ares__htable_strvp_t *iphash;
90  /*! hosthash does not own the entry so won't free on destruction */
91  ares__htable_strvp_t *hosthash;
92};
93
94struct ares_hosts_entry {
95  size_t         refcnt; /*! If the entry is stored multiple times in the
96                          *  ip address hash, we have to reference count it */
97  ares__llist_t *ips;
98  ares__llist_t *hosts;
99};
100
101static ares_status_t ares__read_file_into_buf(const char  *filename,
102                                              ares__buf_t *buf)
103{
104  FILE          *fp        = NULL;
105  unsigned char *ptr       = NULL;
106  size_t         len       = 0;
107  size_t         ptr_len   = 0;
108  long           ftell_len = 0;
109  ares_status_t  status;
110
111  if (filename == NULL || buf == NULL) {
112    return ARES_EFORMERR;
113  }
114
115  fp = fopen(filename, "rb");
116  if (fp == NULL) {
117    int error = ERRNO;
118    switch (error) {
119      case ENOENT:
120      case ESRCH:
121        status = ARES_ENOTFOUND;
122        goto done;
123      default:
124        DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
125                       strerror(error)));
126        DEBUGF(fprintf(stderr, "Error opening file: %s\n", filename));
127        status = ARES_EFILE;
128        goto done;
129    }
130  }
131
132  /* Get length portably, fstat() is POSIX, not C */
133  if (fseek(fp, 0, SEEK_END) != 0) {
134    status = ARES_EFILE;
135    goto done;
136  }
137
138  ftell_len = ftell(fp);
139  if (ftell_len < 0) {
140    status = ARES_EFILE;
141    goto done;
142  }
143  len = (size_t)ftell_len;
144
145  if (fseek(fp, 0, SEEK_SET) != 0) {
146    status = ARES_EFILE;
147    goto done;
148  }
149
150  if (len == 0) {
151    status = ARES_SUCCESS;
152    goto done;
153  }
154
155  /* Read entire data into buffer */
156  ptr_len = len;
157  ptr     = ares__buf_append_start(buf, &ptr_len);
158  if (ptr == NULL) {
159    status = ARES_ENOMEM;
160    goto done;
161  }
162
163  ptr_len = fread(ptr, 1, len, fp);
164  if (ptr_len != len) {
165    status = ARES_EFILE;
166    goto done;
167  }
168
169  ares__buf_append_finish(buf, len);
170  status = ARES_SUCCESS;
171
172done:
173  if (fp != NULL) {
174    fclose(fp);
175  }
176  return status;
177}
178
179static ares_bool_t ares__is_hostname(const char *str)
180{
181  size_t i;
182  for (i = 0; str[i] != 0; i++) {
183    if (!ares__is_hostnamech(str[i])) {
184      return ARES_FALSE;
185    }
186  }
187  return ARES_TRUE;
188}
189
190const void *ares_dns_pton(const char *ipaddr, struct ares_addr *addr,
191                          size_t *out_len)
192{
193  const void *ptr     = NULL;
194  size_t      ptr_len = 0;
195
196  if (ipaddr == NULL || addr == NULL || out_len == NULL) {
197    return NULL;
198  }
199
200  *out_len = 0;
201
202  if (addr->family == AF_INET &&
203      ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) {
204    ptr     = &addr->addr.addr4;
205    ptr_len = sizeof(addr->addr.addr4);
206  } else if (addr->family == AF_INET6 &&
207             ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) {
208    ptr     = &addr->addr.addr6;
209    ptr_len = sizeof(addr->addr.addr6);
210  } else if (addr->family == AF_UNSPEC) {
211    if (ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) {
212      addr->family = AF_INET;
213      ptr          = &addr->addr.addr4;
214      ptr_len      = sizeof(addr->addr.addr4);
215    } else if (ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) {
216      addr->family = AF_INET6;
217      ptr          = &addr->addr.addr6;
218      ptr_len      = sizeof(addr->addr.addr6);
219    }
220  }
221
222  *out_len = ptr_len;
223  return ptr;
224}
225
226static ares_bool_t ares__normalize_ipaddr(const char *ipaddr, char *out,
227                                          size_t out_len)
228{
229  struct ares_addr data;
230  const void      *addr;
231  size_t           addr_len = 0;
232
233  memset(&data, 0, sizeof(data));
234  data.family = AF_UNSPEC;
235
236  addr = ares_dns_pton(ipaddr, &data, &addr_len);
237  if (addr == NULL) {
238    return ARES_FALSE;
239  }
240
241  if (!ares_inet_ntop(data.family, addr, out, (ares_socklen_t)out_len)) {
242    return ARES_FALSE;
243  }
244
245  return ARES_TRUE;
246}
247
248static void ares__hosts_entry_destroy(ares_hosts_entry_t *entry)
249{
250  if (entry == NULL) {
251    return;
252  }
253
254  /* Honor reference counting */
255  if (entry->refcnt != 0) {
256    entry->refcnt--;
257  }
258
259  if (entry->refcnt > 0) {
260    return;
261  }
262
263  ares__llist_destroy(entry->hosts);
264  ares__llist_destroy(entry->ips);
265  ares_free(entry);
266}
267
268static void ares__hosts_entry_destroy_cb(void *entry)
269{
270  ares__hosts_entry_destroy(entry);
271}
272
273void ares__hosts_file_destroy(ares_hosts_file_t *hf)
274{
275  if (hf == NULL) {
276    return;
277  }
278
279  ares_free(hf->filename);
280  ares__htable_strvp_destroy(hf->hosthash);
281  ares__htable_strvp_destroy(hf->iphash);
282  ares_free(hf);
283}
284
285static ares_hosts_file_t *ares__hosts_file_create(const char *filename)
286{
287  ares_hosts_file_t *hf = ares_malloc_zero(sizeof(*hf));
288  if (hf == NULL) {
289    goto fail;
290  }
291
292  hf->ts = time(NULL);
293
294  hf->filename = ares_strdup(filename);
295  if (hf->filename == NULL) {
296    goto fail;
297  }
298
299  hf->iphash = ares__htable_strvp_create(ares__hosts_entry_destroy_cb);
300  if (hf->iphash == NULL) {
301    goto fail;
302  }
303
304  hf->hosthash = ares__htable_strvp_create(NULL);
305  if (hf->hosthash == NULL) {
306    goto fail;
307  }
308
309  return hf;
310
311fail:
312  ares__hosts_file_destroy(hf);
313  return NULL;
314}
315
316typedef enum {
317  ARES_MATCH_NONE   = 0,
318  ARES_MATCH_IPADDR = 1,
319  ARES_MATCH_HOST   = 2
320} ares_hosts_file_match_t;
321
322static ares_status_t ares__hosts_file_merge_entry(
323  const ares_hosts_file_t *hf, ares_hosts_entry_t *existing,
324  ares_hosts_entry_t *entry, ares_hosts_file_match_t matchtype)
325{
326  ares__llist_node_t *node;
327
328  /* If we matched on IP address, we know there can only be 1, so there's no
329   * reason to do anything */
330  if (matchtype != ARES_MATCH_IPADDR) {
331    while ((node = ares__llist_node_first(entry->ips)) != NULL) {
332      const char *ipaddr = ares__llist_node_val(node);
333
334      if (ares__htable_strvp_get_direct(hf->iphash, ipaddr) != NULL) {
335        ares__llist_node_destroy(node);
336        continue;
337      }
338
339      ares__llist_node_move_parent_last(node, existing->ips);
340    }
341  }
342
343
344  while ((node = ares__llist_node_first(entry->hosts)) != NULL) {
345    const char *hostname = ares__llist_node_val(node);
346
347    if (ares__htable_strvp_get_direct(hf->hosthash, hostname) != NULL) {
348      ares__llist_node_destroy(node);
349      continue;
350    }
351
352    ares__llist_node_move_parent_last(node, existing->hosts);
353  }
354
355  ares__hosts_entry_destroy(entry);
356  return ARES_SUCCESS;
357}
358
359static ares_hosts_file_match_t
360  ares__hosts_file_match(const ares_hosts_file_t *hf, ares_hosts_entry_t *entry,
361                         ares_hosts_entry_t **match)
362{
363  ares__llist_node_t *node;
364  *match = NULL;
365
366  for (node = ares__llist_node_first(entry->ips); node != NULL;
367       node = ares__llist_node_next(node)) {
368    const char *ipaddr = ares__llist_node_val(node);
369    *match             = ares__htable_strvp_get_direct(hf->iphash, ipaddr);
370    if (*match != NULL) {
371      return ARES_MATCH_IPADDR;
372    }
373  }
374
375  for (node = ares__llist_node_first(entry->hosts); node != NULL;
376       node = ares__llist_node_next(node)) {
377    const char *host = ares__llist_node_val(node);
378    *match           = ares__htable_strvp_get_direct(hf->hosthash, host);
379    if (*match != NULL) {
380      return ARES_MATCH_HOST;
381    }
382  }
383
384  return ARES_MATCH_NONE;
385}
386
387/*! entry is invalidated upon calling this function, always, even on error */
388static ares_status_t ares__hosts_file_add(ares_hosts_file_t  *hosts,
389                                          ares_hosts_entry_t *entry)
390{
391  ares_hosts_entry_t     *match  = NULL;
392  ares_status_t           status = ARES_SUCCESS;
393  ares__llist_node_t     *node;
394  ares_hosts_file_match_t matchtype;
395  size_t                  num_hostnames;
396
397  /* Record the number of hostnames in this entry file.  If we merge into an
398   * existing record, these will be *appended* to the entry, so we'll count
399   * backwards when adding to the hosts hashtable */
400  num_hostnames = ares__llist_len(entry->hosts);
401
402  matchtype = ares__hosts_file_match(hosts, entry, &match);
403
404  if (matchtype != ARES_MATCH_NONE) {
405    status = ares__hosts_file_merge_entry(hosts, match, entry, matchtype);
406    if (status != ARES_SUCCESS) {
407      ares__hosts_entry_destroy(entry);
408      return status;
409    }
410    /* entry was invalidated above by merging */
411    entry = match;
412  }
413
414  if (matchtype != ARES_MATCH_IPADDR) {
415    const char *ipaddr = ares__llist_last_val(entry->ips);
416
417    if (!ares__htable_strvp_get(hosts->iphash, ipaddr, NULL)) {
418      if (!ares__htable_strvp_insert(hosts->iphash, ipaddr, entry)) {
419        ares__hosts_entry_destroy(entry);
420        return ARES_ENOMEM;
421      }
422      entry->refcnt++;
423    }
424  }
425
426  /* Go backwards, on a merge, hostnames are appended.  Breakout once we've
427   * consumed all the hosts that we appended */
428  for (node = ares__llist_node_last(entry->hosts); node != NULL;
429       node = ares__llist_node_prev(node)) {
430    const char *val = ares__llist_node_val(node);
431
432    if (num_hostnames == 0) {
433      break;
434    }
435
436    num_hostnames--;
437
438    /* first hostname match wins.  If we detect a duplicate hostname for another
439     * ip it will automatically be added to the same entry */
440    if (ares__htable_strvp_get(hosts->hosthash, val, NULL)) {
441      continue;
442    }
443
444    if (!ares__htable_strvp_insert(hosts->hosthash, val, entry)) {
445      return ARES_ENOMEM;
446    }
447  }
448
449  return ARES_SUCCESS;
450}
451
452static ares_bool_t ares__hosts_entry_isdup(ares_hosts_entry_t *entry,
453                                           const char         *host)
454{
455  ares__llist_node_t *node;
456
457  for (node = ares__llist_node_first(entry->ips); node != NULL;
458       node = ares__llist_node_next(node)) {
459    const char *myhost = ares__llist_node_val(node);
460    if (strcasecmp(myhost, host) == 0) {
461      return ARES_TRUE;
462    }
463  }
464
465  return ARES_FALSE;
466}
467
468static ares_status_t ares__parse_hosts_hostnames(ares__buf_t        *buf,
469                                                 ares_hosts_entry_t *entry)
470{
471  entry->hosts = ares__llist_create(ares_free);
472  if (entry->hosts == NULL) {
473    return ARES_ENOMEM;
474  }
475
476  /* Parse hostnames and aliases */
477  while (ares__buf_len(buf)) {
478    char          hostname[256];
479    char         *temp;
480    ares_status_t status;
481    unsigned char comment = '#';
482
483    ares__buf_consume_whitespace(buf, ARES_FALSE);
484
485    if (ares__buf_len(buf) == 0) {
486      break;
487    }
488
489    /* See if it is a comment, if so stop processing */
490    if (ares__buf_begins_with(buf, &comment, 1)) {
491      break;
492    }
493
494    ares__buf_tag(buf);
495
496    /* Must be at end of line */
497    if (ares__buf_consume_nonwhitespace(buf) == 0) {
498      break;
499    }
500
501    status = ares__buf_tag_fetch_string(buf, hostname, sizeof(hostname));
502    if (status != ARES_SUCCESS) {
503      /* Bad entry, just ignore as long as its not the first.  If its the first,
504       * it must be valid */
505      if (ares__llist_len(entry->hosts) == 0) {
506        return ARES_EBADSTR;
507      }
508
509      continue;
510    }
511
512    /* Validate it is a valid hostname characterset */
513    if (!ares__is_hostname(hostname)) {
514      continue;
515    }
516
517    /* Don't add a duplicate to the same entry */
518    if (ares__hosts_entry_isdup(entry, hostname)) {
519      continue;
520    }
521
522    /* Add to list */
523    temp = ares_strdup(hostname);
524    if (temp == NULL) {
525      return ARES_ENOMEM;
526    }
527
528    if (ares__llist_insert_last(entry->hosts, temp) == NULL) {
529      ares_free(temp);
530      return ARES_ENOMEM;
531    }
532  }
533
534  /* Must have at least 1 entry */
535  if (ares__llist_len(entry->hosts) == 0) {
536    return ARES_EBADSTR;
537  }
538
539  return ARES_SUCCESS;
540}
541
542static ares_status_t ares__parse_hosts_ipaddr(ares__buf_t         *buf,
543                                              ares_hosts_entry_t **entry_out)
544{
545  char                addr[INET6_ADDRSTRLEN];
546  char               *temp;
547  ares_hosts_entry_t *entry = NULL;
548  ares_status_t       status;
549
550  *entry_out = NULL;
551
552  ares__buf_tag(buf);
553  ares__buf_consume_nonwhitespace(buf);
554  status = ares__buf_tag_fetch_string(buf, addr, sizeof(addr));
555  if (status != ARES_SUCCESS) {
556    return status;
557  }
558
559  /* Validate and normalize the ip address format */
560  if (!ares__normalize_ipaddr(addr, addr, sizeof(addr))) {
561    return ARES_EBADSTR;
562  }
563
564  entry = ares_malloc_zero(sizeof(*entry));
565  if (entry == NULL) {
566    return ARES_ENOMEM;
567  }
568
569  entry->ips = ares__llist_create(ares_free);
570  if (entry->ips == NULL) {
571    ares__hosts_entry_destroy(entry);
572    return ARES_ENOMEM;
573  }
574
575  temp = ares_strdup(addr);
576  if (temp == NULL) {
577    ares__hosts_entry_destroy(entry);
578    return ARES_ENOMEM;
579  }
580
581  if (ares__llist_insert_first(entry->ips, temp) == NULL) {
582    ares_free(temp);
583    ares__hosts_entry_destroy(entry);
584    return ARES_ENOMEM;
585  }
586
587  *entry_out = entry;
588
589  return ARES_SUCCESS;
590}
591
592static ares_status_t ares__parse_hosts(const char         *filename,
593                                       ares_hosts_file_t **out)
594{
595  ares__buf_t        *buf    = NULL;
596  ares_status_t       status = ARES_EBADRESP;
597  ares_hosts_file_t  *hf     = NULL;
598  ares_hosts_entry_t *entry  = NULL;
599
600  *out = NULL;
601
602  buf = ares__buf_create();
603  if (buf == NULL) {
604    status = ARES_ENOMEM;
605    goto done;
606  }
607
608  status = ares__read_file_into_buf(filename, buf);
609  if (status != ARES_SUCCESS) {
610    goto done;
611  }
612
613  hf = ares__hosts_file_create(filename);
614  if (hf == NULL) {
615    status = ARES_ENOMEM;
616    goto done;
617  }
618
619  while (ares__buf_len(buf)) {
620    unsigned char comment = '#';
621
622    /* -- Start of new line here -- */
623
624    /* Consume any leading whitespace */
625    ares__buf_consume_whitespace(buf, ARES_FALSE);
626
627    if (ares__buf_len(buf) == 0) {
628      break;
629    }
630
631    /* See if it is a comment, if so, consume remaining line */
632    if (ares__buf_begins_with(buf, &comment, 1)) {
633      ares__buf_consume_line(buf, ARES_TRUE);
634      continue;
635    }
636
637    /* Pull off ip address */
638    status = ares__parse_hosts_ipaddr(buf, &entry);
639    if (status == ARES_ENOMEM) {
640      goto done;
641    }
642    if (status != ARES_SUCCESS) {
643      /* Bad line, consume and go onto next */
644      ares__buf_consume_line(buf, ARES_TRUE);
645      continue;
646    }
647
648    /* Parse of the hostnames */
649    status = ares__parse_hosts_hostnames(buf, entry);
650    if (status == ARES_ENOMEM) {
651      goto done;
652    } else if (status != ARES_SUCCESS) {
653      /* Bad line, consume and go onto next */
654      ares__hosts_entry_destroy(entry);
655      entry = NULL;
656      ares__buf_consume_line(buf, ARES_TRUE);
657      continue;
658    }
659
660    /* Append the successful entry to the hosts file */
661    status = ares__hosts_file_add(hf, entry);
662    entry  = NULL; /* is always invalidated by this function, even on error */
663    if (status != ARES_SUCCESS) {
664      goto done;
665    }
666
667    /* Go to next line */
668    ares__buf_consume_line(buf, ARES_TRUE);
669  }
670
671  status = ARES_SUCCESS;
672
673done:
674  ares__hosts_entry_destroy(entry);
675  ares__buf_destroy(buf);
676  if (status != ARES_SUCCESS) {
677    ares__hosts_file_destroy(hf);
678  } else {
679    *out = hf;
680  }
681  return status;
682}
683
684static ares_bool_t ares__hosts_expired(const char              *filename,
685                                       const ares_hosts_file_t *hf)
686{
687  time_t mod_ts = 0;
688
689#ifdef HAVE_STAT
690  struct stat st;
691  if (stat(filename, &st) == 0) {
692    mod_ts = st.st_mtime;
693  }
694#elif defined(_WIN32)
695  struct _stat st;
696  if (_stat(filename, &st) == 0) {
697    mod_ts = st.st_mtime;
698  }
699#else
700  (void)filename;
701#endif
702
703  if (hf == NULL) {
704    return ARES_TRUE;
705  }
706
707  /* Expire every 60s if we can't get a time */
708  if (mod_ts == 0) {
709    mod_ts = time(NULL) - 60;
710  }
711
712  /* If filenames are different, its expired */
713  if (strcasecmp(hf->filename, filename) != 0) {
714    return ARES_TRUE;
715  }
716
717  if (hf->ts <= mod_ts) {
718    return ARES_TRUE;
719  }
720
721  return ARES_FALSE;
722}
723
724static ares_status_t ares__hosts_path(const ares_channel_t *channel,
725                                      ares_bool_t use_env, char **path)
726{
727  char *path_hosts = NULL;
728
729  *path = NULL;
730
731  if (channel->hosts_path) {
732    path_hosts = ares_strdup(channel->hosts_path);
733    if (!path_hosts) {
734      return ARES_ENOMEM;
735    }
736  }
737
738  if (use_env) {
739    if (path_hosts) {
740      ares_free(path_hosts);
741    }
742
743    path_hosts = ares_strdup(getenv("CARES_HOSTS"));
744    if (!path_hosts) {
745      return ARES_ENOMEM;
746    }
747  }
748
749  if (!path_hosts) {
750#ifdef WIN32
751    char  PATH_HOSTS[MAX_PATH] = "";
752    char  tmp[MAX_PATH];
753    HKEY  hkeyHosts;
754    DWORD dwLength = sizeof(tmp);
755    if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ,
756                      &hkeyHosts) != ERROR_SUCCESS) {
757      return ARES_ENOTFOUND;
758    }
759    RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp,
760                     &dwLength);
761    ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH);
762    RegCloseKey(hkeyHosts);
763    strcat(PATH_HOSTS, WIN_PATH_HOSTS);
764#elif defined(WATT32)
765    const char *PATH_HOSTS = _w32_GetHostsFile();
766
767    if (!PATH_HOSTS) {
768      return ARES_ENOTFOUND;
769    }
770#endif
771    path_hosts = ares_strdup(PATH_HOSTS);
772    if (!path_hosts) {
773      return ARES_ENOMEM;
774    }
775  }
776
777  *path = path_hosts;
778  return ARES_SUCCESS;
779}
780
781static ares_status_t ares__hosts_update(ares_channel_t *channel,
782                                        ares_bool_t     use_env)
783{
784  ares_status_t status;
785  char         *filename = NULL;
786
787  status = ares__hosts_path(channel, use_env, &filename);
788  if (status != ARES_SUCCESS) {
789    return status;
790  }
791
792  if (!ares__hosts_expired(filename, channel->hf)) {
793    ares_free(filename);
794    return ARES_SUCCESS;
795  }
796
797  ares__hosts_file_destroy(channel->hf);
798  channel->hf = NULL;
799
800  status = ares__parse_hosts(filename, &channel->hf);
801  ares_free(filename);
802  return status;
803}
804
805ares_status_t ares__hosts_search_ipaddr(ares_channel_t *channel,
806                                        ares_bool_t use_env, const char *ipaddr,
807                                        const ares_hosts_entry_t **entry)
808{
809  ares_status_t status;
810  char          addr[INET6_ADDRSTRLEN];
811
812  *entry = NULL;
813
814  status = ares__hosts_update(channel, use_env);
815  if (status != ARES_SUCCESS) {
816    return status;
817  }
818
819  if (channel->hf == NULL) {
820    return ARES_ENOTFOUND;
821  }
822
823  if (!ares__normalize_ipaddr(ipaddr, addr, sizeof(addr))) {
824    return ARES_EBADNAME;
825  }
826
827  *entry = ares__htable_strvp_get_direct(channel->hf->iphash, addr);
828  if (*entry == NULL) {
829    return ARES_ENOTFOUND;
830  }
831
832  return ARES_SUCCESS;
833}
834
835ares_status_t ares__hosts_search_host(ares_channel_t *channel,
836                                      ares_bool_t use_env, const char *host,
837                                      const ares_hosts_entry_t **entry)
838{
839  ares_status_t status;
840
841  *entry = NULL;
842
843  status = ares__hosts_update(channel, use_env);
844  if (status != ARES_SUCCESS) {
845    return status;
846  }
847
848  if (channel->hf == NULL) {
849    return ARES_ENOTFOUND;
850  }
851
852  *entry = ares__htable_strvp_get_direct(channel->hf->hosthash, host);
853  if (*entry == NULL) {
854    return ARES_ENOTFOUND;
855  }
856
857  return ARES_SUCCESS;
858}
859
860ares_status_t ares__hosts_entry_to_hostent(const ares_hosts_entry_t *entry,
861                                           int family, struct hostent **hostent)
862{
863  ares_status_t       status;
864  size_t              naliases;
865  ares__llist_node_t *node;
866  size_t              idx;
867
868  *hostent = ares_malloc_zero(sizeof(**hostent));
869  if (*hostent == NULL) {
870    status = ARES_ENOMEM;
871    goto fail;
872  }
873
874  (*hostent)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)family;
875
876  /* Copy IP addresses that match the address family */
877  idx = 0;
878  for (node = ares__llist_node_first(entry->ips); node != NULL;
879       node = ares__llist_node_next(node)) {
880    struct ares_addr addr;
881    const void      *ptr     = NULL;
882    size_t           ptr_len = 0;
883    const char      *ipaddr  = ares__llist_node_val(node);
884    char           **temp    = NULL;
885
886    memset(&addr, 0, sizeof(addr));
887
888    addr.family = family;
889    ptr         = ares_dns_pton(ipaddr, &addr, &ptr_len);
890    if (ptr == NULL) {
891      continue;
892    }
893
894    /* If family == AF_UNSPEC, then we want to inherit this for future
895     * conversions as we can only support a single address class */
896    if (family == AF_UNSPEC) {
897      family                 = addr.family;
898      (*hostent)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)addr.family;
899    }
900
901    temp = ares_realloc_zero((*hostent)->h_addr_list,
902                             (idx + 1) * sizeof(*(*hostent)->h_addr_list),
903                             (idx + 2) * sizeof(*(*hostent)->h_addr_list));
904    if (temp == NULL) {
905      status = ARES_ENOMEM;
906      goto fail;
907    }
908
909    (*hostent)->h_addr_list = temp;
910
911    (*hostent)->h_addr_list[idx] = ares_malloc(ptr_len);
912    if ((*hostent)->h_addr_list[idx] == NULL) {
913      status = ARES_ENOMEM;
914      goto fail;
915    }
916
917    memcpy((*hostent)->h_addr_list[idx], ptr, ptr_len);
918    idx++;
919    (*hostent)->h_length = (HOSTENT_LENGTH_TYPE)ptr_len;
920  }
921
922  /* entry didn't match address class */
923  if (idx == 0) {
924    status = ARES_ENOTFOUND;
925    goto fail;
926  }
927
928  /* Copy main hostname */
929  (*hostent)->h_name = ares_strdup(ares__llist_first_val(entry->hosts));
930  if ((*hostent)->h_name == NULL) {
931    status = ARES_ENOMEM;
932    goto fail;
933  }
934
935  /* Copy aliases */
936  naliases = ares__llist_len(entry->hosts) - 1;
937
938  /* Cap at 100, some people use https://github.com/StevenBlack/hosts and we
939   * don't need 200k+ aliases */
940  if (naliases > 100) {
941    naliases = 100;
942  }
943
944  (*hostent)->h_aliases =
945    ares_malloc_zero((naliases + 1) * sizeof(*(*hostent)->h_aliases));
946  if ((*hostent)->h_aliases == NULL) {
947    status = ARES_ENOMEM;
948    goto fail;
949  }
950
951  /* Copy all entries to the alias except the first */
952  idx  = 0;
953  node = ares__llist_node_first(entry->hosts);
954  node = ares__llist_node_next(node);
955  while (node != NULL) {
956    (*hostent)->h_aliases[idx] = ares_strdup(ares__llist_node_val(node));
957    if ((*hostent)->h_aliases[idx] == NULL) {
958      status = ARES_ENOMEM;
959      goto fail;
960    }
961    idx++;
962
963    /* Break out if artificially capped */
964    if (idx == naliases) {
965      break;
966    }
967    node = ares__llist_node_next(node);
968  }
969
970  return ARES_SUCCESS;
971
972fail:
973  ares_free_hostent(*hostent);
974  *hostent = NULL;
975  return status;
976}
977
978static ares_status_t
979  ares__hosts_ai_append_cnames(const ares_hosts_entry_t    *entry,
980                               struct ares_addrinfo_cname **cnames_out)
981{
982  struct ares_addrinfo_cname *cname  = NULL;
983  struct ares_addrinfo_cname *cnames = NULL;
984  const char                 *primaryhost;
985  ares__llist_node_t         *node;
986  ares_status_t               status;
987  size_t                      cnt = 0;
988
989  node        = ares__llist_node_first(entry->hosts);
990  primaryhost = ares__llist_node_val(node);
991  /* Skip to next node to start with aliases */
992  node = ares__llist_node_next(node);
993
994  while (node != NULL) {
995    const char *host = ares__llist_node_val(node);
996
997    /* Cap at 100 entries. , some people use
998     * https://github.com/StevenBlack/hosts and we don't need 200k+ aliases */
999    cnt++;
1000    if (cnt > 100) {
1001      break;
1002    }
1003
1004    cname = ares__append_addrinfo_cname(&cnames);
1005    if (cname == NULL) {
1006      status = ARES_ENOMEM;
1007      goto done;
1008    }
1009
1010    cname->alias = ares_strdup(host);
1011    if (cname->alias == NULL) {
1012      status = ARES_ENOMEM;
1013      goto done;
1014    }
1015
1016    cname->name = ares_strdup(primaryhost);
1017    if (cname->name == NULL) {
1018      status = ARES_ENOMEM;
1019      goto done;
1020    }
1021
1022    node = ares__llist_node_next(node);
1023  }
1024
1025  /* No entries, add only primary */
1026  if (cnames == NULL) {
1027    cname = ares__append_addrinfo_cname(&cnames);
1028    if (cname == NULL) {
1029      status = ARES_ENOMEM;
1030      goto done;
1031    }
1032
1033    cname->name = ares_strdup(primaryhost);
1034    if (cname->name == NULL) {
1035      status = ARES_ENOMEM;
1036      goto done;
1037    }
1038  }
1039  status = ARES_SUCCESS;
1040
1041done:
1042  if (status != ARES_SUCCESS) {
1043    ares__freeaddrinfo_cnames(cnames);
1044    return status;
1045  }
1046
1047  *cnames_out = cnames;
1048  return ARES_SUCCESS;
1049}
1050
1051ares_status_t ares__hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry,
1052                                            const char *name, int family,
1053                                            unsigned short        port,
1054                                            ares_bool_t           want_cnames,
1055                                            struct ares_addrinfo *ai)
1056{
1057  ares_status_t               status;
1058  struct ares_addrinfo_cname *cnames  = NULL;
1059  struct ares_addrinfo_node  *ainodes = NULL;
1060  ares__llist_node_t         *node;
1061
1062  switch (family) {
1063    case AF_INET:
1064    case AF_INET6:
1065    case AF_UNSPEC:
1066      break;
1067    default:
1068      return ARES_EBADFAMILY;
1069  }
1070
1071  ai->name = ares_strdup(name);
1072  if (ai->name == NULL) {
1073    status = ARES_ENOMEM;
1074    goto done;
1075  }
1076
1077  for (node = ares__llist_node_first(entry->ips); node != NULL;
1078       node = ares__llist_node_next(node)) {
1079    struct ares_addr addr;
1080    const void      *ptr     = NULL;
1081    size_t           ptr_len = 0;
1082    const char      *ipaddr  = ares__llist_node_val(node);
1083
1084    memset(&addr, 0, sizeof(addr));
1085    addr.family = family;
1086    ptr         = ares_dns_pton(ipaddr, &addr, &ptr_len);
1087
1088    if (ptr == NULL) {
1089      continue;
1090    }
1091
1092    status = ares_append_ai_node(addr.family, port, 0, ptr, &ainodes);
1093    if (status != ARES_SUCCESS) {
1094      goto done;
1095    }
1096  }
1097
1098  if (want_cnames) {
1099    status = ares__hosts_ai_append_cnames(entry, &cnames);
1100    if (status != ARES_SUCCESS) {
1101      goto done;
1102    }
1103  }
1104
1105  status = ARES_SUCCESS;
1106
1107done:
1108  if (status != ARES_SUCCESS) {
1109    ares__freeaddrinfo_cnames(cnames);
1110    ares__freeaddrinfo_nodes(ainodes);
1111    ares_free(ai->name);
1112    ai->name = NULL;
1113    return status;
1114  }
1115  ares__addrinfo_cat_cnames(&ai->cnames, cnames);
1116  ares__addrinfo_cat_nodes(&ai->nodes, ainodes);
1117
1118  return status;
1119}
1120