2 * Copyright (C) 2004, 2006 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: rdataset.c,v 1.58.2.2.2.12 2006/03/02 00:37:20 marka Exp $ */
24 #include <isc/buffer.h>
26 #include <isc/random.h>
30 #include <dns/ncache.h>
31 #include <dns/rdata.h>
32 #include <dns/rdataset.h>
33 #include <dns/compress.h>
36 dns_rdataset_init(dns_rdataset_t *rdataset) {
39 * Make 'rdataset' a valid, disassociated rdataset.
42 REQUIRE(rdataset != NULL);
44 rdataset->magic = DNS_RDATASET_MAGIC;
45 rdataset->methods = NULL;
46 ISC_LINK_INIT(rdataset, link);
47 rdataset->rdclass = 0;
52 rdataset->attributes = 0;
53 rdataset->count = ISC_UINT32_MAX;
54 rdataset->private1 = NULL;
55 rdataset->private2 = NULL;
56 rdataset->private3 = NULL;
57 rdataset->privateuint4 = 0;
58 rdataset->private5 = NULL;
59 rdataset->private6 = NULL;
63 dns_rdataset_invalidate(dns_rdataset_t *rdataset) {
66 * Invalidate 'rdataset'.
69 REQUIRE(DNS_RDATASET_VALID(rdataset));
70 REQUIRE(rdataset->methods == NULL);
73 ISC_LINK_INIT(rdataset, link);
74 rdataset->rdclass = 0;
79 rdataset->attributes = 0;
80 rdataset->count = ISC_UINT32_MAX;
81 rdataset->private1 = NULL;
82 rdataset->private2 = NULL;
83 rdataset->private3 = NULL;
84 rdataset->privateuint4 = 0;
85 rdataset->private5 = NULL;
89 dns_rdataset_disassociate(dns_rdataset_t *rdataset) {
92 * Disassociate 'rdataset' from its rdata, allowing it to be reused.
95 REQUIRE(DNS_RDATASET_VALID(rdataset));
96 REQUIRE(rdataset->methods != NULL);
98 (rdataset->methods->disassociate)(rdataset);
99 rdataset->methods = NULL;
100 ISC_LINK_INIT(rdataset, link);
101 rdataset->rdclass = 0;
105 rdataset->covers = 0;
106 rdataset->attributes = 0;
107 rdataset->count = ISC_UINT32_MAX;
108 rdataset->private1 = NULL;
109 rdataset->private2 = NULL;
110 rdataset->private3 = NULL;
111 rdataset->privateuint4 = 0;
112 rdataset->private5 = NULL;
113 rdataset->private6 = NULL;
117 dns_rdataset_isassociated(dns_rdataset_t *rdataset) {
119 * Is 'rdataset' associated?
122 REQUIRE(DNS_RDATASET_VALID(rdataset));
124 if (rdataset->methods != NULL)
131 question_disassociate(dns_rdataset_t *rdataset) {
136 question_cursor(dns_rdataset_t *rdataset) {
139 return (ISC_R_NOMORE);
143 question_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
145 * This routine should never be called.
154 question_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
159 question_count(dns_rdataset_t *rdataset) {
161 * This routine should never be called.
169 static dns_rdatasetmethods_t question_methods = {
170 question_disassociate,
181 dns_rdataset_makequestion(dns_rdataset_t *rdataset, dns_rdataclass_t rdclass,
182 dns_rdatatype_t type)
186 * Make 'rdataset' a valid, associated, question rdataset, with a
187 * question class of 'rdclass' and type 'type'.
190 REQUIRE(DNS_RDATASET_VALID(rdataset));
191 REQUIRE(rdataset->methods == NULL);
193 rdataset->methods = &question_methods;
194 rdataset->rdclass = rdclass;
195 rdataset->type = type;
196 rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
200 dns_rdataset_count(dns_rdataset_t *rdataset) {
203 * Return the number of records in 'rdataset'.
206 REQUIRE(DNS_RDATASET_VALID(rdataset));
207 REQUIRE(rdataset->methods != NULL);
209 return ((rdataset->methods->count)(rdataset));
213 dns_rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
216 * Make 'target' refer to the same rdataset as 'source'.
219 REQUIRE(DNS_RDATASET_VALID(source));
220 REQUIRE(source->methods != NULL);
221 REQUIRE(DNS_RDATASET_VALID(target));
222 REQUIRE(target->methods == NULL);
224 (source->methods->clone)(source, target);
228 dns_rdataset_first(dns_rdataset_t *rdataset) {
231 * Move the rdata cursor to the first rdata in the rdataset (if any).
234 REQUIRE(DNS_RDATASET_VALID(rdataset));
235 REQUIRE(rdataset->methods != NULL);
237 return ((rdataset->methods->first)(rdataset));
241 dns_rdataset_next(dns_rdataset_t *rdataset) {
244 * Move the rdata cursor to the next rdata in the rdataset (if any).
247 REQUIRE(DNS_RDATASET_VALID(rdataset));
248 REQUIRE(rdataset->methods != NULL);
250 return ((rdataset->methods->next)(rdataset));
254 dns_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
257 * Make 'rdata' refer to the current rdata.
260 REQUIRE(DNS_RDATASET_VALID(rdataset));
261 REQUIRE(rdataset->methods != NULL);
263 (rdataset->methods->current)(rdataset, rdata);
266 #define MAX_SHUFFLE 32
267 #define WANT_FIXED(r) (((r)->attributes & DNS_RDATASETATTR_FIXEDORDER) != 0)
268 #define WANT_RANDOM(r) (((r)->attributes & DNS_RDATASETATTR_RANDOMIZE) != 0)
276 towire_compare(const void *av, const void *bv) {
277 const struct towire_sort *a = (const struct towire_sort *) av;
278 const struct towire_sort *b = (const struct towire_sort *) bv;
279 return (a->key - b->key);
283 towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
284 dns_compress_t *cctx, isc_buffer_t *target,
285 dns_rdatasetorderfunc_t order, const void *order_arg,
286 isc_boolean_t partial, unsigned int options,
287 unsigned int *countp, void **state)
289 dns_rdata_t rdata = DNS_RDATA_INIT;
292 unsigned int i, count, added, choice;
293 isc_buffer_t savedbuffer, rdlen, rrbuffer;
294 unsigned int headlen;
295 isc_boolean_t question = ISC_FALSE;
296 isc_boolean_t shuffle = ISC_FALSE;
297 dns_rdata_t *shuffled = NULL, shuffled_fixed[MAX_SHUFFLE];
298 struct towire_sort *sorted = NULL, sorted_fixed[MAX_SHUFFLE];
303 * Convert 'rdataset' to wire format, compressing names as specified
304 * in cctx, and storing the result in 'target'.
307 REQUIRE(DNS_RDATASET_VALID(rdataset));
308 REQUIRE(countp != NULL);
309 REQUIRE((order == NULL) == (order_arg == NULL));
310 REQUIRE(cctx != NULL && cctx->mctx != NULL);
313 if ((rdataset->attributes & DNS_RDATASETATTR_QUESTION) != 0) {
316 result = dns_rdataset_first(rdataset);
317 INSIST(result == ISC_R_NOMORE);
318 } else if (rdataset->type == 0) {
320 * This is a negative caching rdataset.
322 unsigned int ncache_opts = 0;
323 if ((options & DNS_RDATASETTOWIRE_OMITDNSSEC) != 0)
324 ncache_opts |= DNS_NCACHETOWIRE_OMITDNSSEC;
325 return (dns_ncache_towire(rdataset, cctx, target, ncache_opts,
328 count = (rdataset->methods->count)(rdataset);
329 result = dns_rdataset_first(rdataset);
330 if (result == ISC_R_NOMORE)
331 return (ISC_R_SUCCESS);
332 if (result != ISC_R_SUCCESS)
337 * Do we want to shuffle this anwer?
339 if (!question && count > 1 &&
340 (!WANT_FIXED(rdataset) || order != NULL) &&
341 rdataset->type != dns_rdatatype_rrsig)
344 if (shuffle && count > MAX_SHUFFLE) {
345 shuffled = isc_mem_get(cctx->mctx, count * sizeof(*shuffled));
346 sorted = isc_mem_get(cctx->mctx, count * sizeof(*sorted));
347 if (shuffled == NULL || sorted == NULL)
350 shuffled = shuffled_fixed;
351 sorted = sorted_fixed;
356 * First we get handles to all of the rdata.
361 dns_rdata_init(&shuffled[i]);
362 dns_rdataset_current(rdataset, &shuffled[i]);
364 result = dns_rdataset_next(rdataset);
365 } while (result == ISC_R_SUCCESS);
366 if (result != ISC_R_NOMORE)
373 if (WANT_FIXED(rdataset)) {
377 INSIST(order != NULL);
378 for (i = 0; i < count; i++) {
379 sorted[i].key = (*order)(&shuffled[i],
381 sorted[i].rdata = &shuffled[i];
383 } else if (WANT_RANDOM(rdataset)) {
387 for (i = 0; i < count; i++) {
391 isc_random_get(&val);
392 choice = i + (val % (count - i));
394 shuffled[i] = shuffled[choice];
395 shuffled[choice] = rdata;
397 sorted[i].key = (*order)(&shuffled[i],
400 sorted[i].key = 0; /* Unused */
401 sorted[i].rdata = &shuffled[i];
410 val = rdataset->count;
411 if (val == ISC_UINT32_MAX)
412 isc_random_get(&val);
414 for (i = 0; i < count; i++) {
416 sorted[j].key = (*order)(&shuffled[i],
419 sorted[j].key = 0; /* Unused */
420 sorted[j].rdata = &shuffled[i];
423 j = 0; /* Wrap around. */
431 qsort(sorted, count, sizeof(sorted[0]),
435 savedbuffer = *target;
441 * Copy out the name, type, class, ttl.
445 dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
446 result = dns_name_towire(owner_name, cctx, target);
447 if (result != ISC_R_SUCCESS)
449 headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t);
451 headlen += sizeof(dns_ttl_t)
452 + 2; /* XXX 2 for rdata len */
453 isc_buffer_availableregion(target, &r);
454 if (r.length < headlen) {
455 result = ISC_R_NOSPACE;
458 isc_buffer_putuint16(target, rdataset->type);
459 isc_buffer_putuint16(target, rdataset->rdclass);
461 isc_buffer_putuint32(target, rdataset->ttl);
464 * Save space for rdlen.
467 isc_buffer_add(target, 2);
473 rdata = *(sorted[i].rdata);
475 dns_rdata_reset(&rdata);
476 dns_rdataset_current(rdataset, &rdata);
478 result = dns_rdata_towire(&rdata, cctx, target);
479 if (result != ISC_R_SUCCESS)
481 INSIST((target->used >= rdlen.used + 2) &&
482 (target->used - rdlen.used - 2 < 65536));
483 isc_buffer_putuint16(&rdlen,
484 (isc_uint16_t)(target->used -
492 result = ISC_R_NOMORE;
494 result = ISC_R_SUCCESS;
496 result = dns_rdataset_next(rdataset);
498 } while (result == ISC_R_SUCCESS);
500 if (result != ISC_R_NOMORE)
505 result = ISC_R_SUCCESS;
509 if (partial && result == ISC_R_NOSPACE) {
510 INSIST(rrbuffer.used < 65536);
511 dns_compress_rollback(cctx, (isc_uint16_t)rrbuffer.used);
516 INSIST(savedbuffer.used < 65536);
517 dns_compress_rollback(cctx, (isc_uint16_t)savedbuffer.used);
519 *target = savedbuffer;
522 if (sorted != NULL && sorted != sorted_fixed)
523 isc_mem_put(cctx->mctx, sorted, count * sizeof(*sorted));
524 if (shuffled != NULL && shuffled != shuffled_fixed)
525 isc_mem_put(cctx->mctx, shuffled, count * sizeof(*shuffled));
530 dns_rdataset_towiresorted(dns_rdataset_t *rdataset,
531 const dns_name_t *owner_name,
532 dns_compress_t *cctx,
533 isc_buffer_t *target,
534 dns_rdatasetorderfunc_t order,
535 const void *order_arg,
536 unsigned int options,
537 unsigned int *countp)
539 return (towiresorted(rdataset, owner_name, cctx, target,
540 order, order_arg, ISC_FALSE, options,
545 dns_rdataset_towirepartial(dns_rdataset_t *rdataset,
546 const dns_name_t *owner_name,
547 dns_compress_t *cctx,
548 isc_buffer_t *target,
549 dns_rdatasetorderfunc_t order,
550 const void *order_arg,
551 unsigned int options,
552 unsigned int *countp,
555 REQUIRE(state == NULL); /* XXX remove when implemented */
556 return (towiresorted(rdataset, owner_name, cctx, target,
557 order, order_arg, ISC_TRUE, options,
562 dns_rdataset_towire(dns_rdataset_t *rdataset,
563 dns_name_t *owner_name,
564 dns_compress_t *cctx,
565 isc_buffer_t *target,
566 unsigned int options,
567 unsigned int *countp)
569 return (towiresorted(rdataset, owner_name, cctx, target,
570 NULL, NULL, ISC_FALSE, options, countp, NULL));
574 dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
575 dns_additionaldatafunc_t add, void *arg)
577 dns_rdata_t rdata = DNS_RDATA_INIT;
581 * For each rdata in rdataset, call 'add' for each name and type in the
582 * rdata which is subject to additional section processing.
585 REQUIRE(DNS_RDATASET_VALID(rdataset));
586 REQUIRE((rdataset->attributes & DNS_RDATASETATTR_QUESTION) == 0);
588 result = dns_rdataset_first(rdataset);
589 if (result != ISC_R_SUCCESS)
593 dns_rdataset_current(rdataset, &rdata);
594 result = dns_rdata_additionaldata(&rdata, add, arg);
595 if (result == ISC_R_SUCCESS)
596 result = dns_rdataset_next(rdataset);
597 dns_rdata_reset(&rdata);
598 } while (result == ISC_R_SUCCESS);
600 if (result != ISC_R_NOMORE)
603 return (ISC_R_SUCCESS);
607 dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name) {
609 REQUIRE(DNS_RDATASET_VALID(rdataset));
610 REQUIRE(rdataset->methods != NULL);
611 if (rdataset->methods->addnoqname == NULL)
612 return (ISC_R_NOTIMPLEMENTED);
613 return((rdataset->methods->addnoqname)(rdataset, name));
617 dns_rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
618 dns_rdataset_t *nsec, dns_rdataset_t *nsecsig)
620 REQUIRE(DNS_RDATASET_VALID(rdataset));
621 REQUIRE(rdataset->methods != NULL);
623 if (rdataset->methods->getnoqname == NULL)
624 return (ISC_R_NOTIMPLEMENTED);
625 return((rdataset->methods->getnoqname)(rdataset, name, nsec, nsecsig));