Coverage Report

Created: 2023-12-09 06:20

/src/c-ares/src/lib/ares__hosts_file.c
Line
Count
Source (jump to first uncovered line)
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
101
static ares_status_t ares__read_file_into_buf(const char  *filename,
102
                                              ares__buf_t *buf)
103
0
{
104
0
  FILE          *fp        = NULL;
105
0
  unsigned char *ptr       = NULL;
106
0
  size_t         len       = 0;
107
0
  size_t         ptr_len   = 0;
108
0
  long           ftell_len = 0;
109
0
  ares_status_t  status;
110
111
0
  if (filename == NULL || buf == NULL) {
112
0
    return ARES_EFORMERR;
113
0
  }
114
115
0
  fp = fopen(filename, "rb");
116
0
  if (fp == NULL) {
117
0
    int error = ERRNO;
118
0
    switch (error) {
119
0
      case ENOENT:
120
0
      case ESRCH:
121
0
        status = ARES_ENOTFOUND;
122
0
        goto done;
123
0
      default:
124
0
        DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
125
0
                       strerror(error)));
126
0
        DEBUGF(fprintf(stderr, "Error opening file: %s\n", filename));
127
0
        status = ARES_EFILE;
128
0
        goto done;
129
0
    }
130
0
  }
131
132
  /* Get length portably, fstat() is POSIX, not C */
133
0
  if (fseek(fp, 0, SEEK_END) != 0) {
134
0
    status = ARES_EFILE;
135
0
    goto done;
136
0
  }
137
138
0
  ftell_len = ftell(fp);
139
0
  if (ftell_len < 0) {
140
0
    status = ARES_EFILE;
141
0
    goto done;
142
0
  }
143
0
  len = (size_t)ftell_len;
144
145
0
  if (fseek(fp, 0, SEEK_SET) != 0) {
146
0
    status = ARES_EFILE;
147
0
    goto done;
148
0
  }
149
150
0
  if (len == 0) {
151
0
    status = ARES_SUCCESS;
152
0
    goto done;
153
0
  }
154
155
  /* Read entire data into buffer */
156
0
  ptr_len = len;
157
0
  ptr     = ares__buf_append_start(buf, &ptr_len);
158
0
  if (ptr == NULL) {
159
0
    status = ARES_ENOMEM;
160
0
    goto done;
161
0
  }
162
163
0
  ptr_len = fread(ptr, 1, len, fp);
164
0
  if (ptr_len != len) {
165
0
    status = ARES_EFILE;
166
0
    goto done;
167
0
  }
168
169
0
  ares__buf_append_finish(buf, len);
170
0
  status = ARES_SUCCESS;
171
172
0
done:
173
0
  if (fp != NULL) {
174
0
    fclose(fp);
175
0
  }
176
0
  return status;
177
0
}
178
179
static ares_bool_t ares__is_hostname(const char *str)
180
0
{
181
0
  size_t i;
182
0
  for (i = 0; str[i] != 0; i++) {
183
0
    if (!ares__is_hostnamech(str[i])) {
184
0
      return ARES_FALSE;
185
0
    }
186
0
  }
187
0
  return ARES_TRUE;
188
0
}
189
190
const void *ares_dns_pton(const char *ipaddr, struct ares_addr *addr,
191
                          size_t *out_len)
192
0
{
193
0
  const void *ptr     = NULL;
194
0
  size_t      ptr_len = 0;
195
196
0
  if (ipaddr == NULL || addr == NULL || out_len == NULL) {
197
0
    return NULL;
198
0
  }
199
200
0
  *out_len = 0;
201
202
0
  if (addr->family == AF_INET &&
203
0
      ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) {
204
0
    ptr     = &addr->addr.addr4;
205
0
    ptr_len = sizeof(addr->addr.addr4);
206
0
  } else if (addr->family == AF_INET6 &&
207
0
             ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) {
208
0
    ptr     = &addr->addr.addr6;
209
0
    ptr_len = sizeof(addr->addr.addr6);
210
0
  } else if (addr->family == AF_UNSPEC) {
211
0
    if (ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) {
212
0
      addr->family = AF_INET;
213
0
      ptr          = &addr->addr.addr4;
214
0
      ptr_len      = sizeof(addr->addr.addr4);
215
0
    } else if (ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) {
216
0
      addr->family = AF_INET6;
217
0
      ptr          = &addr->addr.addr6;
218
0
      ptr_len      = sizeof(addr->addr.addr6);
219
0
    }
220
0
  }
221
222
0
  *out_len = ptr_len;
223
0
  return ptr;
224
0
}
225
226
static ares_bool_t ares__normalize_ipaddr(const char *ipaddr, char *out,
227
                                          size_t out_len)
