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 #if defined(USE_WINSOCK) && defined(HAVE_IPHLPAPI_H)
59 # include <iphlpapi.h>
60 #endif
61
62 #include "ares_inet_net_pton.h"
63
ip_natural_mask(const struct ares_addr * addr)64 static unsigned char ip_natural_mask(const struct ares_addr *addr)
65 {
66 const unsigned char *ptr = NULL;
67 /* This is an odd one. If a raw ipv4 address is specified, then we take
68 * what is called a natural mask, which means we look at the first octet
69 * of the ip address and for values 0-127 we assume it is a class A (/8),
70 * for values 128-191 we assume it is a class B (/16), and for 192-223
71 * we assume it is a class C (/24). 223-239 is Class D which and 240-255 is
72 * Class E, however, there is no pre-defined mask for this, so we'll use
73 * /24 as well as that's what the old code did.
74 *
75 * For IPv6, we'll use /64.
76 */
77
78 if (addr->family == AF_INET6) {
79 return 64;
80 }
81
82 ptr = (const unsigned char *)&addr->addr.addr4;
83 if (*ptr < 128) {
84 return 8;
85 }
86
87 if (*ptr < 192) {
88 return 16;
89 }
90
91 return 24;
92 }
93
sortlist_append(struct apattern ** sortlist,size_t * nsort,const struct apattern * pat)94 static ares_bool_t sortlist_append(struct apattern **sortlist, size_t *nsort,
95 const struct apattern *pat)
96 {
97 struct apattern *newsort;
98
99 newsort = ares_realloc(*sortlist, (*nsort + 1) * sizeof(*newsort));
100 if (newsort == NULL) {
101 return ARES_FALSE; /* LCOV_EXCL_LINE: OutOfMemory */
102 }
103
104 *sortlist = newsort;
105
106 memcpy(&(*sortlist)[*nsort], pat, sizeof(**sortlist));
107 (*nsort)++;
108
109 return ARES_TRUE;
110 }
111
parse_sort(ares_buf_t * buf,struct apattern * pat)112 static ares_status_t parse_sort(ares_buf_t *buf, struct apattern *pat)
113 {
114 ares_status_t status;
115 const unsigned char ip_charset[] = "ABCDEFabcdef0123456789.:";
116 char ipaddr[INET6_ADDRSTRLEN] = "";
117 size_t addrlen;
118
119 memset(pat, 0, sizeof(*pat));
120
121 /* Consume any leading whitespace */
122 ares_buf_consume_whitespace(buf, ARES_TRUE);
123
124 /* If no length, just ignore, return ENOTFOUND as an indicator */
125 if (ares_buf_len(buf) == 0) {
126 return ARES_ENOTFOUND;
127 }
128
129 ares_buf_tag(buf);
130
131 /* Consume ip address */
132 if (ares_buf_consume_charset(buf, ip_charset, sizeof(ip_charset) - 1) == 0) {
133 return ARES_EBADSTR;
134 }
135
136 /* Fetch ip address */
137 status = ares_buf_tag_fetch_string(buf, ipaddr, sizeof(ipaddr));
138 if (status != ARES_SUCCESS) {
139 return status;
140 }
141
142 /* Parse it to make sure its valid */
143 pat->addr.family = AF_UNSPEC;
144 if (ares_dns_pton(ipaddr, &pat->addr, &addrlen) == NULL) {
145 return ARES_EBADSTR;
146 }
147
148 /* See if there is a subnet mask */
149 if (ares_buf_begins_with(buf, (const unsigned char *)"/", 1)) {
150 char maskstr[16];
151 const unsigned char ipv4_charset[] = "0123456789.";
152
153
154 /* Consume / */
155 ares_buf_consume(buf, 1);
156
157 ares_buf_tag(buf);
158
159 /* Consume mask */
160 if (ares_buf_consume_charset(buf, ipv4_charset, sizeof(ipv4_charset) - 1) ==
161 0) {
162 return ARES_EBADSTR;
163 }
164
165 /* Fetch mask */
166 status = ares_buf_tag_fetch_string(buf, maskstr, sizeof(maskstr));
167 if (status != ARES_SUCCESS) {
168 return status;
169 }
170
171 if (ares_str_isnum(maskstr)) {
172 /* Numeric mask */
173 int mask = atoi(maskstr);
174 if (mask < 0 || mask > 128) {
175 return ARES_EBADSTR;
176 }
177 if (pat->addr.family == AF_INET && mask > 32) {
178 return ARES_EBADSTR;
179 }
180 pat->mask = (unsigned char)mask;
181 } else {
182 /* Ipv4 subnet style mask */
183 struct ares_addr maskaddr;
184 const unsigned char *ptr;
185
186 memset(&maskaddr, 0, sizeof(maskaddr));
187 maskaddr.family = AF_INET;
188 if (ares_dns_pton(maskstr, &maskaddr, &addrlen) == NULL) {
189 return ARES_EBADSTR;
190 }
191 ptr = (const unsigned char *)&maskaddr.addr.addr4;
192 pat->mask = (unsigned char)(ares_count_bits_u8(ptr[0]) +
193 ares_count_bits_u8(ptr[1]) +
194 ares_count_bits_u8(ptr[2]) +
195 ares_count_bits_u8(ptr[3]));
196 }
197 } else {
198 pat->mask = ip_natural_mask(&pat->addr);
199 }
200
201 /* Consume any trailing whitespace */
202 ares_buf_consume_whitespace(buf, ARES_TRUE);
203
204 /* If we have any trailing bytes other than whitespace, its a parse failure */
205 if (ares_buf_len(buf) != 0) {
206 return ARES_EBADSTR;
207 }
208
209 return ARES_SUCCESS;
210 }
211
ares_parse_sortlist(struct apattern ** sortlist,size_t * nsort,const char * str)212 ares_status_t ares_parse_sortlist(struct apattern **sortlist, size_t *nsort,
213 const char *str)
214 {
215 ares_buf_t *buf = NULL;
216 ares_status_t status = ARES_SUCCESS;
217 ares_array_t *arr = NULL;
218 size_t num = 0;
219 size_t i;
220
221 if (sortlist == NULL || nsort == NULL || str == NULL) {
222 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
223 }
224
225 if (*sortlist != NULL) {
226 ares_free(*sortlist);
227 }
228
229 *sortlist = NULL;
230 *nsort = 0;
231
232 buf = ares_buf_create_const((const unsigned char *)str, ares_strlen(str));
233 if (buf == NULL) {
234 status = ARES_ENOMEM;
235 goto done;
236 }
237
238 /* Split on space or semicolon */
239 status = ares_buf_split(buf, (const unsigned char *)" ;", 2,
240 ARES_BUF_SPLIT_NONE, 0, &arr);
241 if (status != ARES_SUCCESS) {
242 goto done;
243 }
244
245 num = ares_array_len(arr);
246 for (i = 0; i < num; i++) {
247 ares_buf_t **bufptr = ares_array_at(arr, i);
248 ares_buf_t *entry = *bufptr;
249
250 struct apattern pat;
251
252 status = parse_sort(entry, &pat);
253 if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) {
254 goto done;
255 }
256
257 if (status != ARES_SUCCESS) {
258 continue;
259 }
260
261 if (!sortlist_append(sortlist, nsort, &pat)) {
262 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
263 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
264 }
265 }
266
267 status = ARES_SUCCESS;
268
269 done:
270 ares_buf_destroy(buf);
271 ares_array_destroy(arr);
272
273 if (status != ARES_SUCCESS) {
274 ares_free(*sortlist);
275 *sortlist = NULL;
276 *nsort = 0;
277 }
278
279 return status;
280 }
281
config_search(ares_sysconfig_t * sysconfig,const char * str,size_t max_domains)282 static ares_status_t config_search(ares_sysconfig_t *sysconfig, const char *str,
283 size_t max_domains)
284 {
285 if (sysconfig->domains && sysconfig->ndomains > 0) {
286 /* if we already have some domains present, free them first */
287 ares_strsplit_free(sysconfig->domains, sysconfig->ndomains);
288 sysconfig->domains = NULL;
289 sysconfig->ndomains = 0;
290 }
291
292 sysconfig->domains = ares_strsplit(str, ", ", &sysconfig->ndomains);
293 if (sysconfig->domains == NULL) {
294 return ARES_ENOMEM;
295 }
296
297 /* Truncate if necessary */
298 if (max_domains && sysconfig->ndomains > max_domains) {
299 size_t i;
300 for (i = max_domains; i < sysconfig->ndomains; i++) {
301 ares_free(sysconfig->domains[i]);
302 sysconfig->domains[i] = NULL;
303 }
304 sysconfig->ndomains = max_domains;
305 }
306
307 return ARES_SUCCESS;
308 }
309
buf_fetch_string(ares_buf_t * buf,char * str,size_t str_len)310 static ares_status_t buf_fetch_string(ares_buf_t *buf, char *str,
311 size_t str_len)
312 {
313 ares_status_t status;
314 ares_buf_tag(buf);
315 ares_buf_consume(buf, ares_buf_len(buf));
316
317 status = ares_buf_tag_fetch_string(buf, str, str_len);
318 return status;
319 }
320
config_lookup(ares_sysconfig_t * sysconfig,ares_buf_t * buf,const char * separators)321 static ares_status_t config_lookup(ares_sysconfig_t *sysconfig, ares_buf_t *buf,
322 const char *separators)
323 {
324 ares_status_t status;
325 char lookupstr[32];
326 size_t lookupstr_cnt = 0;
327 char **lookups = NULL;
328 size_t num = 0;
329 size_t i;
330 size_t separators_len = ares_strlen(separators);
331
332 status =
333 ares_buf_split_str(buf, (const unsigned char *)separators, separators_len,
334 ARES_BUF_SPLIT_TRIM, 0, &lookups, &num);
335 if (status != ARES_SUCCESS) {
336 goto done;
337 }
338
339 for (i = 0; i < num; i++) {
340 const char *value = lookups[i];
341 char ch;
342
343 if (ares_strcaseeq(value, "dns") || ares_strcaseeq(value, "bind") ||
344 ares_strcaseeq(value, "resolv") || ares_strcaseeq(value, "resolve")) {
345 ch = 'b';
346 } else if (ares_strcaseeq(value, "files") ||
347 ares_strcaseeq(value, "file") ||
348 ares_strcaseeq(value, "local")) {
349 ch = 'f';
350 } else {
351 continue;
352 }
353
354 /* Look for a duplicate and ignore */
355 if (memchr(lookupstr, ch, lookupstr_cnt) == NULL) {
356 lookupstr[lookupstr_cnt++] = ch;
357 }
358 }
359
360 if (lookupstr_cnt) {
361 lookupstr[lookupstr_cnt] = 0;
362 ares_free(sysconfig->lookups);
363 sysconfig->lookups = ares_strdup(lookupstr);
364 if (sysconfig->lookups == NULL) {
365 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
366 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
367 }
368 }
369
370 status = ARES_SUCCESS;
371
372 done:
373 if (status != ARES_ENOMEM) {
374 status = ARES_SUCCESS;
375 }
376 ares_free_array(lookups, num, ares_free);
377 return status;
378 }
379
process_option(ares_sysconfig_t * sysconfig,ares_buf_t * option)380 static ares_status_t process_option(ares_sysconfig_t *sysconfig,
381 ares_buf_t *option)
382 {
383 char **kv = NULL;
384 size_t num = 0;
385 const char *key;
386 const char *val;
387 unsigned int valint = 0;
388 ares_status_t status;
389
390 /* Split on : */
391 status = ares_buf_split_str(option, (const unsigned char *)":", 1,
392 ARES_BUF_SPLIT_TRIM, 2, &kv, &num);
393 if (status != ARES_SUCCESS) {
394 goto done;
395 }
396
397 if (num < 1) {
398 status = ARES_EBADSTR;
399 goto done;
400 }
401
402 key = kv[0];
403 if (num == 2) {
404 val = kv[1];
405 valint = (unsigned int)strtoul(val, NULL, 10);
406 }
407
408 if (ares_streq(key, "ndots")) {
409 sysconfig->ndots = valint;
410 } else if (ares_streq(key, "retrans") || ares_streq(key, "timeout")) {
411 if (valint == 0) {
412 return ARES_EFORMERR;
413 }
414 sysconfig->timeout_ms = valint * 1000;
415 } else if (ares_streq(key, "retry") || ares_streq(key, "attempts")) {
416 if (valint == 0) {
417 return ARES_EFORMERR;
418 }
419 sysconfig->tries = valint;
420 } else if (ares_streq(key, "rotate")) {
421 sysconfig->rotate = ARES_TRUE;
422 } else if (ares_streq(key, "use-vc") || ares_streq(key, "usevc")) {
423 sysconfig->usevc = ARES_TRUE;
424 }
425
426 done:
427 ares_free_array(kv, num, ares_free);
428 return status;
429 }
430
ares_sysconfig_set_options(ares_sysconfig_t * sysconfig,const char * str)431 ares_status_t ares_sysconfig_set_options(ares_sysconfig_t *sysconfig,
432 const char *str)
433 {
434 ares_buf_t *buf = NULL;
435 ares_array_t *options = NULL;
436 size_t num;
437 size_t i;
438 ares_status_t status;
439
440 buf = ares_buf_create_const((const unsigned char *)str, ares_strlen(str));
441 if (buf == NULL) {
442 return ARES_ENOMEM;
443 }
444
445 status = ares_buf_split(buf, (const unsigned char *)" \t", 2,
446 ARES_BUF_SPLIT_TRIM, 0, &options);
447 if (status != ARES_SUCCESS) {
448 goto done;
449 }
450
451 num = ares_array_len(options);
452 for (i = 0; i < num; i++) {
453 ares_buf_t **bufptr = ares_array_at(options, i);
454 ares_buf_t *valbuf = *bufptr;
455
456 status = process_option(sysconfig, valbuf);
457 /* Out of memory is the only fatal condition */
458 if (status == ARES_ENOMEM) {
459 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
460 }
461 }
462
463 status = ARES_SUCCESS;
464
465 done:
466 ares_array_destroy(options);
467 ares_buf_destroy(buf);
468 return status;
469 }
470
ares_init_by_environment(ares_sysconfig_t * sysconfig)471 ares_status_t ares_init_by_environment(ares_sysconfig_t *sysconfig)
472 {
473 const char *localdomain;
474 const char *res_options;
475 ares_status_t status;
476
477 localdomain = getenv("LOCALDOMAIN");
478 if (localdomain) {
479 char *temp = ares_strdup(localdomain);
480 if (temp == NULL) {
481 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
482 }
483 status = config_search(sysconfig, temp, 1);
484 ares_free(temp);
485 if (status != ARES_SUCCESS) {
486 return status;
487 }
488 }
489
490 res_options = getenv("RES_OPTIONS");
491 if (res_options) {
492 status = ares_sysconfig_set_options(sysconfig, res_options);
493 if (status != ARES_SUCCESS) {
494 return status;
495 }
496 }
497
498 return ARES_SUCCESS;
499 }
500
501 /* Configuration Files:
502 * /etc/resolv.conf
503 * - All Unix-like systems
504 * - Comments start with ; or #
505 * - Lines have a keyword followed by a value that is interpreted specific
506 * to the keyword:
507 * - Keywords:
508 * - nameserver - IP address of nameserver with optional port (using a :
509 * prefix). If using an ipv6 address and specifying a port, the ipv6
510 * address must be encapsulated in brackets. For link-local ipv6
511 * addresses, the interface can also be specified with a % prefix. e.g.:
512 * "nameserver [fe80::1]:1234%iface"
513 * This keyword may be specified multiple times.
514 * - search - whitespace separated list of domains
515 * - domain - obsolete, same as search except only a single domain
516 * - lookup / hostresorder - local, bind, file, files
517 * - sortlist - whitespace separated ip-address/netmask pairs
518 * - options - options controlling resolver variables
519 * - ndots:n - set ndots option
520 * - timeout:n (retrans:n) - timeout per query attempt in seconds
521 * - attempts:n (retry:n) - number of times resolver will send query
522 * - rotate - round-robin selection of name servers
523 * - use-vc / usevc - force tcp
524 * /etc/nsswitch.conf
525 * - Modern Linux, FreeBSD, HP-UX, Solaris
526 * - Search order set via:
527 * "hosts: files dns mdns4_minimal mdns4"
528 * - files is /etc/hosts
529 * - dns is dns
530 * - mdns4_minimal does mdns only if ending in .local
531 * - mdns4 does not limit to domains ending in .local
532 * /etc/netsvc.conf
533 * - AIX
534 * - Search order set via:
535 * "hosts = local , bind"
536 * - bind is dns
537 * - local is /etc/hosts
538 * /etc/svc.conf
539 * - Tru64
540 * - Same format as /etc/netsvc.conf
541 * /etc/host.conf
542 * - Early FreeBSD, Early Linux
543 * - Not worth supporting, format varied based on system, FreeBSD used
544 * just a line per search order, Linux used "order " and a comma
545 * delimited list of "bind" and "hosts"
546 */
547
548
549 /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other
550 * conditions are ignored. Users may mess up config files, but we want to
551 * process anything we can. */
ares_sysconfig_parse_resolv_line(const ares_channel_t * channel,ares_sysconfig_t * sysconfig,ares_buf_t * line)552 ares_status_t ares_sysconfig_parse_resolv_line(const ares_channel_t *channel,
553 ares_sysconfig_t *sysconfig,
554 ares_buf_t *line)
555 {
556 char option[32];
557 char value[512];
558 ares_status_t status = ARES_SUCCESS;
559
560 /* Ignore lines beginning with a comment */
561 if (ares_buf_begins_with(line, (const unsigned char *)"#", 1) ||
562 ares_buf_begins_with(line, (const unsigned char *)";", 1)) {
563 return ARES_SUCCESS;
564 }
565
566 ares_buf_tag(line);
567
568 /* Shouldn't be possible, but if it happens, ignore the line. */
569 if (ares_buf_consume_nonwhitespace(line) == 0) {
570 return ARES_SUCCESS;
571 }
572
573 status = ares_buf_tag_fetch_string(line, option, sizeof(option));
574 if (status != ARES_SUCCESS) {
575 return ARES_SUCCESS;
576 }
577
578 ares_buf_consume_whitespace(line, ARES_TRUE);
579
580 status = buf_fetch_string(line, value, sizeof(value));
581 if (status != ARES_SUCCESS) {
582 return ARES_SUCCESS;
583 }
584
585 ares_str_trim(value);
586 if (*value == 0) {
587 return ARES_SUCCESS;
588 }
589
590 /* At this point we have a string option and a string value, both trimmed
591 * of leading and trailing whitespace. Lets try to evaluate them */
592 if (ares_streq(option, "domain")) {
593 /* Domain is legacy, don't overwrite an existing config set by search */
594 if (sysconfig->domains == NULL) {
595 status = config_search(sysconfig, value, 1);
596 }
597 } else if (ares_streq(option, "lookup") ||
598 ares_streq(option, "hostresorder")) {
599 ares_buf_tag_rollback(line);
600 status = config_lookup(sysconfig, line, " \t");
601 } else if (ares_streq(option, "search")) {
602 status = config_search(sysconfig, value, 0);
603 } else if (ares_streq(option, "nameserver")) {
604 status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig, value,
605 ARES_TRUE);
606 } else if (ares_streq(option, "sortlist")) {
607 /* Ignore all failures except ENOMEM. If the sysadmin set a bad
608 * sortlist, just ignore the sortlist, don't cause an inoperable
609 * channel */
610 status =
611 ares_parse_sortlist(&sysconfig->sortlist, &sysconfig->nsortlist, value);
612 if (status != ARES_ENOMEM) {
613 status = ARES_SUCCESS;
614 }
615 } else if (ares_streq(option, "options")) {
616 status = ares_sysconfig_set_options(sysconfig, value);
617 }
618
619 return status;
620 }
621
622 /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other
623 * conditions are ignored. Users may mess up config files, but we want to
624 * process anything we can. */
parse_nsswitch_line(const ares_channel_t * channel,ares_sysconfig_t * sysconfig,ares_buf_t * line)625 static ares_status_t parse_nsswitch_line(const ares_channel_t *channel,
626 ares_sysconfig_t *sysconfig,
627 ares_buf_t *line)
628 {
629 char option[32];
630 ares_status_t status = ARES_SUCCESS;
631 ares_array_t *sects = NULL;
632 ares_buf_t **bufptr;
633 ares_buf_t *buf;
634
635 (void)channel;
636
637 /* Ignore lines beginning with a comment */
638 if (ares_buf_begins_with(line, (const unsigned char *)"#", 1)) {
639 return ARES_SUCCESS;
640 }
641
642 /* database : values (space delimited) */
643 status = ares_buf_split(line, (const unsigned char *)":", 1,
644 ARES_BUF_SPLIT_TRIM, 2, §s);
645
646 if (status != ARES_SUCCESS || ares_array_len(sects) != 2) {
647 goto done;
648 }
649
650 bufptr = ares_array_at(sects, 0);
651 buf = *bufptr;
652
653 status = buf_fetch_string(buf, option, sizeof(option));
654 if (status != ARES_SUCCESS) {
655 goto done;
656 }
657
658 /* Only support "hosts:" */
659 if (!ares_streq(option, "hosts")) {
660 goto done;
661 }
662
663 /* Values are space separated */
664 bufptr = ares_array_at(sects, 1);
665 buf = *bufptr;
666 status = config_lookup(sysconfig, buf, " \t");
667
668 done:
669 ares_array_destroy(sects);
670 if (status != ARES_ENOMEM) {
671 status = ARES_SUCCESS;
672 }
673 return status;
674 }
675
676 /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other
677 * conditions are ignored. Users may mess up config files, but we want to
678 * process anything we can. */
parse_svcconf_line(const ares_channel_t * channel,ares_sysconfig_t * sysconfig,ares_buf_t * line)679 static ares_status_t parse_svcconf_line(const ares_channel_t *channel,
680 ares_sysconfig_t *sysconfig,
681 ares_buf_t *line)
682 {
683 char option[32];
684 ares_buf_t **bufptr;
685 ares_buf_t *buf;
686 ares_status_t status = ARES_SUCCESS;
687 ares_array_t *sects = NULL;
688
689 (void)channel;
690
691 /* Ignore lines beginning with a comment */
692 if (ares_buf_begins_with(line, (const unsigned char *)"#", 1)) {
693 return ARES_SUCCESS;
694 }
695
696 /* database = values (comma delimited)*/
697 status = ares_buf_split(line, (const unsigned char *)"=", 1,
698 ARES_BUF_SPLIT_TRIM, 2, §s);
699
700 if (status != ARES_SUCCESS || ares_array_len(sects) != 2) {
701 goto done;
702 }
703
704 bufptr = ares_array_at(sects, 0);
705 buf = *bufptr;
706 status = buf_fetch_string(buf, option, sizeof(option));
707 if (status != ARES_SUCCESS) {
708 goto done;
709 }
710
711 /* Only support "hosts=" */
712 if (!ares_streq(option, "hosts")) {
713 goto done;
714 }
715
716 /* Values are comma separated */
717 bufptr = ares_array_at(sects, 1);
718 buf = *bufptr;
719 status = config_lookup(sysconfig, buf, ",");
720
721 done:
722 ares_array_destroy(sects);
723 if (status != ARES_ENOMEM) {
724 status = ARES_SUCCESS;
725 }
726 return status;
727 }
728
729
ares_sysconfig_process_buf(const ares_channel_t * channel,ares_sysconfig_t * sysconfig,ares_buf_t * buf,ares_sysconfig_line_cb_t cb)730 ares_status_t ares_sysconfig_process_buf(const ares_channel_t *channel,
731 ares_sysconfig_t *sysconfig,
732 ares_buf_t *buf,
733 ares_sysconfig_line_cb_t cb)
734 {
735 ares_array_t *lines = NULL;
736 size_t num;
737 size_t i;
738 ares_status_t status;
739
740 status = ares_buf_split(buf, (const unsigned char *)"\n", 1,
741 ARES_BUF_SPLIT_TRIM, 0, &lines);
742 if (status != ARES_SUCCESS) {
743 goto done;
744 }
745
746 num = ares_array_len(lines);
747 for (i = 0; i < num; i++) {
748 ares_buf_t **bufptr = ares_array_at(lines, i);
749 ares_buf_t *line = *bufptr;
750
751 status = cb(channel, sysconfig, line);
752 if (status != ARES_SUCCESS) {
753 goto done;
754 }
755 }
756
757 done:
758 ares_array_destroy(lines);
759 return status;
760 }
761
762 /* Should only return:
763 * ARES_ENOTFOUND - file not found
764 * ARES_EFILE - error reading file (perms)
765 * ARES_ENOMEM - out of memory
766 * ARES_SUCCESS - file processed, doesn't necessarily mean it was a good
767 * file, but we're not erroring out if we can't parse
768 * something (or anything at all) */
process_config_lines(const ares_channel_t * channel,const char * filename,ares_sysconfig_t * sysconfig,ares_sysconfig_line_cb_t cb)769 static ares_status_t process_config_lines(const ares_channel_t *channel,
770 const char *filename,
771 ares_sysconfig_t *sysconfig,
772 ares_sysconfig_line_cb_t cb)
773 {
774 ares_status_t status = ARES_SUCCESS;
775 ares_buf_t *buf = NULL;
776
777 buf = ares_buf_create();
778 if (buf == NULL) {
779 status = ARES_ENOMEM;
780 goto done;
781 }
782
783 status = ares_buf_load_file(filename, buf);
784 if (status != ARES_SUCCESS) {
785 goto done;
786 }
787
788 status = ares_sysconfig_process_buf(channel, sysconfig, buf, cb);
789
790 done:
791 ares_buf_destroy(buf);
792
793 return status;
794 }
795
ares_init_sysconfig_files(const ares_channel_t * channel,ares_sysconfig_t * sysconfig,ares_bool_t process_resolvconf)796 ares_status_t ares_init_sysconfig_files(const ares_channel_t *channel,
797 ares_sysconfig_t *sysconfig,
798 ares_bool_t process_resolvconf)
799 {
800 ares_status_t status = ARES_SUCCESS;
801
802 /* Resolv.conf */
803 if (process_resolvconf) {
804 status = process_config_lines(channel,
805 (channel->resolvconf_path != NULL)
806 ? channel->resolvconf_path
807 : PATH_RESOLV_CONF,
808 sysconfig, ares_sysconfig_parse_resolv_line);
809 if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) {
810 goto done;
811 }
812 }
813
814 /* Nsswitch.conf */
815 status = process_config_lines(channel, "/etc/nsswitch.conf", sysconfig,
816 parse_nsswitch_line);
817 if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) {
818 goto done;
819 }
820
821 /* netsvc.conf */
822 status = process_config_lines(channel, "/etc/netsvc.conf", sysconfig,
823 parse_svcconf_line);
824 if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) {
825 goto done;
826 }
827
828 /* svc.conf */
829 status = process_config_lines(channel, "/etc/svc.conf", sysconfig,
830 parse_svcconf_line);
831 if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) {
832 goto done;
833 }
834
835 status = ARES_SUCCESS;
836
837 done:
838 return status;
839 }
840