quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

ares_sysconfig_files.c (24284B)


      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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. */
    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. */
    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. */
    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 
    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) */
    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 
    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 }