%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; }