Coverage Report

Created: 2025-11-24 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/c-ares/src/lib/ares_sysconfig.c
Line
Count
Source
1
/* MIT License
2
 *
3
 * Copyright (c) 1998 Massachusetts Institute of Technology
4
 * Copyright (c) 2007 Daniel Stenberg
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice (including the next
14
 * paragraph) shall be included in all copies or substantial portions of the
15
 * Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
 * SOFTWARE.
24
 *
25
 * SPDX-License-Identifier: MIT
26
 */
27
28
#include "ares_private.h"
29
30
#ifdef HAVE_SYS_PARAM_H
31
#  include <sys/param.h>
32
#endif
33
34
#ifdef HAVE_NETINET_IN_H
35
#  include <netinet/in.h>
36
#endif
37
38
#ifdef HAVE_NETDB_H
39
#  include <netdb.h>
40
#endif
41
42
#ifdef HAVE_ARPA_INET_H
43
#  include <arpa/inet.h>
44
#endif
45
46
#if defined(ANDROID) || defined(__ANDROID__)
47
#  include <sys/system_properties.h>
48
#  include "ares_android.h"
49
/* From the Bionic sources */
50
#  define DNS_PROP_NAME_PREFIX "net.dns"
51
#  define MAX_DNS_PROPERTIES   8
52
#endif
53
54
#if defined(CARES_USE_LIBRESOLV)
55
#  include <resolv.h>
56
#endif
57
58
#include "ares_inet_net_pton.h"
59
60
61
#if defined(__MVS__)
62
static ares_status_t ares_init_sysconfig_mvs(const ares_channel_t *channel,
63
                                             ares_sysconfig_t     *sysconfig)
64
{
65
  struct __res_state *res = 0;
66
  size_t              count4;
67
  size_t              count6;
68
  int                 i;
69
  __STATEEXTIPV6     *v6;
70
  arse__llist_t      *sconfig = NULL;
71
  ares_status_t       status;
72
73
  if (0 == res) {
74
    int rc = res_init();
75
    while (rc == -1 && h_errno == TRY_AGAIN) {
76
      rc = res_init();
77
    }
78
    if (rc == -1) {
79
      return ARES_ENOMEM;
80
    }
81
    res = __res();
82
  }
83
84
  v6 = res->__res_extIPv6;
85
  if (res->nscount > 0) {
86
    count4 = (size_t)res->nscount;
87
  }
88
89
  if (v6 && v6->__stat_nscount > 0) {
90
    count6 = (size_t)v6->__stat_nscount;
91
  } else {
92
    count6 = 0;
93
  }
94
95
  for (i = 0; i < count4; i++) {
96
    struct sockaddr_in *addr_in = &(res->nsaddr_list[i]);
97
    struct ares_addr    addr;
98
99
    addr.addr.addr4.s_addr = addr_in->sin_addr.s_addr;
100
    addr.family            = AF_INET;
101
102
    status = ares_sconfig_append(channel, &sysconfig->sconfig, &addr,
103
                                 htons(addr_in->sin_port),
104
                                 htons(addr_in->sin_port), NULL);
105
106
    if (status != ARES_SUCCESS) {
107
      return status;
108
    }
109
  }
110
111
  for (i = 0; i < count6; i++) {
112
    struct sockaddr_in6 *addr_in = &(v6->__stat_nsaddr_list[i]);
113
    struct ares_addr     addr;
114
115
    addr.family = AF_INET6;
116
    memcpy(&(addr.addr.addr6), &(addr_in->sin6_addr),
117
           sizeof(addr_in->sin6_addr));
118
119
    status = ares_sconfig_append(channel, &sysconfig->sconfig, &addr,
120
                                 htons(addr_in->sin_port),
121
                                 htons(addr_in->sin_port), NULL);
122
123
    if (status != ARES_SUCCESS) {
124
      return status;
125
    }
126
  }
127
128
  return ARES_SUCCESS;
129
}
130
#endif
131
132
#if defined(__riscos__)
133
static ares_status_t ares_init_sysconfig_riscos(const ares_channel_t *channel,
134
                                                ares_sysconfig_t     *sysconfig)
