• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &sects);
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, &sects);
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