228
0
{
229
0
  struct ares_addr data;
230
0
  const void      *addr;
231
0
  size_t           addr_len = 0;
232
233
0
  memset(&data, 0, sizeof(data));
234
0
  data.family = AF_UNSPEC;
235
236
0
  addr = ares_dns_pton(ipaddr, &data, &addr_len);
237
0
  if (addr == NULL) {
238
0
    return ARES_FALSE;
239
0
  }
240
241
0
  if (!ares_inet_ntop(data.family, addr, out, (ares_socklen_t)out_len)) {
242
0
    return ARES_FALSE;
243
0
  }
244
245
0
  return ARES_TRUE;
246
0
}
247
248
static void ares__hosts_entry_destroy(ares_hosts_entry_t *entry)
249
0
{
250
0
  if (entry == NULL) {
251
0
    return;
252
0
  }
253
254
  /* Honor reference counting */
255
0
  if (entry->refcnt != 0) {
256
0
    entry->refcnt--;
257
0
  }
258
259
0
  if (entry->refcnt > 0) {
260
0
    return;
261
0
  }
262
263
0
  ares__llist_destroy(entry->hosts);
264
0
  ares__llist_destroy(entry->ips);
265
0
  ares_free(entry);
266
0
}
267
268
static void ares__hosts_entry_destroy_cb(void *entry)
269
0
{
270
0
  ares__hosts_entry_destroy(entry);
271
0
}
272
273
void ares__hosts_file_destroy(ares_hosts_file_t *hf)
274
0
{
275
0
  if (hf == NULL) {
276
0
    return;
277
0
  }
278
279
0
  ares_free(hf->filename);
280
0
  ares__htable_strvp_destroy(hf->hosthash);
281
0
  ares__htable_strvp_destroy(hf->iphash);
282
0
  ares_free(hf);
283
0
}
284
285
static ares_hosts_file_t *ares__hosts_file_create(const char *filename)
286
0
{
287
0
  ares_hosts_file_t *hf = ares_malloc_zero(sizeof(*hf));
288
0
  if (hf == NULL) {
289
0
    goto fail;
290
0
  }
291
292
0
  hf->ts = time(NULL);
293
294
0
  hf->filename = ares_strdup(filename);
295
0
  if (hf->filename == NULL) {
296
0
    goto fail;
297
0
  }
298
299
0
  hf->iphash = ares__htable_strvp_create(ares__hosts_entry_destroy_cb);
300
0
  if (hf->iphash == NULL) {
301
0
    goto fail;
302
0
  }
303
304
0
  hf->hosthash = ares__htable_strvp_create(NULL);
305
0
  if (hf->hosthash == NULL) {
306
0
    goto fail;
307
0
  }
308
309
0
  return hf;
310
311
0
fail:
312
0
  ares__hosts_file_destroy(hf);
313
0
  return NULL;
314
0
}
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
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
0
{
326
0
  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
0
  if (matchtype != ARES_MATCH_IPADDR) {
331
0
    while ((node = ares__llist_node_first(entry->ips)) != NULL) {
332
0
      const char *ipaddr = ares__llist_node_val(node);
333
334
0
      if (ares__htable_strvp_get_direct(hf->iphash, ipaddr) != NULL) {
335
0
        ares__llist_node_destroy(node);
336
0
        continue;
337
0
      }
338
339
0
      ares__llist_node_move_parent_last(node, existing->ips);
340
0
    }
341
0
  }
342
343
344
0
  while ((node = ares__llist_node_first(entry->hosts)) != NULL) {
345
0
    const char *hostname = ares__llist_node_val(node);
346
347
0
    if (ares__htable_strvp_get_direct(hf->hosthash, hostname) != NULL) {
348
0
      ares__llist_node_destroy(node);
349
0
      continue;
350
0
    }
351
352
0
    ares__llist_node_move_parent_last(node, existing->hosts);
353
0
  }
354
355
0
  ares__hosts_entry_destroy(entry);
356
0
  return ARES_SUCCESS;
357
0
}
358
359
static ares_hosts_file_match_t
360
  ares__hosts_file_match(const ares_hosts_file_t *hf, ares_hosts_entry_t *entry,
361
                         ares_hosts_entry_t **match)
