| /* MIT License |
| * |
| * Copyright (c) 1998 Massachusetts Institute of Technology |
| * Copyright (c) 2007 Daniel Stenberg |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| * |
| * SPDX-License-Identifier: MIT |
| */ |
| |
| #include "ares_private.h" |
| |
| #ifdef HAVE_SYS_PARAM_H |
| # include <sys/param.h> |
| #endif |
| |
| #ifdef HAVE_NETINET_IN_H |
| # include <netinet/in.h> |
| #endif |
| |
| #ifdef HAVE_NETDB_H |
| # include <netdb.h> |
| #endif |
| |
| #ifdef HAVE_ARPA_INET_H |
| # include <arpa/inet.h> |
| #endif |
| |
| #if defined(ANDROID) || defined(__ANDROID__) |
| # include <sys/system_properties.h> |
| # include "ares_android.h" |
| /* From the Bionic sources */ |
| # define DNS_PROP_NAME_PREFIX "net.dns" |
| # define MAX_DNS_PROPERTIES 8 |
| #endif |
| |
| #if defined(CARES_USE_LIBRESOLV) |
| # include <resolv.h> |
| #endif |
| |
| #if defined(USE_WINSOCK) && defined(HAVE_IPHLPAPI_H) |
| # include <iphlpapi.h> |
| #endif |
| |
| #include "ares_inet_net_pton.h" |
| |
| static unsigned char ip_natural_mask(const struct ares_addr *addr) |
| { |
| const unsigned char *ptr = NULL; |
| /* This is an odd one. If a raw ipv4 address is specified, then we take |
| * what is called a natural mask, which means we look at the first octet |
| * of the ip address and for values 0-127 we assume it is a class A (/8), |
| * for values 128-191 we assume it is a class B (/16), and for 192-223 |
| * we assume it is a class C (/24). 223-239 is Class D which and 240-255 is |
| * Class E, however, there is no pre-defined mask for this, so we'll use |
| * /24 as well as that's what the old code did. |
| * |
| * For IPv6, we'll use /64. |
| */ |
| |
| if (addr->family == AF_INET6) { |
| return 64; |
| } |
| |
| ptr = (const unsigned char *)&addr->addr.addr4; |
| if (*ptr < 128) { |
| return 8; |
| } |
| |
| if (*ptr < 192) { |
| return 16; |
| } |
| |
| return 24; |
| } |
| |
| static ares_bool_t sortlist_append(struct apattern **sortlist, size_t *nsort, |
| const struct apattern *pat) |
| { |
| struct apattern *newsort; |
| |
| newsort = ares_realloc(*sortlist, (*nsort + 1) * sizeof(*newsort)); |
| if (newsort == NULL) { |
| return ARES_FALSE; /* LCOV_EXCL_LINE: OutOfMemory */ |
| } |
| |
| *sortlist = newsort; |
| |
| memcpy(&(*sortlist)[*nsort], pat, sizeof(**sortlist)); |
| (*nsort)++; |
| |
| return ARES_TRUE; |
| } |
| |
| static ares_status_t parse_sort(ares_buf_t *buf, struct apattern *pat) |
| { |
| ares_status_t status; |
| const unsigned char ip_charset[] = "ABCDEFabcdef0123456789.:"; |
| char ipaddr[INET6_ADDRSTRLEN] = ""; |
| size_t addrlen; |
| |
| memset(pat, 0, sizeof(*pat)); |
| |
| /* Consume any leading whitespace */ |
| ares_buf_consume_whitespace(buf, ARES_TRUE); |
| |
| /* If no length, just ignore, return ENOTFOUND as an indicator */ |
| if (ares_buf_len(buf) == 0) { |
| return ARES_ENOTFOUND; |
| } |
| |
| ares_buf_tag(buf); |
| |
| /* Consume ip address */ |
| if (ares_buf_consume_charset(buf, ip_charset, sizeof(ip_charset) - 1) == 0) { |
| return ARES_EBADSTR; |
| } |
| |
| /* Fetch ip address */ |
| status = ares_buf_tag_fetch_string(buf, ipaddr, sizeof(ipaddr)); |
| if (status != ARES_SUCCESS) { |
| return status; |
| } |
| |
| /* Parse it to make sure its valid */ |
| pat->addr.family = AF_UNSPEC; |
| if (ares_dns_pton(ipaddr, &pat->addr, &addrlen) == NULL) { |
| return ARES_EBADSTR; |
| } |
| |
| /* See if there is a subnet mask */ |
| if (ares_buf_begins_with(buf, (const unsigned char *)"/", 1)) { |
| char maskstr[16]; |
| const unsigned char ipv4_charset[] = "0123456789."; |
| |
| |
| /* Consume / */ |
| ares_buf_consume(buf, 1); |
| |
| ares_buf_tag(buf); |
| |
| /* Consume mask */ |
| if (ares_buf_consume_charset(buf, ipv4_charset, sizeof(ipv4_charset) - 1) == |
| 0) { |
| return ARES_EBADSTR; |
| } |
| |
| /* Fetch mask */ |
| status = ares_buf_tag_fetch_string(buf, maskstr, sizeof(maskstr)); |
| if (status != ARES_SUCCESS) { |
| return status; |
| } |
| |
| if (ares_str_isnum(maskstr)) { |
| /* Numeric mask */ |
| int mask = atoi(maskstr); |
| if (mask < 0 || mask > 128) { |
| return ARES_EBADSTR; |
| } |
| if (pat->addr.family == AF_INET && mask > 32) { |
| return ARES_EBADSTR; |
| } |
| pat->mask = (unsigned char)mask; |
| } else { |
| /* Ipv4 subnet style mask */ |
| struct ares_addr maskaddr; |
| const unsigned char *ptr; |
| |
| memset(&maskaddr, 0, sizeof(maskaddr)); |
| maskaddr.family = AF_INET; |
| if (ares_dns_pton(maskstr, &maskaddr, &addrlen) == NULL) { |
| return ARES_EBADSTR; |
| } |
| ptr = (const unsigned char *)&maskaddr.addr.addr4; |
| pat->mask = (unsigned char)(ares_count_bits_u8(ptr[0]) + |
| ares_count_bits_u8(ptr[1]) + |
| ares_count_bits_u8(ptr[2]) + |
| ares_count_bits_u8(ptr[3])); |
| } |
| } else { |
| pat->mask = ip_natural_mask(&pat->addr); |
| } |
| |
| /* Consume any trailing whitespace */ |
| ares_buf_consume_whitespace(buf, ARES_TRUE); |
| |
| /* If we have any trailing bytes other than whitespace, its a parse failure */ |
| if (ares_buf_len(buf) != 0) { |
| return ARES_EBADSTR; |
| } |
| |
| return ARES_SUCCESS; |
| } |
| |
| ares_status_t ares_parse_sortlist(struct apattern **sortlist, size_t *nsort, |
| const char *str) |
| { |
| ares_buf_t *buf = NULL; |
| ares_status_t status = ARES_SUCCESS; |
| ares_array_t *arr = NULL; |
| size_t num = 0; |
| size_t i; |
| |
| if (sortlist == NULL || nsort == NULL || str == NULL) { |
| return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */ |
| } |
| |
| if (*sortlist != NULL) { |
| ares_free(*sortlist); |
| } |
| |
| *sortlist = NULL; |
| *nsort = 0; |
| |
| buf = ares_buf_create_const((const unsigned char *)str, ares_strlen(str)); |
| if (buf == NULL) { |
| status = ARES_ENOMEM; |
| goto done; |
| } |
| |
| /* Split on space or semicolon */ |
| status = ares_buf_split(buf, (const unsigned char *)" ;", 2, |
| ARES_BUF_SPLIT_NONE, 0, &arr); |
| if (status != ARES_SUCCESS) { |
| goto done; |
| } |
| |
| num = ares_array_len(arr); |
| for (i = 0; i < num; i++) { |
| ares_buf_t **bufptr = ares_array_at(arr, i); |
| ares_buf_t *entry = *bufptr; |
| |
| struct apattern pat; |
| |
| status = parse_sort(entry, &pat); |
| if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { |
| goto done; |
| } |
| |
| if (status != ARES_SUCCESS) { |
| continue; |
| } |
| |
| if (!sortlist_append(sortlist, nsort, &pat)) { |
| status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ |
| goto done; /* LCOV_EXCL_LINE: OutOfMemory */ |
| } |
| } |
| |
| status = ARES_SUCCESS; |
| |
| done: |
| ares_buf_destroy(buf); |
| ares_array_destroy(arr); |
| |
| if (status != ARES_SUCCESS) { |
| ares_free(*sortlist); |
| *sortlist = NULL; |
| *nsort = 0; |
| } |
| |
| return status; |
| } |
| |
| static ares_status_t config_search(ares_sysconfig_t *sysconfig, const char *str, |
| size_t max_domains) |
| { |
| if (sysconfig->domains && sysconfig->ndomains > 0) { |
| /* if we already have some domains present, free them first */ |
| ares_strsplit_free(sysconfig->domains, sysconfig->ndomains); |
| sysconfig->domains = NULL; |
| sysconfig->ndomains = 0; |
| } |
| |
| sysconfig->domains = ares_strsplit(str, ", ", &sysconfig->ndomains); |
| if (sysconfig->domains == NULL) { |
| return ARES_ENOMEM; |
| } |
| |
| /* Truncate if necessary */ |
| if (max_domains && sysconfig->ndomains > max_domains) { |
| size_t i; |
| for (i = max_domains; i < sysconfig->ndomains; i++) { |
| ares_free(sysconfig->domains[i]); |
| sysconfig->domains[i] = NULL; |
| } |
| sysconfig->ndomains = max_domains; |
| } |
| |
| return ARES_SUCCESS; |
| } |
| |
| static ares_status_t buf_fetch_string(ares_buf_t *buf, char *str, |
| size_t str_len) |
| { |
| ares_status_t status; |
| ares_buf_tag(buf); |
| ares_buf_consume(buf, ares_buf_len(buf)); |
| |
| status = ares_buf_tag_fetch_string(buf, str, str_len); |
| return status; |
| } |
| |
| static ares_status_t config_lookup(ares_sysconfig_t *sysconfig, ares_buf_t *buf, |
| const char *separators) |
| { |
| ares_status_t status; |
| char lookupstr[32]; |
| size_t lookupstr_cnt = 0; |
| char **lookups = NULL; |
| size_t num = 0; |
| size_t i; |
| size_t separators_len = ares_strlen(separators); |
| |
| status = |
| ares_buf_split_str(buf, (const unsigned char *)separators, separators_len, |
| ARES_BUF_SPLIT_TRIM, 0, &lookups, &num); |
| if (status != ARES_SUCCESS) { |
| goto done; |
| } |
| |
| for (i = 0; i < num; i++) { |
| const char *value = lookups[i]; |
| char ch; |
| |
| if (ares_strcaseeq(value, "dns") || ares_strcaseeq(value, "bind") || |
| ares_strcaseeq(value, "resolv") || ares_strcaseeq(value, "resolve")) { |
| ch = 'b'; |
| } else if (ares_strcaseeq(value, "files") || |
| ares_strcaseeq(value, "file") || |
| ares_strcaseeq(value, "local")) { |
| ch = 'f'; |
| } else { |
| continue; |
| } |
| |
| /* Look for a duplicate and ignore */ |
| if (memchr(lookupstr, ch, lookupstr_cnt) == NULL) { |
| lookupstr[lookupstr_cnt++] = ch; |
| } |
| } |
| |
| if (lookupstr_cnt) { |
| lookupstr[lookupstr_cnt] = 0; |
| ares_free(sysconfig->lookups); |
| sysconfig->lookups = ares_strdup(lookupstr); |
| if (sysconfig->lookups == NULL) { |
| status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ |
| goto done; /* LCOV_EXCL_LINE: OutOfMemory */ |
| } |
| } |
| |
| status = ARES_SUCCESS; |
| |
| done: |
| if (status != ARES_ENOMEM) { |
| status = ARES_SUCCESS; |
| } |
| ares_free_array(lookups, num, ares_free); |
| return status; |
| } |
| |
| static ares_status_t process_option(ares_sysconfig_t *sysconfig, |
| ares_buf_t *option) |
| { |
| char **kv = NULL; |
| size_t num = 0; |
| const char *key; |
| const char *val; |
| unsigned int valint = 0; |
| ares_status_t status; |
| |
| /* Split on : */ |
| status = ares_buf_split_str(option, (const unsigned char *)":", 1, |
| ARES_BUF_SPLIT_TRIM, 2, &kv, &num); |
| if (status != ARES_SUCCESS) { |
| goto done; |
| } |
| |
| if (num < 1) { |
| status = ARES_EBADSTR; |
| goto done; |
| } |
| |
| key = kv[0]; |
| if (num == 2) { |
| val = kv[1]; |
| valint = (unsigned int)strtoul(val, NULL, 10); |
| } |
| |
| if (ares_streq(key, "ndots")) { |
| sysconfig->ndots = valint; |
| } else if (ares_streq(key, "retrans") || ares_streq(key, "timeout")) { |
| if (valint == 0) { |
| return ARES_EFORMERR; |
| } |
| sysconfig->timeout_ms = valint * 1000; |
| } else if (ares_streq(key, "retry") || ares_streq(key, "attempts")) { |
| if (valint == 0) { |
| return ARES_EFORMERR; |
| } |
| sysconfig->tries = valint; |
| } else if (ares_streq(key, "rotate")) { |
| sysconfig->rotate = ARES_TRUE; |
| } else if (ares_streq(key, "use-vc") || ares_streq(key, "usevc")) { |
| sysconfig->usevc = ARES_TRUE; |
| } |
| |
| done: |
| ares_free_array(kv, num, ares_free); |
| return status; |
| } |
| |
| ares_status_t ares_sysconfig_set_options(ares_sysconfig_t *sysconfig, |
| const char *str) |
| { |
| ares_buf_t *buf = NULL; |
| ares_array_t *options = NULL; |
| size_t num; |
| size_t i; |
| ares_status_t status; |
| |
| buf = ares_buf_create_const((const unsigned char *)str, ares_strlen(str)); |
| if (buf == NULL) { |
| return ARES_ENOMEM; |
| } |
| |
| status = ares_buf_split(buf, (const unsigned char *)" \t", 2, |
| ARES_BUF_SPLIT_TRIM, 0, &options); |
| if (status != ARES_SUCCESS) { |
| goto done; |
| } |
| |
| num = ares_array_len(options); |
| for (i = 0; i < num; i++) { |
| ares_buf_t **bufptr = ares_array_at(options, i); |
| ares_buf_t *valbuf = *bufptr; |
| |
| status = process_option(sysconfig, valbuf); |
| /* Out of memory is the only fatal condition */ |
| if (status == ARES_ENOMEM) { |
| goto done; /* LCOV_EXCL_LINE: OutOfMemory */ |
| } |
| } |
| |
| status = ARES_SUCCESS; |
| |
| done: |
| ares_array_destroy(options); |
| ares_buf_destroy(buf); |
| return status; |
| } |
| |
| ares_status_t ares_init_by_environment(ares_sysconfig_t *sysconfig) |
| { |
| const char *localdomain; |
| const char *res_options; |
| ares_status_t status; |
| |
| localdomain = getenv("LOCALDOMAIN"); |
| if (localdomain) { |
| char *temp = ares_strdup(localdomain); |
| if (temp == NULL) { |
| return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ |
| } |
| status = config_search(sysconfig, temp, 1); |
| ares_free(temp); |
| if (status != ARES_SUCCESS) { |
| return status; |
| } |
| } |
| |
| res_options = getenv("RES_OPTIONS"); |
| if (res_options) { |
| status = ares_sysconfig_set_options(sysconfig, res_options); |
| if (status != ARES_SUCCESS) { |
| return status; |
| } |
| } |
| |
| return ARES_SUCCESS; |
| } |
| |
| /* Configuration Files: |
| * /etc/resolv.conf |
| * - All Unix-like systems |
| * - Comments start with ; or # |
| * - Lines have a keyword followed by a value that is interpreted specific |
| * to the keyword: |
| * - Keywords: |
| * - nameserver - IP address of nameserver with optional port (using a : |
| * prefix). If using an ipv6 address and specifying a port, the ipv6 |
| * address must be encapsulated in brackets. For link-local ipv6 |
| * addresses, the interface can also be specified with a % prefix. e.g.: |
| * "nameserver [fe80::1]:1234%iface" |
| * This keyword may be specified multiple times. |
| * - search - whitespace separated list of domains |
| * - domain - obsolete, same as search except only a single domain |
| * - lookup / hostresorder - local, bind, file, files |
| * - sortlist - whitespace separated ip-address/netmask pairs |
| * - options - options controlling resolver variables |
| * - ndots:n - set ndots option |
| * - timeout:n (retrans:n) - timeout per query attempt in seconds |
| * - attempts:n (retry:n) - number of times resolver will send query |
| * - rotate - round-robin selection of name servers |
| * - use-vc / usevc - force tcp |
| * /etc/nsswitch.conf |
| * - Modern Linux, FreeBSD, HP-UX, Solaris |
| * - Search order set via: |
| * "hosts: files dns mdns4_minimal mdns4" |
| * - files is /etc/hosts |
| * - dns is dns |
| * - mdns4_minimal does mdns only if ending in .local |
| * - mdns4 does not limit to domains ending in .local |
| * /etc/netsvc.conf |
| * - AIX |
| * - Search order set via: |
| * "hosts = local , bind" |
| * - bind is dns |
| * - local is /etc/hosts |
| * /etc/svc.conf |
| * - Tru64 |
| * - Same format as /etc/netsvc.conf |
| * /etc/host.conf |
| * - Early FreeBSD, Early Linux |
| * - Not worth supporting, format varied based on system, FreeBSD used |
| * just a line per search order, Linux used "order " and a comma |
| * delimited list of "bind" and "hosts" |
| */ |
| |
| |
| /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other |
| * conditions are ignored. Users may mess up config files, but we want to |
| * process anything we can. */ |
| ares_status_t ares_sysconfig_parse_resolv_line(const ares_channel_t *channel, |
| ares_sysconfig_t *sysconfig, |
| ares_buf_t *line) |
| { |
| char option[32]; |
| char value[512]; |
| ares_status_t status = ARES_SUCCESS; |
| |
| /* Ignore lines beginning with a comment */ |
| if (ares_buf_begins_with(line, (const unsigned char *)"#", 1) || |
| ares_buf_begins_with(line, (const unsigned char *)";", 1)) { |
| return ARES_SUCCESS; |
| } |
| |
| ares_buf_tag(line); |
| |
| /* Shouldn't be possible, but if it happens, ignore the line. */ |
| if (ares_buf_consume_nonwhitespace(line) == 0) { |
| return ARES_SUCCESS; |
| } |
| |
| status = ares_buf_tag_fetch_string(line, option, sizeof(option)); |
| if (status != ARES_SUCCESS) { |
| return ARES_SUCCESS; |
| } |
| |
| ares_buf_consume_whitespace(line, ARES_TRUE); |
| |
| status = buf_fetch_string(line, value, sizeof(value)); |
| if (status != ARES_SUCCESS) { |
| return ARES_SUCCESS; |
| } |
| |
| ares_str_trim(value); |
| if (*value == 0) { |
| return ARES_SUCCESS; |
| } |
| |
| /* At this point we have a string option and a string value, both trimmed |
| * of leading and trailing whitespace. Lets try to evaluate them */ |
| if (ares_streq(option, "domain")) { |
| /* Domain is legacy, don't overwrite an existing config set by search */ |
| if (sysconfig->domains == NULL) { |
| status = config_search(sysconfig, value, 1); |
| } |
| } else if (ares_streq(option, "lookup") || |
| ares_streq(option, "hostresorder")) { |
| ares_buf_tag_rollback(line); |
| status = config_lookup(sysconfig, line, " \t"); |
| } else if (ares_streq(option, "search")) { |
| status = config_search(sysconfig, value, 0); |
| } else if (ares_streq(option, "nameserver")) { |
| status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig, value, |
| ARES_TRUE); |
| } else if (ares_streq(option, "sortlist")) { |
| /* Ignore all failures except ENOMEM. If the sysadmin set a bad |
| * sortlist, just ignore the sortlist, don't cause an inoperable |
| * channel */ |
| status = |
| ares_parse_sortlist(&sysconfig->sortlist, &sysconfig->nsortlist, value); |
| if (status != ARES_ENOMEM) { |
| status = ARES_SUCCESS; |
| } |
| } else if (ares_streq(option, "options")) { |
| status = ares_sysconfig_set_options(sysconfig, value); |
| } |
| |
| return status; |
| } |
| |
| /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other |
| * conditions are ignored. Users may mess up config files, but we want to |
| * process anything we can. */ |
| static ares_status_t parse_nsswitch_line(const ares_channel_t *channel, |
| ares_sysconfig_t *sysconfig, |
| ares_buf_t *line) |
| { |
| char option[32]; |
| ares_status_t status = ARES_SUCCESS; |
| ares_array_t *sects = NULL; |
| ares_buf_t **bufptr; |
| ares_buf_t *buf; |
| |
| (void)channel; |
| |
| /* Ignore lines beginning with a comment */ |
| if (ares_buf_begins_with(line, (const unsigned char *)"#", 1)) { |
| return ARES_SUCCESS; |
| } |
| |
| /* database : values (space delimited) */ |
| status = ares_buf_split(line, (const unsigned char *)":", 1, |
| ARES_BUF_SPLIT_TRIM, 2, §s); |
| |
| if (status != ARES_SUCCESS || ares_array_len(sects) != 2) { |
| goto done; |
| } |
| |
| bufptr = ares_array_at(sects, 0); |
| buf = *bufptr; |
| |
| status = buf_fetch_string(buf, option, sizeof(option)); |
| if (status != ARES_SUCCESS) { |
| goto done; |
| } |
| |
| /* Only support "hosts:" */ |
| if (!ares_streq(option, "hosts")) { |
| goto done; |
| } |
| |
| /* Values are space separated */ |
| bufptr = ares_array_at(sects, 1); |
| buf = *bufptr; |
| status = config_lookup(sysconfig, buf, " \t"); |
| |
| done: |
| ares_array_destroy(sects); |
| if (status != ARES_ENOMEM) { |
| status = ARES_SUCCESS; |
| } |
| return status; |
| } |
| |
| /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other |
| * conditions are ignored. Users may mess up config files, but we want to |
| * process anything we can. */ |
| static ares_status_t parse_svcconf_line(const ares_channel_t *channel, |
| ares_sysconfig_t *sysconfig, |
| ares_buf_t *line) |
| { |
| char option[32]; |
| ares_buf_t **bufptr; |
| ares_buf_t *buf; |
| ares_status_t status = ARES_SUCCESS; |
| ares_array_t *sects = NULL; |
| |
| (void)channel; |
| |
| /* Ignore lines beginning with a comment */ |
| if (ares_buf_begins_with(line, (const unsigned char *)"#", 1)) { |
| return ARES_SUCCESS; |
| } |
| |
| /* database = values (comma delimited)*/ |
| status = ares_buf_split(line, (const unsigned char *)"=", 1, |
| ARES_BUF_SPLIT_TRIM, 2, §s); |
| |
| if (status != ARES_SUCCESS || ares_array_len(sects) != 2) { |
| goto done; |
| } |
| |
| bufptr = ares_array_at(sects, 0); |
| buf = *bufptr; |
| status = buf_fetch_string(buf, option, sizeof(option)); |
| if (status != ARES_SUCCESS) { |
| goto done; |
| } |
| |
| /* Only support "hosts=" */ |
| if (!ares_streq(option, "hosts")) { |
| goto done; |
| } |
| |
| /* Values are comma separated */ |
| bufptr = ares_array_at(sects, 1); |
| buf = *bufptr; |
| status = config_lookup(sysconfig, buf, ","); |
| |
| done: |
| ares_array_destroy(sects); |
| if (status != ARES_ENOMEM) { |
| status = ARES_SUCCESS; |
| } |
| return status; |
| } |
| |
| |
| ares_status_t ares_sysconfig_process_buf(const ares_channel_t *channel, |
| ares_sysconfig_t *sysconfig, |
| ares_buf_t *buf, |
| ares_sysconfig_line_cb_t cb) |
| { |
| ares_array_t *lines = NULL; |
| size_t num; |
| size_t i; |
| ares_status_t status; |
| |
| status = ares_buf_split(buf, (const unsigned char *)"\n", 1, |
| ARES_BUF_SPLIT_TRIM, 0, &lines); |
| if (status != ARES_SUCCESS) { |
| goto done; |
| } |
| |
| num = ares_array_len(lines); |
| for (i = 0; i < num; i++) { |
| ares_buf_t **bufptr = ares_array_at(lines, i); |
| ares_buf_t *line = *bufptr; |
| |
| status = cb(channel, sysconfig, line); |
| if (status != ARES_SUCCESS) { |
| goto done; |
| } |
| } |
| |
| done: |
| ares_array_destroy(lines); |
| return status; |
| } |
| |
| /* Should only return: |
| * ARES_ENOTFOUND - file not found |
| * ARES_EFILE - error reading file (perms) |
| * ARES_ENOMEM - out of memory |
| * ARES_SUCCESS - file processed, doesn't necessarily mean it was a good |
| * file, but we're not erroring out if we can't parse |
| * something (or anything at all) */ |
| static ares_status_t process_config_lines(const ares_channel_t *channel, |
| const char *filename, |
| ares_sysconfig_t *sysconfig, |
| ares_sysconfig_line_cb_t cb) |
| { |
| ares_status_t status = ARES_SUCCESS; |
| ares_buf_t *buf = NULL; |
| |
| buf = ares_buf_create(); |
| if (buf == NULL) { |
| status = ARES_ENOMEM; |
| goto done; |
| } |
| |
| status = ares_buf_load_file(filename, buf); |
| if (status != ARES_SUCCESS) { |
| goto done; |
| } |
| |
| status = ares_sysconfig_process_buf(channel, sysconfig, buf, cb); |
| |
| done: |
| ares_buf_destroy(buf); |
| |
| return status; |
| } |
| |
| ares_status_t ares_init_sysconfig_files(const ares_channel_t *channel, |
| ares_sysconfig_t *sysconfig, |
| ares_bool_t process_resolvconf) |
| { |
| ares_status_t status = ARES_SUCCESS; |
| |
| /* Resolv.conf */ |
| if (process_resolvconf) { |
| status = process_config_lines(channel, |
| (channel->resolvconf_path != NULL) |
| ? channel->resolvconf_path |
| : PATH_RESOLV_CONF, |
| sysconfig, ares_sysconfig_parse_resolv_line); |
| if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { |
| goto done; |
| } |
| } |
| |
| /* Nsswitch.conf */ |
| status = process_config_lines(channel, "/etc/nsswitch.conf", sysconfig, |
| parse_nsswitch_line); |
| if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { |
| goto done; |
| } |
| |
| /* netsvc.conf */ |
| status = process_config_lines(channel, "/etc/netsvc.conf", sysconfig, |
| parse_svcconf_line); |
| if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { |
| goto done; |
| } |
| |
| /* svc.conf */ |
| status = process_config_lines(channel, "/etc/svc.conf", sysconfig, |
| parse_svcconf_line); |
| if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { |
| goto done; |
| } |
| |
| status = ARES_SUCCESS; |
| |
| done: |
| return status; |
| } |