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