362
0
{
363
0
  ares__llist_node_t *node;
364
0
  *match = NULL;
365
366
0
  for (node = ares__llist_node_first(entry->ips); node != NULL;
367
0
       node = ares__llist_node_next(node)) {
368
0
    const char *ipaddr = ares__llist_node_val(node);
369
0
    *match             = ares__htable_strvp_get_direct(hf->iphash, ipaddr);
370
0
    if (*match != NULL) {
371
0
      return ARES_MATCH_IPADDR;
372
0
    }
373
0
  }
374
375
0
  for (node = ares__llist_node_first(entry->hosts); node != NULL;
376
0
       node = ares__llist_node_next(node)) {
377
0
    const char *host = ares__llist_node_val(node);
378
0
    *match           = ares__htable_strvp_get_direct(hf->hosthash, host);
379
0
    if (*match != NULL) {
380
0
      return ARES_MATCH_HOST;
381
0
    }
382
0
  }
383
384
0
  return ARES_MATCH_NONE;
385
0
}
386
387
/*! entry is invalidated upon calling this function, always, even on error */
388
static ares_status_t ares__hosts_file_add(ares_hosts_file_t  *hosts,
389
                                          ares_hosts_entry_t *entry)
390
0
{
391
0
  ares_hosts_entry_t     *match  = NULL;
392
0
  ares_status_t           status = ARES_SUCCESS;
393
0
  ares__llist_node_t     *node;
394
0
  ares_hosts_file_match_t matchtype;
395
0
  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
0
  num_hostnames = ares__llist_len(entry->hosts);
401
402
0
  matchtype = ares__hosts_file_match(hosts, entry, &match);
403
404
0
  if (matchtype != ARES_MATCH_NONE) {
405
0
    status = ares__hosts_file_merge_entry(hosts, match, entry, matchtype);
406
0
    if (status != ARES_SUCCESS) {
407
0
      ares__hosts_entry_destroy(entry);
408
0
      return status;
409
0
    }
410
    /* entry was invalidated above by merging */
411
0
    entry = match;
412
0
  }
413
414
0
  if (matchtype != ARES_MATCH_IPADDR) {
415
0
    const char *ipaddr = ares__llist_last_val(entry->ips);
416
417
0
    if (!ares__htable_strvp_get(hosts->iphash, ipaddr, NULL)) {
418
0
      if (!ares__htable_strvp_insert(hosts->iphash, ipaddr, entry)) {
419
0
        ares__hosts_entry_destroy(entry);
420
0
        return ARES_ENOMEM;
421
0
      }
422
0
      entry->refcnt++;
423
0
    }
424
0
  }
425
426
  /* Go backwards, on a merge, hostnames are appended.  Breakout once we've
427
   * consumed all the hosts that we appended */
428
0
  for (node = ares__llist_node_last(entry->hosts); node != NULL;
429
0
       node = ares__llist_node_prev(node)) {
430
0
    const char *val = ares__llist_node_val(node);
431
432
0
    if (num_hostnames == 0) {
433
0
      break;
434
0
    }
435
436
0
    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
0
    if (ares__htable_strvp_get(hosts->hosthash, val, NULL)) {
441
0
      continue;
442
0
    }
443
444
0
    if (!ares__htable_strvp_insert(hosts->hosthash, val, entry)) {
445
0
      return ARES_ENOMEM;
446
0
    }
447
0
  }
448
449
0
  return ARES_SUCCESS;
450
0
}
451
452
static ares_bool_t ares__hosts_entry_isdup(ares_hosts_entry_t *entry,
453
                                           const char         *host)
454
0
{
455
0
  ares__llist_node_t *node;
456
457
0
  for (node = ares__llist_node_first(entry->ips); node != NULL;
458
0
       node = ares__llist_node_next(node)) {
459
0
    const char *myhost = ares__llist_node_val(node);
460
0
    if (strcasecmp(myhost, host) == 0) {
461
0
      return ARES_TRUE;
462
0
    }
463
0
  }
464
465
0
  return ARES_FALSE;
466
0
}
467
468
static ares_status_t ares__parse_hosts_hostnames(ares__buf_t        *buf,
469
                                                 ares_hosts_entry_t *entry)
