xref: /openbsd-src/usr.bin/dig/lib/dns/rdataset.c (revision 1b7fd029fee492f10987ede8b2a9e48fd5902f33)
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