edns.c
Go to the documentation of this file.
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 */
24size_t
26{
27 assert(edns != NULL);
28 return edns->_size;
29}
30
33{
34 assert(edns != NULL);
35 return edns->_code;
36}
37
38uint8_t *
40{
41 assert(edns != NULL);
42 return edns->_data;
43}
44
46ldns_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
61ldns_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
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 */
115static void
116ldns_edns_set_size(ldns_edns_option *edns, size_t size)
117{
118 assert(edns != NULL);
119 edns->_size = size;
120}
121
122static void
123ldns_edns_set_code(ldns_edns_option *edns, ldns_edns_option_code code)
124{
125 assert(edns != NULL);
126 edns->_code = code;
127}
128
129static void
130ldns_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 */
139ldns_edns_new(ldns_edns_option_code code, size_t size, void *data)
140{
141 ldns_edns_option *edns;
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
154ldns_edns_new_from_data(ldns_edns_option_code code, size_t size, const void *data)
155{
156 ldns_edns_option *edns;
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
177{
178 ldns_edns_option *new_option;
179
180 assert(edns != NULL);
181
183 ldns_edns_get_size(edns),
184 ldns_edns_get_data(edns));
185
186 return new_option;
187}
188
189void
191{
192 if (edns) {
193 if (edns->_data) {
194 LDNS_FREE(edns->_data);
195 }
196 LDNS_FREE(edns);
197 }
198}
199
200void
202{
203 if (edns) {
204 LDNS_FREE(edns);
205 }
206}
207
210{
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
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++) {
245 if (!ldns_edns_option_list_push(new_list, option)) {
246 ldns_edns_deep_free(option);
248 return NULL;
249 }
250 }
251 return new_list;
252}
253
254void
256{
257 if (option_list) {
258 LDNS_FREE(option_list->_options);
259 LDNS_FREE(option_list);
260 }
261}
262
263void
265{
266 size_t i;
267
268 if (option_list) {
269 for (i=0; i < ldns_edns_option_list_get_count(option_list); i++) {
271 }
272 ldns_edns_option_list_free(option_list);
273 }
274}
275
276size_t
278{
279 if (option_list) {
280 return option_list->_option_count;
281 } else {
282 return 0;
283 }
284}
285
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
297size_t
299{
300 if (option_list) {
301 return option_list->_options_size;
302 } else {
303 return 0;
304 }
305}
306
307
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
337bool
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
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
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}
void ldns_buffer_free(ldns_buffer *buffer)
frees the buffer.
Definition buffer.c:137
ldns_buffer * ldns_buffer_new(size_t capacity)
creates a new buffer with the specified capacity.
Definition buffer.c:16
ldns_buffer * ldns_edns_option_list2wireformat_buffer(const ldns_edns_option_list *option_list)
serializes all the EDNS options into a single wireformat buffer
Definition edns.c:426
ldns_buffer * ldns_edns_get_wireformat_buffer(const ldns_edns_option *edns)
serialise the EDNS option into wireformat.
Definition edns.c:84
#define LDNS_OPTIONLIST_INIT
Definition edns.c:16
ldns_status ldns_edns_ede_get_code(const ldns_edns_option *edns, uint16_t *ede_code)
extract the RFC 8914 extended error code value.
Definition edns.c:46
uint8_t * ldns_edns_get_data(const ldns_edns_option *edns)
returns the EDNS option data.
Definition edns.c:39
ldns_edns_option * ldns_edns_new(ldns_edns_option_code code, size_t size, void *data)
allocates a new EDNS structure and fills it.
Definition edns.c:139
ldns_edns_option * ldns_edns_new_from_data(ldns_edns_option_code code, size_t size, const void *data)
allocates a new EDNS structure and fills it.
Definition edns.c:154
ldns_edns_option_code ldns_edns_get_code(const ldns_edns_option *edns)
returns the option code of the EDNS data.
Definition edns.c:32
signed char ldns_edns_option_list_push(ldns_edns_option_list *option_list, ldns_edns_option *option)
adds an EDNS option at the end of the list of options.
Definition edns.c:338
ldns_edns_option_list * ldns_edns_option_list_new(void)
allocates space for a new list of EDNS options
Definition edns.c:209
ldns_edns_option * ldns_edns_option_list_set_option(ldns_edns_option_list *option_list, ldns_edns_option *option, size_t index)
adds an EDNS option to the list of options at the specified index.
Definition edns.c:309
size_t ldns_edns_option_list_get_count(const ldns_edns_option_list *option_list)
returns the number of options in the EDNS options list.
Definition edns.c:277
ldns_status ldns_edns_ede_get_text(const ldns_edns_option *edns, char **ede_text)
extract the optional RFC 8914 extended error code text.
Definition edns.c:61
ldns_edns_option_list * ldns_edns_option_list_clone(ldns_edns_option_list *old_list)
clone the EDNS options list and it's contents
Definition edns.c:224
size_t ldns_edns_get_size(const ldns_edns_option *edns)
returns the size of the EDNS data.
Definition edns.c:25
void ldns_edns_deep_free(ldns_edns_option *edns)
free the EDNS option.
Definition edns.c:190
void ldns_edns_option_list_free(ldns_edns_option_list *option_list)
free the EDNS option list.
Definition edns.c:255
ldns_edns_option * ldns_edns_clone(ldns_edns_option *edns)
clone an EDNS option
Definition edns.c:176
void ldns_edns_option_list_deep_free(ldns_edns_option_list *option_list)
Definition edns.c:264
void ldns_edns_free(ldns_edns_option *edns)
Definition edns.c:201
size_t ldns_edns_option_list_get_options_size(const ldns_edns_option_list *option_list)
returns the total size of all the individual EDNS options in the EDNS list.
Definition edns.c:298
ldns_edns_option * ldns_edns_option_list_get_option(const ldns_edns_option_list *option_list, size_t index)
returns the EDNS option as the specified index in the list of EDNS options.
Definition edns.c:287
ldns_edns_option * ldns_edns_option_list_pop(ldns_edns_option_list *option_list)
removes and returns the EDNS option at the end of the list of options.
Definition edns.c:384
enum ldns_enum_edns_option ldns_edns_option_code
Definition edns.h:46
@ LDNS_EDNS_EDE
Definition edns.h:42
@ LDNS_STATUS_NOT_EDE
Definition error.h:145
@ LDNS_STATUS_EDE_OPTION_MALFORMED
Definition error.h:146
@ LDNS_STATUS_OK
Definition error.h:26
enum ldns_enum_status ldns_status
Definition error.h:148
Including this file will include all ldns files, and define some lookup tables.
implementation of buffers to ease operations
Definition buffer.h:51
ldns_edns_option ** _options
Definition edns.h:113
The struct that stores an ordered EDNS option.
Definition edns.h:97
ldns_edns_option_code _code
Definition edns.h:98
#define LDNS_FREE(ptr)
Definition util.h:60
#define LDNS_MALLOC(type)
Memory management macros.
Definition util.h:49
#define LDNS_XMALLOC(type, count)
Definition util.h:51
#define LDNS_XREALLOC(ptr, type, count)
Definition util.h:57