%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/cares/src/lib/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/cares/src/lib/ares_sysconfig_files.c |
/* 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_setup.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
#include "ares_nameser.h"
#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.h"
#include "ares_inet_net_pton.h"
#include "ares_platform.h"
#include "ares_private.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;
}
*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)) == 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)) ==
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__llist_t *list = NULL;
ares_status_t status = ARES_SUCCESS;
ares__llist_node_t *node = NULL;
if (sortlist == NULL || nsort == NULL || str == NULL) {
return ARES_EFORMERR;
}
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, &list);
if (status != ARES_SUCCESS) {
goto done;
}
for (node = ares__llist_node_first(list); node != NULL;
node = ares__llist_node_next(node)) {
ares__buf_t *entry = ares__llist_node_val(node);
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;
goto done;
}
}
status = ARES_SUCCESS;
done:
ares__buf_destroy(buf);
ares__llist_destroy(list);
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)
{
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;
}
return ARES_SUCCESS;
}
static ares_status_t config_domain(ares_sysconfig_t *sysconfig, char *str)
{
char *q;
/* Set a single search domain. */
q = str;
while (*q && !ISSPACE(*q)) {
q++;
}
*q = '\0';
return config_search(sysconfig, str);
}
static ares_status_t config_lookup(ares_sysconfig_t *sysconfig, const char *str,
const char *bindch, const char *altbindch,
const char *filech)
{
char lookups[3];
char *l;
const char *p;
ares_bool_t found;
if (altbindch == NULL) {
altbindch = bindch;
}
/* Set the lookup order. Only the first letter of each work
* is relevant, and it has to be "b" for DNS or "f" for the
* host file. Ignore everything else.
*/
l = lookups;
p = str;
found = ARES_FALSE;
while (*p) {
if ((*p == *bindch || *p == *altbindch || *p == *filech) &&
l < lookups + 2) {
if (*p == *bindch || *p == *altbindch) {
*l++ = 'b';
} else {
*l++ = 'f';
}
found = ARES_TRUE;
}
while (*p && !ISSPACE(*p) && (*p != ',')) {
p++;
}
while (*p && (ISSPACE(*p) || (*p == ','))) {
p++;
}
}
if (!found) {
return ARES_ENOTINITIALIZED;
}
*l = '\0';
ares_free(sysconfig->lookups);
sysconfig->lookups = ares_strdup(lookups);
if (sysconfig->lookups == NULL) {
return ARES_ENOMEM;
}
return ARES_SUCCESS;
}
static const char *try_option(const char *p, const char *q, const char *opt)
{
size_t len = ares_strlen(opt);
return ((size_t)(q - p) >= len && !strncmp(p, opt, len)) ? &p[len] : NULL;
}
static ares_status_t set_options(ares_sysconfig_t *sysconfig, const char *str)
{
const char *p;
const char *q;
const char *val;
if (str == NULL) {
return ARES_SUCCESS;
}
p = str;
while (*p) {
q = p;
while (*q && !ISSPACE(*q)) {
q++;
}
val = try_option(p, q, "ndots:");
if (val) {
sysconfig->ndots = strtoul(val, NULL, 10);
}
// Outdated option.
val = try_option(p, q, "retrans:");
if (val) {
sysconfig->timeout_ms = strtoul(val, NULL, 10);
}
val = try_option(p, q, "timeout:");
if (val) {
sysconfig->timeout_ms = strtoul(val, NULL, 10) * 1000;
}
// Outdated option.
val = try_option(p, q, "retry:");
if (val) {
sysconfig->tries = strtoul(val, NULL, 10);
}
val = try_option(p, q, "attempts:");
if (val) {
sysconfig->tries = strtoul(val, NULL, 10);
}
val = try_option(p, q, "rotate");
if (val) {
sysconfig->rotate = ARES_TRUE;
}
p = q;
while (ISSPACE(*p)) {
p++;
}
}
return ARES_SUCCESS;
}
static char *try_config(char *s, const char *opt, char scc)
{
size_t len;
char *p;
char *q;
if (!s || !opt) {
/* no line or no option */
return NULL; /* LCOV_EXCL_LINE */
}
/* Hash '#' character is always used as primary comment char, additionally
a not-NUL secondary comment char will be considered when specified. */
/* trim line comment */
p = s;
if (scc) {
while (*p && (*p != '#') && (*p != scc)) {
p++;
}
} else {
while (*p && (*p != '#')) {
p++;
}
}
*p = '\0';
/* trim trailing whitespace */
q = p - 1;
while ((q >= s) && ISSPACE(*q)) {
q--;
}
*++q = '\0';
/* skip leading whitespace */
p = s;
while (*p && ISSPACE(*p)) {
p++;
}
if (!*p) {
/* empty line */
return NULL;
}
if ((len = ares_strlen(opt)) == 0) {
/* empty option */
return NULL; /* LCOV_EXCL_LINE */
}
if (strncmp(p, opt, len) != 0) {
/* line and option do not match */
return NULL;
}
/* skip over given option name */
p += len;
if (!*p) {
/* no option value */
return NULL; /* LCOV_EXCL_LINE */
}
if ((opt[len - 1] != ':') && (opt[len - 1] != '=') && !ISSPACE(*p)) {
/* whitespace between option name and value is mandatory
for given option names which do not end with ':' or '=' */
return NULL;
}
/* skip over whitespace */
while (*p && ISSPACE(*p)) {
p++;
}
if (!*p) {
/* no option value */
return NULL;
}
/* return pointer to option value */
return p;
}
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;
}
status = config_domain(sysconfig, temp);
ares_free(temp);
if (status != ARES_SUCCESS) {
return status;
}
}
res_options = getenv("RES_OPTIONS");
if (res_options) {
status = set_options(sysconfig, res_options);
if (status != ARES_SUCCESS) {
return status;
}
}
return ARES_SUCCESS;
}
ares_status_t ares__init_sysconfig_files(const ares_channel_t *channel,
ares_sysconfig_t *sysconfig)
{
char *p;
FILE *fp = NULL;
char *line = NULL;
size_t linesize = 0;
int error;
const char *resolvconf_path;
ares_status_t status = ARES_SUCCESS;
/* Support path for resolvconf filename set by ares_init_options */
if (channel->resolvconf_path) {
resolvconf_path = channel->resolvconf_path;
} else {
resolvconf_path = PATH_RESOLV_CONF;
}
fp = fopen(resolvconf_path, "r");
if (fp) {
while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) {
if ((p = try_config(line, "domain", ';'))) {
status = config_domain(sysconfig, p);
} else if ((p = try_config(line, "lookup", ';'))) {
status = config_lookup(sysconfig, p, "bind", NULL, "file");
} else if ((p = try_config(line, "search", ';'))) {
status = config_search(sysconfig, p);
} else if ((p = try_config(line, "nameserver", ';'))) {
status =
ares__sconfig_append_fromstr(&sysconfig->sconfig, p, ARES_TRUE);
} else if ((p = try_config(line, "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, p);
if (status != ARES_ENOMEM) {
status = ARES_SUCCESS;
}
} else if ((p = try_config(line, "options", ';'))) {
status = set_options(sysconfig, p);
} else {
status = ARES_SUCCESS;
}
if (status != ARES_SUCCESS) {
fclose(fp);
goto done;
}
}
fclose(fp);
if (status != ARES_EOF) {
goto done;
}
} else {
error = ERRNO;
switch (error) {
case ENOENT:
case ESRCH:
break;
default:
DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
strerror(error)));
DEBUGF(fprintf(stderr, "Error opening file: %s\n", PATH_RESOLV_CONF));
status = ARES_EFILE;
goto done;
}
}
/* Many systems (Solaris, Linux, BSD's) use nsswitch.conf */
fp = fopen("/etc/nsswitch.conf", "r");
if (fp) {
while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) {
if ((p = try_config(line, "hosts:", '\0'))) {
(void)config_lookup(sysconfig, p, "dns", "resolve", "files");
}
}
fclose(fp);
if (status != ARES_EOF) {
goto done;
}
} else {
error = ERRNO;
switch (error) {
case ENOENT:
case ESRCH:
break;
default:
DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
strerror(error)));
DEBUGF(
fprintf(stderr, "Error opening file: %s\n", "/etc/nsswitch.conf"));
break;
}
/* ignore error, maybe we will get luck in next if clause */
}
/* Linux / GNU libc 2.x and possibly others have host.conf */
fp = fopen("/etc/host.conf", "r");
if (fp) {
while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) {
if ((p = try_config(line, "order", '\0'))) {
/* ignore errors */
(void)config_lookup(sysconfig, p, "bind", NULL, "hosts");
}
}
fclose(fp);
if (status != ARES_EOF) {
goto done;
}
} else {
error = ERRNO;
switch (error) {
case ENOENT:
case ESRCH:
break;
default:
DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
strerror(error)));
DEBUGF(fprintf(stderr, "Error opening file: %s\n", "/etc/host.conf"));
break;
}
/* ignore error, maybe we will get luck in next if clause */
}
/* Tru64 uses /etc/svc.conf */
fp = fopen("/etc/svc.conf", "r");
if (fp) {
while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS) {
if ((p = try_config(line, "hosts=", '\0'))) {
/* ignore errors */
(void)config_lookup(sysconfig, p, "bind", NULL, "local");
}
}
fclose(fp);
if (status != ARES_EOF) {
goto done;
}
} else {
error = ERRNO;
switch (error) {
case ENOENT:
case ESRCH:
break;
default:
DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
strerror(error)));
DEBUGF(fprintf(stderr, "Error opening file: %s\n", "/etc/svc.conf"));
break;
}
/* ignore error */
}
status = ARES_SUCCESS;
done:
ares_free(line);
return status;
}