Imported Upstream version 1.18.1
[platform/upstream/c-ares.git] / src / lib / ares_parse_caa_reply.c
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
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 }