%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_search.c |
/* MIT License
*
* Copyright (c) 1998 Massachusetts Institute of Technology
* Copyright (c) The c-ares project and its contributors
*
* 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_STRINGS_H
# include <strings.h>
#endif
#include "ares.h"
#include "ares_private.h"
struct search_query {
/* Arguments passed to ares_search */
ares_channel_t *channel;
char *name; /* copied into an allocated buffer */
int dnsclass;
int type;
ares_callback callback;
void *arg;
char **domains; /* duplicate for ares_reinit() safety */
size_t ndomains;
int status_as_is; /* error status from trying as-is */
size_t next_domain; /* next search domain to try */
ares_bool_t trying_as_is; /* current query is for name as-is */
size_t timeouts; /* number of timeouts we saw for this request */
ares_bool_t ever_got_nodata; /* did we ever get ARES_ENODATA along the way? */
};
static void search_callback(void *arg, int status, int timeouts,
unsigned char *abuf, int alen);
static void end_squery(struct search_query *squery, ares_status_t status,
unsigned char *abuf, size_t alen);
static void ares_search_int(ares_channel_t *channel, const char *name,
int dnsclass, int type, ares_callback callback,
void *arg)
{
struct search_query *squery;
char *s;
const char *p;
ares_status_t status;
size_t ndots;
/* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
if (ares__is_onion_domain(name)) {
callback(arg, ARES_ENOTFOUND, 0, NULL, 0);
return;
}
/* If name only yields one domain to search, then we don't have
* to keep extra state, so just do an ares_query().
*/
status = ares__single_domain(channel, name, &s);
if (status != ARES_SUCCESS) {
callback(arg, (int)status, 0, NULL, 0);
return;
}
if (s) {
ares_query(channel, s, dnsclass, type, callback, arg);
ares_free(s);
return;
}
/* Allocate a search_query structure to hold the state necessary for
* doing multiple lookups.
*/
squery = ares_malloc_zero(sizeof(*squery));
if (!squery) {
callback(arg, ARES_ENOMEM, 0, NULL, 0);
return;
}
squery->channel = channel;
squery->name = ares_strdup(name);
if (!squery->name) {
ares_free(squery);
callback(arg, ARES_ENOMEM, 0, NULL, 0);
return;
}
/* Duplicate domains for safety during ares_reinit() */
if (channel->ndomains) {
squery->domains =
ares__strsplit_duplicate(channel->domains, channel->ndomains);
if (squery->domains == NULL) {
ares_free(squery->name);
ares_free(squery);
callback(arg, ARES_ENOMEM, 0, NULL, 0);
return;
}
squery->ndomains = channel->ndomains;
}
squery->dnsclass = dnsclass;
squery->type = type;
squery->status_as_is = -1;
squery->callback = callback;
squery->arg = arg;
squery->timeouts = 0;
squery->ever_got_nodata = ARES_FALSE;
/* Count the number of dots in name. */
ndots = 0;
for (p = name; *p; p++) {
if (*p == '.') {
ndots++;
}
}
/* If ndots is at least the channel ndots threshold (usually 1),
* then we try the name as-is first. Otherwise, we try the name
* as-is last.
*/
if (ndots >= channel->ndots || squery->ndomains == 0) {
/* Try the name as-is first. */
squery->next_domain = 0;
squery->trying_as_is = ARES_TRUE;
ares_query(channel, name, dnsclass, type, search_callback, squery);
} else {
/* Try the name as-is last; start with the first search domain. */
squery->next_domain = 1;
squery->trying_as_is = ARES_FALSE;
status = ares__cat_domain(name, squery->domains[0], &s);
if (status == ARES_SUCCESS) {
ares_query(channel, s, dnsclass, type, search_callback, squery);
ares_free(s);
} else {
/* failed, free the malloc()ed memory */
ares_free(squery->name);
ares_free(squery);
callback(arg, (int)status, 0, NULL, 0);
}
}
}
void ares_search(ares_channel_t *channel, const char *name, int dnsclass,
int type, ares_callback callback, void *arg)
{
if (channel == NULL) {
return;
}
ares__channel_lock(channel);
ares_search_int(channel, name, dnsclass, type, callback, arg);
ares__channel_unlock(channel);
}
static void search_callback(void *arg, int status, int timeouts,
unsigned char *abuf, int alen)
{
struct search_query *squery = (struct search_query *)arg;
ares_channel_t *channel = squery->channel;
char *s;
squery->timeouts += (size_t)timeouts;
/* Stop searching unless we got a non-fatal error. */
if (status != ARES_ENODATA && status != ARES_ESERVFAIL &&
status != ARES_ENOTFOUND) {
end_squery(squery, (ares_status_t)status, abuf, (size_t)alen);
} else {
/* Save the status if we were trying as-is. */
if (squery->trying_as_is) {
squery->status_as_is = status;
}
/*
* If we ever get ARES_ENODATA along the way, record that; if the search
* should run to the very end and we got at least one ARES_ENODATA,
* then callers like ares_gethostbyname() may want to try a T_A search
* even if the last domain we queried for T_AAAA resource records
* returned ARES_ENOTFOUND.
*/
if (status == ARES_ENODATA) {
squery->ever_got_nodata = ARES_TRUE;
}
if (squery->next_domain < squery->ndomains) {
ares_status_t mystatus;
/* Try the next domain. */
mystatus = ares__cat_domain(squery->name,
squery->domains[squery->next_domain], &s);
if (mystatus != ARES_SUCCESS) {
end_squery(squery, mystatus, NULL, 0);
} else {
squery->trying_as_is = ARES_FALSE;
squery->next_domain++;
ares_query(channel, s, squery->dnsclass, squery->type, search_callback,
squery);
ares_free(s);
}
} else if (squery->status_as_is == -1) {
/* Try the name as-is at the end. */
squery->trying_as_is = ARES_TRUE;
ares_query(channel, squery->name, squery->dnsclass, squery->type,
search_callback, squery);
} else {
if (squery->status_as_is == ARES_ENOTFOUND && squery->ever_got_nodata) {
end_squery(squery, ARES_ENODATA, NULL, 0);
} else {
end_squery(squery, (ares_status_t)squery->status_as_is, NULL, 0);
}
}
}
}
static void end_squery(struct search_query *squery, ares_status_t status,
unsigned char *abuf, size_t alen)
{
squery->callback(squery->arg, (int)status, (int)squery->timeouts, abuf,
(int)alen);
ares__strsplit_free(squery->domains, squery->ndomains);
ares_free(squery->name);
ares_free(squery);
}
/* Concatenate two domains. */
ares_status_t ares__cat_domain(const char *name, const char *domain, char **s)
{
size_t nlen = ares_strlen(name);
size_t dlen = ares_strlen(domain);
*s = ares_malloc(nlen + 1 + dlen + 1);
if (!*s) {
return ARES_ENOMEM;
}
memcpy(*s, name, nlen);
(*s)[nlen] = '.';
if (strcmp(domain, ".") == 0) {
/* Avoid appending the root domain to the separator, which would set *s to
an ill-formed value (ending in two consecutive dots). */
dlen = 0;
}
memcpy(*s + nlen + 1, domain, dlen);
(*s)[nlen + 1 + dlen] = 0;
return ARES_SUCCESS;
}
/* Determine if this name only yields one query. If it does, set *s to
* the string we should query, in an allocated buffer. If not, set *s
* to NULL.
*/
ares_status_t ares__single_domain(const ares_channel_t *channel,
const char *name, char **s)
{
size_t len = ares_strlen(name);
const char *hostaliases;
FILE *fp;
char *line = NULL;
ares_status_t status;
size_t linesize;
const char *p;
const char *q;
int error;
/* If the name contains a trailing dot, then the single query is the name
* sans the trailing dot.
*/
if ((len > 0) && (name[len - 1] == '.')) {
*s = ares_strdup(name);
return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
}
if (!(channel->flags & ARES_FLAG_NOALIASES) && !strchr(name, '.')) {
/* The name might be a host alias. */
hostaliases = getenv("HOSTALIASES");
if (hostaliases) {
fp = fopen(hostaliases, "r");
if (fp) {
while ((status = ares__read_line(fp, &line, &linesize)) ==
ARES_SUCCESS) {
if (strncasecmp(line, name, len) != 0 || !ISSPACE(line[len])) {
continue;
}
p = line + len;
while (ISSPACE(*p)) {
p++;
}
if (*p) {
q = p + 1;
while (*q && !ISSPACE(*q)) {
q++;
}
*s = ares_malloc((size_t)(q - p + 1));
if (*s) {
memcpy(*s, p, (size_t)(q - p));
(*s)[q - p] = 0;
}
ares_free(line);
fclose(fp);
return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
}
}
ares_free(line);
fclose(fp);
if (status != ARES_SUCCESS && status != ARES_EOF) {
return status;
}
} 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", hostaliases));
*s = NULL;
return ARES_EFILE;
}
}
}
}
if (channel->flags & ARES_FLAG_NOSEARCH || channel->ndomains == 0) {
/* No domain search to do; just try the name as-is. */
*s = ares_strdup(name);
return (*s) ? ARES_SUCCESS : ARES_ENOMEM;
}
*s = NULL;
return ARES_SUCCESS;
}