Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/ldns/edns.c
39475 views
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
}
437
438