470
0
{
471
0
  entry->hosts = ares__llist_create(ares_free);
472
0
  if (entry->hosts == NULL) {
473
0
    return ARES_ENOMEM;
474
0
  }
475
476
  /* Parse hostnames and aliases */
477
0
  while (ares__buf_len(buf)) {
478
0
    char          hostname[256];
479
0
    char         *temp;
480
0
    ares_status_t status;
481
0
    unsigned char comment = '#';
482
483
0
    ares__buf_consume_whitespace(buf, ARES_FALSE);
484
485
0
    if (ares__buf_len(buf) == 0) {
486
0
      break;
487
0
    }
488
489
    /* See if it is a comment, if so stop processing */
490
0
    if (ares__buf_begins_with(buf, &comment, 1)) {
491
0
      break;
492
0
    }
493
494
0
    ares__buf_tag(buf);
495
496
    /* Must be at end of line */
497
0
    if (ares__buf_consume_nonwhitespace(buf) == 0) {
498
0
      break;
499
0
    }
500
501
0
    status = ares__buf_tag_fetch_string(buf, hostname, sizeof(hostname));
502
0
    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
0
      if (ares__llist_len(entry->hosts) == 0) {
506
0
        return ARES_EBADSTR;
507
0
      }
508
509
0
      continue;
510
0
    }
511
512
    /* Validate it is a valid hostname characterset */
513
0
    if (!ares__is_hostname(hostname)) {
514
0
      continue;
515
0
    }
516
517
    /* Don't add a duplicate to the same entry */
518
0
    if (ares__hosts_entry_isdup(entry, hostname)) {
519
0
      continue;
520
0
    }
521
522
    /* Add to list */
523
0
    temp = ares_strdup(hostname);
524
0
    if (temp == NULL) {
525
0
      return ARES_ENOMEM;
526
0
    }
527
528
0
    if (ares__llist_insert_last(entry->hosts, temp) == NULL) {
529
0
      ares_free(temp);
530
0
      return ARES_ENOMEM;
531
0
    }
532
0
  }
533
534
  /* Must have at least 1 entry */
535
0
  if (ares__llist_len(entry->hosts) == 0) {
536
0
    return ARES_EBADSTR;
537
0
  }
538
539
0
  return ARES_SUCCESS;
540
0
}
541
542
static ares_status_t ares__parse_hosts_ipaddr(ares__buf_t         *buf,
543
                                              ares_hosts_entry_t **entry_out)
544
0
{
545
0
  char                addr[INET6_ADDRSTRLEN];
546
0
  char               *temp;
547
0
  ares_hosts_entry_t *entry = NULL;
548
0
  ares_status_t       status;
549
550
0
  *entry_out = NULL;
551
552
0
  ares__buf_tag(buf);
553
0
  ares__buf_consume_nonwhitespace(buf);
554
0
  status = ares__buf_tag_fetch_string(buf, addr, sizeof(addr));
555
0
  if (status != ARES_SUCCESS) {
556
0
    return status;
557
0
  }
558
559
  /* Validate and normalize the ip address format */
560
0
  if (!ares__normalize_ipaddr(addr, addr, sizeof(addr))) {
561
0
    return ARES_EBADSTR;
562
0
  }
563
564
0
  entry = ares_malloc_zero(sizeof(*entry));
565
0
  if (entry == NULL) {
566
0
    return ARES_ENOMEM;
567
0
  }
568
569
0
  entry->ips = ares__llist_create(ares_free);
570
0
  if (entry->ips == NULL) {
571
0
    ares__hosts_entry_destroy(entry);
572
0
    return ARES_ENOMEM;
573
0
  }
574
575
0
  temp = ares_strdup(addr);
576
0
  if (temp == NULL) {
577
0
    ares__hosts_entry_destroy(entry);
578
0
    return ARES_ENOMEM;
579
0
  }
580
581
0
  if (ares__llist_insert_first(entry->ips, temp) == NULL) {
582
0
    ares_free(temp);
583
0
    ares__hosts_entry_destroy(entry);
584
0
    return ARES_ENOMEM;
585
0
  }
586
587
0
  *entry_out = entry;
588
589
0
  return ARES_SUCCESS;
590
0
}
591
592
static ares_status_t ares__parse_hosts(const char         *filename,
593
                                       ares_hosts_file_t **out)
