]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ldns/edns.c
ifconfig(8): wordsmith -G and -g descriptions
[FreeBSD/FreeBSD.git] / contrib / ldns / edns.c
1 /*
2  * edns.c
3  *
4  * edns implementation
5  *
6  * a Net::DNS like library for C
7  *
8  * (c) NLnet Labs, 2004-2022
9  *
10  * See the file LICENSE for the license
11  */
12
13 #include <ldns/ldns.h>
14
15 #define LDNS_OPTIONLIST_INIT 8
16
17 /*
18  * Access functions
19  * functions to get and set type checking
20  */
21
22 /* read */
23 size_t
24 ldns_edns_get_size(const ldns_edns_option *edns)
25 {
26         assert(edns != NULL);
27         return edns->_size;
28 }
29
30 ldns_edns_option_code
31 ldns_edns_get_code(const ldns_edns_option *edns)
32 {
33         assert(edns != NULL);
34         return edns->_code;
35 }
36
37 uint8_t *
38 ldns_edns_get_data(const ldns_edns_option *edns)
39 {
40         assert(edns != NULL);
41         return edns->_data;
42 }
43
44 ldns_buffer *
45 ldns_edns_get_wireformat_buffer(const ldns_edns_option *edns)
46 {
47         uint16_t option;
48         size_t size;
49         uint8_t* data;
50         ldns_buffer* buffer;
51
52         if (edns == NULL) {
53                 return NULL;
54         }
55
56         option = ldns_edns_get_code(edns);
57         size = ldns_edns_get_size(edns);
58         data = ldns_edns_get_data(edns);
59
60         buffer = ldns_buffer_new(size + 4);
61
62         if (buffer == NULL) {
63                 return NULL;
64         }
65
66         ldns_buffer_write_u16(buffer, option);
67         ldns_buffer_write_u16(buffer, size);
68         ldns_buffer_write(buffer, data, size);
69
70         ldns_buffer_flip(buffer);
71
72         return buffer;
73 }
74
75 /* write */
76 static void
77 ldns_edns_set_size(ldns_edns_option *edns, size_t size)
78 {
79         assert(edns != NULL);
80         edns->_size = size;
81 }
82
83 static void
84 ldns_edns_set_code(ldns_edns_option *edns, ldns_edns_option_code code)
85 {
86         assert(edns != NULL);
87         edns->_code = code;
88 }
89
90 static void
91 ldns_edns_set_data(ldns_edns_option *edns, void *data)
92 {
93         /* only copy the pointer */
94         assert(edns != NULL);
95         edns->_data = data;
96 }
97
98 /* note: data must be allocated memory */
99 ldns_edns_option *
100 ldns_edns_new(ldns_edns_option_code code, size_t size, void *data)
101 {
102         ldns_edns_option *edns;
103         edns = LDNS_MALLOC(ldns_edns_option);
104         if (!edns) {
105                 return NULL;
106         }
107         ldns_edns_set_code(edns, code);
108         ldns_edns_set_size(edns, size);
109         ldns_edns_set_data(edns, data);
110
111         return edns;
112 }
113
114 ldns_edns_option *
115 ldns_edns_new_from_data(ldns_edns_option_code code, size_t size, const void *data)
116 {
117         ldns_edns_option *edns;
118         edns = LDNS_MALLOC(ldns_edns_option);
119         if (!edns) {
120                 return NULL;
121         }
122         edns->_data = LDNS_XMALLOC(uint8_t, size);
123         if (!edns->_data) {
124                 LDNS_FREE(edns);
125                 return NULL;
126         }
127
128         /* set the values */
129         ldns_edns_set_code(edns, code);
130         ldns_edns_set_size(edns, size);
131         memcpy(edns->_data, data, size);
132
133         return edns;
134 }
135
136 ldns_edns_option *
137 ldns_edns_clone(ldns_edns_option *edns)
138 {
139         ldns_edns_option *new_option;
140
141         assert(edns != NULL);
142
143         new_option = ldns_edns_new_from_data(ldns_edns_get_code(edns),
144                 ldns_edns_get_size(edns),
145                 ldns_edns_get_data(edns));
146
147         return new_option;
148 }
149
150 void
151 ldns_edns_deep_free(ldns_edns_option *edns)
152 {
153         if (edns) {
154                 if (edns->_data) {
155                         LDNS_FREE(edns->_data);
156                 }
157                 LDNS_FREE(edns);
158         }
159 }
160
161 void 
162 ldns_edns_free(ldns_edns_option *edns)
163 {
164         if (edns) {
165                 LDNS_FREE(edns);
166         }
167 }
168
169 ldns_edns_option_list*
170 ldns_edns_option_list_new()
171 {
172         ldns_edns_option_list *option_list = LDNS_MALLOC(ldns_edns_option_list);
173         if(!option_list) {
174                 return NULL;
175         }
176
177         option_list->_option_count = 0;
178         option_list->_option_capacity = 0;
179         option_list->_options_size = 0;
180         option_list->_options = NULL;
181         return option_list;
182 }
183
184 ldns_edns_option_list *
185 ldns_edns_option_list_clone(ldns_edns_option_list *old_list)
186 {
187         size_t i;
188         ldns_edns_option_list *new_list;
189
190         if (!old_list) {
191                 return NULL;
192         }
193
194         new_list = ldns_edns_option_list_new();
195         if (!new_list) {
196                 return NULL;
197         }
198
199         if (old_list->_option_count == 0) {
200                 return new_list;
201         }
202
203         /* adding options also updates the total options size */
204         for (i = 0; i < old_list->_option_count; i++) {
205                 ldns_edns_option *option = ldns_edns_clone(ldns_edns_option_list_get_option(old_list, i));
206                 if (!ldns_edns_option_list_push(new_list, option)) {
207                         ldns_edns_deep_free(option);
208                         ldns_edns_option_list_deep_free(new_list);
209                         return NULL;
210                 }
211         }
212         return new_list;
213 }
214
215 void
216 ldns_edns_option_list_free(ldns_edns_option_list *option_list)
217 {
218         if (option_list) {
219                 LDNS_FREE(option_list->_options);
220                 LDNS_FREE(option_list);
221         }
222 }
223
224 void
225 ldns_edns_option_list_deep_free(ldns_edns_option_list *option_list)
226 {
227         size_t i;
228
229         if (option_list) {
230                 for (i=0; i < ldns_edns_option_list_get_count(option_list); i++) {
231                         ldns_edns_deep_free(ldns_edns_option_list_get_option(option_list, i));
232                 }
233                 ldns_edns_option_list_free(option_list);
234         }
235 }
236
237 size_t
238 ldns_edns_option_list_get_count(const ldns_edns_option_list *option_list)
239 {
240         if (option_list) {
241                 return option_list->_option_count;
242         } else {
243                 return 0;
244         }
245 }
246
247 ldns_edns_option *
248 ldns_edns_option_list_get_option(const ldns_edns_option_list *option_list, size_t index)
249 {
250         if (option_list && index < ldns_edns_option_list_get_count(option_list)) {
251                 assert(option_list->_options[index]);
252                 return option_list->_options[index];
253         } else {
254                 return NULL;
255         }
256 }
257
258 size_t
259 ldns_edns_option_list_get_options_size(const ldns_edns_option_list *option_list)
260 {
261         if (option_list) {
262                 return option_list->_options_size;
263         } else {
264                 return 0;
265         }
266 }
267
268
269 ldns_edns_option *
270 ldns_edns_option_list_set_option(ldns_edns_option_list *option_list,
271         ldns_edns_option *option, size_t index)
272 {
273         ldns_edns_option* old;
274
275         assert(option_list != NULL);
276
277         if (index > ldns_edns_option_list_get_count(option_list)) {
278                 return NULL;
279         }
280
281         if (option == NULL) {
282                 return NULL;
283         }
284
285         old = ldns_edns_option_list_get_option(option_list, index);
286
287         /* shrink the total EDNS size if the old EDNS option exists */
288         if (old != NULL) {
289                 option_list->_options_size -= (ldns_edns_get_size(old) + 4);
290         }
291
292         option_list->_options_size += (ldns_edns_get_size(option) + 4);
293
294         option_list->_options[index] = option;
295         return old;
296 }
297
298 bool
299 ldns_edns_option_list_push(ldns_edns_option_list *option_list,
300         ldns_edns_option *option)
301 {
302         size_t cap;
303         size_t option_count;
304
305         assert(option_list != NULL);
306
307         if (option == NULL) {
308                 return false;
309         }
310
311         cap = option_list->_option_capacity;
312         option_count = ldns_edns_option_list_get_count(option_list);
313
314         /* verify we need to grow the array to fit the new option */
315         if (option_count+1 > cap) {
316                 ldns_edns_option **new_list;
317
318                 /* initialize the capacity if needed, otherwise grow by doubling */
319                 if (cap == 0) {
320                         cap = LDNS_OPTIONLIST_INIT; /* initial list size */
321                 } else {
322                         cap *= 2;
323                 }
324
325                 new_list = LDNS_XREALLOC(option_list->_options,
326                         ldns_edns_option *, cap);
327
328                 if (!new_list) {
329                         return false;
330                 }
331
332                 option_list->_options = new_list;
333                 option_list->_option_capacity = cap;
334         }
335
336         /* add the new option */
337         ldns_edns_option_list_set_option(option_list, option,
338                 option_list->_option_count);
339         option_list->_option_count += 1;
340
341         return true;
342 }
343
344 ldns_edns_option *
345 ldns_edns_option_list_pop(ldns_edns_option_list *option_list)
346 {
347         ldns_edns_option* pop;
348         size_t count;
349         size_t cap;
350
351         assert(option_list != NULL);
352
353         cap = option_list->_option_capacity;
354         count = ldns_edns_option_list_get_count(option_list);
355
356         if (count == 0) {
357                 return NULL;
358         }
359         /* get the last option from the list */
360         pop = ldns_edns_option_list_get_option(option_list, count-1);
361
362         /* shrink the array */
363         if (cap > LDNS_OPTIONLIST_INIT && count-1 <= cap/2) {
364                 ldns_edns_option **new_list;
365
366                 cap /= 2;
367
368                 new_list = LDNS_XREALLOC(option_list->_options,
369                         ldns_edns_option *, cap);
370                 if (new_list) {
371                         option_list->_options = new_list;
372                 }
373                 /* if the realloc fails, the capacity for the list remains unchanged */
374         }
375
376         /* shrink the total EDNS size of the options if the popped EDNS option exists */
377         if (pop != NULL) {
378                 option_list->_options_size -= (ldns_edns_get_size(pop) + 4);
379         }
380
381         option_list->_option_count = count - 1;
382
383         return pop;
384 }
385
386 ldns_buffer *
387 ldns_edns_option_list2wireformat_buffer(const ldns_edns_option_list *option_list)
388 {
389         size_t i, list_size, options_size, option, size;
390         ldns_buffer* buffer;
391         ldns_edns_option *edns;
392         uint8_t* data = NULL;
393
394         if (!option_list) {
395                 return NULL;
396         }
397
398         /* get the number of EDNS options in the list*/
399         list_size = ldns_edns_option_list_get_count(option_list);
400
401         /* create buffer the size of the total EDNS wireformat options */
402         options_size = ldns_edns_option_list_get_options_size(option_list);
403         buffer = ldns_buffer_new(options_size);
404         
405         if (!buffer) {
406                 return NULL;
407         }
408
409         /* write individual serialized EDNS options to final buffer*/
410         for (i = 0; i < list_size; i++) {
411                 edns = ldns_edns_option_list_get_option(option_list, i);
412
413                 if (edns == NULL) {
414                         /* this shouldn't be possible */
415                         return NULL;
416                 }
417
418                 option = ldns_edns_get_code(edns);
419                 size = ldns_edns_get_size(edns);
420                 data = ldns_edns_get_data(edns);
421
422                 /* make sure the option fits */
423                 if (!(ldns_buffer_available(buffer, size + 4))) {
424                         ldns_buffer_free(buffer);
425                         return NULL;
426                 }
427
428                 ldns_buffer_write_u16(buffer, option);
429                 ldns_buffer_write_u16(buffer, size);
430                 ldns_buffer_write(buffer, data, size);
431         }
432
433         ldns_buffer_flip(buffer);
434
435         return buffer;
436 }