PageRenderTime 67ms CodeModel.GetById 19ms app.highlight 41ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/bind9/lib/dns/rdataslab.c

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