1 /* MIT License
2 *
3 * Copyright (c) 2019 Andrew Selivanov
4 * Copyright (c) 2023 Brad House
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * SPDX-License-Identifier: MIT
26 */
27 #include "ares_setup.h"
28
29 #ifdef HAVE_NETINET_IN_H
30 # include <netinet/in.h>
31 #endif
32 #ifdef HAVE_NETDB_H
33 # include <netdb.h>
34 #endif
35 #ifdef HAVE_ARPA_INET_H
36 # include <arpa/inet.h>
37 #endif
38
39 #ifdef HAVE_STRINGS_H
40 # include <strings.h>
41 #endif
42
43 #ifdef HAVE_LIMITS_H
44 # include <limits.h>
45 #endif
46
47 #include "ares.h"
48 #include "ares_private.h"
49
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)50 ares_status_t ares__parse_into_addrinfo(const unsigned char *abuf, size_t alen,
51 ares_bool_t cname_only_is_enodata,
52 unsigned short port,
53 struct ares_addrinfo *ai)
54 {
55 ares_status_t status;
56 ares_dns_record_t *dnsrec = NULL;
57 size_t i;
58 size_t ancount;
59 const char *hostname = NULL;
60 ares_bool_t got_a = ARES_FALSE;
61 ares_bool_t got_aaaa = ARES_FALSE;
62 ares_bool_t got_cname = ARES_FALSE;
63 struct ares_addrinfo_cname *cnames = NULL;
64 struct ares_addrinfo_node *nodes = NULL;
65
66 status = ares_dns_parse(abuf, alen, 0, &dnsrec);
67 if (status != ARES_SUCCESS) {
68 goto done;
69 }
70
71 /* Save question hostname */
72 status = ares_dns_record_query_get(dnsrec, 0, &hostname, NULL, NULL);
73 if (status != ARES_SUCCESS) {
74 goto done;
75 }
76
77 ancount = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER);
78 if (ancount == 0) {
79 status = ARES_ENODATA;
80 goto done;
81 }
82
83 for (i = 0; i < ancount; i++) {
84 ares_dns_rec_type_t rtype;
85 const ares_dns_rr_t *rr =
86 ares_dns_record_rr_get(dnsrec, ARES_SECTION_ANSWER, i);
87
88 if (ares_dns_rr_get_class(rr) != ARES_CLASS_IN) {
89 continue;
90 }
91
92 rtype = ares_dns_rr_get_type(rr);
93
94 /* Issue #683
95 * Old code did this hostname sanity check, however it appears this is
96 * flawed logic. Other resolvers don't do this sanity check. Leaving
97 * this code commented out for future reference.
98 *
99 * rname = ares_dns_rr_get_name(rr);
100 * if ((rtype == ARES_REC_TYPE_A || rtype == ARES_REC_TYPE_AAAA) &&
101 * strcasecmp(rname, hostname) != 0) {
102 * continue;
103 * }
104 */
105
106 if (rtype == ARES_REC_TYPE_CNAME) {
107 struct ares_addrinfo_cname *cname;
108
109 got_cname = ARES_TRUE;
110 /* replace hostname with data from cname
111 * SA: Seems wrong as it introduces order dependency. */
112 hostname = ares_dns_rr_get_str(rr, ARES_RR_CNAME_CNAME);
113
114 cname = ares__append_addrinfo_cname(&cnames);
115 if (cname == NULL) {
116 status = ARES_ENOMEM;
117 goto done;
118 }
119 cname->ttl = (int)ares_dns_rr_get_ttl(rr);
120 cname->alias = ares_strdup(ares_dns_rr_get_name(rr));
121 if (cname->alias == NULL) {
122 status = ARES_ENOMEM;
123 goto done;
124 }
125 cname->name = ares_strdup(hostname);
126 if (cname->name == NULL) {
127 status = ARES_ENOMEM;
128 goto done;
129 }
130 } else if (rtype == ARES_REC_TYPE_A) {
131 got_a = ARES_TRUE;
132 status =
133 ares_append_ai_node(AF_INET, port, ares_dns_rr_get_ttl(rr),
134 ares_dns_rr_get_addr(rr, ARES_RR_A_ADDR), &nodes);
135 if (status != ARES_SUCCESS) {
136 goto done;
137 }
138 } else if (rtype == ARES_REC_TYPE_AAAA) {
139 got_aaaa = ARES_TRUE;
140 status = ares_append_ai_node(AF_INET6, port, ares_dns_rr_get_ttl(rr),
141 ares_dns_rr_get_addr6(rr, ARES_RR_AAAA_ADDR),
142 &nodes);
143 if (status != ARES_SUCCESS) {
144 goto done;
145 }
146 } else {
147 continue;
148 }
149 }
150
151 if (!got_a && !got_aaaa &&
152 (!got_cname || (got_cname && cname_only_is_enodata))) {
153 status = ARES_ENODATA;
154 goto done;
155 }
156
157 /* save the hostname as ai->name */
158 if (ai->name == NULL || strcasecmp(ai->name, hostname) != 0) {
159 ares_free(ai->name);
160 ai->name = ares_strdup(hostname);
161 if (ai->name == NULL) {
162 status = ARES_ENOMEM;
163 goto done;
164 }
165 }
166
167 if (got_a || got_aaaa) {
168 ares__addrinfo_cat_nodes(&ai->nodes, nodes);
169 nodes = NULL;
170 }
171
172 if (got_cname) {
173 ares__addrinfo_cat_cnames(&ai->cnames, cnames);
174 cnames = NULL;
175 }
176
177 done:
178 ares__freeaddrinfo_cnames(cnames);
179 ares__freeaddrinfo_nodes(nodes);
180 ares_dns_record_destroy(dnsrec);
181
182 /* compatibility */
183 if (status == ARES_EBADNAME) {
184 status = ARES_EBADRESP;
185 }
186
187 return status;
188 }
189