135
{
136
  char         *line;
137
  ares_status_t status = ARES_SUCCESS;
138
139
  /* Under RISC OS, name servers are listed in the
140
     system variable Inet$Resolvers, space separated. */
141
  line = getenv("Inet$Resolvers");
142
  if (line) {
143
    char *resolvers = ares_strdup(line);
144
    char *pos;
145
    char *space;
146
147
    if (!resolvers) {
148
      return ARES_ENOMEM;
149
    }
150
151
    pos = resolvers;
152
    do {
153
      space = strchr(pos, ' ');
154
      if (space) {
155
        *space = '\0';
156
      }
157
      status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig, pos,
158
                                           ARES_TRUE);
159
      if (status != ARES_SUCCESS) {
160
        break;
161
      }
162
      pos = space + 1;
163
    } while (space);
164
165
    ares_free(resolvers);
166
  }
167
168
  return status;
169
}
170
#endif
171
172
#if defined(WATT32)
173
static ares_status_t ares_init_sysconfig_watt32(const ares_channel_t *channel,
174
                                                ares_sysconfig_t     *sysconfig)
175
{
176
  size_t        i;
177
  ares_status_t status;
178
179
  sock_init();
180
181
  for (i = 0; def_nameservers[i]; i++) {
182
    struct ares_addr addr;
183
184
    addr.family            = AF_INET;
185
    addr.addr.addr4.s_addr = htonl(def_nameservers[i]);
186
187
    status =
188
      ares_sconfig_append(channel, &sysconfig->sconfig, &addr, 0, 0, NULL);
189
190
    if (status != ARES_SUCCESS) {
191
      return status;
192
    }
193
  }
194
195
  return ARES_SUCCESS;
196
}
197
#endif
198
199
#if defined(ANDROID) || defined(__ANDROID__)
200
static ares_status_t ares_init_sysconfig_android(const ares_channel_t *channel,
201
                                                 ares_sysconfig_t *sysconfig)
202
{
203
  size_t        i;
204
  char        **dns_servers;
205
  char         *domains;
206
  size_t        num_servers;
207
  ares_status_t status = ARES_EFILE;
208
209
  /* Use the Android connectivity manager to get a list
210
   * of DNS servers. As of Android 8 (Oreo) net.dns#
211
   * system properties are no longer available. Google claims this
212
   * improves privacy. Apps now need the ACCESS_NETWORK_STATE
213
   * permission and must use the ConnectivityManager which
214
   * is Java only. */
215
  dns_servers = ares_get_android_server_list(MAX_DNS_PROPERTIES, &num_servers);
216
  if (dns_servers != NULL) {
217
    for (i = 0; i < num_servers; i++) {
218
      status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig,
219
                                           dns_servers[i], ARES_TRUE);
220
      if (status != ARES_SUCCESS) {
221
        return status;
222
      }
223
    }
224
    for (i = 0; i < num_servers; i++) {
225
      ares_free(dns_servers[i]);
226
    }
227
    ares_free(dns_servers);
228
  }
229
230
  domains            = ares_get_android_search_domains_list();
231
  sysconfig->domains = ares_strsplit(domains, ", ", &sysconfig->ndomains);
232
  ares_free(domains);
233
234
#  ifdef HAVE___SYSTEM_PROPERTY_GET
235
  /* Old way using the system property still in place as
236
   * a fallback. Older android versions can still use this.
237
   * it's possible for older apps not not have added the new
238
   * permission and we want to try to avoid breaking those.
239
   *
240
   * We'll only run this if we don't have any dns servers
241
   * because this will get the same ones (if it works). */
