Changeset View
Changeset View
Standalone View
Standalone View
contrib/ldns/edns.c
- This file was added.
/* | |||||
* edns.c | |||||
* | |||||
* edns implementation | |||||
* | |||||
* a Net::DNS like library for C | |||||
* | |||||
* (c) NLnet Labs, 2004-2022 | |||||
* | |||||
* See the file LICENSE for the license | |||||
*/ | |||||
#include <ldns/ldns.h> | |||||
#define LDNS_OPTIONLIST_INIT 8 | |||||
/* | |||||
* Access functions | |||||
* functions to get and set type checking | |||||
*/ | |||||
/* read */ | |||||
size_t | |||||
ldns_edns_get_size(const ldns_edns_option *edns) | |||||
{ | |||||
assert(edns != NULL); | |||||
return edns->_size; | |||||
} | |||||
ldns_edns_option_code | |||||
ldns_edns_get_code(const ldns_edns_option *edns) | |||||
{ | |||||
assert(edns != NULL); | |||||
return edns->_code; | |||||
} | |||||
uint8_t * | |||||
ldns_edns_get_data(const ldns_edns_option *edns) | |||||
{ | |||||
assert(edns != NULL); | |||||
return edns->_data; | |||||
} | |||||
ldns_buffer * | |||||
ldns_edns_get_wireformat_buffer(const ldns_edns_option *edns) | |||||
{ | |||||
uint16_t option; | |||||
size_t size; | |||||
uint8_t* data; | |||||
ldns_buffer* buffer; | |||||
if (edns == NULL) { | |||||
return NULL; | |||||
} | |||||
option = ldns_edns_get_code(edns); | |||||
size = ldns_edns_get_size(edns); | |||||
data = ldns_edns_get_data(edns); | |||||
buffer = ldns_buffer_new(size + 4); | |||||
if (buffer == NULL) { | |||||
return NULL; | |||||
} | |||||
ldns_buffer_write_u16(buffer, option); | |||||
ldns_buffer_write_u16(buffer, size); | |||||
ldns_buffer_write(buffer, data, size); | |||||
ldns_buffer_flip(buffer); | |||||
return buffer; | |||||
} | |||||
/* write */ | |||||
static void | |||||
ldns_edns_set_size(ldns_edns_option *edns, size_t size) | |||||
{ | |||||
assert(edns != NULL); | |||||
edns->_size = size; | |||||
} | |||||
static void | |||||
ldns_edns_set_code(ldns_edns_option *edns, ldns_edns_option_code code) | |||||
{ | |||||
assert(edns != NULL); | |||||
edns->_code = code; | |||||
} | |||||
static void | |||||
ldns_edns_set_data(ldns_edns_option *edns, void *data) | |||||
{ | |||||
/* only copy the pointer */ | |||||
assert(edns != NULL); | |||||
edns->_data = data; | |||||
} | |||||
/* note: data must be allocated memory */ | |||||
ldns_edns_option * | |||||
ldns_edns_new(ldns_edns_option_code code, size_t size, void *data) | |||||
{ | |||||
ldns_edns_option *edns; | |||||
edns = LDNS_MALLOC(ldns_edns_option); | |||||
if (!edns) { | |||||
return NULL; | |||||
} | |||||
ldns_edns_set_code(edns, code); | |||||
ldns_edns_set_size(edns, size); | |||||
ldns_edns_set_data(edns, data); | |||||
return edns; | |||||
} | |||||
ldns_edns_option * | |||||
ldns_edns_new_from_data(ldns_edns_option_code code, size_t size, const void *data) | |||||
{ | |||||
ldns_edns_option *edns; | |||||
edns = LDNS_MALLOC(ldns_edns_option); | |||||
if (!edns) { | |||||
return NULL; | |||||
} | |||||
edns->_data = LDNS_XMALLOC(uint8_t, size); | |||||
if (!edns->_data) { | |||||
LDNS_FREE(edns); | |||||
return NULL; | |||||
} | |||||
/* set the values */ | |||||
ldns_edns_set_code(edns, code); | |||||
ldns_edns_set_size(edns, size); | |||||
memcpy(edns->_data, data, size); | |||||
return edns; | |||||
} | |||||
ldns_edns_option * | |||||
ldns_edns_clone(ldns_edns_option *edns) | |||||
{ | |||||
ldns_edns_option *new_option; | |||||
assert(edns != NULL); | |||||
new_option = ldns_edns_new_from_data(ldns_edns_get_code(edns), | |||||
ldns_edns_get_size(edns), | |||||
ldns_edns_get_data(edns)); | |||||
return new_option; | |||||
} | |||||
void | |||||
ldns_edns_deep_free(ldns_edns_option *edns) | |||||
{ | |||||
if (edns) { | |||||
if (edns->_data) { | |||||
LDNS_FREE(edns->_data); | |||||
} | |||||
LDNS_FREE(edns); | |||||
} | |||||
} | |||||
void | |||||
ldns_edns_free(ldns_edns_option *edns) | |||||
{ | |||||
if (edns) { | |||||
LDNS_FREE(edns); | |||||
} | |||||
} | |||||
ldns_edns_option_list* | |||||
ldns_edns_option_list_new() | |||||
{ | |||||
ldns_edns_option_list *option_list = LDNS_MALLOC(ldns_edns_option_list); | |||||
if(!option_list) { | |||||
return NULL; | |||||
} | |||||
option_list->_option_count = 0; | |||||
option_list->_option_capacity = 0; | |||||
option_list->_options_size = 0; | |||||
option_list->_options = NULL; | |||||
return option_list; | |||||
} | |||||
ldns_edns_option_list * | |||||
ldns_edns_option_list_clone(ldns_edns_option_list *old_list) | |||||
{ | |||||
size_t i; | |||||
ldns_edns_option_list *new_list; | |||||
if (!old_list) { | |||||
return NULL; | |||||
} | |||||
new_list = ldns_edns_option_list_new(); | |||||
if (!new_list) { | |||||
return NULL; | |||||
} | |||||
if (old_list->_option_count == 0) { | |||||
return new_list; | |||||
} | |||||
/* adding options also updates the total options size */ | |||||
for (i = 0; i < old_list->_option_count; i++) { | |||||
ldns_edns_option *option = ldns_edns_clone(ldns_edns_option_list_get_option(old_list, i)); | |||||
if (!ldns_edns_option_list_push(new_list, option)) { | |||||
ldns_edns_deep_free(option); | |||||
ldns_edns_option_list_deep_free(new_list); | |||||
return NULL; | |||||
} | |||||
} | |||||
return new_list; | |||||
} | |||||
void | |||||
ldns_edns_option_list_free(ldns_edns_option_list *option_list) | |||||
{ | |||||
if (option_list) { | |||||
LDNS_FREE(option_list->_options); | |||||
LDNS_FREE(option_list); | |||||
} | |||||
} | |||||
void | |||||
ldns_edns_option_list_deep_free(ldns_edns_option_list *option_list) | |||||
{ | |||||
size_t i; | |||||
if (option_list) { | |||||
for (i=0; i < ldns_edns_option_list_get_count(option_list); i++) { | |||||
ldns_edns_deep_free(ldns_edns_option_list_get_option(option_list, i)); | |||||
} | |||||
ldns_edns_option_list_free(option_list); | |||||
} | |||||
} | |||||
size_t | |||||
ldns_edns_option_list_get_count(const ldns_edns_option_list *option_list) | |||||
{ | |||||
if (option_list) { | |||||
return option_list->_option_count; | |||||
} else { | |||||
return 0; | |||||
} | |||||
} | |||||
ldns_edns_option * | |||||
ldns_edns_option_list_get_option(const ldns_edns_option_list *option_list, size_t index) | |||||
{ | |||||
if (option_list && index < ldns_edns_option_list_get_count(option_list)) { | |||||
assert(option_list->_options[index]); | |||||
return option_list->_options[index]; | |||||
} else { | |||||
return NULL; | |||||
} | |||||
} | |||||
size_t | |||||
ldns_edns_option_list_get_options_size(const ldns_edns_option_list *option_list) | |||||
{ | |||||
if (option_list) { | |||||
return option_list->_options_size; | |||||
} else { | |||||
return 0; | |||||
} | |||||
} | |||||
ldns_edns_option * | |||||
ldns_edns_option_list_set_option(ldns_edns_option_list *option_list, | |||||
ldns_edns_option *option, size_t index) | |||||
{ | |||||
ldns_edns_option* old; | |||||
assert(option_list != NULL); | |||||
if (index > ldns_edns_option_list_get_count(option_list)) { | |||||
return NULL; | |||||
} | |||||
if (option == NULL) { | |||||
return NULL; | |||||
} | |||||
old = ldns_edns_option_list_get_option(option_list, index); | |||||
/* shrink the total EDNS size if the old EDNS option exists */ | |||||
if (old != NULL) { | |||||
option_list->_options_size -= (ldns_edns_get_size(old) + 4); | |||||
} | |||||
option_list->_options_size += (ldns_edns_get_size(option) + 4); | |||||
option_list->_options[index] = option; | |||||
return old; | |||||
} | |||||
bool | |||||
ldns_edns_option_list_push(ldns_edns_option_list *option_list, | |||||
ldns_edns_option *option) | |||||
{ | |||||
size_t cap; | |||||
size_t option_count; | |||||
assert(option_list != NULL); | |||||
if (option == NULL) { | |||||
return false; | |||||
} | |||||
cap = option_list->_option_capacity; | |||||
option_count = ldns_edns_option_list_get_count(option_list); | |||||
/* verify we need to grow the array to fit the new option */ | |||||
if (option_count+1 > cap) { | |||||
ldns_edns_option **new_list; | |||||
/* initialize the capacity if needed, otherwise grow by doubling */ | |||||
if (cap == 0) { | |||||
cap = LDNS_OPTIONLIST_INIT; /* initial list size */ | |||||
} else { | |||||
cap *= 2; | |||||
} | |||||
new_list = LDNS_XREALLOC(option_list->_options, | |||||
ldns_edns_option *, cap); | |||||
if (!new_list) { | |||||
return false; | |||||
} | |||||
option_list->_options = new_list; | |||||
option_list->_option_capacity = cap; | |||||
} | |||||
/* add the new option */ | |||||
ldns_edns_option_list_set_option(option_list, option, | |||||
option_list->_option_count); | |||||
option_list->_option_count += 1; | |||||
return true; | |||||
} | |||||
ldns_edns_option * | |||||
ldns_edns_option_list_pop(ldns_edns_option_list *option_list) | |||||
{ | |||||
ldns_edns_option* pop; | |||||
size_t count; | |||||
size_t cap; | |||||
assert(option_list != NULL); | |||||
cap = option_list->_option_capacity; | |||||
count = ldns_edns_option_list_get_count(option_list); | |||||
if (count == 0) { | |||||
return NULL; | |||||
} | |||||
/* get the last option from the list */ | |||||
pop = ldns_edns_option_list_get_option(option_list, count-1); | |||||
/* shrink the array */ | |||||
if (cap > LDNS_OPTIONLIST_INIT && count-1 <= cap/2) { | |||||
ldns_edns_option **new_list; | |||||
cap /= 2; | |||||
new_list = LDNS_XREALLOC(option_list->_options, | |||||
ldns_edns_option *, cap); | |||||
if (new_list) { | |||||
option_list->_options = new_list; | |||||
} | |||||
/* if the realloc fails, the capacity for the list remains unchanged */ | |||||
} | |||||
/* shrink the total EDNS size of the options if the popped EDNS option exists */ | |||||
if (pop != NULL) { | |||||
option_list->_options_size -= (ldns_edns_get_size(pop) + 4); | |||||
} | |||||
option_list->_option_count = count - 1; | |||||
return pop; | |||||
} | |||||
ldns_buffer * | |||||
ldns_edns_option_list2wireformat_buffer(const ldns_edns_option_list *option_list) | |||||
{ | |||||
size_t i, list_size, options_size, option, size; | |||||
ldns_buffer* buffer; | |||||
ldns_edns_option *edns; | |||||
uint8_t* data = NULL; | |||||
if (!option_list) { | |||||
return NULL; | |||||
} | |||||
/* get the number of EDNS options in the list*/ | |||||
list_size = ldns_edns_option_list_get_count(option_list); | |||||
/* create buffer the size of the total EDNS wireformat options */ | |||||
options_size = ldns_edns_option_list_get_options_size(option_list); | |||||
buffer = ldns_buffer_new(options_size); | |||||
if (!buffer) { | |||||
return NULL; | |||||
} | |||||
/* write individual serialized EDNS options to final buffer*/ | |||||
for (i = 0; i < list_size; i++) { | |||||
edns = ldns_edns_option_list_get_option(option_list, i); | |||||
if (edns == NULL) { | |||||
/* this shouldn't be possible */ | |||||
return NULL; | |||||
} | |||||
option = ldns_edns_get_code(edns); | |||||
size = ldns_edns_get_size(edns); | |||||
data = ldns_edns_get_data(edns); | |||||
/* make sure the option fits */ | |||||
if (!(ldns_buffer_available(buffer, size + 4))) { | |||||
ldns_buffer_free(buffer); | |||||
return NULL; | |||||
} | |||||
ldns_buffer_write_u16(buffer, option); | |||||
ldns_buffer_write_u16(buffer, size); | |||||
ldns_buffer_write(buffer, data, size); | |||||
} | |||||
ldns_buffer_flip(buffer); | |||||
return buffer; | |||||
} |