1 /* 2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /*! \file */ 18 19 #include <stdint.h> 20 #include <stdlib.h> 21 22 #include <isc/buffer.h> 23 #include <isc/util.h> 24 25 #include <dns/name.h> 26 #include <dns/rdata.h> 27 #include <dns/rdataset.h> 28 #include <dns/compress.h> 29 30 void 31 dns_rdataset_init(dns_rdataset_t *rdataset) { 32 33 /* 34 * Make 'rdataset' a valid, disassociated rdataset. 35 */ 36 37 REQUIRE(rdataset != NULL); 38 39 rdataset->methods = NULL; 40 ISC_LINK_INIT(rdataset, link); 41 rdataset->rdclass = 0; 42 rdataset->type = 0; 43 rdataset->ttl = 0; 44 rdataset->trust = 0; 45 rdataset->covers = 0; 46 rdataset->attributes = 0; 47 rdataset->count = UINT32_MAX; 48 rdataset->private1 = NULL; 49 rdataset->private2 = NULL; 50 rdataset->private3 = NULL; 51 rdataset->privateuint4 = 0; 52 rdataset->private5 = NULL; 53 rdataset->private6 = NULL; 54 rdataset->private7 = NULL; 55 rdataset->resign = 0; 56 } 57 58 void 59 dns_rdataset_disassociate(dns_rdataset_t *rdataset) { 60 61 /* 62 * Disassociate 'rdataset' from its rdata, allowing it to be reused. 63 */ 64 65 REQUIRE(rdataset->methods != NULL); 66 67 (rdataset->methods->disassociate)(rdataset); 68 rdataset->methods = NULL; 69 ISC_LINK_INIT(rdataset, link); 70 rdataset->rdclass = 0; 71 rdataset->type = 0; 72 rdataset->ttl = 0; 73 rdataset->trust = 0; 74 rdataset->covers = 0; 75 rdataset->attributes = 0; 76 rdataset->count = UINT32_MAX; 77 rdataset->private1 = NULL; 78 rdataset->private2 = NULL; 79 rdataset->private3 = NULL; 80 rdataset->privateuint4 = 0; 81 rdataset->private5 = NULL; 82 rdataset->private6 = NULL; 83 } 84 85 isc_boolean_t 86 dns_rdataset_isassociated(dns_rdataset_t *rdataset) { 87 /* 88 * Is 'rdataset' associated? 89 */ 90 91 if (rdataset->methods != NULL) 92 return (ISC_TRUE); 93 94 return (ISC_FALSE); 95 } 96 97 static void 98 question_disassociate(dns_rdataset_t *rdataset) { 99 UNUSED(rdataset); 100 } 101 102 static isc_result_t 103 question_cursor(dns_rdataset_t *rdataset) { 104 UNUSED(rdataset); 105 106 return (ISC_R_NOMORE); 107 } 108 109 static void 110 question_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { 111 /* 112 * This routine should never be called. 113 */ 114 UNUSED(rdataset); 115 UNUSED(rdata); 116 117 REQUIRE(0); 118 } 119 120 static void 121 question_clone(dns_rdataset_t *source, dns_rdataset_t *target) { 122 *target = *source; 123 } 124 125 static unsigned int 126 question_count(dns_rdataset_t *rdataset) { 127 /* 128 * This routine should never be called. 129 */ 130 UNUSED(rdataset); 131 REQUIRE(0); 132 133 return (0); 134 } 135 136 static dns_rdatasetmethods_t question_methods = { 137 question_disassociate, 138 question_cursor, 139 question_cursor, 140 question_current, 141 question_clone, 142 question_count, 143 NULL, 144 NULL, 145 NULL, 146 NULL, 147 NULL, 148 NULL, 149 NULL, 150 NULL, 151 NULL, 152 NULL 153 }; 154 155 void 156 dns_rdataset_makequestion(dns_rdataset_t *rdataset, dns_rdataclass_t rdclass, 157 dns_rdatatype_t type) 158 { 159 160 /* 161 * Make 'rdataset' a valid, associated, question rdataset, with a 162 * question class of 'rdclass' and type 'type'. 163 */ 164 165 REQUIRE(rdataset->methods == NULL); 166 167 rdataset->methods = &question_methods; 168 rdataset->rdclass = rdclass; 169 rdataset->type = type; 170 rdataset->attributes |= DNS_RDATASETATTR_QUESTION; 171 } 172 173 void 174 dns_rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) { 175 176 /* 177 * Make 'target' refer to the same rdataset as 'source'. 178 */ 179 180 REQUIRE(source->methods != NULL); 181 REQUIRE(target->methods == NULL); 182 183 (source->methods->clone)(source, target); 184 } 185 186 isc_result_t 187 dns_rdataset_first(dns_rdataset_t *rdataset) { 188 189 /* 190 * Move the rdata cursor to the first rdata in the rdataset (if any). 191 */ 192 193 REQUIRE(rdataset->methods != NULL); 194 195 return ((rdataset->methods->first)(rdataset)); 196 } 197 198 isc_result_t 199 dns_rdataset_next(dns_rdataset_t *rdataset) { 200 201 /* 202 * Move the rdata cursor to the next rdata in the rdataset (if any). 203 */ 204 205 REQUIRE(rdataset->methods != NULL); 206 207 return ((rdataset->methods->next)(rdataset)); 208 } 209 210 void 211 dns_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { 212 213 /* 214 * Make 'rdata' refer to the current rdata. 215 */ 216 217 REQUIRE(rdataset->methods != NULL); 218 219 (rdataset->methods->current)(rdataset, rdata); 220 } 221 222 #define MAX_SHUFFLE 32 223 #define WANT_FIXED(r) (((r)->attributes & DNS_RDATASETATTR_FIXEDORDER) != 0) 224 #define WANT_RANDOM(r) (((r)->attributes & DNS_RDATASETATTR_RANDOMIZE) != 0) 225 226 struct towire_sort { 227 int key; 228 dns_rdata_t *rdata; 229 }; 230 231 static int 232 towire_compare(const void *av, const void *bv) { 233 const struct towire_sort *a = (const struct towire_sort *) av; 234 const struct towire_sort *b = (const struct towire_sort *) bv; 235 return (a->key - b->key); 236 } 237 238 static isc_result_t 239 towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name, 240 dns_compress_t *cctx, isc_buffer_t *target, 241 dns_rdatasetorderfunc_t order, const void *order_arg, 242 isc_boolean_t partial, unsigned int *countp) 243 { 244 dns_rdata_t rdata = DNS_RDATA_INIT; 245 isc_region_t r; 246 isc_result_t result; 247 unsigned int i, count = 0, added, choice; 248 isc_buffer_t savedbuffer, rdlen, rrbuffer; 249 unsigned int headlen; 250 isc_boolean_t question = ISC_FALSE; 251 isc_boolean_t shuffle = ISC_FALSE; 252 dns_rdata_t *in = NULL, in_fixed[MAX_SHUFFLE]; 253 struct towire_sort *out = NULL, out_fixed[MAX_SHUFFLE]; 254 255 /* 256 * Convert 'rdataset' to wire format, compressing names as specified 257 * in cctx, and storing the result in 'target'. 258 */ 259 260 REQUIRE(rdataset->methods != NULL); 261 REQUIRE(countp != NULL); 262 REQUIRE((order == NULL) == (order_arg == NULL)); 263 REQUIRE(cctx != NULL); 264 265 if ((rdataset->attributes & DNS_RDATASETATTR_QUESTION) != 0) { 266 question = ISC_TRUE; 267 count = 1; 268 result = dns_rdataset_first(rdataset); 269 INSIST(result == ISC_R_NOMORE); 270 } else { 271 count = (rdataset->methods->count)(rdataset); 272 result = dns_rdataset_first(rdataset); 273 if (result == ISC_R_NOMORE) 274 return (ISC_R_SUCCESS); 275 if (result != ISC_R_SUCCESS) 276 return (result); 277 } 278 279 /* 280 * Do we want to shuffle this answer? 281 */ 282 if (!question && count > 1 && 283 (!WANT_FIXED(rdataset) || order != NULL) && 284 rdataset->type != dns_rdatatype_rrsig) 285 shuffle = ISC_TRUE; 286 287 if (shuffle && count > MAX_SHUFFLE) { 288 in = reallocarray(NULL, count, sizeof(*in)); 289 out = reallocarray(NULL, count, sizeof(*out)); 290 if (in == NULL || out == NULL) 291 shuffle = ISC_FALSE; 292 } else { 293 in = in_fixed; 294 out = out_fixed; 295 } 296 297 if (shuffle) { 298 /* 299 * First we get handles to all of the rdata. 300 */ 301 i = 0; 302 do { 303 INSIST(i < count); 304 dns_rdata_init(&in[i]); 305 dns_rdataset_current(rdataset, &in[i]); 306 i++; 307 result = dns_rdataset_next(rdataset); 308 } while (result == ISC_R_SUCCESS); 309 if (result != ISC_R_NOMORE) 310 goto cleanup; 311 INSIST(i == count); 312 313 /* 314 * Now we shuffle. 315 */ 316 if (WANT_FIXED(rdataset)) { 317 /* 318 * 'Fixed' order. 319 */ 320 INSIST(order != NULL); 321 for (i = 0; i < count; i++) { 322 out[i].key = (*order)(&in[i], order_arg); 323 out[i].rdata = &in[i]; 324 } 325 } else if (WANT_RANDOM(rdataset)) { 326 /* 327 * 'Random' order. 328 */ 329 for (i = 0; i < count; i++) { 330 choice = i + arc4random_uniform(count - i); 331 rdata = in[i]; 332 in[i] = in[choice]; 333 in[choice] = rdata; 334 if (order != NULL) 335 out[i].key = (*order)(&in[i], 336 order_arg); 337 else 338 out[i].key = 0; /* Unused */ 339 out[i].rdata = &in[i]; 340 } 341 } else { 342 /* 343 * "Cyclic" order. 344 */ 345 uint32_t val; 346 unsigned int j; 347 348 val = rdataset->count; 349 if (val == UINT32_MAX) 350 val = arc4random(); 351 j = val % count; 352 for (i = 0; i < count; i++) { 353 if (order != NULL) 354 out[i].key = (*order)(&in[j], 355 order_arg); 356 else 357 out[i].key = 0; /* Unused */ 358 out[i].rdata = &in[j]; 359 j++; 360 if (j == count) 361 j = 0; /* Wrap around. */ 362 } 363 } 364 365 /* 366 * Sorted order. 367 */ 368 if (order != NULL) 369 qsort(out, count, sizeof(out[0]), towire_compare); 370 } 371 372 savedbuffer = *target; 373 i = 0; 374 added = 0; 375 376 do { 377 /* 378 * Copy out the name, type, class, ttl. 379 */ 380 381 rrbuffer = *target; 382 dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14); 383 result = dns_name_towire(owner_name, cctx, target); 384 if (result != ISC_R_SUCCESS) 385 goto rollback; 386 headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t); 387 if (!question) 388 headlen += sizeof(dns_ttl_t) 389 + 2; /* XXX 2 for rdata len */ 390 isc_buffer_availableregion(target, &r); 391 if (r.length < headlen) { 392 result = ISC_R_NOSPACE; 393 goto rollback; 394 } 395 isc_buffer_putuint16(target, rdataset->type); 396 isc_buffer_putuint16(target, rdataset->rdclass); 397 if (!question) { 398 isc_buffer_putuint32(target, rdataset->ttl); 399 400 /* 401 * Save space for rdlen. 402 */ 403 rdlen = *target; 404 isc_buffer_add(target, 2); 405 406 /* 407 * Copy out the rdata 408 */ 409 if (shuffle) 410 rdata = *(out[i].rdata); 411 else { 412 dns_rdata_reset(&rdata); 413 dns_rdataset_current(rdataset, &rdata); 414 } 415 result = dns_rdata_towire(&rdata, cctx, target); 416 if (result != ISC_R_SUCCESS) 417 goto rollback; 418 INSIST((target->used >= rdlen.used + 2) && 419 (target->used - rdlen.used - 2 < 65536)); 420 isc_buffer_putuint16(&rdlen, 421 (uint16_t)(target->used - 422 rdlen.used - 2)); 423 added++; 424 } 425 426 if (shuffle) { 427 i++; 428 if (i == count) 429 result = ISC_R_NOMORE; 430 else 431 result = ISC_R_SUCCESS; 432 } else { 433 result = dns_rdataset_next(rdataset); 434 } 435 } while (result == ISC_R_SUCCESS); 436 437 if (result != ISC_R_NOMORE) 438 goto rollback; 439 440 *countp += count; 441 442 result = ISC_R_SUCCESS; 443 goto cleanup; 444 445 rollback: 446 if (partial && result == ISC_R_NOSPACE) { 447 INSIST(rrbuffer.used < 65536); 448 dns_compress_rollback(cctx, (uint16_t)rrbuffer.used); 449 *countp += added; 450 *target = rrbuffer; 451 goto cleanup; 452 } 453 INSIST(savedbuffer.used < 65536); 454 dns_compress_rollback(cctx, (uint16_t)savedbuffer.used); 455 *countp = 0; 456 *target = savedbuffer; 457 458 cleanup: 459 if (out != NULL && out != out_fixed) 460 free(out); 461 if (in != NULL && in != in_fixed) 462 free(in); 463 return (result); 464 } 465 466 isc_result_t 467 dns_rdataset_towiresorted(dns_rdataset_t *rdataset, 468 const dns_name_t *owner_name, 469 dns_compress_t *cctx, 470 isc_buffer_t *target, 471 dns_rdatasetorderfunc_t order, 472 const void *order_arg, 473 unsigned int *countp) 474 { 475 return (towiresorted(rdataset, owner_name, cctx, target, 476 order, order_arg, ISC_FALSE, countp)); 477 } 478 479 isc_result_t 480 dns_rdataset_towire(dns_rdataset_t *rdataset, 481 dns_name_t *owner_name, 482 dns_compress_t *cctx, 483 isc_buffer_t *target, 484 unsigned int *countp) 485 { 486 return (towiresorted(rdataset, owner_name, cctx, target, 487 NULL, NULL, ISC_FALSE, countp)); 488 } 489