242
  if (sysconfig->sconfig == NULL) {
243
    char propname[PROP_NAME_MAX];
244
    char propvalue[PROP_VALUE_MAX] = "";
245
    for (i = 1; i <= MAX_DNS_PROPERTIES; i++) {
246
      snprintf(propname, sizeof(propname), "%s%zu", DNS_PROP_NAME_PREFIX, i);
247
      if (__system_property_get(propname, propvalue) < 1) {
248
        break;
249
      }
250
      status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig,
251
                                           propvalue, ARES_TRUE);
252
      if (status != ARES_SUCCESS) {
253
        return status;
254
      }
255
    }
256
  }
257
#  endif /* HAVE___SYSTEM_PROPERTY_GET */
258
259
  return status;
260
}
261
#endif
262
263
#if defined(__QNX__)
264
static ares_status_t
265
  ares_init_sysconfig_qnx(const ares_channel_t *channel,
266
                          ares_sysconfig_t     *sysconfig)
267
{
268
  /* QNX:
269
   *   1. use confstr(_CS_RESOLVE, ...) as primary resolv.conf data, replacing
270
   *      "_" with " ".  If that is empty, then do normal /etc/resolv.conf
271
   *      processing.
272
   *   2. We want to process /etc/nsswitch.conf as normal.
273
   *   3. if confstr(_CS_DOMAIN, ...) this is the domain name.  Use this as
274
   *      preference over anything else found.
275
   */
276
  ares_buf_t    *buf                = ares_buf_create();
277
  unsigned char *data               = NULL;
278
  size_t         data_size          = 0;
279
  ares_bool_t    process_resolvconf = ARES_TRUE;
280
  ares_status_t  status             = ARES_SUCCESS;
281
282
  /* Prefer confstr(_CS_RESOLVE, ...) */
283
  buf = ares_buf_create();
284
  if (buf == NULL) {
285
    status = ARES_ENOMEM;
286
    goto done;
287
  }
288
289
  data_size = 1024;
290
  data      = ares_buf_append_start(buf, &data_size);
291
  if (data == NULL) {
292
    status = ARES_ENOMEM;
293
    goto done;
294
  }
295
296
  data_size = confstr(_CS_RESOLVE, (char *)data, data_size);
297
  if (data_size > 1) {
298
    /* confstr returns byte for NULL terminator, strip */
299
    data_size--;
300
301
    ares_buf_append_finish(buf, data_size);
302
    /* Its odd, this uses _ instead of " " between keywords, otherwise the
303
     * format is the same as resolv.conf, replace. */
304
    ares_buf_replace(buf, (const unsigned char *)"_", 1,
305
                     (const unsigned char *)" ", 1);
306
307
    status = ares_sysconfig_process_buf(channel, sysconfig, buf,
308
                                        ares_sysconfig_parse_resolv_line);
309
    if (status != ARES_SUCCESS) {
310
      /* ENOMEM is really the only error we'll get here */
311
      goto done;
312
    }
313
314
    /* don't read resolv.conf if we processed *any* nameservers */
315
    if (ares_llist_len(sysconfig->sconfig) != 0) {
316
      process_resolvconf = ARES_FALSE;
317
    }
318
  }
319
320
  /* Process files */
321
  status = ares_init_sysconfig_files(channel, sysconfig, process_resolvconf);
322
  if (status != ARES_SUCCESS) {
323
    goto done;
324
  }
325
326
  /* Read confstr(_CS_DOMAIN, ...), but if we had a search path specified with
327
   * more than one domain, lets prefer that instead.  Its not exactly clear
328
   * the best way to handle this. */
329
  if (sysconfig->ndomains <= 1) {
330
    char   domain[256];
331
    size_t domain_len;
332
333
    domain_len = confstr(_CS_DOMAIN, domain, sizeof(domain_len));
334
    if (domain_len != 0) {
335
      ares_strsplit_free(sysconfig->domains, sysconfig->ndomains);
336
      sysconfig->domains = ares_strsplit(domain, ", ", &sysconfig->ndomains);
337
      if (sysconfig->domains == NULL) {
338
        status = ARES_ENOMEM;
339
        goto done;
340
      }
341
    }
342
  }
343
344
done:
345
  ares_buf_destroy(buf);
346
347
  return status;
348
}
349
#endif
350
351
#if defined(CARES_USE_LIBRESOLV)
352
static ares_status_t
353
  ares_init_sysconfig_libresolv(const ares_channel_t *channel,
354
                                ares_sysconfig_t     *sysconfig)
