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