15185a700Sflorian /*
25185a700Sflorian * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian *
45185a700Sflorian * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian * copyright notice and this permission notice appear in all copies.
75185a700Sflorian *
85185a700Sflorian * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian */
165185a700Sflorian
175185a700Sflorian /*! \file */
185185a700Sflorian
195185a700Sflorian #include <stdint.h>
205185a700Sflorian #include <stdlib.h>
215185a700Sflorian
225185a700Sflorian #include <isc/buffer.h>
235185a700Sflorian #include <isc/util.h>
245185a700Sflorian
255185a700Sflorian #include <dns/name.h>
265185a700Sflorian #include <dns/rdata.h>
275185a700Sflorian #include <dns/rdataset.h>
285185a700Sflorian #include <dns/compress.h>
295185a700Sflorian
305185a700Sflorian void
dns_rdataset_init(dns_rdataset_t * rdataset)315185a700Sflorian dns_rdataset_init(dns_rdataset_t *rdataset) {
325185a700Sflorian
335185a700Sflorian /*
345185a700Sflorian * Make 'rdataset' a valid, disassociated rdataset.
355185a700Sflorian */
365185a700Sflorian
375185a700Sflorian REQUIRE(rdataset != NULL);
385185a700Sflorian
395185a700Sflorian rdataset->methods = NULL;
405185a700Sflorian ISC_LINK_INIT(rdataset, link);
415185a700Sflorian rdataset->rdclass = 0;
425185a700Sflorian rdataset->type = 0;
435185a700Sflorian rdataset->ttl = 0;
445185a700Sflorian rdataset->covers = 0;
455185a700Sflorian rdataset->attributes = 0;
465185a700Sflorian rdataset->count = UINT32_MAX;
475185a700Sflorian rdataset->private1 = NULL;
485185a700Sflorian rdataset->private2 = NULL;
495185a700Sflorian }
505185a700Sflorian
515185a700Sflorian void
dns_rdataset_disassociate(dns_rdataset_t * rdataset)525185a700Sflorian dns_rdataset_disassociate(dns_rdataset_t *rdataset) {
535185a700Sflorian
545185a700Sflorian /*
555185a700Sflorian * Disassociate 'rdataset' from its rdata, allowing it to be reused.
565185a700Sflorian */
575185a700Sflorian
585185a700Sflorian REQUIRE(rdataset->methods != NULL);
595185a700Sflorian
605185a700Sflorian (rdataset->methods->disassociate)(rdataset);
615185a700Sflorian rdataset->methods = NULL;
625185a700Sflorian ISC_LINK_INIT(rdataset, link);
635185a700Sflorian rdataset->rdclass = 0;
645185a700Sflorian rdataset->type = 0;
655185a700Sflorian rdataset->ttl = 0;
665185a700Sflorian rdataset->covers = 0;
675185a700Sflorian rdataset->attributes = 0;
685185a700Sflorian rdataset->count = UINT32_MAX;
695185a700Sflorian rdataset->private1 = NULL;
705185a700Sflorian rdataset->private2 = NULL;
715185a700Sflorian }
725185a700Sflorian
731fb015a8Sflorian int
dns_rdataset_isassociated(dns_rdataset_t * rdataset)745185a700Sflorian dns_rdataset_isassociated(dns_rdataset_t *rdataset) {
755185a700Sflorian /*
765185a700Sflorian * Is 'rdataset' associated?
775185a700Sflorian */
785185a700Sflorian
795185a700Sflorian if (rdataset->methods != NULL)
801fb015a8Sflorian return (1);
815185a700Sflorian
821fb015a8Sflorian return (0);
835185a700Sflorian }
845185a700Sflorian
855185a700Sflorian static void
question_disassociate(dns_rdataset_t * rdataset)865185a700Sflorian question_disassociate(dns_rdataset_t *rdataset) {
875185a700Sflorian UNUSED(rdataset);
885185a700Sflorian }
895185a700Sflorian
905185a700Sflorian static isc_result_t
question_cursor(dns_rdataset_t * rdataset)915185a700Sflorian question_cursor(dns_rdataset_t *rdataset) {
925185a700Sflorian UNUSED(rdataset);
935185a700Sflorian
945185a700Sflorian return (ISC_R_NOMORE);
955185a700Sflorian }
965185a700Sflorian
975185a700Sflorian static void
question_current(dns_rdataset_t * rdataset,dns_rdata_t * rdata)985185a700Sflorian question_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
995185a700Sflorian /*
1005185a700Sflorian * This routine should never be called.
1015185a700Sflorian */
1025185a700Sflorian UNUSED(rdataset);
1035185a700Sflorian UNUSED(rdata);
1045185a700Sflorian
1055185a700Sflorian REQUIRE(0);
1065185a700Sflorian }
1075185a700Sflorian
1085185a700Sflorian static void
question_clone(dns_rdataset_t * source,dns_rdataset_t * target)1095185a700Sflorian question_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
1105185a700Sflorian *target = *source;
1115185a700Sflorian }
1125185a700Sflorian
1135185a700Sflorian static unsigned int
question_count(dns_rdataset_t * rdataset)1145185a700Sflorian question_count(dns_rdataset_t *rdataset) {
1155185a700Sflorian /*
1165185a700Sflorian * This routine should never be called.
1175185a700Sflorian */
1185185a700Sflorian UNUSED(rdataset);
1195185a700Sflorian REQUIRE(0);
1205185a700Sflorian
1215185a700Sflorian return (0);
1225185a700Sflorian }
1235185a700Sflorian
1245185a700Sflorian static dns_rdatasetmethods_t question_methods = {
1255185a700Sflorian question_disassociate,
1265185a700Sflorian question_cursor,
1275185a700Sflorian question_cursor,
1285185a700Sflorian question_current,
1295185a700Sflorian question_clone,
1305185a700Sflorian question_count,
1315185a700Sflorian };
1325185a700Sflorian
1335185a700Sflorian void
dns_rdataset_makequestion(dns_rdataset_t * rdataset,dns_rdataclass_t rdclass,dns_rdatatype_t type)1345185a700Sflorian dns_rdataset_makequestion(dns_rdataset_t *rdataset, dns_rdataclass_t rdclass,
1355185a700Sflorian dns_rdatatype_t type)
1365185a700Sflorian {
1375185a700Sflorian
1385185a700Sflorian /*
1395185a700Sflorian * Make 'rdataset' a valid, associated, question rdataset, with a
1405185a700Sflorian * question class of 'rdclass' and type 'type'.
1415185a700Sflorian */
1425185a700Sflorian
1435185a700Sflorian REQUIRE(rdataset->methods == NULL);
1445185a700Sflorian
1455185a700Sflorian rdataset->methods = &question_methods;
1465185a700Sflorian rdataset->rdclass = rdclass;
1475185a700Sflorian rdataset->type = type;
1485185a700Sflorian rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
1495185a700Sflorian }
1505185a700Sflorian
1515185a700Sflorian isc_result_t
dns_rdataset_first(dns_rdataset_t * rdataset)1525185a700Sflorian dns_rdataset_first(dns_rdataset_t *rdataset) {
1535185a700Sflorian
1545185a700Sflorian /*
1555185a700Sflorian * Move the rdata cursor to the first rdata in the rdataset (if any).
1565185a700Sflorian */
1575185a700Sflorian
1585185a700Sflorian REQUIRE(rdataset->methods != NULL);
1595185a700Sflorian
1605185a700Sflorian return ((rdataset->methods->first)(rdataset));
1615185a700Sflorian }
1625185a700Sflorian
1635185a700Sflorian isc_result_t
dns_rdataset_next(dns_rdataset_t * rdataset)1645185a700Sflorian dns_rdataset_next(dns_rdataset_t *rdataset) {
1655185a700Sflorian
1665185a700Sflorian /*
1675185a700Sflorian * Move the rdata cursor to the next rdata in the rdataset (if any).
1685185a700Sflorian */
1695185a700Sflorian
1705185a700Sflorian REQUIRE(rdataset->methods != NULL);
1715185a700Sflorian
1725185a700Sflorian return ((rdataset->methods->next)(rdataset));
1735185a700Sflorian }
1745185a700Sflorian
1755185a700Sflorian void
dns_rdataset_current(dns_rdataset_t * rdataset,dns_rdata_t * rdata)1765185a700Sflorian dns_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
1775185a700Sflorian
1785185a700Sflorian /*
1795185a700Sflorian * Make 'rdata' refer to the current rdata.
1805185a700Sflorian */
1815185a700Sflorian
1825185a700Sflorian REQUIRE(rdataset->methods != NULL);
1835185a700Sflorian
1845185a700Sflorian (rdataset->methods->current)(rdataset, rdata);
1855185a700Sflorian }
1865185a700Sflorian
1875185a700Sflorian #define MAX_SHUFFLE 32
1885185a700Sflorian
1895185a700Sflorian struct towire_sort {
1905185a700Sflorian int key;
1915185a700Sflorian dns_rdata_t *rdata;
1925185a700Sflorian };
1935185a700Sflorian
1945185a700Sflorian static isc_result_t
towiresorted(dns_rdataset_t * rdataset,const dns_name_t * owner_name,dns_compress_t * cctx,isc_buffer_t * target,unsigned int * countp)1955185a700Sflorian towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
196f43ca53aSflorian dns_compress_t *cctx, isc_buffer_t *target, unsigned int *countp)
1975185a700Sflorian {
1985185a700Sflorian dns_rdata_t rdata = DNS_RDATA_INIT;
1995185a700Sflorian isc_region_t r;
2005185a700Sflorian isc_result_t result;
201*1b7fd029Stb unsigned int i, count = 0;
202f43ca53aSflorian isc_buffer_t savedbuffer, rdlen;
2035185a700Sflorian unsigned int headlen;
2041fb015a8Sflorian int question = 0;
2051fb015a8Sflorian int shuffle = 0;
2065185a700Sflorian dns_rdata_t *in = NULL, in_fixed[MAX_SHUFFLE];
2075185a700Sflorian struct towire_sort *out = NULL, out_fixed[MAX_SHUFFLE];
2085185a700Sflorian
2095185a700Sflorian /*
2105185a700Sflorian * Convert 'rdataset' to wire format, compressing names as specified
2115185a700Sflorian * in cctx, and storing the result in 'target'.
2125185a700Sflorian */
2135185a700Sflorian
2145185a700Sflorian REQUIRE(rdataset->methods != NULL);
2155185a700Sflorian REQUIRE(countp != NULL);
2165185a700Sflorian REQUIRE(cctx != NULL);
2175185a700Sflorian
2185185a700Sflorian if ((rdataset->attributes & DNS_RDATASETATTR_QUESTION) != 0) {
2191fb015a8Sflorian question = 1;
2205185a700Sflorian count = 1;
2215185a700Sflorian result = dns_rdataset_first(rdataset);
2225185a700Sflorian INSIST(result == ISC_R_NOMORE);
2235185a700Sflorian } else {
2245185a700Sflorian count = (rdataset->methods->count)(rdataset);
2255185a700Sflorian result = dns_rdataset_first(rdataset);
2265185a700Sflorian if (result == ISC_R_NOMORE)
2275185a700Sflorian return (ISC_R_SUCCESS);
2285185a700Sflorian if (result != ISC_R_SUCCESS)
2295185a700Sflorian return (result);
2305185a700Sflorian }
2315185a700Sflorian
2325185a700Sflorian /*
2335185a700Sflorian * Do we want to shuffle this answer?
2345185a700Sflorian */
235f43ca53aSflorian if (!question && count > 1 && rdataset->type != dns_rdatatype_rrsig)
2361fb015a8Sflorian shuffle = 1;
2375185a700Sflorian
2385185a700Sflorian if (shuffle && count > MAX_SHUFFLE) {
2395148cc0dSderaadt in = reallocarray(NULL, count, sizeof(*in));
2405148cc0dSderaadt out = reallocarray(NULL, count, sizeof(*out));
2415185a700Sflorian if (in == NULL || out == NULL)
2421fb015a8Sflorian shuffle = 0;
2435185a700Sflorian } else {
2445185a700Sflorian in = in_fixed;
2455185a700Sflorian out = out_fixed;
2465185a700Sflorian }
2475185a700Sflorian
2485185a700Sflorian if (shuffle) {
249f43ca53aSflorian uint32_t val;
250f43ca53aSflorian unsigned int j;
251f43ca53aSflorian
2525185a700Sflorian /*
2535185a700Sflorian * First we get handles to all of the rdata.
2545185a700Sflorian */
2555185a700Sflorian i = 0;
2565185a700Sflorian do {
2575185a700Sflorian INSIST(i < count);
2585185a700Sflorian dns_rdata_init(&in[i]);
2595185a700Sflorian dns_rdataset_current(rdataset, &in[i]);
2605185a700Sflorian i++;
2615185a700Sflorian result = dns_rdataset_next(rdataset);
2625185a700Sflorian } while (result == ISC_R_SUCCESS);
2635185a700Sflorian if (result != ISC_R_NOMORE)
2645185a700Sflorian goto cleanup;
2655185a700Sflorian INSIST(i == count);
2665185a700Sflorian
2675185a700Sflorian /*
2685185a700Sflorian * Now we shuffle.
2695185a700Sflorian */
270f43ca53aSflorian
2715185a700Sflorian /*
2725185a700Sflorian * "Cyclic" order.
2735185a700Sflorian */
2745185a700Sflorian
2755185a700Sflorian val = rdataset->count;
2765185a700Sflorian if (val == UINT32_MAX)
2775185a700Sflorian val = arc4random();
2785185a700Sflorian j = val % count;
2795185a700Sflorian for (i = 0; i < count; i++) {
2805185a700Sflorian out[i].key = 0; /* Unused */
2815185a700Sflorian out[i].rdata = &in[j];
2825185a700Sflorian j++;
2835185a700Sflorian if (j == count)
2845185a700Sflorian j = 0; /* Wrap around. */
2855185a700Sflorian }
2865185a700Sflorian }
2875185a700Sflorian
2885185a700Sflorian savedbuffer = *target;
2895185a700Sflorian i = 0;
2905185a700Sflorian
2915185a700Sflorian do {
2925185a700Sflorian /*
2935185a700Sflorian * Copy out the name, type, class, ttl.
2945185a700Sflorian */
2955185a700Sflorian
2965185a700Sflorian dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
2975185a700Sflorian result = dns_name_towire(owner_name, cctx, target);
2985185a700Sflorian if (result != ISC_R_SUCCESS)
2995185a700Sflorian goto rollback;
3005185a700Sflorian headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t);
3015185a700Sflorian if (!question)
3025185a700Sflorian headlen += sizeof(dns_ttl_t)
3035185a700Sflorian + 2; /* XXX 2 for rdata len */
3045185a700Sflorian isc_buffer_availableregion(target, &r);
3055185a700Sflorian if (r.length < headlen) {
3065185a700Sflorian result = ISC_R_NOSPACE;
3075185a700Sflorian goto rollback;
3085185a700Sflorian }
3095185a700Sflorian isc_buffer_putuint16(target, rdataset->type);
3105185a700Sflorian isc_buffer_putuint16(target, rdataset->rdclass);
3115185a700Sflorian if (!question) {
3125185a700Sflorian isc_buffer_putuint32(target, rdataset->ttl);
3135185a700Sflorian
3145185a700Sflorian /*
3155185a700Sflorian * Save space for rdlen.
3165185a700Sflorian */
3175185a700Sflorian rdlen = *target;
3185185a700Sflorian isc_buffer_add(target, 2);
3195185a700Sflorian
3205185a700Sflorian /*
3215185a700Sflorian * Copy out the rdata
3225185a700Sflorian */
3235185a700Sflorian if (shuffle)
3245185a700Sflorian rdata = *(out[i].rdata);
3255185a700Sflorian else {
3265185a700Sflorian dns_rdata_reset(&rdata);
3275185a700Sflorian dns_rdataset_current(rdataset, &rdata);
3285185a700Sflorian }
3295185a700Sflorian result = dns_rdata_towire(&rdata, cctx, target);
3305185a700Sflorian if (result != ISC_R_SUCCESS)
3315185a700Sflorian goto rollback;
3325185a700Sflorian INSIST((target->used >= rdlen.used + 2) &&
3335185a700Sflorian (target->used - rdlen.used - 2 < 65536));
3345185a700Sflorian isc_buffer_putuint16(&rdlen,
3355185a700Sflorian (uint16_t)(target->used -
3365185a700Sflorian rdlen.used - 2));
3375185a700Sflorian }
3385185a700Sflorian
3395185a700Sflorian if (shuffle) {
3405185a700Sflorian i++;
3415185a700Sflorian if (i == count)
3425185a700Sflorian result = ISC_R_NOMORE;
3435185a700Sflorian else
3445185a700Sflorian result = ISC_R_SUCCESS;
3455185a700Sflorian } else {
3465185a700Sflorian result = dns_rdataset_next(rdataset);
3475185a700Sflorian }
3485185a700Sflorian } while (result == ISC_R_SUCCESS);
3495185a700Sflorian
3505185a700Sflorian if (result != ISC_R_NOMORE)
3515185a700Sflorian goto rollback;
3525185a700Sflorian
3535185a700Sflorian *countp += count;
3545185a700Sflorian
3555185a700Sflorian result = ISC_R_SUCCESS;
3565185a700Sflorian goto cleanup;
3575185a700Sflorian
3585185a700Sflorian rollback:
3595185a700Sflorian INSIST(savedbuffer.used < 65536);
3605185a700Sflorian dns_compress_rollback(cctx, (uint16_t)savedbuffer.used);
3615185a700Sflorian *countp = 0;
3625185a700Sflorian *target = savedbuffer;
3635185a700Sflorian
3645185a700Sflorian cleanup:
3655185a700Sflorian if (out != NULL && out != out_fixed)
3665185a700Sflorian free(out);
3675185a700Sflorian if (in != NULL && in != in_fixed)
3685185a700Sflorian free(in);
3695185a700Sflorian return (result);
3705185a700Sflorian }
3715185a700Sflorian
3725185a700Sflorian isc_result_t
dns_rdataset_towiresorted(dns_rdataset_t * rdataset,const dns_name_t * owner_name,dns_compress_t * cctx,isc_buffer_t * target,unsigned int * countp)3735185a700Sflorian dns_rdataset_towiresorted(dns_rdataset_t *rdataset,
3745185a700Sflorian const dns_name_t *owner_name,
3755185a700Sflorian dns_compress_t *cctx,
3765185a700Sflorian isc_buffer_t *target,
3775185a700Sflorian unsigned int *countp)
3785185a700Sflorian {
379f43ca53aSflorian return (towiresorted(rdataset, owner_name, cctx, target, countp));
3805185a700Sflorian }
3815185a700Sflorian
3825185a700Sflorian isc_result_t
dns_rdataset_towire(dns_rdataset_t * rdataset,dns_name_t * owner_name,dns_compress_t * cctx,isc_buffer_t * target,unsigned int * countp)3835185a700Sflorian dns_rdataset_towire(dns_rdataset_t *rdataset,
3845185a700Sflorian dns_name_t *owner_name,
3855185a700Sflorian dns_compress_t *cctx,
3865185a700Sflorian isc_buffer_t *target,
3875185a700Sflorian unsigned int *countp)
3885185a700Sflorian {
389f43ca53aSflorian return (towiresorted(rdataset, owner_name, cctx, target, countp));
3905185a700Sflorian }
391