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