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