594
0
{
595
0
  ares__buf_t        *buf    = NULL;
596
0
  ares_status_t       status = ARES_EBADRESP;
597
0
  ares_hosts_file_t  *hf     = NULL;
598
0
  ares_hosts_entry_t *entry  = NULL;
599
600
0
  *out = NULL;
601
602
0
  buf = ares__buf_create();
603
0
  if (buf == NULL) {
604
0
    status = ARES_ENOMEM;
605
0
    goto done;
606
0
  }
607
608
0
  status = ares__read_file_into_buf(filename, buf);
609
0
  if (status != ARES_SUCCESS) {
610
0
    goto done;
611
0
  }
612
613
0
  hf = ares__hosts_file_create(filename);
614
0
  if (hf == NULL) {
615
0
    status = ARES_ENOMEM;
616
0
    goto done;
617
0
  }
618
619
0
  while (ares__buf_len(buf)) {
620
0
    unsigned char comment = '#';
621
622
    /* -- Start of new line here -- */
623
624
    /* Consume any leading whitespace */
625
0
    ares__buf_consume_whitespace(buf, ARES_FALSE);
626
627
0
    if (ares__buf_len(buf) == 0) {
628
0
      break;
629
0
    }
630
631
    /* See if it is a comment, if so, consume remaining line */
632
0
    if (ares__buf_begins_with(buf, &comment, 1)) {
633
0
      ares__buf_consume_line(buf, ARES_TRUE);
634
0
      continue;
635
0
    }
636
637
    /* Pull off ip address */
638
0
    status = ares__parse_hosts_ipaddr(buf, &entry);
639
0
    if (status == ARES_ENOMEM) {
640
0
      goto done;
641
0
    }
642
0
    if (status != ARES_SUCCESS) {
643
      /* Bad line, consume and go onto next */
644
0
      ares__buf_consume_line(buf, ARES_TRUE);
645
0
      continue;
646
0
    }
647
648
    /* Parse of the hostnames */
649
0
    status = ares__parse_hosts_hostnames(buf, entry);
650
0
    if (status == ARES_ENOMEM) {
651
0
      goto done;
652
0
    } else if (status != ARES_SUCCESS) {
653
      /* Bad line, consume and go onto next */
654
0
      ares__hosts_entry_destroy(entry);
655
0
      entry = NULL;
656
0
      ares__buf_consume_line(buf, ARES_TRUE);
657
0
      continue;
658
0
    }
659
660
    /* Append the successful entry to the hosts file */
661
0
    status = ares__hosts_file_add(hf, entry);
662
0
    entry  = NULL; /* is always invalidated by this function, even on error */
663
0
    if (status != ARES_SUCCESS) {
664
0
      goto done;
665
0
    }
666
667
    /* Go to next line */
668
0
    ares__buf_consume_line(buf, ARES_TRUE);
669
0
  }
670
671
0
  status = ARES_SUCCESS;
672
673
0
done:
674
0
  ares__hosts_entry_destroy(entry);
675
0
  ares__buf_destroy(buf);
676
0
  if (status != ARES_SUCCESS) {
677
0
    ares__hosts_file_destroy(hf);
678
0
  } else {
679
0
    *out = hf;
680
0
  }
681
0
  return status;
682
0
}
683
684
static ares_bool_t ares__hosts_expired(const char              *filename,
685
                                       const ares_hosts_file_t *hf)
686
0
{
687
0
  time_t mod_ts = 0;
688
689
0
#ifdef HAVE_STAT
690
0
  struct stat st;
691
0
  if (stat(filename, &st) == 0) {
692
0
    mod_ts = st.st_mtime;
693
0
  }
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
0
  if (hf == NULL) {
704
0
    return ARES_TRUE;
705
0
  }
706
707
  /* Expire every 60s if we can't get a time */
708
0
  if (mod_ts == 0) {
709
0
    mod_ts = time(NULL) - 60;
710
0
  }
711
712
  /* If filenames are different, its expired */
713
0
  if (strcasecmp(hf->filename, filename) != 0) {
714
0
    return ARES_TRUE;
715
0
  }
716
717
0
  if (hf->ts <= mod_ts) {
718
0
    return ARES_TRUE;
719
0
  }
720
721
0
  return ARES_FALSE;
722
0
}
723
724
static ares_status_t ares__hosts_path(const ares_channel_t *channel,
725
                                      ares_bool_t use_env, char **path)
