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->covers = 0; 45 rdataset->attributes = 0; 46 rdataset->count = UINT32_MAX; 47 rdataset->private1 = NULL; 48 rdataset->private2 = NULL; 49 } 50 51 void 52 dns_rdataset_disassociate(dns_rdataset_t *rdataset) { 53 54 /* 55 * Disassociate 'rdataset' from its rdata, allowing it to be reused. 56 */ 57 58 REQUIRE(rdataset->methods != NULL); 59 60 (rdataset->methods->disassociate)(rdataset); 61 rdataset->methods = NULL; 62 ISC_LINK_INIT(rdataset, link); 63 rdataset->rdclass = 0; 64 rdataset->type = 0; 65 rdataset->ttl = 0; 66 rdataset->covers = 0; 67 rdataset->attributes = 0; 68 rdataset->count = UINT32_MAX; 69 rdataset->private1 = NULL; 70 rdataset->private2 = NULL; 71 } 72 73 int 74 dns_rdataset_isassociated(dns_rdataset_t *rdataset) { 75 /* 76 * Is 'rdataset' associated? 77 */ 78 79 if (rdataset->methods != NULL) 80 return (1); 81 82 return (0); 83 } 84 85 static void 86 question_disassociate(dns_rdataset_t *rdataset) { 87 UNUSED(rdataset); 88 } 89 90 static isc_result_t 91 question_cursor(dns_rdataset_t *rdataset) { 92 UNUSED(rdataset); 93 94 return (ISC_R_NOMORE); 95 } 96 97 static void 98 question_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { 99 /* 100 * This routine should never be called. 101 */ 102 UNUSED(rdataset); 103 UNUSED(rdata); 104 105 REQUIRE(0); 106 } 107 108 static void 109 question_clone(dns_rdataset_t *source, dns_rdataset_t *target) { 110 *target = *source; 111 } 112 113 static unsigned int 114 question_count(dns_rdataset_t *rdataset) { 115 /* 116 * This routine should never be called. 117 */ 118 UNUSED(rdataset); 119 REQUIRE(0); 120 121 return (0); 122 } 123 124 static dns_rdatasetmethods_t question_methods = { 125 question_disassociate, 126 question_cursor, 127 question_cursor, 128 question_current, 129 question_clone, 130 question_count, 131 }; 132 133 void 134 dns_rdataset_makequestion(dns_rdataset_t *rdataset, dns_rdataclass_t rdclass, 135 dns_rdatatype_t type) 136 { 137 138 /* 139 * Make 'rdataset' a valid, associated, question rdataset, with a 140 * question class of 'rdclass' and type 'type'. 141 */ 142 143 REQUIRE(rdataset->methods == NULL); 144 145 rdataset->methods = &question_methods; 146 rdataset->rdclass = rdclass; 147 rdataset->type = type; 148 rdataset->attributes |= DNS_RDATASETATTR_QUESTION; 149 } 150 151 isc_result_t 152 dns_rdataset_first(dns_rdataset_t *rdataset) { 153 154 /* 155 * Move the rdata cursor to the first rdata in the rdataset (if any). 156 */ 157 158 REQUIRE(rdataset->methods != NULL); 159 160 return ((rdataset->methods->first)(rdataset)); 161 } 162 163 isc_result_t 164 dns_rdataset_next(dns_rdataset_t *rdataset) { 165 166 /* 167 * Move the rdata cursor to the next rdata in the rdataset (if any). 168 */ 169 170 REQUIRE(rdataset->methods != NULL); 171 172 return ((rdataset->methods->next)(rdataset)); 173 } 174 175 void 176 dns_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { 177 178 /* 179 * Make 'rdata' refer to the current rdata. 180 */ 181 182 REQUIRE(rdataset->methods != NULL); 183 184 (rdataset->methods->current)(rdataset, rdata); 185 } 186 187 #define MAX_SHUFFLE 32 188 189 struct towire_sort { 190 int key; 191 dns_rdata_t *rdata; 192 }; 193 194 static isc_result_t 195 towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name, 196 dns_compress_t *cctx, isc_buffer_t *target, unsigned int *countp) 197 { 198 dns_rdata_t rdata = DNS_RDATA_INIT; 199 isc_region_t r; 200 isc_result_t result; 201 unsigned int i, count = 0, added; 202 isc_buffer_t savedbuffer, rdlen; 203 unsigned int headlen; 204 int question = 0; 205 int shuffle = 0; 206 dns_rdata_t *in = NULL, in_fixed[MAX_SHUFFLE]; 207 struct towire_sort *out = NULL, out_fixed[MAX_SHUFFLE]; 208 209 /* 210 * Convert 'rdataset' to wire format, compressing names as specified 211 * in cctx, and storing the result in 'target'. 212 */ 213 214 REQUIRE(rdataset->methods != NULL); 215 REQUIRE(countp != NULL); 216 REQUIRE(cctx != NULL); 217 218 if ((rdataset->attributes & DNS_RDATASETATTR_QUESTION) != 0) { 219 question = 1; 220 count = 1; 221 result = dns_rdataset_first(rdataset); 222 INSIST(result == ISC_R_NOMORE); 223 } else { 224 count = (rdataset->methods->count)(rdataset); 225 result = dns_rdataset_first(rdataset); 226 if (result == ISC_R_NOMORE) 227 return (ISC_R_SUCCESS); 228 if (result != ISC_R_SUCCESS) 229 return (result); 230 } 231 232 /* 233 * Do we want to shuffle this answer? 234 */ 235 if (!question && count > 1 && rdataset->type != dns_rdatatype_rrsig) 236 shuffle = 1; 237 238 if (shuffle && count > MAX_SHUFFLE) { 239 in = reallocarray(NULL, count, sizeof(*in)); 240 out = reallocarray(NULL, count, sizeof(*out)); 241 if (in == NULL || out == NULL) 242 shuffle = 0; 243 } else { 244 in = in_fixed; 245 out = out_fixed; 246 } 247 248 if (shuffle) { 249 uint32_t val; 250 unsigned int j; 251 252 /* 253 * First we get handles to all of the rdata. 254 */ 255 i = 0; 256 do { 257 INSIST(i < count); 258 dns_rdata_init(&in[i]); 259 dns_rdataset_current(rdataset, &in[i]); 260 i++; 261 result = dns_rdataset_next(rdataset); 262 } while (result == ISC_R_SUCCESS); 263 if (result != ISC_R_NOMORE) 264 goto cleanup; 265 INSIST(i == count); 266 267 /* 268 * Now we shuffle. 269 */ 270 271 /* 272 * "Cyclic" order. 273 */ 274 275 val = rdataset->count; 276 if (val == UINT32_MAX) 277 val = arc4random(); 278 j = val % count; 279 for (i = 0; i < count; i++) { 280 out[i].key = 0; /* Unused */ 281 out[i].rdata = &in[j]; 282 j++; 283 if (j == count) 284 j = 0; /* Wrap around. */ 285 } 286 } 287 288 savedbuffer = *target; 289 i = 0; 290 added = 0; 291 292 do { 293 /* 294 * Copy out the name, type, class, ttl. 295 */ 296 297 dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14); 298 result = dns_name_towire(owner_name, cctx, target); 299 if (result != ISC_R_SUCCESS) 300 goto rollback; 301 headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t); 302 if (!question) 303 headlen += sizeof(dns_ttl_t) 304 + 2; /* XXX 2 for rdata len */ 305 isc_buffer_availableregion(target, &r); 306 if (r.length < headlen) { 307 result = ISC_R_NOSPACE; 308 goto rollback; 309 } 310 isc_buffer_putuint16(target, rdataset->type); 311 isc_buffer_putuint16(target, rdataset->rdclass); 312 if (!question) { 313 isc_buffer_putuint32(target, rdataset->ttl); 314 315 /* 316 * Save space for rdlen. 317 */ 318 rdlen = *target; 319 isc_buffer_add(target, 2); 320 321 /* 322 * Copy out the rdata 323 */ 324 if (shuffle) 325 rdata = *(out[i].rdata); 326 else { 327 dns_rdata_reset(&rdata); 328 dns_rdataset_current(rdataset, &rdata); 329 } 330 result = dns_rdata_towire(&rdata, cctx, target); 331 if (result != ISC_R_SUCCESS) 332 goto rollback; 333 INSIST((target->used >= rdlen.used + 2) && 334 (target->used - rdlen.used - 2 < 65536)); 335 isc_buffer_putuint16(&rdlen, 336 (uint16_t)(target->used - 337 rdlen.used - 2)); 338 added++; 339 } 340 341 if (shuffle) { 342 i++; 343 if (i == count) 344 result = ISC_R_NOMORE; 345 else 346 result = ISC_R_SUCCESS; 347 } else { 348 result = dns_rdataset_next(rdataset); 349 } 350 } while (result == ISC_R_SUCCESS); 351 352 if (result != ISC_R_NOMORE) 353 goto rollback; 354 355 *countp += count; 356 357 result = ISC_R_SUCCESS; 358 goto cleanup; 359 360 rollback: 361 INSIST(savedbuffer.used < 65536); 362 dns_compress_rollback(cctx, (uint16_t)savedbuffer.used); 363 *countp = 0; 364 *target = savedbuffer; 365 366 cleanup: 367 if (out != NULL && out != out_fixed) 368 free(out); 369 if (in != NULL && in != in_fixed) 370 free(in); 371 return (result); 372 } 373 374 isc_result_t 375 dns_rdataset_towiresorted(dns_rdataset_t *rdataset, 376 const dns_name_t *owner_name, 377 dns_compress_t *cctx, 378 isc_buffer_t *target, 379 unsigned int *countp) 380 { 381 return (towiresorted(rdataset, owner_name, cctx, target, countp)); 382 } 383 384 isc_result_t 385 dns_rdataset_towire(dns_rdataset_t *rdataset, 386 dns_name_t *owner_name, 387 dns_compress_t *cctx, 388 isc_buffer_t *target, 389 unsigned int *countp) 390 { 391 return (towiresorted(rdataset, owner_name, cctx, target, countp)); 392 } 393