355
{
356
  struct __res_state       res;
357
  ares_status_t            status = ARES_SUCCESS;
358
  union res_sockaddr_union addr[MAXNS];
359
  int                      nscount;
360
  size_t                   i;
361
  size_t                   entries = 0;
362
  ares_buf_t              *ipbuf   = NULL;
363
364
  memset(&res, 0, sizeof(res));
365
366
  if (res_ninit(&res) != 0 || !(res.options & RES_INIT)) {
367
    return ARES_EFILE;
368
  }
369
370
  nscount = res_getservers(&res, addr, MAXNS);
371
372
  for (i = 0; i < (size_t)nscount; ++i) {
373
    char           ipaddr[INET6_ADDRSTRLEN] = "";
374
    char          *ipstr                    = NULL;
375
    unsigned short port                     = 0;
376
    unsigned int   ll_scope                 = 0;
377
378
    sa_family_t    family = addr[i].sin.sin_family;
379
    if (family == AF_INET) {
380
      ares_inet_ntop(family, &addr[i].sin.sin_addr, ipaddr, sizeof(ipaddr));
381
      port = ntohs(addr[i].sin.sin_port);
382
    } else if (family == AF_INET6) {
383
      ares_inet_ntop(family, &addr[i].sin6.sin6_addr, ipaddr, sizeof(ipaddr));
384
      port     = ntohs(addr[i].sin6.sin6_port);
385
      ll_scope = addr[i].sin6.sin6_scope_id;
386
    } else {
387
      continue;
388
    }
389
390
391
    /* [ip]:port%iface */
392
    ipbuf = ares_buf_create();
393
    if (ipbuf == NULL) {
394
      status = ARES_ENOMEM;
395
      goto done;
396
    }
397
398
    status = ares_buf_append_str(ipbuf, "[");
399
    if (status != ARES_SUCCESS) {
400
      goto done;
401
    }
402
403
    status = ares_buf_append_str(ipbuf, ipaddr);
404
    if (status != ARES_SUCCESS) {
405
      goto done;
406
    }
407
408
    status = ares_buf_append_str(ipbuf, "]");
409
    if (status != ARES_SUCCESS) {
410
      goto done;
411
    }
412
413
    if (port) {
414
      status = ares_buf_append_str(ipbuf, ":");
415
      if (status != ARES_SUCCESS) {
416
        goto done;
417
      }
418
      status = ares_buf_append_num_dec(ipbuf, port, 0);
419
      if (status != ARES_SUCCESS) {
420
        goto done;
421
      }
422
    }
423
424
    if (ll_scope) {
425
      status = ares_buf_append_str(ipbuf, "%");
426
      if (status != ARES_SUCCESS) {
427
        goto done;
428
      }
429
      status = ares_buf_append_num_dec(ipbuf, ll_scope, 0);
430
      if (status != ARES_SUCCESS) {
431
        goto done;
432
      }
433
    }
434
435
    ipstr = ares_buf_finish_str(ipbuf, NULL);
436
    ipbuf = NULL;
437
    if (ipstr == NULL) {
438
      status = ARES_ENOMEM;
439
      goto done;
440
    }
441
442
    status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig, ipstr,
443
                                         ARES_TRUE);
444
445
    ares_free(ipstr);
446
    if (status != ARES_SUCCESS) {
447
      goto done;
448
    }
449
  }
450
451
  while ((entries < MAXDNSRCH) && res.dnsrch[entries]) {
452
    entries++;
453
  }
