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