diff options
Diffstat (limited to 'src/lib/ares__parse_into_addrinfo.c')
-rw-r--r-- | src/lib/ares__parse_into_addrinfo.c | 339 |
1 files changed, 147 insertions, 192 deletions
diff --git a/src/lib/ares__parse_into_addrinfo.c b/src/lib/ares__parse_into_addrinfo.c index 4393f04..aa14022 100644 --- a/src/lib/ares__parse_into_addrinfo.c +++ b/src/lib/ares__parse_into_addrinfo.c @@ -1,18 +1,29 @@ -/* Copyright (C) 2019 by Andrew Selivanov +/* MIT License * - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without - * fee is hereby granted, provided that the above copyright - * notice appear in all copies and that both that copyright - * notice and this permission notice appear in supporting - * documentation, and that the name of M.I.T. not be used in - * advertising or publicity pertaining to distribution of the - * software without specific, written prior permission. - * M.I.T. makes no representations about the suitability of - * this software for any purpose. It is provided "as is" - * without express or implied warranty. + * Copyright (c) 2019 Andrew Selivanov + * Copyright (c) 2023 Brad House + * + * 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_NETINET_IN_H @@ -25,8 +36,6 @@ # include <arpa/inet.h> #endif -#include "ares_nameser.h" - #ifdef HAVE_STRINGS_H # include <strings.h> #endif @@ -36,194 +45,140 @@ #endif #include "ares.h" -#include "ares_dns.h" #include "ares_private.h" -int ares__parse_into_addrinfo(const unsigned char *abuf, - int alen, int cname_only_is_enodata, - unsigned short port, - struct ares_addrinfo *ai) +ares_status_t ares__parse_into_addrinfo(const unsigned char *abuf, size_t alen, + ares_bool_t cname_only_is_enodata, + unsigned short port, + struct ares_addrinfo *ai) { - unsigned int qdcount, ancount; - int status, i, rr_type, rr_class, rr_len, rr_ttl; - int got_a = 0, got_aaaa = 0, got_cname = 0; - long len; - const unsigned char *aptr; - char *question_hostname = NULL; - char *hostname, *rr_name = NULL, *rr_data; - struct ares_addrinfo_cname *cname, *cnames = NULL; - struct ares_addrinfo_node *nodes = NULL; - - /* Give up if abuf doesn't have room for a header. */ - if (alen < HFIXEDSZ) - return ARES_EBADRESP; - - /* Fetch the question and answer count from the header. */ - qdcount = DNS_HEADER_QDCOUNT(abuf); - ancount = DNS_HEADER_ANCOUNT(abuf); - if (qdcount != 1) - return ARES_EBADRESP; - - - /* Expand the name from the question, and skip past the question. */ - aptr = abuf + HFIXEDSZ; - status = ares__expand_name_for_response(aptr, abuf, alen, &question_hostname, &len, 0); - if (status != ARES_SUCCESS) - return status; - if (aptr + len + QFIXEDSZ > abuf + alen) - { - status = ARES_EBADRESP; - goto failed_stat; + ares_status_t status; + ares_dns_record_t *dnsrec = NULL; + size_t i; + size_t ancount; + const char *hostname = NULL; + ares_bool_t got_a = ARES_FALSE; + ares_bool_t got_aaaa = ARES_FALSE; + ares_bool_t got_cname = ARES_FALSE; + struct ares_addrinfo_cname *cnames = NULL; + struct ares_addrinfo_node *nodes = NULL; + + status = ares_dns_parse(abuf, alen, 0, &dnsrec); + if (status != ARES_SUCCESS) { + goto done; + } + + /* Save question hostname */ + status = ares_dns_record_query_get(dnsrec, 0, &hostname, NULL, NULL); + if (status != ARES_SUCCESS) { + goto done; + } + + ancount = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); + if (ancount == 0) { + status = ARES_ENODATA; + goto done; + } + + for (i = 0; i < ancount; i++) { + const char *rname = NULL; + ares_dns_rec_type_t rtype; + const ares_dns_rr_t *rr = + ares_dns_record_rr_get(dnsrec, ARES_SECTION_ANSWER, i); + + if (ares_dns_rr_get_class(rr) != ARES_CLASS_IN) { + continue; } - hostname = question_hostname; - - aptr += len + QFIXEDSZ; - - /* Examine each answer resource record (RR) in turn. */ - for (i = 0; i < (int)ancount; i++) - { - /* Decode the RR up to the data field. */ - status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len, 0); - if (status != ARES_SUCCESS) - { - rr_name = NULL; - goto failed_stat; - } - - aptr += len; - if (aptr + RRFIXEDSZ > abuf + alen) - { - status = ARES_EBADRESP; - goto failed_stat; - } - rr_type = DNS_RR_TYPE(aptr); - rr_class = DNS_RR_CLASS(aptr); - rr_len = DNS_RR_LEN(aptr); - rr_ttl = DNS_RR_TTL(aptr); - aptr += RRFIXEDSZ; - if (aptr + rr_len > abuf + alen) - { - status = ARES_EBADRESP; - goto failed_stat; - } - - if (rr_class == C_IN && rr_type == T_A - && rr_len == sizeof(struct in_addr) - && strcasecmp(rr_name, hostname) == 0) - { - got_a = 1; - if (aptr + sizeof(struct in_addr) > abuf + alen) - { /* LCOV_EXCL_START: already checked above */ - status = ARES_EBADRESP; - goto failed_stat; - } /* LCOV_EXCL_STOP */ - - status = ares_append_ai_node(AF_INET, port, rr_ttl, aptr, &nodes); - if (status != ARES_SUCCESS) - goto failed_stat; - } - else if (rr_class == C_IN && rr_type == T_AAAA - && rr_len == sizeof(struct ares_in6_addr) - && strcasecmp(rr_name, hostname) == 0) - { - got_aaaa = 1; - if (aptr + sizeof(struct ares_in6_addr) > abuf + alen) - { /* LCOV_EXCL_START: already checked above */ - status = ARES_EBADRESP; - goto failed_stat; - } /* LCOV_EXCL_STOP */ - - status = ares_append_ai_node(AF_INET6, port, rr_ttl, aptr, &nodes); - if (status != ARES_SUCCESS) - goto failed_stat; - } - - if (rr_class == C_IN && rr_type == T_CNAME) - { - got_cname = 1; - status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data, - &len, 1); - if (status != ARES_SUCCESS) - { - goto failed_stat; - } - - /* Decode the RR data and replace the hostname with it. */ - /* SA: Seems wrong as it introduses order dependency. */ - hostname = rr_data; - - cname = ares__append_addrinfo_cname(&cnames); - if (!cname) - { - status = ARES_ENOMEM; - ares_free(rr_data); - goto failed_stat; - } - cname->ttl = rr_ttl; - cname->alias = rr_name; - cname->name = rr_data; - rr_name = NULL; - } - else - { - /* rr_name is only saved for cname */ - ares_free(rr_name); - rr_name = NULL; - } - - - aptr += rr_len; - if (aptr > abuf + alen) - { /* LCOV_EXCL_START: already checked above */ - status = ARES_EBADRESP; - goto failed_stat; - } /* LCOV_EXCL_STOP */ + rtype = ares_dns_rr_get_type(rr); + rname = ares_dns_rr_get_name(rr); + + /* Old code did this hostname sanity check */ + if ((rtype == ARES_REC_TYPE_A || rtype == ARES_REC_TYPE_AAAA) && + strcasecmp(rname, hostname) != 0) { + continue; } - if (status == ARES_SUCCESS) - { - if (!got_a && !got_aaaa) - { - if (!got_cname || (got_cname && cname_only_is_enodata)) - { - status = ARES_ENODATA; - goto failed_stat; - } - } - - /* save the question hostname as ai->name */ - if (ai->name == NULL || strcasecmp(ai->name, question_hostname) != 0) - { - ares_free(ai->name); - ai->name = ares_strdup(question_hostname); - if (!ai->name) - { - status = ARES_ENOMEM; - goto failed_stat; - } - } - - if (got_a || got_aaaa) - { - ares__addrinfo_cat_nodes(&ai->nodes, nodes); - nodes = NULL; - } - - if (got_cname) - { - ares__addrinfo_cat_cnames(&ai->cnames, cnames); - cnames = NULL; - } + if (rtype == ARES_REC_TYPE_CNAME) { + struct ares_addrinfo_cname *cname; + + got_cname = ARES_TRUE; + /* replace hostname with data from cname + * SA: Seems wrong as it introduces order dependency. */ + hostname = ares_dns_rr_get_str(rr, ARES_RR_CNAME_CNAME); + + cname = ares__append_addrinfo_cname(&cnames); + if (cname == NULL) { + status = ARES_ENOMEM; + goto done; + } + cname->ttl = (int)ares_dns_rr_get_ttl(rr); + cname->alias = ares_strdup(ares_dns_rr_get_name(rr)); + if (cname->alias == NULL) { + status = ARES_ENOMEM; + goto done; + } + cname->name = ares_strdup(hostname); + if (cname->name == NULL) { + status = ARES_ENOMEM; + goto done; + } + } else if (rtype == ARES_REC_TYPE_A) { + got_a = ARES_TRUE; + status = + ares_append_ai_node(AF_INET, port, ares_dns_rr_get_ttl(rr), + ares_dns_rr_get_addr(rr, ARES_RR_A_ADDR), &nodes); + if (status != ARES_SUCCESS) { + goto done; + } + } else if (rtype == ARES_REC_TYPE_AAAA) { + got_aaaa = ARES_TRUE; + status = ares_append_ai_node(AF_INET6, port, ares_dns_rr_get_ttl(rr), + ares_dns_rr_get_addr6(rr, ARES_RR_AAAA_ADDR), + &nodes); + if (status != ARES_SUCCESS) { + goto done; + } + } else { + continue; + } + } + + if (!got_a && !got_aaaa && + (!got_cname || (got_cname && cname_only_is_enodata))) { + status = ARES_ENODATA; + goto done; + } + + /* save the hostname as ai->name */ + if (ai->name == NULL || strcasecmp(ai->name, hostname) != 0) { + ares_free(ai->name); + ai->name = ares_strdup(hostname); + if (ai->name == NULL) { + status = ARES_ENOMEM; + goto done; } + } - ares_free(question_hostname); - return status; + if (got_a || got_aaaa) { + ares__addrinfo_cat_nodes(&ai->nodes, nodes); + nodes = NULL; + } -failed_stat: - ares_free(question_hostname); - ares_free(rr_name); + if (got_cname) { + ares__addrinfo_cat_cnames(&ai->cnames, cnames); + cnames = NULL; + } + +done: ares__freeaddrinfo_cnames(cnames); ares__freeaddrinfo_nodes(nodes); + ares_dns_record_destroy(dnsrec); + + /* compatibiltiy */ + if (status == ARES_EBADNAME) { + status = ARES_EBADRESP; + } + return status; } |