xref: /openbsd/usr.bin/dig/lib/dns/rdataset.c (revision 1b7fd029)
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
dns_rdataset_init(dns_rdataset_t * rdataset)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
dns_rdataset_disassociate(dns_rdataset_t * rdataset)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
dns_rdataset_isassociated(dns_rdataset_t * rdataset)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
question_disassociate(dns_rdataset_t * rdataset)86 question_disassociate(dns_rdataset_t *rdataset) {
87 	UNUSED(rdataset);
88 }
89 
90 static isc_result_t
question_cursor(dns_rdataset_t * rdataset)91 question_cursor(dns_rdataset_t *rdataset) {
92 	UNUSED(rdataset);
93 
94 	return (ISC_R_NOMORE);
95 }
96 
97 static void
question_current(dns_rdataset_t * rdataset,dns_rdata_t * rdata)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
question_clone(dns_rdataset_t * source,dns_rdataset_t * target)109 question_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
110 	*target = *source;
111 }
112 
113 static unsigned int
question_count(dns_rdataset_t * rdataset)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
dns_rdataset_makequestion(dns_rdataset_t * rdataset,dns_rdataclass_t rdclass,dns_rdatatype_t type)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
dns_rdataset_first(dns_rdataset_t * rdataset)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
dns_rdataset_next(dns_rdataset_t * rdataset)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
dns_rdataset_current(dns_rdataset_t * rdataset,dns_rdata_t * rdata)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
towiresorted(dns_rdataset_t * rdataset,const dns_name_t * owner_name,dns_compress_t * cctx,isc_buffer_t * target,unsigned int * countp)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;
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 
291 	do {
292 		/*
293 		 * Copy out the name, type, class, ttl.
294 		 */
295 
296 		dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
297 		result = dns_name_towire(owner_name, cctx, target);
298 		if (result != ISC_R_SUCCESS)
299 			goto rollback;
300 		headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t);
301 		if (!question)
302 			headlen += sizeof(dns_ttl_t)
303 				+ 2;  /* XXX 2 for rdata len */
304 		isc_buffer_availableregion(target, &r);
305 		if (r.length < headlen) {
306 			result = ISC_R_NOSPACE;
307 			goto rollback;
308 		}
309 		isc_buffer_putuint16(target, rdataset->type);
310 		isc_buffer_putuint16(target, rdataset->rdclass);
311 		if (!question) {
312 			isc_buffer_putuint32(target, rdataset->ttl);
313 
314 			/*
315 			 * Save space for rdlen.
316 			 */
317 			rdlen = *target;
318 			isc_buffer_add(target, 2);
319 
320 			/*
321 			 * Copy out the rdata
322 			 */
323 			if (shuffle)
324 				rdata = *(out[i].rdata);
325 			else {
326 				dns_rdata_reset(&rdata);
327 				dns_rdataset_current(rdataset, &rdata);
328 			}
329 			result = dns_rdata_towire(&rdata, cctx, target);
330 			if (result != ISC_R_SUCCESS)
331 				goto rollback;
332 			INSIST((target->used >= rdlen.used + 2) &&
333 			       (target->used - rdlen.used - 2 < 65536));
334 			isc_buffer_putuint16(&rdlen,
335 					     (uint16_t)(target->used -
336 							    rdlen.used - 2));
337 		}
338 
339 		if (shuffle) {
340 			i++;
341 			if (i == count)
342 				result = ISC_R_NOMORE;
343 			else
344 				result = ISC_R_SUCCESS;
345 		} else {
346 			result = dns_rdataset_next(rdataset);
347 		}
348 	} while (result == ISC_R_SUCCESS);
349 
350 	if (result != ISC_R_NOMORE)
351 		goto rollback;
352 
353 	*countp += count;
354 
355 	result = ISC_R_SUCCESS;
356 	goto cleanup;
357 
358  rollback:
359 	INSIST(savedbuffer.used < 65536);
360 	dns_compress_rollback(cctx, (uint16_t)savedbuffer.used);
361 	*countp = 0;
362 	*target = savedbuffer;
363 
364  cleanup:
365 	if (out != NULL && out != out_fixed)
366 		free(out);
367 	if (in != NULL && in != in_fixed)
368 		free(in);
369 	return (result);
370 }
371 
372 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)373 dns_rdataset_towiresorted(dns_rdataset_t *rdataset,
374 			  const dns_name_t *owner_name,
375 			  dns_compress_t *cctx,
376 			  isc_buffer_t *target,
377 			  unsigned int *countp)
378 {
379 	return (towiresorted(rdataset, owner_name, cctx, target, countp));
380 }
381 
382 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)383 dns_rdataset_towire(dns_rdataset_t *rdataset,
384 		    dns_name_t *owner_name,
385 		    dns_compress_t *cctx,
386 		    isc_buffer_t *target,
387 		    unsigned int *countp)
388 {
389 	return (towiresorted(rdataset, owner_name, cctx, target, countp));
390 }
391