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
ldns_edns_get_size(const ldns_edns_option * edns)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
ldns_edns_get_code(const ldns_edns_option * edns)31 ldns_edns_get_code(const ldns_edns_option *edns)
32 {
33 assert(edns != NULL);
34 return edns->_code;
35 }
36
37 uint8_t *
ldns_edns_get_data(const ldns_edns_option * edns)38 ldns_edns_get_data(const ldns_edns_option *edns)
39 {
40 assert(edns != NULL);
41 return edns->_data;
42 }
43
44 ldns_buffer *
ldns_edns_get_wireformat_buffer(const ldns_edns_option * edns)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
ldns_edns_set_size(ldns_edns_option * edns,size_t size)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
ldns_edns_set_code(ldns_edns_option * edns,ldns_edns_option_code code)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
ldns_edns_set_data(ldns_edns_option * edns,void * data)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 *
ldns_edns_new(ldns_edns_option_code code,size_t size,void * data)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 *
ldns_edns_new_from_data(ldns_edns_option_code code,size_t size,const void * data)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 *
ldns_edns_clone(ldns_edns_option * edns)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
ldns_edns_deep_free(ldns_edns_option * edns)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
ldns_edns_free(ldns_edns_option * edns)162 ldns_edns_free(ldns_edns_option *edns)
163 {
164 if (edns) {
165 LDNS_FREE(edns);
166 }
167 }
168
169 ldns_edns_option_list*
ldns_edns_option_list_new()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 *
ldns_edns_option_list_clone(ldns_edns_option_list * old_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
ldns_edns_option_list_free(ldns_edns_option_list * option_list)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
ldns_edns_option_list_deep_free(ldns_edns_option_list * option_list)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
ldns_edns_option_list_get_count(const ldns_edns_option_list * option_list)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 *
ldns_edns_option_list_get_option(const ldns_edns_option_list * option_list,size_t index)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
ldns_edns_option_list_get_options_size(const ldns_edns_option_list * option_list)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 *
ldns_edns_option_list_set_option(ldns_edns_option_list * option_list,ldns_edns_option * option,size_t index)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
ldns_edns_option_list_push(ldns_edns_option_list * option_list,ldns_edns_option * option)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 *
ldns_edns_option_list_pop(ldns_edns_option_list * option_list)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 *
ldns_edns_option_list2wireformat_buffer(const ldns_edns_option_list * option_list)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 }
437