summaryrefslogtreecommitdiff
path: root/src/lib/ares__parse_into_addrinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/ares__parse_into_addrinfo.c')
-rw-r--r--src/lib/ares__parse_into_addrinfo.c339
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;
}