• 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_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 
82 struct 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 
94 struct 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 
ares__read_file_into_buf(const char * filename,ares__buf_t * buf)101 static 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 
172 done:
173   if (fp != NULL) {
174     fclose(fp);
175   }
176   return status;
177 }
178 
ares__is_hostname(const char * str)179 static 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 
ares_dns_pton(const char * ipaddr,struct ares_addr * addr,size_t * out_len)190 const 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 
ares__normalize_ipaddr(const char * ipaddr,char * out,size_t out_len)226 static 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 
ares__hosts_entry_destroy(ares_hosts_entry_t * entry)248 static 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 
ares__hosts_entry_destroy_cb(void * entry)268 static void ares__hosts_entry_destroy_cb(void *entry)
269 {
270   ares__hosts_entry_destroy(entry);
271 }
272 
ares__hosts_file_destroy(ares_hosts_file_t * hf)273 void 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 
ares__hosts_file_create(const char * filename)285 static 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 
311 fail:
312   ares__hosts_file_destroy(hf);
313   return NULL;
314 }
315 
316 typedef enum {
317   ARES_MATCH_NONE   = 0,
318   ARES_MATCH_IPADDR = 1,
319   ARES_MATCH_HOST   = 2
320 } ares_hosts_file_match_t;
321 
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)322 static 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 
359 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)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 */
ares__hosts_file_add(ares_hosts_file_t * hosts,ares_hosts_entry_t * entry)388 static 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 
ares__hosts_entry_isdup(ares_hosts_entry_t * entry,const char * host)452 static 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 
ares__parse_hosts_hostnames(ares__buf_t * buf,ares_hosts_entry_t * entry)468 static 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 
ares__parse_hosts_ipaddr(ares__buf_t * buf,ares_hosts_entry_t ** entry_out)542 static 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 
ares__parse_hosts(const char * filename,ares_hosts_file_t ** out)592 static 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 
673 done:
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 
ares__hosts_expired(const char * filename,const ares_hosts_file_t * hf)684 static 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 
ares__hosts_path(const ares_channel_t * channel,ares_bool_t use_env,char ** path)724 static 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 
ares__hosts_update(ares_channel_t * channel,ares_bool_t use_env)781 static 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 
ares__hosts_search_ipaddr(ares_channel_t * channel,ares_bool_t use_env,const char * ipaddr,const ares_hosts_entry_t ** entry)805 ares_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 
ares__hosts_search_host(ares_channel_t * channel,ares_bool_t use_env,const char * host,const ares_hosts_entry_t ** entry)835 ares_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 
ares__hosts_entry_to_hostent(const ares_hosts_entry_t * entry,int family,struct hostent ** hostent)860 ares_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 
972 fail:
973   ares_free_hostent(*hostent);
974   *hostent = NULL;
975   return status;
976 }
977 
978 static ares_status_t
ares__hosts_ai_append_cnames(const ares_hosts_entry_t * entry,struct ares_addrinfo_cname ** cnames_out)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 
1041 done:
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 
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)1051 ares_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 
1107 done:
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