xref: /freebsd/contrib/ldns/edns.c (revision d44c9549ef31884ac069b0306bad248a74bb2825)
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