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 */ 23size_t 24ldns_edns_get_size(const ldns_edns_option *edns) 25{ 26 assert(edns != NULL); 27 return edns->_size; 28} 29 30ldns_edns_option_code 31ldns_edns_get_code(const ldns_edns_option *edns) 32{ 33 assert(edns != NULL); 34 return edns->_code; 35} 36 37uint8_t * 38ldns_edns_get_data(const ldns_edns_option *edns) 39{ 40 assert(edns != NULL); 41 return edns->_data; 42} 43 44ldns_buffer * 45ldns_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 */ 76static void 77ldns_edns_set_size(ldns_edns_option *edns, size_t size) 78{ 79 assert(edns != NULL); 80 edns->_size = size; 81} 82 83static void 84ldns_edns_set_code(ldns_edns_option *edns, ldns_edns_option_code code) 85{ 86 assert(edns != NULL); 87 edns->_code = code; 88} 89 90static void 91ldns_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 */ 99ldns_edns_option * 100ldns_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 114ldns_edns_option * 115ldns_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 136ldns_edns_option * 137ldns_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 150void 151ldns_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 161void 162ldns_edns_free(ldns_edns_option *edns) 163{ 164 if (edns) { 165 LDNS_FREE(edns); 166 } 167} 168 169ldns_edns_option_list* 170ldns_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 184ldns_edns_option_list * 185ldns_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 215void 216ldns_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 224void 225ldns_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 237size_t 238ldns_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 247ldns_edns_option * 248ldns_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 258size_t 259ldns_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 269ldns_edns_option * 270ldns_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 298bool 299ldns_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 344ldns_edns_option * 345ldns_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 386ldns_buffer * 387ldns_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