726
0
{
727
0
  char *path_hosts = NULL;
728
729
0
  *path = NULL;
730
731
0
  if (channel->hosts_path) {
732
0
    path_hosts = ares_strdup(channel->hosts_path);
733
0
    if (!path_hosts) {
734
0
      return ARES_ENOMEM;
735
0
    }
736
0
  }
737
738
0
  if (use_env) {
739
0
    if (path_hosts) {
740
0
      ares_free(path_hosts);
741
0
    }
742
743
0
    path_hosts = ares_strdup(getenv("CARES_HOSTS"));
744
0
    if (!path_hosts) {
745
0
      return ARES_ENOMEM;
746
0
    }
747
0
  }
748
749
0
  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
0
    path_hosts = ares_strdup(PATH_HOSTS);
772
0
    if (!path_hosts) {
773
0
      return ARES_ENOMEM;
774
0
    }
775
0
  }
776
777
0
  *path = path_hosts;
778
0
  return ARES_SUCCESS;
779
0
}
780
781
static ares_status_t ares__hosts_update(ares_channel_t *channel,
782
                                        ares_bool_t     use_env)
783
0
{
784
0
  ares_status_t status;
785
0
  char         *filename = NULL;
786
787
0
  status = ares__hosts_path(channel, use_env, &filename);
788
0
  if (status != ARES_SUCCESS) {
789
0
    return status;
790
0
  }
791
792
0
  if (!ares__hosts_expired(filename, channel->hf)) {
793
0
    ares_free(filename);
794
0
    return ARES_SUCCESS;
795
0
  }
796
797
0
  ares__hosts_file_destroy(channel->hf);
798
0
  channel->hf = NULL;
799
800
0
  status = ares__parse_hosts(filename, &channel->hf);
801
0
  ares_free(filename);
802
0
  return status;
803
0
}
804
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
0
{
809
0
  ares_status_t status;
810
0
  char          addr[INET6_ADDRSTRLEN];
811
812
0
  *entry = NULL;
813
814
0
  status = ares__hosts_update(channel, use_env);
815
0
  if (status != ARES_SUCCESS) {
816
0
    return status;
817
0
  }
818
819
0
  if (channel->hf == NULL) {
820
0
    return ARES_ENOTFOUND;
821
0
  }
822
823
0
  if (!ares__normalize_ipaddr(ipaddr, addr, sizeof(addr))) {
824
0
    return ARES_EBADNAME;
825
0
  }
826
827
0
  *entry = ares__htable_strvp_get_direct(channel->hf->iphash, addr);
828
0
  if (*entry == NULL) {
829
0
    return ARES_ENOTFOUND;
830
0
  }
831
832
0
  return ARES_SUCCESS;
833
0
}
834
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
0
{
839
0
  ares_status_t status;
840
841
0
  *entry = NULL;
842
843
0
  status = ares__hosts_update(channel, use_env);
844
0
  if (status != ARES_SUCCESS) {
845
0
    return status;
846
0
  }
847
848
0
  if (channel->hf == NULL) {
849
0
    return ARES_ENOTFOUND;
850
0
  }
851
852
0
  *entry = ares__htable_strvp_get_direct(channel->hf->hosthash, host);
853
0
  if (*entry == NULL) {
854
0
    return ARES_ENOTFOUND;
855
0
  }
856
857
0
  return ARES_SUCCESS;
858
0
}
859
860
ares_status_t ares__hosts_entry_to_hostent(const ares_hosts_entry_t *entry,
861
                                           int family, struct hostent **hostent)
862
0
{
863
0
  ares_status_t       status;
864
0
  size_t              naliases;
865
0
  ares__llist_node_t *node;
866
0
  size_t              idx;
867
868
0
  *hostent = ares_malloc_zero(sizeof(**hostent));
869
0
  if (*hostent == NULL) {
870
0
    status = ARES_ENOMEM;
871
0
    goto fail;
872
0
  }
873
874
0
  (*hostent)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)family;
875
876
  /* Copy IP addresses that match the address family */
877
0
  idx = 0;
