1
2 /* Copyright 2020 by <danny.sonnenschein@platynum.ch>
3 *
4 * Permission to use, copy, modify, and distribute this
5 * software and its documentation for any purpose and without
6 * fee is hereby granted, provided that the above copyright
7 * notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting
9 * documentation, and that the name of M.I.T. not be used in
10 * advertising or publicity pertaining to distribution of the
11 * software without specific, written prior permission.
12 * M.I.T. makes no representations about the suitability of
13 * this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
15 */
16
17 #include "ares_setup.h"
18
19 #ifdef HAVE_NETINET_IN_H
20 # include <netinet/in.h>
21 #endif
22 #ifdef HAVE_NETDB_H
23 # include <netdb.h>
24 #endif
25 #ifdef HAVE_ARPA_INET_H
26 # include <arpa/inet.h>
27 #endif
28
29 #include "ares_nameser.h"
30
31 #ifdef HAVE_STRINGS_H
32 # include <strings.h>
33 #endif
34
35 #include "ares.h"
36 #include "ares_dns.h"
37 #include "ares_data.h"
38 #include "ares_private.h"
39
40 int
ares_parse_caa_reply(const unsigned char * abuf,int alen,struct ares_caa_reply ** caa_out)41 ares_parse_caa_reply (const unsigned char *abuf, int alen,
42 struct ares_caa_reply **caa_out)
43 {
44 unsigned int qdcount, ancount, i;
45 const unsigned char *aptr;
46 const unsigned char *strptr;
47 int status, rr_type, rr_class, rr_len;
48 long len;
49 char *hostname = NULL, *rr_name = NULL;
50 struct ares_caa_reply *caa_head = NULL;
51 struct ares_caa_reply *caa_last = NULL;
52 struct ares_caa_reply *caa_curr;
53
54 /* Set *caa_out to NULL for all failure cases. */
55 *caa_out = NULL;
56
57 /* Give up if abuf doesn't have room for a header. */
58 if (alen < HFIXEDSZ)
59 return ARES_EBADRESP;
60
61 /* Fetch the question and answer count from the header. */
62 qdcount = DNS_HEADER_QDCOUNT (abuf);
63 ancount = DNS_HEADER_ANCOUNT (abuf);
64 if (qdcount != 1)
65 return ARES_EBADRESP;
66 if (ancount == 0)
67 return ARES_ENODATA;
68
69 /* Expand the name from the question, and skip past the question. */
70 aptr = abuf + HFIXEDSZ;
71 status = ares_expand_name (aptr, abuf, alen, &hostname, &len);
72 if (status != ARES_SUCCESS)
73 return status;
74
75 if (aptr + len + QFIXEDSZ > abuf + alen)
76 {
77 ares_free (hostname);
78 return ARES_EBADRESP;
79 }
80 aptr += len + QFIXEDSZ;
81
82 /* Examine each answer resource record (RR) in turn. */
83 for (i = 0; i < ancount; i++)
84 {
85 /* Decode the RR up to the data field. */
86 status = ares_expand_name (aptr, abuf, alen, &rr_name, &len);
87 if (status != ARES_SUCCESS)
88 {
89 break;
90 }
91 aptr += len;
92 if (aptr + RRFIXEDSZ > abuf + alen)
93 {
94 status = ARES_EBADRESP;
95 break;
96 }
97 rr_type = DNS_RR_TYPE (aptr);
98 rr_class = DNS_RR_CLASS (aptr);
99 rr_len = DNS_RR_LEN (aptr);
100 aptr += RRFIXEDSZ;
101 if (aptr + rr_len > abuf + alen)
102 {
103 status = ARES_EBADRESP;
104 break;
105 }
106
107 /* Check if we are really looking at a CAA record */
108 if ((rr_class == C_IN || rr_class == C_CHAOS) && rr_type == T_CAA)
109 {
110 strptr = aptr;
111
112 /* Allocate storage for this CAA answer appending it to the list */
113 caa_curr = ares_malloc_data(ARES_DATATYPE_CAA_REPLY);
114 if (!caa_curr)
115 {
116 status = ARES_ENOMEM;
117 break;
118 }
119 if (caa_last)
120 {
121 caa_last->next = caa_curr;
122 }
123 else
124 {
125 caa_head = caa_curr;
126 }
127 caa_last = caa_curr;
128 if (rr_len < 2)
129 {
130 status = ARES_EBADRESP;
131 break;
132 }
133 caa_curr->critical = (int)*strptr++;
134 caa_curr->plength = (int)*strptr++;
135 if (caa_curr->plength <= 0 || (int)caa_curr->plength >= rr_len - 2)
136 {
137 status = ARES_EBADRESP;
138 break;
139 }
140 caa_curr->property = ares_malloc (caa_curr->plength + 1/* Including null byte */);
141 if (caa_curr->property == NULL)
142 {
143 status = ARES_ENOMEM;
144 break;
145 }
146 memcpy ((char *) caa_curr->property, strptr, caa_curr->plength);
147 /* Make sure we NULL-terminate */
148 caa_curr->property[caa_curr->plength] = 0;
149 strptr += caa_curr->plength;
150
151 caa_curr->length = rr_len - caa_curr->plength - 2;
152 if (caa_curr->length <= 0)
153 {
154 status = ARES_EBADRESP;
155 break;
156 }
157 caa_curr->value = ares_malloc (caa_curr->length + 1/* Including null byte */);
158 if (caa_curr->value == NULL)
159 {
160 status = ARES_ENOMEM;
161 break;
162 }
163 memcpy ((char *) caa_curr->value, strptr, caa_curr->length);
164 /* Make sure we NULL-terminate */
165 caa_curr->value[caa_curr->length] = 0;
166 }
167
168 /* Propagate any failures */
169 if (status != ARES_SUCCESS)
170 {
171 break;
172 }
173
174 /* Don't lose memory in the next iteration */
175 ares_free (rr_name);
176 rr_name = NULL;
177
178 /* Move on to the next record */
179 aptr += rr_len;
180 }
181
182 if (hostname)
183 ares_free (hostname);
184 if (rr_name)
185 ares_free (rr_name);
186
187 /* clean up on error */
188 if (status != ARES_SUCCESS)
189 {
190 if (caa_head)
191 ares_free_data (caa_head);
192 return status;
193 }
194
195 /* everything looks fine, return the data */
196 *caa_out = caa_head;
197
198 return ARES_SUCCESS;
199 }
200