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