• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* MIT License
2  *
3  * Copyright (c) 2024 Brad House
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to deal
7  * in the Software without restriction, including without limitation the rights
8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  * copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  * SPDX-License-Identifier: MIT
25  */
26 #include "ares_private.h"
27 #include "ares_dns_private.h"
28 
29 typedef struct {
30   unsigned char *data;
31   size_t         len;
32 } multistring_data_t;
33 
34 struct ares_dns_multistring {
35   /*! whether or not cached concatenated string is valid */
36   ares_bool_t    cache_invalidated;
37   /*! combined/concatenated string cache */
38   unsigned char *cache_str;
39   /*! length of combined/concatenated string */
40   size_t         cache_str_len;
41   /*! Data making up strings */
42   ares_array_t  *strs; /*!< multistring_data_t type */
43 };
44 
ares_dns_multistring_free_cb(void * arg)45 static void ares_dns_multistring_free_cb(void *arg)
46 {
47   multistring_data_t *data = arg;
48   if (data == NULL) {
49     return;
50   }
51   ares_free(data->data);
52 }
53 
ares_dns_multistring_create(void)54 ares_dns_multistring_t *ares_dns_multistring_create(void)
55 {
56   ares_dns_multistring_t *strs = ares_malloc_zero(sizeof(*strs));
57   if (strs == NULL) {
58     return NULL;
59   }
60 
61   strs->strs =
62     ares_array_create(sizeof(multistring_data_t), ares_dns_multistring_free_cb);
63   if (strs->strs == NULL) {
64     ares_free(strs);
65     return NULL;
66   }
67 
68   return strs;
69 }
70 
ares_dns_multistring_clear(ares_dns_multistring_t * strs)71 void ares_dns_multistring_clear(ares_dns_multistring_t *strs)
72 {
73   if (strs == NULL) {
74     return;
75   }
76 
77   while (ares_array_len(strs->strs)) {
78     ares_array_remove_last(strs->strs);
79   }
80 }
81 
ares_dns_multistring_destroy(ares_dns_multistring_t * strs)82 void ares_dns_multistring_destroy(ares_dns_multistring_t *strs)
83 {
84   if (strs == NULL) {
85     return;
86   }
87   ares_dns_multistring_clear(strs);
88   ares_array_destroy(strs->strs);
89   ares_free(strs->cache_str);
90   ares_free(strs);
91 }
92 
ares_dns_multistring_swap_own(ares_dns_multistring_t * strs,size_t idx,unsigned char * str,size_t len)93 ares_status_t ares_dns_multistring_swap_own(ares_dns_multistring_t *strs,
94                                             size_t idx, unsigned char *str,
95                                             size_t len)
96 {
97   multistring_data_t *data;
98 
99   if (strs == NULL || str == NULL || len == 0) {
100     return ARES_EFORMERR;
101   }
102 
103   strs->cache_invalidated = ARES_TRUE;
104 
105   data = ares_array_at(strs->strs, idx);
106   if (data == NULL) {
107     return ARES_EFORMERR;
108   }
109 
110   ares_free(data->data);
111   data->data = str;
112   data->len  = len;
113   return ARES_SUCCESS;
114 }
115 
ares_dns_multistring_del(ares_dns_multistring_t * strs,size_t idx)116 ares_status_t ares_dns_multistring_del(ares_dns_multistring_t *strs, size_t idx)
117 {
118   if (strs == NULL) {
119     return ARES_EFORMERR;
120   }
121 
122   strs->cache_invalidated = ARES_TRUE;
123 
124   return ares_array_remove_at(strs->strs, idx);
125 }
126 
ares_dns_multistring_add_own(ares_dns_multistring_t * strs,unsigned char * str,size_t len)127 ares_status_t ares_dns_multistring_add_own(ares_dns_multistring_t *strs,
128                                            unsigned char *str, size_t len)
129 {
130   multistring_data_t *data;
131   ares_status_t       status;
132 
133   if (strs == NULL) {
134     return ARES_EFORMERR;
135   }
136 
137   strs->cache_invalidated = ARES_TRUE;
138 
139   /* NOTE: its ok to have an empty string added */
140   if (str == NULL && len != 0) {
141     return ARES_EFORMERR;
142   }
143 
144   status = ares_array_insert_last((void **)&data, strs->strs);
145   if (status != ARES_SUCCESS) {
146     return status;
147   }
148 
149   /* Issue #921, ares_dns_multistring_get() doesn't have a way to indicate
150    * success or fail on a zero-length string which is actually valid.  So we
151    * are going to allocate a 1-byte buffer to use as a placeholder in this
152    * case */
153   if (str == NULL) {
154     str = ares_malloc_zero(1);
155     if (str == NULL) {
156       ares_array_remove_last(strs->strs);
157       return ARES_ENOMEM;
158     }
159   }
160 
161   data->data = str;
162   data->len  = len;
163 
164   return ARES_SUCCESS;
165 }
166 
ares_dns_multistring_cnt(const ares_dns_multistring_t * strs)167 size_t ares_dns_multistring_cnt(const ares_dns_multistring_t *strs)
168 {
169   if (strs == NULL) {
170     return 0;
171   }
172   return ares_array_len(strs->strs);
173 }
174 
175 const unsigned char *
ares_dns_multistring_get(const ares_dns_multistring_t * strs,size_t idx,size_t * len)176   ares_dns_multistring_get(const ares_dns_multistring_t *strs, size_t idx,
177                            size_t *len)
178 {
179   const multistring_data_t *data;
180 
181   if (strs == NULL || len == NULL) {
182     return NULL;
183   }
184 
185   data = ares_array_at_const(strs->strs, idx);
186   if (data == NULL) {
187     return NULL;
188   }
189 
190   *len = data->len;
191   return data->data;
192 }
193 
ares_dns_multistring_combined(ares_dns_multistring_t * strs,size_t * len)194 const unsigned char *ares_dns_multistring_combined(ares_dns_multistring_t *strs,
195                                                    size_t                 *len)
196 {
197   ares_buf_t *buf = NULL;
198   size_t      i;
199 
200   if (strs == NULL || len == NULL) {
201     return NULL;
202   }
203 
204   *len = 0;
205 
206   /* Return cache if possible */
207   if (!strs->cache_invalidated) {
208     *len = strs->cache_str_len;
209     return strs->cache_str;
210   }
211 
212   /* Clear cache */
213   ares_free(strs->cache_str);
214   strs->cache_str     = NULL;
215   strs->cache_str_len = 0;
216 
217   buf = ares_buf_create();
218 
219   for (i = 0; i < ares_array_len(strs->strs); i++) {
220     const multistring_data_t *data = ares_array_at_const(strs->strs, i);
221     if (data == NULL ||
222         ares_buf_append(buf, data->data, data->len) != ARES_SUCCESS) {
223       ares_buf_destroy(buf);
224       return NULL;
225     }
226   }
227 
228   strs->cache_str =
229     (unsigned char *)ares_buf_finish_str(buf, &strs->cache_str_len);
230   if (strs->cache_str != NULL) {
231     strs->cache_invalidated = ARES_FALSE;
232   }
233   *len = strs->cache_str_len;
234   return strs->cache_str;
235 }
236 
ares_dns_multistring_parse_buf(ares_buf_t * buf,size_t remaining_len,ares_dns_multistring_t ** strs,ares_bool_t validate_printable)237 ares_status_t ares_dns_multistring_parse_buf(ares_buf_t *buf,
238                                              size_t      remaining_len,
239                                              ares_dns_multistring_t **strs,
240                                              ares_bool_t validate_printable)
241 {
242   unsigned char len;
243   ares_status_t status   = ARES_EBADRESP;
244   size_t        orig_len = ares_buf_len(buf);
245 
246   if (buf == NULL) {
247     return ARES_EFORMERR;
248   }
249 
250   if (remaining_len == 0) {
251     return ARES_EBADRESP;
252   }
253 
254   if (strs != NULL) {
255     *strs = ares_dns_multistring_create();
256     if (*strs == NULL) {
257       return ARES_ENOMEM;
258     }
259   }
260 
261   while (orig_len - ares_buf_len(buf) < remaining_len) {
262     status = ares_buf_fetch_bytes(buf, &len, 1);
263     if (status != ARES_SUCCESS) {
264       break; /* LCOV_EXCL_LINE: DefensiveCoding */
265     }
266 
267 
268     /* When used by the _str() parser, it really needs to be validated to
269      * be a valid printable ascii string.  Do that here */
270     if (len && validate_printable && ares_buf_len(buf) >= len) {
271       size_t      mylen;
272       const char *data = (const char *)ares_buf_peek(buf, &mylen);
273       if (!ares_str_isprint(data, len)) {
274         status = ARES_EBADSTR;
275         break;
276       }
277     }
278 
279     if (strs != NULL) {
280       unsigned char *data = NULL;
281       if (len) {
282         status = ares_buf_fetch_bytes_dup(buf, len, ARES_TRUE, &data);
283         if (status != ARES_SUCCESS) {
284           break;
285         }
286       }
287       status = ares_dns_multistring_add_own(*strs, data, len);
288       if (status != ARES_SUCCESS) {
289         ares_free(data);
290         break;
291       }
292     } else {
293       status = ares_buf_consume(buf, len);
294       if (status != ARES_SUCCESS) {
295         break;
296       }
297     }
298 
299   }
300 
301   if (status != ARES_SUCCESS && strs != NULL) {
302     ares_dns_multistring_destroy(*strs);
303     *strs = NULL;
304   }
305 
306   return status;
307 }
308