1 /*
2  * Copyright (C) 2004-2014  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or 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.
8  *
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.
16  */
17 
18 /* $Id$ */
19 
20 /*! \file */
21 
22 #include <config.h>
23 
24 #include <stdlib.h>
25 
26 #include <isc/mem.h>
27 #include <isc/region.h>
28 #include <isc/string.h>		/* Required for HP/UX (and others?) */
29 #include <isc/util.h>
30 
31 #include <dns/result.h>
32 #include <dns/rdata.h>
33 #include <dns/rdataset.h>
34 #include <dns/rdataslab.h>
35 
36 /*
37  * The rdataslab structure allows iteration to occur in both load order
38  * and DNSSEC order.  The structure is as follows:
39  *
40  *	header		(reservelen bytes)
41  *	record count	(2 bytes)
42  *	offset table	(4 x record count bytes in load order)
43  *	data records
44  *		data length	(2 bytes)
45  *		order		(2 bytes)
46  *		meta data	(1 byte for RRSIG's)
47  *		data		(data length bytes)
48  *
49  * If DNS_RDATASET_FIXED is defined to be zero (0) the format of a
50  * rdataslab is as follows:
51  *
52  *	header		(reservelen bytes)
53  *	record count	(2 bytes)
54  *	data records
55  *		data length	(2 bytes)
56  *		meta data	(1 byte for RRSIG's)
57  *		data		(data length bytes)
58  *
59  * Offsets are from the end of the header.
60  *
61  * Load order traversal is performed by walking the offset table to find
62  * the start of the record (DNS_RDATASET_FIXED = 1).
63  *
64  * DNSSEC order traversal is performed by walking the data records.
65  *
66  * The order is stored with record to allow for efficient reconstruction
67  * of the offset table following a merge or subtraction.
68  *
69  * The iterator methods here currently only support DNSSEC order iteration.
70  *
71  * The iterator methods in rbtdb support both load order and DNSSEC order
72  * iteration.
73  *
74  * WARNING:
75  *	rbtdb.c directly interacts with the slab's raw structures.  If the
76  *	structure changes then rbtdb.c also needs to be updated to reflect
77  *	the changes.  See the areas tagged with "RDATASLAB".
78  */
79 
80 struct xrdata {
81 	dns_rdata_t	rdata;
82 	unsigned int	order;
83 };
84 
85 /*% Note: the "const void *" are just to make qsort happy.  */
86 static int
compare_rdata(const void * p1,const void * p2)87 compare_rdata(const void *p1, const void *p2) {
88 	const struct xrdata *x1 = p1;
89 	const struct xrdata *x2 = p2;
90 	return (dns_rdata_compare(&x1->rdata, &x2->rdata));
91 }
92 
93 #if DNS_RDATASET_FIXED
94 static void
fillin_offsets(unsigned char * offsetbase,unsigned int * offsettable,unsigned length)95 fillin_offsets(unsigned char *offsetbase, unsigned int *offsettable,
96 	       unsigned length)
97 {
98 	unsigned int i, j;
99 	unsigned char *raw;
100 
101 	for (i = 0, j = 0; i < length; i++) {
102 
103 		if (offsettable[i] == 0)
104 			continue;
105 
106 		/*
107 		 * Fill in offset table.
108 		 */
109 		raw = &offsetbase[j*4 + 2];
110 		*raw++ = (offsettable[i] & 0xff000000) >> 24;
111 		*raw++ = (offsettable[i] & 0xff0000) >> 16;
112 		*raw++ = (offsettable[i] & 0xff00) >> 8;
113 		*raw = offsettable[i] & 0xff;
114 
115 		/*
116 		 * Fill in table index.
117 		 */
118 		raw = offsetbase + offsettable[i] + 2;
119 		*raw++ = (j & 0xff00) >> 8;
120 		*raw = j++ & 0xff;
121 	}
122 }
123 #endif
124 
125 isc_result_t
dns_rdataslab_fromrdataset(dns_rdataset_t * rdataset,isc_mem_t * mctx,isc_region_t * region,unsigned int reservelen)126 dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx,
127 			   isc_region_t *region, unsigned int reservelen)
128 {
129 	/*
130 	 * Use &removed as a sentinal pointer for duplicate
131 	 * rdata as rdata.data == NULL is valid.
132 	 */
133 	static unsigned char removed;
134 	struct xrdata  *x;
135 	unsigned char  *rawbuf;
136 #if DNS_RDATASET_FIXED
137 	unsigned char  *offsetbase;
138 #endif
139 	unsigned int	buflen;
140 	isc_result_t	result;
141 	unsigned int	nitems;
142 	unsigned int	nalloc;
143 	unsigned int	i;
144 #if DNS_RDATASET_FIXED
145 	unsigned int   *offsettable;
146 #endif
147 	unsigned int	length;
148 
149 	buflen = reservelen + 2;
150 
151 	nitems = dns_rdataset_count(rdataset);
152 
153 	/*
154 	 * If there are no rdata then we can just need to allocate a header
155 	 * with zero a record count.
156 	 */
157 	if (nitems == 0) {
158 		if (rdataset->type != 0)
159 			return (ISC_R_FAILURE);
160 		rawbuf = isc_mem_get(mctx, buflen);
161 		if (rawbuf == NULL)
162 			return (ISC_R_NOMEMORY);
163 		region->base = rawbuf;
164 		region->length = buflen;
165 		rawbuf += reservelen;
166 		*rawbuf++ = 0;
167 		*rawbuf = 0;
168 		return (ISC_R_SUCCESS);
169 	}
170 
171 	if (nitems > 0xffff)
172 		return (ISC_R_NOSPACE);
173 
174 	/*
175 	 * Remember the original number of items.
176 	 */
177 	nalloc = nitems;
178 	x = isc_mem_get(mctx, nalloc * sizeof(struct xrdata));
179 	if (x == NULL)
180 		return (ISC_R_NOMEMORY);
181 
182 	/*
183 	 * Save all of the rdata members into an array.
184 	 */
185 	result = dns_rdataset_first(rdataset);
186 	if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE)
187 		goto free_rdatas;
188 	for (i = 0; i < nalloc && result == ISC_R_SUCCESS; i++) {
189 		INSIST(result == ISC_R_SUCCESS);
190 		dns_rdata_init(&x[i].rdata);
191 		dns_rdataset_current(rdataset, &x[i].rdata);
192 		INSIST(x[i].rdata.data != &removed);
193 #if DNS_RDATASET_FIXED
194 		x[i].order = i;
195 #endif
196 		result = dns_rdataset_next(rdataset);
197 	}
198 	if (i != nalloc || result != ISC_R_NOMORE) {
199 		/*
200 		 * Somehow we iterated over fewer rdatas than
201 		 * dns_rdataset_count() said there were or there
202 		 * were more items than dns_rdataset_count said
203 		 * there were.
204 		 */
205 		result = ISC_R_FAILURE;
206 		goto free_rdatas;
207 	}
208 
209 	/*
210 	 * Put into DNSSEC order.
211 	 */
212 	if (nalloc > 1U)
213 		qsort(x, nalloc, sizeof(struct xrdata), compare_rdata);
214 
215 	/*
216 	 * Remove duplicates and compute the total storage required.
217 	 *
218 	 * If an rdata is not a duplicate, accumulate the storage size
219 	 * required for the rdata.  We do not store the class, type, etc,
220 	 * just the rdata, so our overhead is 2 bytes for the number of
221 	 * records, and 8 for each rdata, (length(2), offset(4) and order(2))
222 	 * and then the rdata itself.
223 	 */
224 	for (i = 1; i < nalloc; i++) {
225 		if (compare_rdata(&x[i-1].rdata, &x[i].rdata) == 0) {
226 			x[i-1].rdata.data = &removed;
227 #if DNS_RDATASET_FIXED
228 			/*
229 			 * Preserve the least order so A, B, A -> A, B
230 			 * after duplicate removal.
231 			 */
232 			if (x[i-1].order < x[i].order)
233 				x[i].order = x[i-1].order;
234 #endif
235 			nitems--;
236 		} else {
237 #if DNS_RDATASET_FIXED
238 			buflen += (8 + x[i-1].rdata.length);
239 #else
240 			buflen += (2 + x[i-1].rdata.length);
241 #endif
242 			/*
243 			 * Provide space to store the per RR meta data.
244 			 */
245 			if (rdataset->type == dns_rdatatype_rrsig)
246 				buflen++;
247 		}
248 	}
249 
250 	/*
251 	 * Don't forget the last item!
252 	 */
253 #if DNS_RDATASET_FIXED
254 	buflen += (8 + x[i-1].rdata.length);
255 #else
256 	buflen += (2 + x[i-1].rdata.length);
257 #endif
258 	/*
259 	 * Provide space to store the per RR meta data.
260 	 */
261 	if (rdataset->type == dns_rdatatype_rrsig)
262 		buflen++;
263 
264 	/*
265 	 * Ensure that singleton types are actually singletons.
266 	 */
267 	if (nitems > 1 && dns_rdatatype_issingleton(rdataset->type)) {
268 		/*
269 		 * We have a singleton type, but there's more than one
270 		 * RR in the rdataset.
271 		 */
272 		result = DNS_R_SINGLETON;
273 		goto free_rdatas;
274 	}
275 
276 	/*
277 	 * Allocate the memory, set up a buffer, start copying in
278 	 * data.
279 	 */
280 	rawbuf = isc_mem_get(mctx, buflen);
281 	if (rawbuf == NULL) {
282 		result = ISC_R_NOMEMORY;
283 		goto free_rdatas;
284 	}
285 
286 #if DNS_RDATASET_FIXED
287 	/* Allocate temporary offset table. */
288 	offsettable = isc_mem_get(mctx, nalloc * sizeof(unsigned int));
289 	if (offsettable == NULL) {
290 		isc_mem_put(mctx, rawbuf, buflen);
291 		result = ISC_R_NOMEMORY;
292 		goto free_rdatas;
293 	}
294 	memset(offsettable, 0, nalloc * sizeof(unsigned int));
295 #endif
296 
297 	region->base = rawbuf;
298 	region->length = buflen;
299 
300 	rawbuf += reservelen;
301 #if DNS_RDATASET_FIXED
302 	offsetbase = rawbuf;
303 #endif
304 
305 	*rawbuf++ = (nitems & 0xff00) >> 8;
306 	*rawbuf++ = (nitems & 0x00ff);
307 
308 #if DNS_RDATASET_FIXED
309 	/* Skip load order table.  Filled in later. */
310 	rawbuf += nitems * 4;
311 #endif
312 
313 	for (i = 0; i < nalloc; i++) {
314 		if (x[i].rdata.data == &removed)
315 			continue;
316 #if DNS_RDATASET_FIXED
317 		offsettable[x[i].order] = rawbuf - offsetbase;
318 #endif
319 		length = x[i].rdata.length;
320 		if (rdataset->type == dns_rdatatype_rrsig)
321 			length++;
322 		INSIST(length <= 0xffff);
323 		*rawbuf++ = (length & 0xff00) >> 8;
324 		*rawbuf++ = (length & 0x00ff);
325 #if DNS_RDATASET_FIXED
326 		rawbuf += 2;	/* filled in later */
327 #endif
328 		/*
329 		 * Store the per RR meta data.
330 		 */
331 		if (rdataset->type == dns_rdatatype_rrsig) {
332 			*rawbuf++ |= (x[i].rdata.flags & DNS_RDATA_OFFLINE) ?
333 					    DNS_RDATASLAB_OFFLINE : 0;
334 		}
335 		memmove(rawbuf, x[i].rdata.data, x[i].rdata.length);
336 		rawbuf += x[i].rdata.length;
337 	}
338 
339 #if DNS_RDATASET_FIXED
340 	fillin_offsets(offsetbase, offsettable, nalloc);
341 	isc_mem_put(mctx, offsettable, nalloc * sizeof(unsigned int));
342 #endif
343 
344 	result = ISC_R_SUCCESS;
345 
346  free_rdatas:
347 	isc_mem_put(mctx, x, nalloc * sizeof(struct xrdata));
348 	return (result);
349 }
350 
351 static void
rdataset_disassociate(dns_rdataset_t * rdataset)352 rdataset_disassociate(dns_rdataset_t *rdataset) {
353 	UNUSED(rdataset);
354 }
355 
356 static isc_result_t
rdataset_first(dns_rdataset_t * rdataset)357 rdataset_first(dns_rdataset_t *rdataset) {
358 	unsigned char *raw = rdataset->private3;
359 	unsigned int count;
360 
361 	count = raw[0] * 256 + raw[1];
362 	if (count == 0) {
363 		rdataset->private5 = NULL;
364 		return (ISC_R_NOMORE);
365 	}
366 #if DNS_RDATASET_FIXED
367 	raw += 2 + (4 * count);
368 #else
369 	raw += 2;
370 #endif
371 	/*
372 	 * The privateuint4 field is the number of rdata beyond the cursor
373 	 * position, so we decrement the total count by one before storing
374 	 * it.
375 	 */
376 	count--;
377 	rdataset->privateuint4 = count;
378 	rdataset->private5 = raw;
379 
380 	return (ISC_R_SUCCESS);
381 }
382 
383 static isc_result_t
rdataset_next(dns_rdataset_t * rdataset)384 rdataset_next(dns_rdataset_t *rdataset) {
385 	unsigned int count;
386 	unsigned int length;
387 	unsigned char *raw;
388 
389 	count = rdataset->privateuint4;
390 	if (count == 0)
391 		return (ISC_R_NOMORE);
392 	count--;
393 	rdataset->privateuint4 = count;
394 	raw = rdataset->private5;
395 	length = raw[0] * 256 + raw[1];
396 #if DNS_RDATASET_FIXED
397 	raw += length + 4;
398 #else
399 	raw += length + 2;
400 #endif
401 	rdataset->private5 = raw;
402 
403 	return (ISC_R_SUCCESS);
404 }
405 
406 static void
rdataset_current(dns_rdataset_t * rdataset,dns_rdata_t * rdata)407 rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
408 	unsigned char *raw = rdataset->private5;
409 	isc_region_t r;
410 	unsigned int length;
411 	unsigned int flags = 0;
412 
413 	REQUIRE(raw != NULL);
414 
415 	length = raw[0] * 256 + raw[1];
416 #if DNS_RDATASET_FIXED
417 	raw += 4;
418 #else
419 	raw += 2;
420 #endif
421 	if (rdataset->type == dns_rdatatype_rrsig) {
422 		if (*raw & DNS_RDATASLAB_OFFLINE)
423 			flags |= DNS_RDATA_OFFLINE;
424 		length--;
425 		raw++;
426 	}
427 	r.length = length;
428 	r.base = raw;
429 	dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r);
430 	rdata->flags |= flags;
431 }
432 
433 static void
rdataset_clone(dns_rdataset_t * source,dns_rdataset_t * target)434 rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
435 	*target = *source;
436 
437 	/*
438 	 * Reset iterator state.
439 	 */
440 	target->privateuint4 = 0;
441 	target->private5 = NULL;
442 }
443 
444 static unsigned int
rdataset_count(dns_rdataset_t * rdataset)445 rdataset_count(dns_rdataset_t *rdataset) {
446 	unsigned char *raw = rdataset->private3;
447 	unsigned int count;
448 
449 	count = raw[0] * 256 + raw[1];
450 
451 	return (count);
452 }
453 
454 static dns_rdatasetmethods_t rdataset_methods = {
455 	rdataset_disassociate,
456 	rdataset_first,
457 	rdataset_next,
458 	rdataset_current,
459 	rdataset_clone,
460 	rdataset_count,
461 	NULL,
462 	NULL,
463 	NULL,
464 	NULL,
465 	NULL,
466 	NULL,
467 	NULL,
468 	NULL,
469 	NULL
470 };
471 
472 void
dns_rdataslab_tordataset(unsigned char * slab,unsigned int reservelen,dns_rdataclass_t rdclass,dns_rdatatype_t rdtype,dns_rdatatype_t covers,dns_ttl_t ttl,dns_rdataset_t * rdataset)473 dns_rdataslab_tordataset(unsigned char *slab, unsigned int reservelen,
474 			 dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
475 			 dns_rdatatype_t covers, dns_ttl_t ttl,
476 			 dns_rdataset_t *rdataset)
477 {
478 	REQUIRE(slab != NULL);
479 	REQUIRE(!dns_rdataset_isassociated(rdataset));
480 
481 	rdataset->methods = &rdataset_methods;
482 	rdataset->rdclass = rdclass;
483 	rdataset->type = rdtype;
484 	rdataset->covers = covers;
485 	rdataset->ttl = ttl;
486 	rdataset->trust = 0;
487 	rdataset->private1 = NULL;
488 	rdataset->private2 = NULL;
489 	rdataset->private3 = slab + reservelen;
490 
491 	/*
492 	 * Reset iterator state.
493 	 */
494 	rdataset->privateuint4 = 0;
495 	rdataset->private5 = NULL;
496 }
497 
498 unsigned int
dns_rdataslab_size(unsigned char * slab,unsigned int reservelen)499 dns_rdataslab_size(unsigned char *slab, unsigned int reservelen) {
500 	unsigned int count, length;
501 	unsigned char *current;
502 
503 	REQUIRE(slab != NULL);
504 
505 	current = slab + reservelen;
506 	count = *current++ * 256;
507 	count += *current++;
508 #if DNS_RDATASET_FIXED
509 	current += (4 * count);
510 #endif
511 	while (count > 0) {
512 		count--;
513 		length = *current++ * 256;
514 		length += *current++;
515 #if DNS_RDATASET_FIXED
516 		current += length + 2;
517 #else
518 		current += length;
519 #endif
520 	}
521 
522 	return ((unsigned int)(current - slab));
523 }
524 
525 /*
526  * Make the dns_rdata_t 'rdata' refer to the slab item
527  * beginning at '*current', which is part of a slab of type
528  * 'type' and class 'rdclass', and advance '*current' to
529  * point to the next item in the slab.
530  */
531 static inline void
rdata_from_slab(unsigned char ** current,dns_rdataclass_t rdclass,dns_rdatatype_t type,dns_rdata_t * rdata)532 rdata_from_slab(unsigned char **current,
533 	      dns_rdataclass_t rdclass, dns_rdatatype_t type,
534 	      dns_rdata_t *rdata)
535 {
536 	unsigned char *tcurrent = *current;
537 	isc_region_t region;
538 	unsigned int length;
539 	isc_boolean_t offline = ISC_FALSE;
540 
541 	length = *tcurrent++ * 256;
542 	length += *tcurrent++;
543 
544 	if (type == dns_rdatatype_rrsig) {
545 		if ((*tcurrent & DNS_RDATASLAB_OFFLINE) != 0)
546 			offline = ISC_TRUE;
547 		length--;
548 		tcurrent++;
549 	}
550 	region.length = length;
551 #if DNS_RDATASET_FIXED
552 	tcurrent += 2;
553 #endif
554 	region.base = tcurrent;
555 	tcurrent += region.length;
556 	dns_rdata_fromregion(rdata, rdclass, type, &region);
557 	if (offline)
558 		rdata->flags |= DNS_RDATA_OFFLINE;
559 	*current = tcurrent;
560 }
561 
562 /*
563  * Return true iff 'slab' (slab data of type 'type' and class 'rdclass')
564  * contains an rdata identical to 'rdata'.  This does case insensitive
565  * comparisons per DNSSEC.
566  */
567 static inline isc_boolean_t
rdata_in_slab(unsigned char * slab,unsigned int reservelen,dns_rdataclass_t rdclass,dns_rdatatype_t type,dns_rdata_t * rdata)568 rdata_in_slab(unsigned char *slab, unsigned int reservelen,
569 	      dns_rdataclass_t rdclass, dns_rdatatype_t type,
570 	      dns_rdata_t *rdata)
571 {
572 	unsigned int count, i;
573 	unsigned char *current;
574 	dns_rdata_t trdata = DNS_RDATA_INIT;
575 	int n;
576 
577 	current = slab + reservelen;
578 	count = *current++ * 256;
579 	count += *current++;
580 
581 #if DNS_RDATASET_FIXED
582 	current += (4 * count);
583 #endif
584 
585 	for (i = 0; i < count; i++) {
586 		rdata_from_slab(&current, rdclass, type, &trdata);
587 
588 		n = dns_rdata_compare(&trdata, rdata);
589 		if (n == 0)
590 			return (ISC_TRUE);
591 		if (n > 0)	/* In DNSSEC order. */
592 			break;
593 		dns_rdata_reset(&trdata);
594 	}
595 	return (ISC_FALSE);
596 }
597 
598 isc_result_t
dns_rdataslab_merge(unsigned char * oslab,unsigned char * nslab,unsigned int reservelen,isc_mem_t * mctx,dns_rdataclass_t rdclass,dns_rdatatype_t type,unsigned int flags,unsigned char ** tslabp)599 dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab,
600 		    unsigned int reservelen, isc_mem_t *mctx,
601 		    dns_rdataclass_t rdclass, dns_rdatatype_t type,
602 		    unsigned int flags, unsigned char **tslabp)
603 {
604 	unsigned char *ocurrent, *ostart, *ncurrent, *tstart, *tcurrent, *data;
605 	unsigned int ocount, ncount, count, olength, tlength, tcount, length;
606 	dns_rdata_t ordata = DNS_RDATA_INIT;
607 	dns_rdata_t nrdata = DNS_RDATA_INIT;
608 	isc_boolean_t added_something = ISC_FALSE;
609 	unsigned int oadded = 0;
610 	unsigned int nadded = 0;
611 	unsigned int nncount = 0;
612 #if DNS_RDATASET_FIXED
613 	unsigned int oncount;
614 	unsigned int norder = 0;
615 	unsigned int oorder = 0;
616 	unsigned char *offsetbase;
617 	unsigned int *offsettable;
618 #endif
619 
620 	/*
621 	 * XXX  Need parameter to allow "delete rdatasets in nslab" merge,
622 	 * or perhaps another merge routine for this purpose.
623 	 */
624 
625 	REQUIRE(tslabp != NULL && *tslabp == NULL);
626 	REQUIRE(oslab != NULL && nslab != NULL);
627 
628 	ocurrent = oslab + reservelen;
629 	ocount = *ocurrent++ * 256;
630 	ocount += *ocurrent++;
631 #if DNS_RDATASET_FIXED
632 	ocurrent += (4 * ocount);
633 #endif
634 	ostart = ocurrent;
635 	ncurrent = nslab + reservelen;
636 	ncount = *ncurrent++ * 256;
637 	ncount += *ncurrent++;
638 #if DNS_RDATASET_FIXED
639 	ncurrent += (4 * ncount);
640 #endif
641 	INSIST(ocount > 0 && ncount > 0);
642 
643 #if DNS_RDATASET_FIXED
644 	oncount = ncount;
645 #endif
646 
647 	/*
648 	 * Yes, this is inefficient!
649 	 */
650 
651 	/*
652 	 * Figure out the length of the old slab's data.
653 	 */
654 	olength = 0;
655 	for (count = 0; count < ocount; count++) {
656 		length = *ocurrent++ * 256;
657 		length += *ocurrent++;
658 #if DNS_RDATASET_FIXED
659 		olength += length + 8;
660 		ocurrent += length + 2;
661 #else
662 		olength += length + 2;
663 		ocurrent += length;
664 #endif
665 	}
666 
667 	/*
668 	 * Start figuring out the target length and count.
669 	 */
670 	tlength = reservelen + 2 + olength;
671 	tcount = ocount;
672 
673 	/*
674 	 * Add in the length of rdata in the new slab that aren't in
675 	 * the old slab.
676 	 */
677 	do {
678 		dns_rdata_init(&nrdata);
679 		rdata_from_slab(&ncurrent, rdclass, type, &nrdata);
680 		if (!rdata_in_slab(oslab, reservelen, rdclass, type, &nrdata))
681 		{
682 			/*
683 			 * This rdata isn't in the old slab.
684 			 */
685 #if DNS_RDATASET_FIXED
686 			tlength += nrdata.length + 8;
687 #else
688 			tlength += nrdata.length + 2;
689 #endif
690 			if (type == dns_rdatatype_rrsig)
691 				tlength++;
692 			tcount++;
693 			nncount++;
694 			added_something = ISC_TRUE;
695 		}
696 		ncount--;
697 	} while (ncount > 0);
698 	ncount = nncount;
699 
700 	if (((flags & DNS_RDATASLAB_EXACT) != 0) &&
701 	    (tcount != ncount + ocount))
702 		return (DNS_R_NOTEXACT);
703 
704 	if (!added_something && (flags & DNS_RDATASLAB_FORCE) == 0)
705 		return (DNS_R_UNCHANGED);
706 
707 	/*
708 	 * Ensure that singleton types are actually singletons.
709 	 */
710 	if (tcount > 1 && dns_rdatatype_issingleton(type)) {
711 		/*
712 		 * We have a singleton type, but there's more than one
713 		 * RR in the rdataset.
714 		 */
715 		return (DNS_R_SINGLETON);
716 	}
717 
718 	if (tcount > 0xffff)
719 		return (ISC_R_NOSPACE);
720 
721 	/*
722 	 * Copy the reserved area from the new slab.
723 	 */
724 	tstart = isc_mem_get(mctx, tlength);
725 	if (tstart == NULL)
726 		return (ISC_R_NOMEMORY);
727 	memmove(tstart, nslab, reservelen);
728 	tcurrent = tstart + reservelen;
729 #if DNS_RDATASET_FIXED
730 	offsetbase = tcurrent;
731 #endif
732 
733 	/*
734 	 * Write the new count.
735 	 */
736 	*tcurrent++ = (tcount & 0xff00) >> 8;
737 	*tcurrent++ = (tcount & 0x00ff);
738 
739 #if DNS_RDATASET_FIXED
740 	/*
741 	 * Skip offset table.
742 	 */
743 	tcurrent += (tcount * 4);
744 
745 	offsettable = isc_mem_get(mctx,
746 				  (ocount + oncount) * sizeof(unsigned int));
747 	if (offsettable == NULL) {
748 		isc_mem_put(mctx, tstart, tlength);
749 		return (ISC_R_NOMEMORY);
750 	}
751 	memset(offsettable, 0, (ocount + oncount) * sizeof(unsigned int));
752 #endif
753 
754 	/*
755 	 * Merge the two slabs.
756 	 */
757 	ocurrent = ostart;
758 	INSIST(ocount != 0);
759 #if DNS_RDATASET_FIXED
760 	oorder = ocurrent[2] * 256 + ocurrent[3];
761 	INSIST(oorder < ocount);
762 #endif
763 	rdata_from_slab(&ocurrent, rdclass, type, &ordata);
764 
765 	ncurrent = nslab + reservelen + 2;
766 #if DNS_RDATASET_FIXED
767 	ncurrent += (4 * oncount);
768 #endif
769 
770 	if (ncount > 0) {
771 		do {
772 			dns_rdata_reset(&nrdata);
773 #if DNS_RDATASET_FIXED
774 			norder = ncurrent[2] * 256 + ncurrent[3];
775 
776 			INSIST(norder < oncount);
777 #endif
778 			rdata_from_slab(&ncurrent, rdclass, type, &nrdata);
779 		} while (rdata_in_slab(oslab, reservelen, rdclass,
780 				       type, &nrdata));
781 	}
782 
783 	while (oadded < ocount || nadded < ncount) {
784 		isc_boolean_t fromold;
785 		if (oadded == ocount)
786 			fromold = ISC_FALSE;
787 		else if (nadded == ncount)
788 			fromold = ISC_TRUE;
789 		else
790 			fromold = ISC_TF(compare_rdata(&ordata, &nrdata) < 0);
791 		if (fromold) {
792 #if DNS_RDATASET_FIXED
793 			offsettable[oorder] = tcurrent - offsetbase;
794 #endif
795 			length = ordata.length;
796 			data = ordata.data;
797 			if (type == dns_rdatatype_rrsig) {
798 				length++;
799 				data--;
800 			}
801 			*tcurrent++ = (length & 0xff00) >> 8;
802 			*tcurrent++ = (length & 0x00ff);
803 #if DNS_RDATASET_FIXED
804 			tcurrent += 2;	/* fill in later */
805 #endif
806 			memmove(tcurrent, data, length);
807 			tcurrent += length;
808 			oadded++;
809 			if (oadded < ocount) {
810 				dns_rdata_reset(&ordata);
811 #if DNS_RDATASET_FIXED
812 				oorder = ocurrent[2] * 256 + ocurrent[3];
813 				INSIST(oorder < ocount);
814 #endif
815 				rdata_from_slab(&ocurrent, rdclass, type,
816 						&ordata);
817 			}
818 		} else {
819 #if DNS_RDATASET_FIXED
820 			offsettable[ocount + norder] = tcurrent - offsetbase;
821 #endif
822 			length = nrdata.length;
823 			data = nrdata.data;
824 			if (type == dns_rdatatype_rrsig) {
825 				length++;
826 				data--;
827 			}
828 			*tcurrent++ = (length & 0xff00) >> 8;
829 			*tcurrent++ = (length & 0x00ff);
830 #if DNS_RDATASET_FIXED
831 			tcurrent += 2;	/* fill in later */
832 #endif
833 			memmove(tcurrent, data, length);
834 			tcurrent += length;
835 			nadded++;
836 			if (nadded < ncount) {
837 				do {
838 					dns_rdata_reset(&nrdata);
839 #if DNS_RDATASET_FIXED
840 					norder = ncurrent[2] * 256 + ncurrent[3];
841 					INSIST(norder < oncount);
842 #endif
843 					rdata_from_slab(&ncurrent, rdclass,
844 							type, &nrdata);
845 				} while (rdata_in_slab(oslab, reservelen,
846 						       rdclass, type,
847 						       &nrdata));
848 			}
849 		}
850 	}
851 
852 #if DNS_RDATASET_FIXED
853 	fillin_offsets(offsetbase, offsettable, ocount + oncount);
854 
855 	isc_mem_put(mctx, offsettable,
856 		    (ocount + oncount) * sizeof(unsigned int));
857 #endif
858 
859 	INSIST(tcurrent == tstart + tlength);
860 
861 	*tslabp = tstart;
862 
863 	return (ISC_R_SUCCESS);
864 }
865 
866 isc_result_t
dns_rdataslab_subtract(unsigned char * mslab,unsigned char * sslab,unsigned int reservelen,isc_mem_t * mctx,dns_rdataclass_t rdclass,dns_rdatatype_t type,unsigned int flags,unsigned char ** tslabp)867 dns_rdataslab_subtract(unsigned char *mslab, unsigned char *sslab,
868 		       unsigned int reservelen, isc_mem_t *mctx,
869 		       dns_rdataclass_t rdclass, dns_rdatatype_t type,
870 		       unsigned int flags, unsigned char **tslabp)
871 {
872 	unsigned char *mcurrent, *sstart, *scurrent, *tstart, *tcurrent;
873 	unsigned int mcount, scount, rcount ,count, tlength, tcount, i;
874 	dns_rdata_t srdata = DNS_RDATA_INIT;
875 	dns_rdata_t mrdata = DNS_RDATA_INIT;
876 #if DNS_RDATASET_FIXED
877 	unsigned char *offsetbase;
878 	unsigned int *offsettable;
879 	unsigned int order;
880 #endif
881 
882 	REQUIRE(tslabp != NULL && *tslabp == NULL);
883 	REQUIRE(mslab != NULL && sslab != NULL);
884 
885 	mcurrent = mslab + reservelen;
886 	mcount = *mcurrent++ * 256;
887 	mcount += *mcurrent++;
888 	scurrent = sslab + reservelen;
889 	scount = *scurrent++ * 256;
890 	scount += *scurrent++;
891 	INSIST(mcount > 0 && scount > 0);
892 
893 	/*
894 	 * Yes, this is inefficient!
895 	 */
896 
897 	/*
898 	 * Start figuring out the target length and count.
899 	 */
900 	tlength = reservelen + 2;
901 	tcount = 0;
902 	rcount = 0;
903 
904 #if DNS_RDATASET_FIXED
905 	mcurrent += 4 * mcount;
906 	scurrent += 4 * scount;
907 #endif
908 	sstart = scurrent;
909 
910 	/*
911 	 * Add in the length of rdata in the mslab that aren't in
912 	 * the sslab.
913 	 */
914 	for (i = 0; i < mcount; i++) {
915 		unsigned char *mrdatabegin = mcurrent;
916 		rdata_from_slab(&mcurrent, rdclass, type, &mrdata);
917 		scurrent = sstart;
918 		for (count = 0; count < scount; count++) {
919 			dns_rdata_reset(&srdata);
920 			rdata_from_slab(&scurrent, rdclass, type, &srdata);
921 			if (dns_rdata_compare(&mrdata, &srdata) == 0)
922 				break;
923 		}
924 		if (count == scount) {
925 			/*
926 			 * This rdata isn't in the sslab, and thus isn't
927 			 * being subtracted.
928 			 */
929 			tlength += (unsigned int)(mcurrent - mrdatabegin);
930 			tcount++;
931 		} else
932 			rcount++;
933 		dns_rdata_reset(&mrdata);
934 	}
935 
936 #if DNS_RDATASET_FIXED
937 	tlength += (4 * tcount);
938 #endif
939 
940 	/*
941 	 * Check that all the records originally existed.  The numeric
942 	 * check only works as rdataslabs do not contain duplicates.
943 	 */
944 	if (((flags & DNS_RDATASLAB_EXACT) != 0) && (rcount != scount))
945 		return (DNS_R_NOTEXACT);
946 
947 	/*
948 	 * Don't continue if the new rdataslab would be empty.
949 	 */
950 	if (tcount == 0)
951 		return (DNS_R_NXRRSET);
952 
953 	/*
954 	 * If nothing is going to change, we can stop.
955 	 */
956 	if (rcount == 0)
957 		return (DNS_R_UNCHANGED);
958 
959 	/*
960 	 * Copy the reserved area from the mslab.
961 	 */
962 	tstart = isc_mem_get(mctx, tlength);
963 	if (tstart == NULL)
964 		return (ISC_R_NOMEMORY);
965 	memmove(tstart, mslab, reservelen);
966 	tcurrent = tstart + reservelen;
967 #if DNS_RDATASET_FIXED
968 	offsetbase = tcurrent;
969 
970 	offsettable = isc_mem_get(mctx, mcount * sizeof(unsigned int));
971 	if (offsettable == NULL) {
972 		isc_mem_put(mctx, tstart, tlength);
973 		return (ISC_R_NOMEMORY);
974 	}
975 	memset(offsettable, 0, mcount * sizeof(unsigned int));
976 #endif
977 
978 	/*
979 	 * Write the new count.
980 	 */
981 	*tcurrent++ = (tcount & 0xff00) >> 8;
982 	*tcurrent++ = (tcount & 0x00ff);
983 
984 #if DNS_RDATASET_FIXED
985 	tcurrent += (4 * tcount);
986 #endif
987 
988 	/*
989 	 * Copy the parts of mslab not in sslab.
990 	 */
991 	mcurrent = mslab + reservelen;
992 	mcount = *mcurrent++ * 256;
993 	mcount += *mcurrent++;
994 #if DNS_RDATASET_FIXED
995 	mcurrent += (4 * mcount);
996 #endif
997 	for (i = 0; i < mcount; i++) {
998 		unsigned char *mrdatabegin = mcurrent;
999 #if DNS_RDATASET_FIXED
1000 		order = mcurrent[2] * 256 + mcurrent[3];
1001 		INSIST(order < mcount);
1002 #endif
1003 		rdata_from_slab(&mcurrent, rdclass, type, &mrdata);
1004 		scurrent = sstart;
1005 		for (count = 0; count < scount; count++) {
1006 			dns_rdata_reset(&srdata);
1007 			rdata_from_slab(&scurrent, rdclass, type, &srdata);
1008 			if (dns_rdata_compare(&mrdata, &srdata) == 0)
1009 				break;
1010 		}
1011 		if (count == scount) {
1012 			/*
1013 			 * This rdata isn't in the sslab, and thus should be
1014 			 * copied to the tslab.
1015 			 */
1016 			unsigned int length;
1017 			length = (unsigned int)(mcurrent - mrdatabegin);
1018 #if DNS_RDATASET_FIXED
1019 			offsettable[order] = tcurrent - offsetbase;
1020 #endif
1021 			memmove(tcurrent, mrdatabegin, length);
1022 			tcurrent += length;
1023 		}
1024 		dns_rdata_reset(&mrdata);
1025 	}
1026 
1027 #if DNS_RDATASET_FIXED
1028 	fillin_offsets(offsetbase, offsettable, mcount);
1029 
1030 	isc_mem_put(mctx, offsettable, mcount * sizeof(unsigned int));
1031 #endif
1032 
1033 	INSIST(tcurrent == tstart + tlength);
1034 
1035 	*tslabp = tstart;
1036 
1037 	return (ISC_R_SUCCESS);
1038 }
1039 
1040 isc_boolean_t
dns_rdataslab_equal(unsigned char * slab1,unsigned char * slab2,unsigned int reservelen)1041 dns_rdataslab_equal(unsigned char *slab1, unsigned char *slab2,
1042 		    unsigned int reservelen)
1043 {
1044 	unsigned char *current1, *current2;
1045 	unsigned int count1, count2;
1046 	unsigned int length1, length2;
1047 
1048 	current1 = slab1 + reservelen;
1049 	count1 = *current1++ * 256;
1050 	count1 += *current1++;
1051 
1052 	current2 = slab2 + reservelen;
1053 	count2 = *current2++ * 256;
1054 	count2 += *current2++;
1055 
1056 	if (count1 != count2)
1057 		return (ISC_FALSE);
1058 
1059 #if DNS_RDATASET_FIXED
1060 	current1 += (4 * count1);
1061 	current2 += (4 * count2);
1062 #endif
1063 
1064 	while (count1 > 0) {
1065 		length1 = *current1++ * 256;
1066 		length1 += *current1++;
1067 
1068 		length2 = *current2++ * 256;
1069 		length2 += *current2++;
1070 
1071 #if DNS_RDATASET_FIXED
1072 		current1 += 2;
1073 		current2 += 2;
1074 #endif
1075 
1076 		if (length1 != length2 ||
1077 		    memcmp(current1, current2, length1) != 0)
1078 			return (ISC_FALSE);
1079 
1080 		current1 += length1;
1081 		current2 += length1;
1082 
1083 		count1--;
1084 	}
1085 	return (ISC_TRUE);
1086 }
1087 
1088 isc_boolean_t
dns_rdataslab_equalx(unsigned char * slab1,unsigned char * slab2,unsigned int reservelen,dns_rdataclass_t rdclass,dns_rdatatype_t type)1089 dns_rdataslab_equalx(unsigned char *slab1, unsigned char *slab2,
1090 		     unsigned int reservelen, dns_rdataclass_t rdclass,
1091 		     dns_rdatatype_t type)
1092 {
1093 	unsigned char *current1, *current2;
1094 	unsigned int count1, count2;
1095 	dns_rdata_t rdata1 = DNS_RDATA_INIT;
1096 	dns_rdata_t rdata2 = DNS_RDATA_INIT;
1097 
1098 	current1 = slab1 + reservelen;
1099 	count1 = *current1++ * 256;
1100 	count1 += *current1++;
1101 
1102 	current2 = slab2 + reservelen;
1103 	count2 = *current2++ * 256;
1104 	count2 += *current2++;
1105 
1106 	if (count1 != count2)
1107 		return (ISC_FALSE);
1108 
1109 #if DNS_RDATASET_FIXED
1110 	current1 += (4 * count1);
1111 	current2 += (4 * count2);
1112 #endif
1113 
1114 	while (count1-- > 0) {
1115 		rdata_from_slab(&current1, rdclass, type, &rdata1);
1116 		rdata_from_slab(&current2, rdclass, type, &rdata2);
1117 		if (dns_rdata_compare(&rdata1, &rdata2) != 0)
1118 			return (ISC_FALSE);
1119 		dns_rdata_reset(&rdata1);
1120 		dns_rdata_reset(&rdata2);
1121 	}
1122 	return (ISC_TRUE);
1123 }
1124