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 */
24 size_t
26 {
27  assert(edns != NULL);
28  return edns->_size;
29 }
30 
33 {
34  assert(edns != NULL);
35  return edns->_code;
36 }
37 
38 uint8_t *
40 {
41  assert(edns != NULL);
42  return edns->_data;
43 }
44 
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 
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 
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
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
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
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 */
139 ldns_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 
154 ldns_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 
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
191 {
192  if (edns) {
193  if (edns->_data) {
194  LDNS_FREE(edns->_data);
195  }
196  LDNS_FREE(edns);
197  }
198 }
199 
200 void
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 
254 void
256 {
257  if (option_list) {
258  LDNS_FREE(option_list->_options);
259  LDNS_FREE(option_list);
260  }
261 }
262 
263 void
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 
276 size_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 
297 size_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 
337 bool
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 
425 ldns_buffer *
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_edns_option * ldns_edns_clone(ldns_edns_option *edns)
clone an EDNS option
Definition: edns.c:176
ldns_edns_option_list * ldns_edns_option_list_new(void)
allocates space for a new list of EDNS options
Definition: edns.c:209
uint8_t * ldns_edns_get_data(const ldns_edns_option *edns)
returns the EDNS option data.
Definition: edns.c:39
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
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
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
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
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
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
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
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
void ldns_edns_option_list_deep_free(ldns_edns_option_list *option_list)
Definition: edns.c:264
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_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
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
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