/src/c-ares/src/lib/ares_parse_txt_reply.c
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | /* Copyright 1998 by the Massachusetts Institute of Technology. |
3 | | * Copyright (C) 2009 by Jakub Hrozek <jhrozek@redhat.com> |
4 | | * |
5 | | * Permission to use, copy, modify, and distribute this |
6 | | * software and its documentation for any purpose and without |
7 | | * fee is hereby granted, provided that the above copyright |
8 | | * notice appear in all copies and that both that copyright |
9 | | * notice and this permission notice appear in supporting |
10 | | * documentation, and that the name of M.I.T. not be used in |
11 | | * advertising or publicity pertaining to distribution of the |
12 | | * software without specific, written prior permission. |
13 | | * M.I.T. makes no representations about the suitability of |
14 | | * this software for any purpose. It is provided "as is" |
15 | | * without express or implied warranty. |
16 | | * |
17 | | * SPDX-License-Identifier: MIT |
18 | | */ |
19 | | |
20 | | #include "ares_setup.h" |
21 | | |
22 | | #ifdef HAVE_NETINET_IN_H |
23 | | # include <netinet/in.h> |
24 | | #endif |
25 | | #ifdef HAVE_NETDB_H |
26 | | # include <netdb.h> |
27 | | #endif |
28 | | #ifdef HAVE_ARPA_INET_H |
29 | | # include <arpa/inet.h> |
30 | | #endif |
31 | | |
32 | | #include "ares_nameser.h" |
33 | | |
34 | | #ifdef HAVE_STRINGS_H |
35 | | # include <strings.h> |
36 | | #endif |
37 | | |
38 | | #include "ares.h" |
39 | | #include "ares_dns.h" |
40 | | #include "ares_data.h" |
41 | | #include "ares_private.h" |
42 | | |
43 | | static int |
44 | | ares__parse_txt_reply (const unsigned char *abuf, int alen, |
45 | | int ex, void **txt_out) |
46 | 1.62k | { |
47 | 1.62k | size_t substr_len; |
48 | 1.62k | unsigned int qdcount, ancount, i; |
49 | 1.62k | const unsigned char *aptr; |
50 | 1.62k | const unsigned char *strptr; |
51 | 1.62k | int status, rr_type, rr_class, rr_len; |
52 | 1.62k | long len; |
53 | 1.62k | char *hostname = NULL, *rr_name = NULL; |
54 | 1.62k | struct ares_txt_ext *txt_head = NULL; |
55 | 1.62k | struct ares_txt_ext *txt_last = NULL; |
56 | 1.62k | struct ares_txt_ext *txt_curr; |
57 | | |
58 | | /* Set *txt_out to NULL for all failure cases. */ |
59 | 1.62k | *txt_out = NULL; |
60 | | |
61 | | /* Give up if abuf doesn't have room for a header. */ |
62 | 1.62k | if (alen < HFIXEDSZ) |
63 | 6 | return ARES_EBADRESP; |
64 | | |
65 | | /* Fetch the question and answer count from the header. */ |
66 | 1.61k | qdcount = DNS_HEADER_QDCOUNT (abuf); |
67 | 1.61k | ancount = DNS_HEADER_ANCOUNT (abuf); |
68 | 1.61k | if (qdcount != 1) |
69 | 18 | return ARES_EBADRESP; |
70 | 1.59k | if (ancount == 0) |
71 | 29 | return ARES_ENODATA; |
72 | | |
73 | | /* Expand the name from the question, and skip past the question. */ |
74 | 1.57k | aptr = abuf + HFIXEDSZ; |
75 | 1.57k | status = ares_expand_name (aptr, abuf, alen, &hostname, &len); |
76 | 1.57k | if (status != ARES_SUCCESS) |
77 | 84 | return status; |
78 | | |
79 | 1.48k | if (aptr + len + QFIXEDSZ > abuf + alen) |
80 | 51 | { |
81 | 51 | ares_free (hostname); |
82 | 51 | return ARES_EBADRESP; |
83 | 51 | } |
84 | 1.43k | aptr += len + QFIXEDSZ; |
85 | | |
86 | | /* Examine each answer resource record (RR) in turn. */ |
87 | 269k | for (i = 0; i < ancount; i++) |
88 | 269k | { |
89 | | /* Decode the RR up to the data field. */ |
90 | 269k | status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); |
91 | 269k | if (status != ARES_SUCCESS) |
92 | 865 | { |
93 | 865 | break; |
94 | 865 | } |
95 | 268k | aptr += len; |
96 | 268k | if (aptr + RRFIXEDSZ > abuf + alen) |
97 | 168 | { |
98 | 168 | status = ARES_EBADRESP; |
99 | 168 | break; |
100 | 168 | } |
101 | 268k | rr_type = DNS_RR_TYPE (aptr); |
102 | 268k | rr_class = DNS_RR_CLASS (aptr); |
103 | 268k | rr_len = DNS_RR_LEN (aptr); |
104 | 268k | aptr += RRFIXEDSZ; |
105 | 268k | if (aptr + rr_len > abuf + alen) |
106 | 22 | { |
107 | 22 | status = ARES_EBADRESP; |
108 | 22 | break; |
109 | 22 | } |
110 | | |
111 | | /* Check if we are really looking at a TXT record */ |
112 | 268k | if ((rr_class == C_IN || rr_class == C_CHAOS) && rr_type == T_TXT) |
113 | 545 | { |
114 | | /* |
115 | | * There may be multiple substrings in a single TXT record. Each |
116 | | * substring may be up to 255 characters in length, with a |
117 | | * "length byte" indicating the size of the substring payload. |
118 | | * RDATA contains both the length-bytes and payloads of all |
119 | | * substrings contained therein. |
120 | | */ |
121 | | |
122 | 545 | strptr = aptr; |
123 | 1.80k | while (strptr < (aptr + rr_len)) |
124 | 1.27k | { |
125 | 1.27k | substr_len = (unsigned char)*strptr; |
126 | 1.27k | if (strptr + substr_len + 1 > aptr + rr_len) |
127 | 10 | { |
128 | 10 | status = ARES_EBADRESP; |
129 | 10 | break; |
130 | 10 | } |
131 | | |
132 | | /* Allocate storage for this TXT answer appending it to the list */ |
133 | 1.26k | txt_curr = ares_malloc_data(ex ? ARES_DATATYPE_TXT_EXT : |
134 | 1.26k | ARES_DATATYPE_TXT_REPLY); |
135 | 1.26k | if (!txt_curr) |
136 | 0 | { |
137 | 0 | status = ARES_ENOMEM; |
138 | 0 | break; |
139 | 0 | } |
140 | 1.26k | if (txt_last) |
141 | 1.22k | { |
142 | 1.22k | txt_last->next = txt_curr; |
143 | 1.22k | } |
144 | 36 | else |
145 | 36 | { |
146 | 36 | txt_head = txt_curr; |
147 | 36 | } |
148 | 1.26k | txt_last = txt_curr; |
149 | | |
150 | 1.26k | if (ex) |
151 | 0 | txt_curr->record_start = (strptr == aptr); |
152 | 1.26k | txt_curr->length = substr_len; |
153 | 1.26k | txt_curr->txt = ares_malloc (substr_len + 1/* Including null byte */); |
154 | 1.26k | if (txt_curr->txt == NULL) |
155 | 0 | { |
156 | 0 | status = ARES_ENOMEM; |
157 | 0 | break; |
158 | 0 | } |
159 | | |
160 | 1.26k | ++strptr; |
161 | 1.26k | memcpy ((char *) txt_curr->txt, strptr, substr_len); |
162 | | |
163 | | /* Make sure we NULL-terminate */ |
164 | 1.26k | txt_curr->txt[substr_len] = 0; |
165 | | |
166 | 1.26k | strptr += substr_len; |
167 | 1.26k | } |
168 | 545 | } |
169 | | |
170 | | /* Propagate any failures */ |
171 | 268k | if (status != ARES_SUCCESS) |
172 | 10 | { |
173 | 10 | break; |
174 | 10 | } |
175 | | |
176 | | /* Don't lose memory in the next iteration */ |
177 | 268k | ares_free (rr_name); |
178 | 268k | rr_name = NULL; |
179 | | |
180 | | /* Move on to the next record */ |
181 | 268k | aptr += rr_len; |
182 | 268k | } |
183 | | |
184 | 1.43k | if (hostname) |
185 | 1.43k | ares_free (hostname); |
186 | 1.43k | if (rr_name) |
187 | 200 | ares_free (rr_name); |
188 | | |
189 | | /* clean up on error */ |
190 | 1.43k | if (status != ARES_SUCCESS) |
191 | 1.06k | { |
192 | 1.06k | if (txt_head) |
193 | 30 | ares_free_data (txt_head); |
194 | 1.06k | return status; |
195 | 1.06k | } |
196 | | |
197 | | /* everything looks fine, return the data */ |
198 | 370 | *txt_out = txt_head; |
199 | | |
200 | 370 | return ARES_SUCCESS; |
201 | 1.43k | } |
202 | | |
203 | | int |
204 | | ares_parse_txt_reply (const unsigned char *abuf, int alen, |
205 | | struct ares_txt_reply **txt_out) |
206 | 1.62k | { |
207 | 1.62k | return ares__parse_txt_reply(abuf, alen, 0, (void **) txt_out); |
208 | 1.62k | } |
209 | | |
210 | | |
211 | | int |
212 | | ares_parse_txt_reply_ext (const unsigned char *abuf, int alen, |
213 | | struct ares_txt_ext **txt_out) |
214 | 0 | { |
215 | 0 | return ares__parse_txt_reply(abuf, alen, 1, (void **) txt_out); |
216 | 0 | } |