454
455
  if (entries) {
456
    sysconfig->domains = ares_malloc_zero(entries * sizeof(char *));
457
    if (sysconfig->domains == NULL) {
458
      status = ARES_ENOMEM;
459
      goto done;
460
    } else {
461
      sysconfig->ndomains = entries;
462
      for (i = 0; i < sysconfig->ndomains; i++) {
463
        sysconfig->domains[i] = ares_strdup(res.dnsrch[i]);
464
        if (sysconfig->domains[i] == NULL) {
465
          status = ARES_ENOMEM;
466
          goto done;
467
        }
468
      }
469
    }
470
  }
471
472
  if (res.ndots >= 0) {
473
    sysconfig->ndots = (size_t)res.ndots;
474
  }
475
/* Apple does not allow configuration of retry, so this is a static dummy
476
 * value, ignore */
477
#  ifndef __APPLE__
478
  if (res.retry > 0) {
479
    sysconfig->tries = (size_t)res.retry;
480
  }
481
#  endif
482
  if (res.options & RES_ROTATE) {
483
    sysconfig->rotate = ARES_TRUE;
484
  }
485
486
  if (res.retrans > 0) {
487
/* Apple does not allow configuration of retrans, so this is a dummy value
488
 * that is extremely high (5s) */
489
#  ifndef __APPLE__
490
    if (res.retrans > 0) {
491
      sysconfig->timeout_ms = (unsigned int)res.retrans * 1000;
492
    }
493
#  endif
494
  }
495
496
done:
497
  ares_buf_destroy(ipbuf);
498
  res_ndestroy(&res);
499
  return status;
500
}
501
#endif
502
503
static void ares_sysconfig_free(ares_sysconfig_t *sysconfig)
504
4.20k
{
505
4.20k
  ares_llist_destroy(sysconfig->sconfig);
506
4.20k
  ares_strsplit_free(sysconfig->domains, sysconfig->ndomains);
507
4.20k
  ares_free(sysconfig->sortlist);
508
4.20k
  ares_free(sysconfig->lookups);
509
4.20k
  memset(sysconfig, 0, sizeof(*sysconfig));
510
4.20k
}
511
512
static ares_status_t ares_sysconfig_apply(ares_channel_t         *channel,
513
                                          const ares_sysconfig_t *sysconfig)
