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