878
0
  for (node = ares__llist_node_first(entry->ips); node != NULL;
879
0
       node = ares__llist_node_next(node)) {
880
0
    struct ares_addr addr;
881
0
    const void      *ptr     = NULL;
882
0
    size_t           ptr_len = 0;
883
0
    const char      *ipaddr  = ares__llist_node_val(node);
884
0
    char           **temp    = NULL;
885
886
0
    memset(&addr, 0, sizeof(addr));
887
888
0
    addr.family = family;
889
0
    ptr         = ares_dns_pton(ipaddr, &addr, &ptr_len);
890
0
    if (ptr == NULL) {
891
0
      continue;
892
0
    }
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
0
    if (family == AF_UNSPEC) {
897
0
      family                 = addr.family;
898
0
      (*hostent)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)addr.family;
899
0
    }
900
901
0
    temp = ares_realloc_zero((*hostent)->h_addr_list,
902
0
                             (idx + 1) * sizeof(*(*hostent)->h_addr_list),
903
0
                             (idx + 2) * sizeof(*(*hostent)->h_addr_list));
904
0
    if (temp == NULL) {
905
0
      status = ARES_ENOMEM;
906
0
      goto fail;
907
0
    }
908
909
0
    (*hostent)->h_addr_list = temp;
910
911
0
    (*hostent)->h_addr_list[idx] = ares_malloc(ptr_len);
912
0
    if ((*hostent)->h_addr_list[idx] == NULL) {
913
0
      status = ARES_ENOMEM;
914
0
      goto fail;
915
0
    }
916
917
0
    memcpy((*hostent)->h_addr_list[idx], ptr, ptr_len);
918
0
    idx++;
919
0
    (*hostent)->h_length = (HOSTENT_LENGTH_TYPE)ptr_len;
920
0
  }
921
922
  /* entry didn't match address class */
923
0
  if (idx == 0) {
924
0
    status = ARES_ENOTFOUND;
925
0
    goto fail;
926
0
  }
927
928
  /* Copy main hostname */
929
0
  (*hostent)->h_name = ares_strdup(ares__llist_first_val(entry->hosts));
930
0
  if ((*hostent)->h_name == NULL) {
931
0
    status = ARES_ENOMEM;
932
0
    goto fail;
933
0
  }
934
935
  /* Copy aliases */
936
0
  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
0
  if (naliases > 100) {
941
0
    naliases = 100;
942
0
  }
943
944
0
  (*hostent)->h_aliases =
945
0
    ares_malloc_zero((naliases + 1) * sizeof(*(*hostent)->h_aliases));
946
0
  if ((*hostent)->h_aliases == NULL) {
947
0
    status = ARES_ENOMEM;
948
0
    goto fail;
949
0
  }
950
951
  /* Copy all entries to the alias except the first */
952
0
  idx  = 0;
953
0
  node = ares__llist_node_first(entry->hosts);
954
0
  node = ares__llist_node_next(node);
955
0
  while (node != NULL) {
956
0
    (*hostent)->h_aliases[idx] = ares_strdup(ares__llist_node_val(node));
957
0
    if ((*hostent)->h_aliases[idx] == NULL) {
958
0
      status = ARES_ENOMEM;
959
0
      goto fail;
960
0
    }
961
0
    idx++;
962
963
    /* Break out if artificially capped */
964
0
    if (idx == naliases) {
965
0
      break;
966
0
    }
967
0
    node = ares__llist_node_next(node);
968
0
  }
969
970
0
  return ARES_SUCCESS;
971
972
0
fail:
973
0
  ares_free_hostent(*hostent);
974
0
  *hostent = NULL;
975
0
  return status;
976
0
}
977
978
static ares_status_t
979
  ares__hosts_ai_append_cnames(const ares_hosts_entry_t    *entry,
980
                               struct ares_addrinfo_cname **cnames_out)