514
4.20k
{
515
4.20k
  ares_status_t status;
516
517
4.20k
  if (sysconfig->sconfig && !(channel->optmask & ARES_OPT_SERVERS)) {
518
4.20k
    status = ares_servers_update(channel, sysconfig->sconfig, ARES_FALSE);
519
4.20k
    if (status != ARES_SUCCESS) {
520
0
      return status;
521
0
    }
522
4.20k
  }
523
524
4.20k
  if (sysconfig->domains && !(channel->optmask & ARES_OPT_DOMAINS)) {
525
    /* Make sure we duplicate first then replace so even if there is
526
     * ARES_ENOMEM, the channel stays in a good state */
527
4.20k
    char **temp =
528
4.20k
      ares_strsplit_duplicate(sysconfig->domains, sysconfig->ndomains);
529
4.20k
    if (temp == NULL) {
530
0
      return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
531
0
    }
532
533
4.20k
    ares_strsplit_free(channel->domains, channel->ndomains);
534
4.20k
    channel->domains  = temp;
535
4.20k
    channel->ndomains = sysconfig->ndomains;
536
4.20k
  }
537
538
4.20k
  if (sysconfig->lookups && !(channel->optmask & ARES_OPT_LOOKUPS)) {
539
4.20k
    char *temp = ares_strdup(sysconfig->lookups);
540
4.20k
    if (temp == NULL) {
541
0
      return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
542
0
    }
543
544
4.20k
    ares_free(channel->lookups);
545
4.20k
    channel->lookups = temp;
546
4.20k
  }
547
548
4.20k
  if (sysconfig->sortlist && !(channel->optmask & ARES_OPT_SORTLIST)) {
549
0
    struct apattern *temp =
550
0
      ares_malloc(sizeof(*channel->sortlist) * sysconfig->nsortlist);
551
0
    if (temp == NULL) {
552
0
      return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
553
0
    }
554
0
    memcpy(temp, sysconfig->sortlist,
555
0
           sizeof(*channel->sortlist) * sysconfig->nsortlist);
556
557
0
    ares_free(channel->sortlist);
558
0
    channel->sortlist = temp;
559
0
    channel->nsort    = sysconfig->nsortlist;
560
0
  }
561
562
4.20k
  if (!(channel->optmask & ARES_OPT_NDOTS)) {
563
4.20k
    channel->ndots = sysconfig->ndots;
564
4.20k
  }
565
566
4.20k
  if (sysconfig->tries && !(channel->optmask & ARES_OPT_TRIES)) {
567
0
    channel->tries = sysconfig->tries;
568
0
  }
569
570
4.20k
  if (sysconfig->timeout_ms && !(channel->optmask & ARES_OPT_TIMEOUTMS)) {
571
0
    channel->timeout = sysconfig->timeout_ms;
572
0
  }
573
574
4.20k
  if (!(channel->optmask & (ARES_OPT_ROTATE | ARES_OPT_NOROTATE))) {
575
4.20k
    channel->rotate = sysconfig->rotate;
576
4.20k
  }
577
578
4.20k
  if (sysconfig->usevc) {
579
0
    channel->flags |= ARES_FLAG_USEVC;
580
0
  }
581
582
4.20k
  return ARES_SUCCESS;
583
4.20k
}
584
585
ares_status_t ares_init_by_sysconfig(ares_channel_t *channel)
586
4.20k
{
587
4.20k
  ares_status_t    status;
588
4.20k
  ares_sysconfig_t sysconfig;
589
590
4.20k
  memset(&sysconfig, 0, sizeof(sysconfig));
591
4.20k
  sysconfig.ndots = 1; /* Default value if not otherwise set */
592
593
#if defined(USE_WINSOCK)
594
  status = ares_init_sysconfig_windows(channel, &sysconfig);
595
#elif defined(__MVS__)
596
  status = ares_init_sysconfig_mvs(channel, &sysconfig);
597
#elif defined(__riscos__)
598
  status = ares_init_sysconfig_riscos(channel, &sysconfig);
599
#elif defined(WATT32)
600
  status = ares_init_sysconfig_watt32(channel, &sysconfig);
601
#elif defined(ANDROID) || defined(__ANDROID__)
602
  status = ares_init_sysconfig_android(channel, &sysconfig);
603
#elif defined(__APPLE__)
604
  status = ares_init_sysconfig_macos(channel, &sysconfig);
605
#elif defined(CARES_USE_LIBRESOLV)
606
  status = ares_init_sysconfig_libresolv(channel, &sysconfig);
607
#elif defined(__QNX__)
608
  status = ares_init_sysconfig_qnx(channel, &sysconfig);
609
#else
610
4.20k
  status = ares_init_sysconfig_files(channel, &sysconfig, ARES_TRUE);
611
4.20k
#endif
612
613
4.20k
  if (status != ARES_SUCCESS) {
614
0
    goto done;
615
0
  }
616
617
  /* Environment is supposed to override sysconfig */
618
4.20k
  status = ares_init_by_environment(&sysconfig);
619
4.20k
  if (status != ARES_SUCCESS) {
620
0
    goto done;
621
0
  }
622
623
  /* Lock when applying the configuration to the channel.  Don't need to
624
   * lock prior to this. */
625
626
4.20k
  ares_channel_lock(channel);
627
628
4.20k
  status = ares_sysconfig_apply(channel, &sysconfig);
629
4.20k
  ares_channel_unlock(channel);
630
631
4.20k
  if (status != ARES_SUCCESS) {
632
0
    goto done;
633
0
  }
634
635
4.20k
done:
636
4.20k
  ares_sysconfig_free(&sysconfig);
637
638
4.20k
  return status;
639
4.20k
}