• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ares_dns_pton(const char * ipaddr,struct ares_addr * addr,size_t * out_len)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 
ares_normalize_ipaddr(const char * ipaddr,char * out,size_t out_len)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 
ares_hosts_entry_destroy(ares_hosts_entry_t * entry)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 
ares_hosts_entry_destroy_cb(void * entry)176 static void ares_hosts_entry_destroy_cb(void *entry)
177 {
178   ares_hosts_entry_destroy(entry);
179 }
180 
ares_hosts_file_destroy(ares_hosts_file_t * hf)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 
ares_hosts_file_create(const char * filename)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 
ares_hosts_file_merge_entry(const ares_hosts_file_t * hf,ares_hosts_entry_t * existing,ares_hosts_entry_t * entry,ares_hosts_file_match_t matchtype)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
ares_hosts_file_match(const ares_hosts_file_t * hf,ares_hosts_entry_t * entry,ares_hosts_entry_t ** match)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 */
ares_hosts_file_add(ares_hosts_file_t * hosts,ares_hosts_entry_t * entry)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 
ares_hosts_entry_isdup(ares_hosts_entry_t * entry,const char * host)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 
ares_parse_hosts_hostnames(ares_buf_t * buf,ares_hosts_entry_t * entry)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 
ares_parse_hosts_ipaddr(ares_buf_t * buf,ares_hosts_entry_t ** entry_out)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 
ares_parse_hosts(const char * filename,ares_hosts_file_t ** out)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 
ares_hosts_expired(const char * filename,const ares_hosts_file_t * hf)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 
ares_hosts_path(const ares_channel_t * channel,ares_bool_t use_env,char ** path)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 
ares_hosts_update(ares_channel_t * channel,ares_bool_t use_env)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 
ares_hosts_search_ipaddr(ares_channel_t * channel,ares_bool_t use_env,const char * ipaddr,const ares_hosts_entry_t ** entry)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 
ares_hosts_search_host(ares_channel_t * channel,ares_bool_t use_env,const char * host,const ares_hosts_entry_t ** entry)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
ares_hosts_ai_append_cnames(const ares_hosts_entry_t * entry,struct ares_addrinfo_cname ** cnames_out)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 
ares_hosts_entry_to_addrinfo(const ares_hosts_entry_t * entry,const char * name,int family,unsigned short port,ares_bool_t want_cnames,struct ares_addrinfo * ai)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 
ares_hosts_entry_to_hostent(const ares_hosts_entry_t * entry,int family,struct hostent ** hostent)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 }
953