981
0
{
982
0
  struct ares_addrinfo_cname *cname  = NULL;
983
0
  struct ares_addrinfo_cname *cnames = NULL;
984
0
  const char                 *primaryhost;
985
0
  ares__llist_node_t         *node;
986
0
  ares_status_t               status;
987
0
  size_t                      cnt = 0;
988
989
0
  node        = ares__llist_node_first(entry->hosts);
990
0
  primaryhost = ares__llist_node_val(node);
991
  /* Skip to next node to start with aliases */
992
0
  node = ares__llist_node_next(node);
993
994
0
  while (node != NULL) {
995
0
    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
0
    cnt++;
1000
0
    if (cnt > 100) {
1001
0
      break;
1002
0
    }
1003
1004
0
    cname = ares__append_addrinfo_cname(&cnames);
1005
0
    if (cname == NULL) {
1006
0
      status = ARES_ENOMEM;
1007
0
      goto done;
1008
0
    }
1009
1010
0
    cname->alias = ares_strdup(host);
1011
0
    if (cname->alias == NULL) {
1012
0
      status = ARES_ENOMEM;
1013
0
      goto done;
1014
0
    }
1015
1016
0
    cname->name = ares_strdup(primaryhost);
1017
0
    if (cname->name == NULL) {
1018
0
      status = ARES_ENOMEM;
1019
0
      goto done;
1020
0
    }
1021
1022
0
    node = ares__llist_node_next(node);
1023
0
  }
1024
1025
  /* No entries, add only primary */
1026
0
  if (cnames == NULL) {
1027
0
    cname = ares__append_addrinfo_cname(&cnames);
1028
0
    if (cname == NULL) {
1029
0
      status = ARES_ENOMEM;
1030
0
      goto done;
1031
0
    }
1032
1033
0
    cname->name = ares_strdup(primaryhost);
1034
0
    if (cname->name == NULL) {
1035
0
      status = ARES_ENOMEM;
1036
0
      goto done;
1037
0
    }
1038
0
  }
1039
0
  status = ARES_SUCCESS;
1040
1041
0
done:
1042
0
  if (status != ARES_SUCCESS) {
1043
0
    ares__freeaddrinfo_cnames(cnames);
1044
0
    return status;
1045
0
  }
1046
1047
0
  *cnames_out = cnames;
1048
0
  return ARES_SUCCESS;
1049
0
}
1050
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
0
{
1057
0
  ares_status_t               status;
1058
0
  struct ares_addrinfo_cname *cnames  = NULL;
1059
0
  struct ares_addrinfo_node  *ainodes = NULL;
1060
0
  ares__llist_node_t         *node;
1061
1062
0
  switch (family) {
1063
0
    case AF_INET:
1064
0
    case AF_INET6:
1065
0
    case AF_UNSPEC:
1066
0
      break;
1067
0
    default:
1068
0
      return ARES_EBADFAMILY;
1069
0
  }
1070
1071
0
  ai->name = ares_strdup(name);
1072
0
  if (ai->name == NULL) {
1073
0
    status = ARES_ENOMEM;
1074
0
    goto done;
1075
0
  }
1076
1077
0
  for (node = ares__llist_node_first(entry->ips); node != NULL;
1078
0
       node = ares__llist_node_next(node)) {
1079
0
    struct ares_addr addr;
1080
0
    const void      *ptr     = NULL;
1081
0
    size_t           ptr_len = 0;
1082
0
    const char      *ipaddr  = ares__llist_node_val(node);
1083
1084
0
    memset(&addr, 0, sizeof(addr));
1085
0
    addr.family = family;
1086
0
    ptr         = ares_dns_pton(ipaddr, &addr, &ptr_len);
1087
1088
0
    if (ptr == NULL) {
1089
0
      continue;
1090
0
    }
1091
1092
0
    status = ares_append_ai_node(addr.family, port, 0, ptr, &ainodes);
1093
0
    if (status != ARES_SUCCESS) {
1094
0
      goto done;
1095
0
    }
1096
0
  }
1097
1098
0
  if (want_cnames) {
1099
0
    status = ares__hosts_ai_append_cnames(entry, &cnames);
1100
0
    if (status != ARES_SUCCESS) {
1101
0
      goto done;
1102
0
    }
1103
0
  }
1104
1105
0
  status = ARES_SUCCESS;
1106
1107
0
done:
1108
0
  if (status != ARES_SUCCESS) {
1109
0
    ares__freeaddrinfo_cnames(cnames);
1110
0
    ares__freeaddrinfo_nodes(ainodes);
1111
0
    ares_free(ai->name);
1112
0
    ai->name = NULL;
1113
0
    return status;
1114
0
  }
1115
0
  ares__addrinfo_cat_cnames(&ai->cnames, cnames);
1116
0
  ares__addrinfo_cat_nodes(&ai->nodes, ainodes);
1117
1118
0
  return status;
1119
0
}