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