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_setup.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 #include "ares_nameser.h"
47
48 #if defined(ANDROID) || defined(__ANDROID__)
49 # include <sys/system_properties.h>
50 # include "ares_android.h"
51 /* From the Bionic sources */
52 # define DNS_PROP_NAME_PREFIX "net.dns"
53 # define MAX_DNS_PROPERTIES 8
54 #endif
55
56 #if defined(CARES_USE_LIBRESOLV)
57 # include <resolv.h>
58 #endif
59
60 #if defined(USE_WINSOCK) && defined(HAVE_IPHLPAPI_H)
61 # include <iphlpapi.h>
62 #endif
63
64 #include "ares.h"
65 #include "ares_inet_net_pton.h"
66 #include "ares_platform.h"
67 #include "ares_private.h"
68
ip_natural_mask(const struct ares_addr *addr)69 static unsigned char ip_natural_mask(const struct ares_addr *addr)
70 {
71 const unsigned char *ptr = NULL;
72 /* This is an odd one. If a raw ipv4 address is specified, then we take
73 * what is called a natural mask, which means we look at the first octet
74 * of the ip address and for values 0-127 we assume it is a class A (/8),
75 * for values 128-191 we assume it is a class B (/16), and for 192-223
76 * we assume it is a class C (/24). 223-239 is Class D which and 240-255 is
77 * Class E, however, there is no pre-defined mask for this, so we'll use
78 * /24 as well as that's what the old code did.
79 *
80 * For IPv6, we'll use /64.
81 */
82
83 if (addr->family == AF_INET6) {
84 return 64;
85 }
86
87 ptr = (const unsigned char *)&addr->addr.addr4;
88 if (*ptr < 128) {
89 return 8;
90 }
91
92 if (*ptr < 192) {
93 return 16;
94 }
95
96 return 24;
97 }
98
sortlist_append(struct apattern **sortlist, size_t *nsort, const struct apattern *pat)99 static ares_bool_t sortlist_append(struct apattern **sortlist, size_t *nsort,
100 const struct apattern *pat)
101 {
102 struct apattern *newsort;
103
104 newsort = ares_realloc(*sortlist, (*nsort + 1) * sizeof(*newsort));
105 if (newsort == NULL) {
106 return ARES_FALSE;
107 }
108
109 *sortlist = newsort;
110
111 memcpy(&(*sortlist)[*nsort], pat, sizeof(**sortlist));
112 (*nsort)++;
113
114 return ARES_TRUE;
115 }
116
parse_sort(ares__buf_t *buf, struct apattern *pat)117 static ares_status_t parse_sort(ares__buf_t *buf, struct apattern *pat)
118 {
119 ares_status_t status;
120 const unsigned char ip_charset[] = "ABCDEFabcdef0123456789.:";
121 char ipaddr[INET6_ADDRSTRLEN] = "";
122 size_t addrlen;
123
124 memset(pat, 0, sizeof(*pat));
125
126 /* Consume any leading whitespace */
127 ares__buf_consume_whitespace(buf, ARES_TRUE);
128
129 /* If no length, just ignore, return ENOTFOUND as an indicator */
130 if (ares__buf_len(buf) == 0) {
131 return ARES_ENOTFOUND;
132 }
133
134 ares__buf_tag(buf);
135
136 /* Consume ip address */
137 if (ares__buf_consume_charset(buf, ip_charset, sizeof(ip_charset)) == 0) {
138 return ARES_EBADSTR;
139 }
140
141 /* Fetch ip address */
142 status = ares__buf_tag_fetch_string(buf, ipaddr, sizeof(ipaddr));
143 if (status != ARES_SUCCESS) {
144 return status;
145 }
146
147 /* Parse it to make sure its valid */
148 pat->addr.family = AF_UNSPEC;
149 if (ares_dns_pton(ipaddr, &pat->addr, &addrlen) == NULL) {
150 return ARES_EBADSTR;
151 }
152
153 /* See if there is a subnet mask */
154 if (ares__buf_begins_with(buf, (const unsigned char *)"/", 1)) {
155 char maskstr[16];
156 const unsigned char ipv4_charset[] = "0123456789.";
157
158
159 /* Consume / */
160 ares__buf_consume(buf, 1);
161
162 ares__buf_tag(buf);
163
164 /* Consume mask */
165 if (ares__buf_consume_charset(buf, ipv4_charset, sizeof(ipv4_charset)) ==
166 0) {
167 return ARES_EBADSTR;
168 }
169
170 /* Fetch mask */
171 status = ares__buf_tag_fetch_string(buf, maskstr, sizeof(maskstr));
172 if (status != ARES_SUCCESS) {
173 return status;
174 }
175
176 if (ares_str_isnum(maskstr)) {
177 /* Numeric mask */
178 int mask = atoi(maskstr);
179 if (mask < 0 || mask > 128) {
180 return ARES_EBADSTR;
181 }
182 if (pat->addr.family == AF_INET && mask > 32) {
183 return ARES_EBADSTR;
184 }
185 pat->mask = (unsigned char)mask;
186 } else {
187 /* Ipv4 subnet style mask */
188 struct ares_addr maskaddr;
189 const unsigned char *ptr;
190
191 memset(&maskaddr, 0, sizeof(maskaddr));
192 maskaddr.family = AF_INET;
193 if (ares_dns_pton(maskstr, &maskaddr, &addrlen) == NULL) {
194 return ARES_EBADSTR;
195 }
196 ptr = (const unsigned char *)&maskaddr.addr.addr4;
197 pat->mask = (unsigned char)(ares__count_bits_u8(ptr[0]) +
198 ares__count_bits_u8(ptr[1]) +
199 ares__count_bits_u8(ptr[2]) +
200 ares__count_bits_u8(ptr[3]));
201 }
202 } else {
203 pat->mask = ip_natural_mask(&pat->addr);
204 }
205
206 /* Consume any trailing whitespace */
207 ares__buf_consume_whitespace(buf, ARES_TRUE);
208
209 /* If we have any trailing bytes other than whitespace, its a parse failure */
210 if (ares__buf_len(buf) != 0) {
211 return ARES_EBADSTR;
212 }
213
214 return ARES_SUCCESS;
215 }
216
ares__parse_sortlist(struct apattern **sortlist, size_t *nsort, const char *str)217 ares_status_t ares__parse_sortlist(struct apattern **sortlist, size_t *nsort,
218 const char *str)
219 {
220 ares__buf_t *buf = NULL;
221 ares__llist_t *list = NULL;
222 ares_status_t status = ARES_SUCCESS;
223 ares__llist_node_t *node = NULL;
224
225 if (sortlist == NULL || nsort == NULL || str == NULL) {
226 return ARES_EFORMERR;
227 }
228
229 if (*sortlist != NULL) {
230 ares_free(*sortlist);
231 }
232
233 *sortlist = NULL;
234 *nsort = 0;
235
236 buf = ares__buf_create_const((const unsigned char *)str, ares_strlen(str));
237 if (buf == NULL) {
238 status = ARES_ENOMEM;
239 goto done;
240 }
241
242 /* Split on space or semicolon */
243 status = ares__buf_split(buf, (const unsigned char *)" ;", 2,
244 ARES_BUF_SPLIT_NONE, &list);
245 if (status != ARES_SUCCESS) {
246 goto done;
247 }
248
249 for (node = ares__llist_node_first(list); node != NULL;
250 node = ares__llist_node_next(node)) {
251 ares__buf_t *entry = ares__llist_node_val(node);
252
253 struct apattern pat;
254
255 status = parse_sort(entry, &pat);
256 if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) {
257 goto done;
258 }
259
260 if (status != ARES_SUCCESS) {
261 continue;
262 }
263
264 if (!sortlist_append(sortlist, nsort, &pat)) {
265 status = ARES_ENOMEM;
266 goto done;
267 }
268 }
269
270 status = ARES_SUCCESS;
271
272 done:
273 ares__buf_destroy(buf);
274 ares__llist_destroy(list);
275
276 if (status != ARES_SUCCESS) {
277 ares_free(*sortlist);
278 *sortlist = NULL;
279 *nsort = 0;
280 }
281
282 return status;
283 }
284
config_search(ares_sysconfig_t *sysconfig, const char *str)285 static ares_status_t config_search(ares_sysconfig_t *sysconfig, const char *str)
286 {
287 if (sysconfig->domains && sysconfig->ndomains > 0) {
288 /* if we already have some domains present, free them first */
289 ares__strsplit_free(sysconfig->domains, sysconfig->ndomains);
290 sysconfig->domains = NULL;
291 sysconfig->ndomains = 0;
292 }
293
294 sysconfig->domains = ares__strsplit(str, ", ", &sysconfig->ndomains);
295 if (sysconfig->domains == NULL) {
296 return ARES_ENOMEM;
297 }
298
299 return ARES_SUCCESS;
300 }
301
config_domain(ares_sysconfig_t *sysconfig, char *str)302 static ares_status_t config_domain(ares_sysconfig_t *sysconfig, char *str)
303 {
304 char *q;
305
306 /* Set a single search domain. */
307 q = str;
308 while (*q && !ISSPACE(*q)) {
309 q++;
310 }
311 *q = '\0';
312
313 return config_search(sysconfig, str);
314 }
315
config_lookup(ares_sysconfig_t *sysconfig, const char *str, const char *bindch, const char *altbindch, const char *filech)316 static ares_status_t config_lookup(ares_sysconfig_t *sysconfig, const char *str,
317 const char *bindch, const char *altbindch,
318 const char *filech)
319 {
320 char lookups[3];
321 char *l;
322 const char *p;
323 ares_bool_t found;
324
325 if (altbindch == NULL) {
326 altbindch = bindch;
327 }
328
329 /* Set the lookup order. Only the first letter of each work
330 * is relevant, and it has to be "b" for DNS or "f" for the
331 * host file. Ignore everything else.
332 */
333 l = lookups;
334 p = str;
335 found = ARES_FALSE;
336 while (*p) {
337 if ((*p == *bindch || *p == *altbindch || *p == *filech) &&
338 l < lookups + 2) {
339 if (*p == *bindch || *p == *altbindch) {
340 *l++ = 'b';
341 } else {
342 *l++ = 'f';
343 }
344 found = ARES_TRUE;
345 }
346 while (*p && !ISSPACE(*p) && (*p != ',')) {
347 p++;
348 }
349 while (*p && (ISSPACE(*p) || (*p == ','))) {
350 p++;
351 }
352 }
353 if (!found) {
354 return ARES_ENOTINITIALIZED;
355 }
356 *l = '\0';
357
358 ares_free(sysconfig->lookups);
359 sysconfig->lookups = ares_strdup(lookups);
360 if (sysconfig->lookups == NULL) {
361 return ARES_ENOMEM;
362 }
363 return ARES_SUCCESS;
364 }
365
try_option(const char *p, const char *q, const char *opt)366 static const char *try_option(const char *p, const char *q, const char *opt)
367 {
368 size_t len = ares_strlen(opt);
369 return ((size_t)(q - p) >= len && !strncmp(p, opt, len)) ? &p[len] : NULL;
370 }
371
set_options(ares_sysconfig_t *sysconfig, const char *str)372 static ares_status_t set_options(ares_sysconfig_t *sysconfig, const char *str)
373 {
374 const char *p;
375 const char *q;
376 const char *val;
377
378 if (str == NULL) {
379 return ARES_SUCCESS;
380 }
381
382 p = str;
383 while (*p) {
384 q = p;
385 while (*q && !ISSPACE(*q)) {
386 q++;
387 }
388 val = try_option(p, q, "ndots:");
389 if (val) {
390 sysconfig->ndots = strtoul(val, NULL, 10);
391 }
392
393 // Outdated option.
394 val = try_option(p, q, "retrans:");
395 if (val) {
396 sysconfig->timeout_ms = strtoul(val, NULL, 10);
397 }
398
399 val = try_option(p, q, "timeout:");
400 if (val) {
401 sysconfig->timeout_ms = strtoul(val, NULL, 10) * 1000;
402 }
403
404 // Outdated option.
405 val = try_option(p, q, "retry:");
406 if (val) {
407 sysconfig->tries = strtoul(val, NULL, 10);
408 }
409
410 val = try_option(p, q, "attempts:");
411 if (val) {
412 sysconfig->tries = strtoul(val, NULL, 10);
413 }
414
415 val = try_option(p, q, "rotate");
416 if (val) {
417 sysconfig->rotate = ARES_TRUE;
418 }
419
420 p = q;
421 while (ISSPACE(*p)) {
422 p++;
423 }
424 }
425
426 return ARES_SUCCESS;
427 }
428
try_config(char *s, const char *opt, char scc)429 static char *try_config(char *s, const char *opt, char scc)
430 {
431 size_t len;
432 char *p;
433 char *q;
434
435 if (!s || !opt) {
436 /* no line or no option */
437 return NULL; /* LCOV_EXCL_LINE */
438 }
439
440 /* Hash '#' character is always used as primary comment char, additionally
441 a not-NUL secondary comment char will be considered when specified. */
442
443 /* trim line comment */
444 p = s;
445 if (scc) {
446 while (*p && (*p != '#') && (*p != scc)) {
447 p++;
448 }
449 } else {
450 while (*p && (*p != '#')) {
451 p++;
452 }
453 }
454 *p = '\0';
455
456 /* trim trailing whitespace */
457 q = p - 1;
458 while ((q >= s) && ISSPACE(*q)) {
459 q--;
460 }
461 *++q = '\0';
462
463 /* skip leading whitespace */
464 p = s;
465 while (*p && ISSPACE(*p)) {
466 p++;
467 }
468
469 if (!*p) {
470 /* empty line */
471 return NULL;
472 }
473
474 if ((len = ares_strlen(opt)) == 0) {
475 /* empty option */
476 return NULL; /* LCOV_EXCL_LINE */
477 }
478
479 if (strncmp(p, opt, len) != 0) {
480 /* line and option do not match */
481 return NULL;
482 }
483
484 /* skip over given option name */
485 p += len;
486
487 if (!*p) {
488 /* no option value */
489 return NULL; /* LCOV_EXCL_LINE */
490 }
491
492 if ((opt[len - 1] != ':') && (opt[len - 1] != '=') && !ISSPACE(*p)) {
493 /* whitespace between option name and value is mandatory
494 for given option names which do not end with ':' or '=' */
495 return NULL;
496 }
497
498 /* skip over whitespace */
499 while (*p && ISSPACE(*p)) {
500 p++;
501 }
502
503 if (!*p) {
504 /* no option value */
505 return NULL;
506 }
507
508 /* return pointer to option value */
509 return p;
510 }
511
ares__init_by_environment(ares_sysconfig_t *sysconfig)512 ares_status_t ares__init_by_environment(ares_sysconfig_t *sysconfig)
513 {
514 const char *localdomain;
515 const char *res_options;
516 ares_status_t status;
517
518 localdomain = getenv("LOCALDOMAIN");
519 if (localdomain) {
520 char *temp = ares_strdup(localdomain);
521 if (temp == NULL) {
522 return ARES_ENOMEM;
523 }
524 status = config_domain(sysconfig, temp);
525 ares_free(temp);
526 if (status != ARES_SUCCESS) {
527 return status;
528 }
529 }
530
531 res_options = getenv("RES_OPTIONS");
532 if (res_options) {
533 status = set_options(sysconfig, res_options);
534 if (status != ARES_SUCCESS) {
535 return status;
536 }
537 }
538
539 return ARES_SUCCESS;
540 }
541
ares__init_sysconfig_files(const ares_channel_t *channel, ares_sysconfig_t *sysconfig)542 ares_status_t ares__init_sysconfig_files(const ares_channel_t *channel,
543 ares_sysconfig_t *sysconfig)
544 {
545 char *p;
546 FILE *fp = NULL;
547 char *line = NULL;
548 size_t linesize = 0;
549 int error;
550 const char *resolvconf_path;
551 ares_status_t status = ARES_SUCCESS;
552
553 /* Support path for resolvconf filename set by ares_init_options */
554 if (channel->resolvconf_path) {
555 resolvconf_path = channel->resolvconf_path;
556 } else {
557 resolvconf_path = PATH_RESOLV_CONF;
558 }
559
560 fp = fopen(resolvconf_path, "r");
561 if (fp) {
562 while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) {
563 if ((p = try_config(line, "domain", ';'))) {
564 status = config_domain(sysconfig, p);
565 } else if ((p = try_config(line, "lookup", ';'))) {
566 status = config_lookup(sysconfig, p, "bind", NULL, "file");
567 } else if ((p = try_config(line, "search", ';'))) {
568 status = config_search(sysconfig, p);
569 } else if ((p = try_config(line, "nameserver", ';'))) {
570 status =
571 ares__sconfig_append_fromstr(&sysconfig->sconfig, p, ARES_TRUE);
572 } else if ((p = try_config(line, "sortlist", ';'))) {
573 /* Ignore all failures except ENOMEM. If the sysadmin set a bad
574 * sortlist, just ignore the sortlist, don't cause an inoperable
575 * channel */
576 status =
577 ares__parse_sortlist(&sysconfig->sortlist, &sysconfig->nsortlist, p);
578 if (status != ARES_ENOMEM) {
579 status = ARES_SUCCESS;
580 }
581 } else if ((p = try_config(line, "options", ';'))) {
582 status = set_options(sysconfig, p);
583 } else {
584 status = ARES_SUCCESS;
585 }
586 if (status != ARES_SUCCESS) {
587 fclose(fp);
588 goto done;
589 }
590 }
591 fclose(fp);
592
593 if (status != ARES_EOF) {
594 goto done;
595 }
596 } else {
597 error = ERRNO;
598 switch (error) {
599 case ENOENT:
600 case ESRCH:
601 break;
602 default:
603 DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
604 strerror(error)));
605 DEBUGF(fprintf(stderr, "Error opening file: %s\n", PATH_RESOLV_CONF));
606 status = ARES_EFILE;
607 goto done;
608 }
609 }
610
611
612 /* Many systems (Solaris, Linux, BSD's) use nsswitch.conf */
613 fp = fopen("/etc/nsswitch.conf", "r");
614 if (fp) {
615 while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) {
616 if ((p = try_config(line, "hosts:", '\0'))) {
617 (void)config_lookup(sysconfig, p, "dns", "resolve", "files");
618 }
619 }
620 fclose(fp);
621 if (status != ARES_EOF) {
622 goto done;
623 }
624 } else {
625 error = ERRNO;
626 switch (error) {
627 case ENOENT:
628 case ESRCH:
629 break;
630 default:
631 DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
632 strerror(error)));
633 DEBUGF(
634 fprintf(stderr, "Error opening file: %s\n", "/etc/nsswitch.conf"));
635 break;
636 }
637 /* ignore error, maybe we will get luck in next if clause */
638 }
639
640
641 /* Linux / GNU libc 2.x and possibly others have host.conf */
642 fp = fopen("/etc/host.conf", "r");
643 if (fp) {
644 while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) {
645 if ((p = try_config(line, "order", '\0'))) {
646 /* ignore errors */
647 (void)config_lookup(sysconfig, p, "bind", NULL, "hosts");
648 }
649 }
650 fclose(fp);
651 if (status != ARES_EOF) {
652 goto done;
653 }
654 } else {
655 error = ERRNO;
656 switch (error) {
657 case ENOENT:
658 case ESRCH:
659 break;
660 default:
661 DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
662 strerror(error)));
663 DEBUGF(fprintf(stderr, "Error opening file: %s\n", "/etc/host.conf"));
664 break;
665 }
666
667 /* ignore error, maybe we will get luck in next if clause */
668 }
669
670
671 /* Tru64 uses /etc/svc.conf */
672 fp = fopen("/etc/svc.conf", "r");
673 if (fp) {
674 while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) {
675 if ((p = try_config(line, "hosts=", '\0'))) {
676 /* ignore errors */
677 (void)config_lookup(sysconfig, p, "bind", NULL, "local");
678 }
679 }
680 fclose(fp);
681 if (status != ARES_EOF) {
682 goto done;
683 }
684 } else {
685 error = ERRNO;
686 switch (error) {
687 case ENOENT:
688 case ESRCH:
689 break;
690 default:
691 DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
692 strerror(error)));
693 DEBUGF(fprintf(stderr, "Error opening file: %s\n", "/etc/svc.conf"));
694 break;
695 }
696 /* ignore error */
697 }
698
699 status = ARES_SUCCESS;
700
701 done:
702 ares_free(line);
703
704 return status;
705 }
706