Coverage Report

Created: 2025-12-14 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/rdataslab.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
/*! \file */
15
16
#include <ctype.h>
17
#include <stdbool.h>
18
#include <stdlib.h>
19
20
#include <isc/ascii.h>
21
#include <isc/atomic.h>
22
#include <isc/list.h>
23
#include <isc/mem.h>
24
#include <isc/region.h>
25
#include <isc/result.h>
26
#include <isc/string.h>
27
#include <isc/util.h>
28
29
#include <dns/db.h>
30
#include <dns/rdata.h>
31
#include <dns/rdataset.h>
32
#include <dns/rdataslab.h>
33
#include <dns/stats.h>
34
35
#include "rdataslab_p.h"
36
37
/*
38
 * The memory structure of an rdataslab is as follows:
39
 *
40
 *  header    (dns_slabheader_t)
41
 *  record count  (2 bytes)
42
 *  data records
43
 *    data length (2 bytes)
44
 *    order   (2 bytes)
45
 *    meta data (1 byte for RRSIG, 0 for all other types)
46
 *    data    (data length bytes)
47
 *
48
 * A "bare" rdataslab is everything after "header".
49
 *
50
 * When a slab is created, data records are sorted into DNSSEC order.
51
 */
52
53
static void
54
rdataset_disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG);
55
static isc_result_t
56
rdataset_first(dns_rdataset_t *rdataset);
57
static isc_result_t
58
rdataset_next(dns_rdataset_t *rdataset);
59
static void
60
rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata);
61
static void
62
rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG);
63
static unsigned int
64
rdataset_count(dns_rdataset_t *rdataset);
65
static isc_result_t
66
rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
67
        dns_rdataset_t *neg, dns_rdataset_t *negsig DNS__DB_FLARG);
68
static isc_result_t
69
rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
70
        dns_rdataset_t *neg, dns_rdataset_t *negsig DNS__DB_FLARG);
71
static void
72
rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
73
static void
74
rdataset_expire(dns_rdataset_t *rdataset DNS__DB_FLARG);
75
static void
76
rdataset_clearprefetch(dns_rdataset_t *rdataset);
77
static void
78
rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name);
79
static dns_slabheader_t *
80
rdataset_getheader(const dns_rdataset_t *rdataset);
81
82
dns_rdatasetmethods_t dns_rdataslab_rdatasetmethods = {
83
  .disassociate = rdataset_disassociate,
84
  .first = rdataset_first,
85
  .next = rdataset_next,
86
  .current = rdataset_current,
87
  .clone = rdataset_clone,
88
  .count = rdataset_count,
89
  .getnoqname = rdataset_getnoqname,
90
  .getclosest = rdataset_getclosest,
91
  .settrust = rdataset_settrust,
92
  .expire = rdataset_expire,
93
  .clearprefetch = rdataset_clearprefetch,
94
  .getownercase = rdataset_getownercase,
95
};
96
97
/*% Note: the "const void *" are just to make qsort happy.  */
98
static int
99
0
compare_rdata(const void *p1, const void *p2) {
100
0
  return dns_rdata_compare(p1, p2);
101
0
}
102
103
static isc_result_t
104
makeslab(dns_rdataset_t *rdataset, isc_mem_t *mctx, isc_region_t *region,
105
0
   uint32_t maxrrperset) {
106
  /*
107
   * Use &removed as a sentinel pointer for duplicate
108
   * rdata as rdata.data == NULL is valid.
109
   */
110
0
  static unsigned char removed;
111
0
  dns_rdata_t *rdata = NULL;
112
0
  unsigned char *rawbuf = NULL;
113
0
  unsigned int headerlen = sizeof(dns_slabheader_t);
114
0
  unsigned int buflen = headerlen + 2;
115
0
  isc_result_t result;
116
0
  unsigned int nitems;
117
0
  unsigned int nalloc;
118
0
  unsigned int length;
119
0
  size_t i;
120
0
  size_t rdatasize;
121
122
  /*
123
   * If the source rdataset is also a slab, we don't need
124
   * to do anything special, just copy the whole slab to a
125
   * new buffer.
126
   */
127
0
  if (rdataset->methods == &dns_rdataslab_rdatasetmethods) {
128
0
    dns_slabheader_t *header = rdataset_getheader(rdataset);
129
0
    buflen = dns_rdataslab_size(header);
130
131
0
    rawbuf = isc_mem_get(mctx, buflen);
132
0
    region->base = rawbuf;
133
0
    region->length = buflen;
134
135
0
    memmove(rawbuf, header, buflen);
136
0
    return ISC_R_SUCCESS;
137
0
  }
138
139
  /*
140
   * If there are no rdata then we just need to allocate a header
141
   * with a zero record count.
142
   */
143
0
  nitems = dns_rdataset_count(rdataset);
144
0
  if (nitems == 0) {
145
0
    if (rdataset->type != 0) {
146
0
      return ISC_R_FAILURE;
147
0
    }
148
0
    rawbuf = isc_mem_get(mctx, buflen);
149
0
    region->base = rawbuf;
150
0
    region->length = buflen;
151
0
    rawbuf += headerlen;
152
0
    put_uint16(rawbuf, 0);
153
0
    return ISC_R_SUCCESS;
154
0
  }
155
156
0
  if (maxrrperset > 0 && nitems > maxrrperset) {
157
0
    return DNS_R_TOOMANYRECORDS;
158
0
  }
159
160
0
  if (nitems > 0xffff) {
161
0
    return ISC_R_NOSPACE;
162
0
  }
163
164
  /*
165
   * Remember the original number of items.
166
   */
167
0
  nalloc = nitems;
168
169
0
  RUNTIME_CHECK(!ckd_mul(&rdatasize, nalloc, sizeof(rdata[0])));
170
0
  rdata = isc_mem_get(mctx, rdatasize);
171
172
  /*
173
   * Save all of the rdata members into an array.
174
   */
175
0
  result = dns_rdataset_first(rdataset);
176
0
  if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) {
177
0
    goto free_rdatas;
178
0
  }
179
0
  for (i = 0; i < nalloc && result == ISC_R_SUCCESS; i++) {
180
0
    INSIST(result == ISC_R_SUCCESS);
181
0
    dns_rdata_init(&rdata[i]);
182
0
    dns_rdataset_current(rdataset, &rdata[i]);
183
0
    INSIST(rdata[i].data != &removed);
184
0
    result = dns_rdataset_next(rdataset);
185
0
  }
186
0
  if (i != nalloc || result != ISC_R_NOMORE) {
187
    /*
188
     * Somehow we iterated over fewer rdatas than
189
     * dns_rdataset_count() said there were or there
190
     * were more items than dns_rdataset_count said
191
     * there were.
192
     */
193
0
    result = ISC_R_FAILURE;
194
0
    goto free_rdatas;
195
0
  }
196
197
  /*
198
   * Put into DNSSEC order.
199
   */
200
0
  if (nalloc > 1U) {
201
0
    qsort(rdata, nalloc, sizeof(rdata[0]), compare_rdata);
202
0
  }
203
204
  /*
205
   * Remove duplicates and compute the total storage required.
206
   *
207
   * If an rdata is not a duplicate, accumulate the storage size
208
   * required for the rdata.  We do not store the class, type, etc,
209
   * just the rdata, so our overhead is 2 bytes for the number of
210
   * records, and 2 bytes for the length of each rdata, plus the
211
   * rdata itself.
212
   */
213
0
  for (i = 1; i < nalloc; i++) {
214
0
    if (compare_rdata(&rdata[i - 1], &rdata[i]) == 0) {
215
0
      rdata[i - 1].data = &removed;
216
0
      nitems--;
217
0
    } else {
218
0
      buflen += (2 + rdata[i - 1].length);
219
      /*
220
       * Provide space to store the per RR meta data.
221
       */
222
0
      if (rdataset->type == dns_rdatatype_rrsig) {
223
0
        buflen++;
224
0
      }
225
0
    }
226
0
  }
227
228
  /*
229
   * Don't forget the last item!
230
   */
231
0
  buflen += (2 + rdata[i - 1].length);
232
233
  /*
234
   * Provide space to store the per RR meta data.
235
   */
236
0
  if (rdataset->type == dns_rdatatype_rrsig) {
237
0
    buflen++;
238
0
  }
239
240
  /*
241
   * Ensure that singleton types are actually singletons.
242
   */
243
0
  if (nitems > 1 && dns_rdatatype_issingleton(rdataset->type)) {
244
    /*
245
     * We have a singleton type, but there's more than one
246
     * RR in the rdataset.
247
     */
248
0
    result = DNS_R_SINGLETON;
249
0
    goto free_rdatas;
250
0
  }
251
252
  /*
253
   * Allocate the memory, set up a buffer, start copying in
254
   * data.
255
   */
256
0
  rawbuf = isc_mem_get(mctx, buflen);
257
258
0
  region->base = rawbuf;
259
0
  region->length = buflen;
260
0
  rawbuf += headerlen;
261
0
  put_uint16(rawbuf, nitems);
262
263
0
  for (i = 0; i < nalloc; i++) {
264
0
    if (rdata[i].data == &removed) {
265
0
      continue;
266
0
    }
267
0
    length = rdata[i].length;
268
0
    if (rdataset->type == dns_rdatatype_rrsig) {
269
0
      length++;
270
0
    }
271
0
    INSIST(length <= 0xffff);
272
273
0
    put_uint16(rawbuf, length);
274
275
    /*
276
     * Store the per RR meta data.
277
     */
278
0
    if (rdataset->type == dns_rdatatype_rrsig) {
279
0
      *rawbuf++ = (rdata[i].flags & DNS_RDATA_OFFLINE)
280
0
              ? DNS_RDATASLAB_OFFLINE
281
0
              : 0;
282
0
    }
283
0
    if (rdata[i].length != 0) {
284
0
      memmove(rawbuf, rdata[i].data, rdata[i].length);
285
0
    }
286
0
    rawbuf += rdata[i].length;
287
0
  }
288
289
0
  result = ISC_R_SUCCESS;
290
291
0
free_rdatas:
292
0
  isc_mem_put(mctx, rdata, rdatasize);
293
0
  return result;
294
0
}
295
296
isc_result_t
297
dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx,
298
0
         isc_region_t *region, uint32_t maxrrperset) {
299
0
  isc_result_t result;
300
301
0
  if (rdataset->type == dns_rdatatype_none &&
302
0
      rdataset->covers == dns_rdatatype_none)
303
0
  {
304
0
    return DNS_R_DISALLOWED;
305
0
  }
306
307
0
  result = makeslab(rdataset, mctx, region, maxrrperset);
308
0
  if (result == ISC_R_SUCCESS) {
309
0
    dns_slabheader_t *new = (dns_slabheader_t *)region->base;
310
0
    dns_typepair_t typepair;
311
312
0
    if (rdataset->attributes.negative) {
313
0
      INSIST(rdataset->type == dns_rdatatype_none);
314
0
      INSIST(rdataset->covers != dns_rdatatype_none);
315
0
      typepair = DNS_TYPEPAIR_VALUE(rdataset->covers,
316
0
                  dns_rdatatype_none);
317
0
    } else {
318
0
      INSIST(rdataset->type != dns_rdatatype_none);
319
0
      INSIST(dns_rdatatype_issig(rdataset->type) ||
320
0
             rdataset->covers == dns_rdatatype_none);
321
0
      typepair = DNS_TYPEPAIR_VALUE(rdataset->type,
322
0
                  rdataset->covers);
323
0
    }
324
325
0
    *new = (dns_slabheader_t){
326
0
      .headers_link = CDS_LIST_HEAD_INIT(new->headers_link),
327
0
      .typepair = typepair,
328
0
      .trust = rdataset->trust,
329
0
      .ttl = rdataset->ttl,
330
0
      .dirtylink = ISC_LINK_INITIALIZER,
331
0
    };
332
0
  }
333
334
0
  return result;
335
0
}
336
337
unsigned int
338
0
dns_rdataslab_size(dns_slabheader_t *header) {
339
0
  REQUIRE(header != NULL);
340
341
0
  unsigned char *slab = (unsigned char *)header +
342
0
            sizeof(dns_slabheader_t);
343
0
  INSIST(slab != NULL);
344
345
0
  unsigned char *current = slab;
346
0
  uint16_t count = get_uint16(current);
347
348
0
  while (count-- > 0) {
349
0
    uint16_t length = get_uint16(current);
350
0
    current += length;
351
0
  }
352
353
0
  return (unsigned int)(current - slab) + sizeof(dns_slabheader_t);
354
0
}
355
356
unsigned int
357
0
dns_rdataslab_count(dns_slabheader_t *header) {
358
0
  REQUIRE(header != NULL);
359
360
0
  unsigned char *current = (unsigned char *)header + sizeof(*header);
361
0
  uint16_t count = get_uint16(current);
362
363
0
  return count;
364
0
}
365
366
/*
367
 * Make the dns_rdata_t 'rdata' refer to the slab item
368
 * beginning at '*current' (which is part of a slab of type
369
 * 'type' and class 'rdclass') and advance '*current' to
370
 * point to the next item in the slab.
371
 */
372
static void
373
rdata_from_slabitem(unsigned char **current, dns_rdataclass_t rdclass,
374
0
        dns_rdatatype_t type, dns_rdata_t *rdata) {
375
0
  unsigned char *tcurrent = *current;
376
0
  isc_region_t region;
377
0
  bool offline = false;
378
0
  uint16_t length = get_uint16(tcurrent);
379
380
0
  if (type == dns_rdatatype_rrsig) {
381
0
    if ((*tcurrent & DNS_RDATASLAB_OFFLINE) != 0) {
382
0
      offline = true;
383
0
    }
384
0
    length--;
385
0
    tcurrent++;
386
0
  }
387
0
  region.length = length;
388
0
  region.base = tcurrent;
389
0
  tcurrent += region.length;
390
0
  dns_rdata_fromregion(rdata, rdclass, type, &region);
391
0
  if (offline) {
392
0
    rdata->flags |= DNS_RDATA_OFFLINE;
393
0
  }
394
0
  *current = tcurrent;
395
0
}
396
397
static void
398
rdata_to_slabitem(unsigned char **current, dns_rdatatype_t type,
399
0
      dns_rdata_t *rdata) {
400
0
  unsigned int length = rdata->length;
401
0
  unsigned char *data = rdata->data;
402
0
  unsigned char *p = *current;
403
404
0
  if (type == dns_rdatatype_rrsig) {
405
0
    length++;
406
0
    data--;
407
0
  }
408
409
0
  put_uint16(p, length);
410
0
  memmove(p, data, length);
411
0
  p += length;
412
413
0
  *current = p;
414
0
}
415
416
typedef struct slabinfo {
417
  unsigned char *pos;
418
  dns_rdata_t rdata;
419
  bool dup;
420
} slabinfo_t;
421
422
isc_result_t
423
dns_rdataslab_merge(dns_slabheader_t *oheader, dns_slabheader_t *nheader,
424
        isc_mem_t *mctx, dns_rdataclass_t rdclass,
425
        dns_rdatatype_t type, unsigned int flags,
426
0
        uint32_t maxrrperset, dns_slabheader_t **theaderp) {
427
0
  isc_result_t result = ISC_R_SUCCESS;
428
0
  unsigned char *ocurrent = NULL, *ncurrent = NULL, *tcurrent = NULL;
429
0
  unsigned int ocount, ncount, tlength, tcount = 0;
430
0
  slabinfo_t *oinfo = NULL, *ninfo = NULL;
431
0
  size_t o = 0, n = 0;
432
433
0
  REQUIRE(theaderp != NULL && *theaderp == NULL);
434
0
  REQUIRE(oheader != NULL && nheader != NULL);
435
436
0
  ocurrent = (unsigned char *)oheader + sizeof(dns_slabheader_t);
437
0
  ocount = get_uint16(ocurrent);
438
439
0
  ncurrent = (unsigned char *)nheader + sizeof(dns_slabheader_t);
440
0
  ncount = get_uint16(ncurrent);
441
442
0
  INSIST(ocount > 0 && ncount > 0);
443
444
0
  if (maxrrperset > 0 && ocount + ncount > maxrrperset) {
445
0
    return DNS_R_TOOMANYRECORDS;
446
0
  }
447
448
  /*
449
   * Figure out the target length. Start with the header,
450
   * plus 2 octets for the count.
451
   */
452
0
  tlength = sizeof(dns_slabheader_t) + 2;
453
454
  /*
455
   * Gather the rdatas in the old slab and add their lengths to
456
   * the larget length.
457
   */
458
0
  oinfo = isc_mem_cget(mctx, ocount, sizeof(struct slabinfo));
459
0
  for (size_t i = 0; i < ocount; i++) {
460
0
    oinfo[i].pos = ocurrent;
461
0
    dns_rdata_init(&oinfo[i].rdata);
462
0
    rdata_from_slabitem(&ocurrent, rdclass, type, &oinfo[i].rdata);
463
0
    tlength += ocurrent - oinfo[i].pos;
464
0
  }
465
466
  /*
467
   * Then add the length of rdatas in the new slab that aren't
468
   * duplicated in the old slab.
469
   */
470
0
  ninfo = isc_mem_cget(mctx, ncount, sizeof(struct slabinfo));
471
0
  for (size_t i = 0; i < ncount; i++) {
472
0
    ninfo[i].pos = ncurrent;
473
0
    dns_rdata_init(&ninfo[i].rdata);
474
0
    rdata_from_slabitem(&ncurrent, rdclass, type, &ninfo[i].rdata);
475
476
0
    for (size_t j = 0; j < ocount; j++) {
477
0
      if (oinfo[j].dup) {
478
        /*
479
         * This was already found to be
480
         * duplicated; no need to compare
481
         * it again.
482
         */
483
0
        continue;
484
0
      }
485
486
0
      if (dns_rdata_compare(&oinfo[j].rdata,
487
0
                &ninfo[i].rdata) == 0)
488
0
      {
489
        /*
490
         * Found a dup. Mark the old copy as a
491
         * duplicate so we don't check it again;
492
         * mark the new copy as a duplicate so we
493
         * don't copy it to the target.
494
         */
495
0
        oinfo[j].dup = ninfo[i].dup = true;
496
0
        break;
497
0
      }
498
0
    }
499
500
0
    if (ninfo[i].dup) {
501
0
      continue;
502
0
    }
503
504
    /*
505
     * We will be copying this item to the target, so
506
     * add its length to tlength and increment tcount.
507
     */
508
0
    tlength += ncurrent - ninfo[i].pos;
509
0
    tcount++;
510
0
  }
511
512
  /*
513
   * If the EXACT flag is set, there can't be any rdata in
514
   * the new slab that was also in the old. If tcount is less
515
   * than ncount, then we found such a duplicate.
516
   */
517
0
  if (((flags & DNS_RDATASLAB_EXACT) != 0) && (tcount < ncount)) {
518
0
    CLEANUP(DNS_R_NOTEXACT);
519
0
  }
520
521
  /*
522
   * If nothing's being copied in from the new slab, and the
523
   * FORCE flag isn't set, we're done.
524
   */
525
0
  if (tcount == 0 && (flags & DNS_RDATASLAB_FORCE) == 0) {
526
0
    CLEANUP(DNS_R_UNCHANGED);
527
0
  }
528
529
  /* Add to tcount the total number of items from the old slab. */
530
0
  tcount += ocount;
531
532
  /* Resposition ncurrent at the first item. */
533
0
  ncurrent = (unsigned char *)nheader + sizeof(dns_slabheader_t) + 2;
534
535
  /* Single types can't have more than one RR. */
536
0
  if (tcount > 1 && dns_rdatatype_issingleton(type)) {
537
0
    CLEANUP(DNS_R_SINGLETON);
538
0
  }
539
540
0
  if (tcount > 0xffff) {
541
0
    CLEANUP(ISC_R_NOSPACE);
542
0
  }
543
544
  /* Allocate the target buffer and copy the new slab's header */
545
0
  unsigned char *tstart = isc_mem_get(mctx, tlength);
546
547
0
  memmove(tstart, nheader, sizeof(dns_slabheader_t));
548
0
  tcurrent = tstart + sizeof(dns_slabheader_t);
549
550
  /* Write the new count, then start merging the slabs. */
551
0
  put_uint16(tcurrent, tcount);
552
553
  /*
554
   * Now walk the sets together, adding each item in DNSSEC order,
555
   * and skipping over any more dups in the new slab.
556
   */
557
0
  while (o < ocount || n < ncount) {
558
0
    bool fromold;
559
560
    /* Skip to the next non-duplicate in the new slab. */
561
0
    for (; n < ncount && ninfo[n].dup; n++)
562
0
      ;
563
564
0
    if (o == ocount) {
565
0
      fromold = false;
566
0
    } else if (n == ncount) {
567
0
      fromold = true;
568
0
    } else {
569
0
      fromold = dns_rdata_compare(&oinfo[o].rdata,
570
0
                &ninfo[n].rdata) < 0;
571
0
    }
572
573
0
    if (fromold) {
574
0
      rdata_to_slabitem(&tcurrent, type, &oinfo[o].rdata);
575
0
      if (++o < ocount) {
576
        /* Skip to the next rdata in the old slab */
577
0
        continue;
578
0
      }
579
0
    } else {
580
0
      rdata_to_slabitem(&tcurrent, type, &ninfo[n++].rdata);
581
0
    }
582
0
  }
583
584
0
  INSIST(tcurrent == tstart + tlength);
585
586
0
  *theaderp = (dns_slabheader_t *)tstart;
587
588
0
cleanup:
589
0
  isc_mem_cput(mctx, oinfo, ocount, sizeof(struct slabinfo));
590
0
  isc_mem_cput(mctx, ninfo, ncount, sizeof(struct slabinfo));
591
592
0
  return result;
593
0
}
594
595
isc_result_t
596
dns_rdataslab_subtract(dns_slabheader_t *oheader, dns_slabheader_t *sheader,
597
           isc_mem_t *mctx, dns_rdataclass_t rdclass,
598
           dns_rdatatype_t type, unsigned int flags,
599
0
           dns_slabheader_t **theaderp) {
600
0
  isc_result_t result = ISC_R_SUCCESS;
601
0
  unsigned char *ocurrent = NULL, *scurrent = NULL;
602
0
  unsigned char *tstart = NULL, *tcurrent = NULL;
603
0
  unsigned int ocount, scount, tlength;
604
0
  unsigned int tcount = 0, rcount = 0;
605
0
  slabinfo_t *oinfo = NULL, *sinfo = NULL;
606
607
0
  REQUIRE(theaderp != NULL && *theaderp == NULL);
608
0
  REQUIRE(oheader != NULL && sheader != NULL);
609
610
0
  ocurrent = (unsigned char *)oheader + sizeof(dns_slabheader_t);
611
0
  ocount = get_uint16(ocurrent);
612
613
0
  scurrent = (unsigned char *)sheader + sizeof(dns_slabheader_t);
614
0
  scount = get_uint16(scurrent);
615
616
0
  INSIST(ocount > 0 && scount > 0);
617
618
  /* Get info about the rdatas being subtracted */
619
0
  sinfo = isc_mem_cget(mctx, scount, sizeof(struct slabinfo));
620
0
  for (size_t i = 0; i < scount; i++) {
621
0
    sinfo[i].pos = scurrent;
622
0
    dns_rdata_init(&sinfo[i].rdata);
623
0
    rdata_from_slabitem(&scurrent, rdclass, type, &sinfo[i].rdata);
624
0
  }
625
626
  /*
627
   * Figure out the target length. Start with the header,
628
   * plus 2 octets for the count.
629
   */
630
0
  tlength = sizeof(dns_slabheader_t) + 2;
631
632
  /*
633
   * Add the length of the rdatas in the old slab that
634
   * aren't being subtracted.
635
   */
636
0
  oinfo = isc_mem_cget(mctx, ocount, sizeof(struct slabinfo));
637
0
  for (size_t i = 0; i < ocount; i++) {
638
0
    bool matched = false;
639
640
0
    oinfo[i].pos = ocurrent;
641
0
    dns_rdata_init(&oinfo[i].rdata);
642
0
    rdata_from_slabitem(&ocurrent, rdclass, type, &oinfo[i].rdata);
643
644
0
    for (size_t j = 0; j < scount; j++) {
645
0
      if (sinfo[j].dup) {
646
0
        continue;
647
0
      } else if (dns_rdata_compare(&oinfo[i].rdata,
648
0
                 &sinfo[j].rdata) == 0)
649
0
      {
650
0
        matched = true;
651
0
        oinfo[i].dup = sinfo[j].dup = true;
652
0
        break;
653
0
      }
654
0
    }
655
656
0
    if (matched) {
657
      /* This item will be subtracted. */
658
0
      rcount++;
659
0
    } else {
660
      /*
661
       * This rdata wasn't in the slab to be subtracted,
662
       * so copy it to the target.  Add its length to
663
       * tlength and increment tcount.
664
       */
665
0
      tlength += ocurrent - oinfo[i].pos;
666
0
      tcount++;
667
0
    }
668
0
  }
669
670
  /*
671
   * If the EXACT flag wasn't set, check that all the records that
672
   * were to be subtracted actually did exist in the original slab.
673
   * (The numeric check works here because rdataslabs do not contain
674
   * duplicates.)
675
   */
676
0
  if ((flags & DNS_RDATASLAB_EXACT) != 0 && rcount != scount) {
677
0
    CLEANUP(DNS_R_NOTEXACT);
678
0
  }
679
680
  /*
681
   * If the resulting rdataslab would be empty, don't bother to
682
   * create a new buffer, just return.
683
   */
684
0
  if (tcount == 0) {
685
0
    CLEANUP(DNS_R_NXRRSET);
686
0
  }
687
688
  /*
689
   * If nothing is going to change, stop.
690
   */
691
0
  if (rcount == 0) {
692
0
    CLEANUP(DNS_R_UNCHANGED);
693
0
  }
694
695
  /*
696
   * Allocate the target buffer and copy the old slab's header.
697
   */
698
0
  tstart = isc_mem_get(mctx, tlength);
699
0
  memmove(tstart, oheader, sizeof(dns_slabheader_t));
700
0
  tcurrent = tstart + sizeof(dns_slabheader_t);
701
702
  /*
703
   * Write the new count.
704
   */
705
0
  put_uint16(tcurrent, tcount);
706
707
  /*
708
   * Copy the parts of the old slab that didn't have duplicates.
709
   */
710
0
  for (size_t i = 0; i < ocount; i++) {
711
0
    if (!oinfo[i].dup) {
712
0
      rdata_to_slabitem(&tcurrent, type, &oinfo[i].rdata);
713
0
    }
714
0
  }
715
716
0
  INSIST(tcurrent == tstart + tlength);
717
718
0
  *theaderp = (dns_slabheader_t *)tstart;
719
720
0
cleanup:
721
0
  isc_mem_cput(mctx, oinfo, ocount, sizeof(struct slabinfo));
722
0
  isc_mem_cput(mctx, sinfo, scount, sizeof(struct slabinfo));
723
724
0
  return result;
725
0
}
726
727
bool
728
0
dns_rdataslab_equal(dns_slabheader_t *slab1, dns_slabheader_t *slab2) {
729
0
  unsigned char *current1 = NULL, *current2 = NULL;
730
0
  unsigned int count1, count2;
731
732
0
  current1 = (unsigned char *)slab1 + sizeof(dns_slabheader_t);
733
0
  count1 = get_uint16(current1);
734
735
0
  current2 = (unsigned char *)slab2 + sizeof(dns_slabheader_t);
736
0
  count2 = get_uint16(current2);
737
738
0
  if (count1 != count2) {
739
0
    return false;
740
0
  } else if (count1 == 0) {
741
0
    return true;
742
0
  }
743
744
0
  while (count1-- > 0) {
745
0
    unsigned int length1 = get_uint16(current1);
746
0
    unsigned int length2 = get_uint16(current2);
747
748
0
    if (length1 != length2 ||
749
0
        memcmp(current1, current2, length1) != 0)
750
0
    {
751
0
      return false;
752
0
    }
753
754
0
    current1 += length1;
755
0
    current2 += length1;
756
0
  }
757
0
  return true;
758
0
}
759
760
bool
761
dns_rdataslab_equalx(dns_slabheader_t *slab1, dns_slabheader_t *slab2,
762
0
         dns_rdataclass_t rdclass, dns_rdatatype_t type) {
763
0
  unsigned char *current1 = NULL, *current2 = NULL;
764
0
  unsigned int count1, count2;
765
766
0
  current1 = (unsigned char *)slab1 + sizeof(dns_slabheader_t);
767
0
  count1 = get_uint16(current1);
768
769
0
  current2 = (unsigned char *)slab2 + sizeof(dns_slabheader_t);
770
0
  count2 = get_uint16(current2);
771
772
0
  if (count1 != count2) {
773
0
    return false;
774
0
  } else if (count1 == 0) {
775
0
    return true;
776
0
  }
777
778
0
  while (count1-- > 0) {
779
0
    dns_rdata_t rdata1 = DNS_RDATA_INIT;
780
0
    dns_rdata_t rdata2 = DNS_RDATA_INIT;
781
782
0
    rdata_from_slabitem(&current1, rdclass, type, &rdata1);
783
0
    rdata_from_slabitem(&current2, rdclass, type, &rdata2);
784
0
    if (dns_rdata_compare(&rdata1, &rdata2) != 0) {
785
0
      return false;
786
0
    }
787
0
  }
788
0
  return true;
789
0
}
790
791
void
792
0
dns_slabheader_setownercase(dns_slabheader_t *header, const dns_name_t *name) {
793
0
  REQUIRE(!CASESET(header));
794
795
0
  bool casefullylower = true;
796
797
  /*
798
   * We do not need to worry about label lengths as they are all
799
   * less than or equal to 63.
800
   */
801
0
  memset(header->upper, 0, sizeof(header->upper));
802
0
  for (size_t i = 0; i < name->length; i++) {
803
0
    if (isupper(name->ndata[i])) {
804
0
      header->upper[i / 8] |= 1 << (i % 8);
805
0
      casefullylower = false;
806
0
    }
807
0
  }
808
0
  if (casefullylower) {
809
0
    DNS_SLABHEADER_SETATTR(header,
810
0
               DNS_SLABHEADERATTR_CASEFULLYLOWER);
811
0
  }
812
0
  DNS_SLABHEADER_SETATTR(header, DNS_SLABHEADERATTR_CASESET);
813
0
}
814
815
void
816
0
dns_slabheader_copycase(dns_slabheader_t *dest, dns_slabheader_t *src) {
817
0
  REQUIRE(!CASESET(dest));
818
0
  if (CASESET(src)) {
819
0
    memmove(dest->upper, src->upper, sizeof(src->upper));
820
0
    if (CASEFULLYLOWER(src)) {
821
0
      DNS_SLABHEADER_SETATTR(
822
0
        dest, DNS_SLABHEADERATTR_CASEFULLYLOWER);
823
0
    }
824
0
    DNS_SLABHEADER_SETATTR(dest, DNS_SLABHEADERATTR_CASESET);
825
0
  }
826
0
}
827
828
void
829
0
dns_slabheader_reset(dns_slabheader_t *h, dns_dbnode_t *node) {
830
0
  h->heap_index = 0;
831
0
  h->heap = NULL;
832
0
  h->node = node;
833
834
0
  atomic_init(&h->attributes, 0);
835
0
  atomic_init(&h->last_refresh_fail_ts, 0);
836
837
0
  ISC_LINK_INIT(h, dirtylink);
838
839
0
  STATIC_ASSERT(sizeof(h->attributes) == 2,
840
0
          "The .attributes field of dns_slabheader_t needs to be "
841
0
          "16-bit int type exactly.");
842
0
}
843
844
dns_slabheader_t *
845
0
dns_slabheader_new(isc_mem_t *mctx, dns_dbnode_t *node) {
846
0
  dns_slabheader_t *h = NULL;
847
848
0
  h = isc_mem_get(mctx, sizeof(*h));
849
0
  *h = (dns_slabheader_t){
850
0
    .node = node,
851
0
    .dirtylink = ISC_LINK_INITIALIZER,
852
0
  };
853
0
  return h;
854
0
}
855
856
void
857
0
dns_slabheader_destroy(dns_slabheader_t **headerp) {
858
0
  unsigned int size;
859
0
  dns_slabheader_t *header = *headerp;
860
861
0
  *headerp = NULL;
862
863
0
  isc_mem_t *mctx = header->node->mctx;
864
0
  dns_db_deletedata(header->node, header);
865
866
0
  if (EXISTS(header)) {
867
0
    size = dns_rdataslab_size(header);
868
0
  } else {
869
0
    size = sizeof(*header);
870
0
  }
871
872
0
  isc_mem_put(mctx, header, size);
873
0
}
874
875
void
876
0
dns_slabheader_freeproof(isc_mem_t *mctx, dns_slabheader_proof_t **proofp) {
877
0
  unsigned int buflen;
878
0
  uint8_t *rawbuf;
879
0
  dns_slabheader_proof_t *proof = *proofp;
880
0
  *proofp = NULL;
881
882
0
  if (dns_name_dynamic(&proof->name)) {
883
0
    dns_name_free(&proof->name, mctx);
884
0
  }
885
0
  if (proof->neg != NULL) {
886
0
    rawbuf = proof->neg;
887
0
    rawbuf -= sizeof(dns_slabheader_t);
888
0
    buflen = dns_rdataslab_size((dns_slabheader_t *)rawbuf);
889
890
0
    isc_mem_put(mctx, rawbuf, buflen);
891
0
  }
892
0
  if (proof->negsig != NULL) {
893
0
    rawbuf = proof->negsig;
894
0
    rawbuf -= sizeof(dns_slabheader_t);
895
0
    buflen = dns_rdataslab_size((dns_slabheader_t *)rawbuf);
896
897
0
    isc_mem_put(mctx, rawbuf, buflen);
898
0
  }
899
0
  isc_mem_put(mctx, proof, sizeof(*proof));
900
0
}
901
902
/* Fixed RRSet helper macros */
903
904
static void
905
0
rdataset_disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG) {
906
0
  dns_dbnode_t *node = rdataset->slab.node;
907
908
0
  dns__db_detachnode(&node DNS__DB_FLARG_PASS);
909
0
}
910
911
static isc_result_t
912
0
rdataset_first(dns_rdataset_t *rdataset) {
913
0
  unsigned char *raw = rdataset->slab.raw;
914
0
  uint16_t count = peek_uint16(raw);
915
0
  if (count == 0) {
916
0
    rdataset->slab.iter_pos = NULL;
917
0
    rdataset->slab.iter_count = 0;
918
0
    return ISC_R_NOMORE;
919
0
  }
920
921
  /*
922
   * iter_count is the number of rdata beyond the cursor
923
   * position, so we decrement the total count by one before
924
   * storing it.
925
   *
926
   * 'raw' points to the first record.
927
   */
928
0
  rdataset->slab.iter_pos = raw + sizeof(uint16_t);
929
0
  rdataset->slab.iter_count = count - 1;
930
931
0
  return ISC_R_SUCCESS;
932
0
}
933
934
static isc_result_t
935
0
rdataset_next(dns_rdataset_t *rdataset) {
936
0
  uint16_t count = rdataset->slab.iter_count;
937
0
  if (count == 0) {
938
0
    rdataset->slab.iter_pos = NULL;
939
0
    return ISC_R_NOMORE;
940
0
  }
941
0
  rdataset->slab.iter_count = count - 1;
942
943
  /*
944
   * Skip forward one record (length + 4) or one offset (4).
945
   */
946
0
  unsigned char *raw = rdataset->slab.iter_pos;
947
0
  uint16_t length = peek_uint16(raw);
948
0
  raw += length;
949
0
  rdataset->slab.iter_pos = raw + sizeof(uint16_t);
950
951
0
  return ISC_R_SUCCESS;
952
0
}
953
954
static void
955
0
rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
956
0
  unsigned char *raw = NULL;
957
0
  unsigned int length;
958
0
  isc_region_t r;
959
0
  unsigned int flags = 0;
960
961
0
  raw = rdataset->slab.iter_pos;
962
0
  REQUIRE(raw != NULL);
963
964
  /*
965
   * Find the start of the record if not already in iter_pos
966
   * then skip the length and order fields.
967
   */
968
0
  length = get_uint16(raw);
969
970
0
  if (rdataset->type == dns_rdatatype_rrsig) {
971
0
    if (*raw & DNS_RDATASLAB_OFFLINE) {
972
0
      flags |= DNS_RDATA_OFFLINE;
973
0
    }
974
0
    length--;
975
0
    raw++;
976
0
  }
977
0
  r.length = length;
978
0
  r.base = raw;
979
0
  dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r);
980
0
  rdata->flags |= flags;
981
0
}
982
983
static void
984
0
rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG) {
985
0
  dns_dbnode_t *node = source->slab.node;
986
0
  dns_dbnode_t *cloned_node = NULL;
987
988
0
  dns__db_attachnode(node, &cloned_node DNS__DB_FLARG_PASS);
989
0
  INSIST(!ISC_LINK_LINKED(target, link));
990
0
  *target = *source;
991
0
  ISC_LINK_INIT(target, link);
992
993
0
  target->slab.iter_pos = NULL;
994
0
  target->slab.iter_count = 0;
995
0
}
996
997
static unsigned int
998
0
rdataset_count(dns_rdataset_t *rdataset) {
999
0
  unsigned char *raw = NULL;
1000
0
  unsigned int count;
1001
1002
0
  raw = rdataset->slab.raw;
1003
0
  count = get_uint16(raw);
1004
1005
0
  return count;
1006
0
}
1007
1008
static isc_result_t
1009
rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
1010
        dns_rdataset_t *nsec,
1011
0
        dns_rdataset_t *nsecsig DNS__DB_FLARG) {
1012
0
  dns_db_t *db = rdataset->slab.db;
1013
0
  dns_dbnode_t *node = rdataset->slab.node;
1014
0
  const dns_slabheader_proof_t *noqname = rdataset->slab.noqname;
1015
1016
  /*
1017
   * Normally, rdataset->slab.raw points to the data immediately
1018
   * following a dns_slabheader in memory. Here, though, it will
1019
   * point to a bare rdataslab, a pointer to which is stored in
1020
   * the dns_slabheader's `noqname` field.
1021
   *
1022
   * The 'keepcase' attribute is set to prevent setownercase and
1023
   * getownercase methods from affecting the case of NSEC/NSEC3
1024
   * owner names.
1025
   */
1026
0
  dns__db_attachnode(node, &(dns_dbnode_t *){ NULL } DNS__DB_FLARG_PASS);
1027
0
  *nsec = (dns_rdataset_t){
1028
0
    .methods = &dns_rdataslab_rdatasetmethods,
1029
0
    .rdclass = db->rdclass,
1030
0
    .type = noqname->type,
1031
0
    .ttl = rdataset->ttl,
1032
0
    .trust = rdataset->trust,
1033
0
    .slab.db = db,
1034
0
    .slab.node = node,
1035
0
    .slab.raw = noqname->neg,
1036
0
    .link = nsec->link,
1037
0
    .attributes = nsec->attributes,
1038
0
    .magic = nsec->magic,
1039
0
  };
1040
0
  nsec->attributes.keepcase = true;
1041
1042
0
  dns__db_attachnode(node, &(dns_dbnode_t *){ NULL } DNS__DB_FLARG_PASS);
1043
0
  *nsecsig = (dns_rdataset_t){
1044
0
    .methods = &dns_rdataslab_rdatasetmethods,
1045
0
    .rdclass = db->rdclass,
1046
0
    .type = dns_rdatatype_rrsig,
1047
0
    .covers = noqname->type,
1048
0
    .ttl = rdataset->ttl,
1049
0
    .trust = rdataset->trust,
1050
0
    .slab.db = db,
1051
0
    .slab.node = node,
1052
0
    .slab.raw = noqname->negsig,
1053
0
    .link = nsecsig->link,
1054
0
    .attributes = nsecsig->attributes,
1055
0
    .magic = nsecsig->magic,
1056
0
  };
1057
0
  nsecsig->attributes.keepcase = true;
1058
1059
0
  dns_name_clone(&noqname->name, name);
1060
1061
0
  return ISC_R_SUCCESS;
1062
0
}
1063
1064
static isc_result_t
1065
rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
1066
        dns_rdataset_t *nsec,
1067
0
        dns_rdataset_t *nsecsig DNS__DB_FLARG) {
1068
0
  dns_db_t *db = rdataset->slab.db;
1069
0
  dns_dbnode_t *node = rdataset->slab.node;
1070
0
  const dns_slabheader_proof_t *closest = rdataset->slab.closest;
1071
1072
  /*
1073
   * Normally, rdataset->slab.raw points to the data immediately
1074
   * following a dns_slabheader in memory. Here, though, it will
1075
   * point to a bare rdataslab, a pointer to which is stored in
1076
   * the dns_slabheader's `closest` field.
1077
   *
1078
   * The 'keepcase' attribute is set to prevent setownercase and
1079
   * getownercase methods from affecting the case of NSEC/NSEC3
1080
   * owner names.
1081
   */
1082
0
  dns__db_attachnode(node, &(dns_dbnode_t *){ NULL } DNS__DB_FLARG_PASS);
1083
0
  *nsec = (dns_rdataset_t){
1084
0
    .methods = &dns_rdataslab_rdatasetmethods,
1085
0
    .rdclass = db->rdclass,
1086
0
    .type = closest->type,
1087
0
    .ttl = rdataset->ttl,
1088
0
    .trust = rdataset->trust,
1089
0
    .slab.db = db,
1090
0
    .slab.node = node,
1091
0
    .slab.raw = closest->neg,
1092
0
    .link = nsec->link,
1093
0
    .attributes = nsec->attributes,
1094
0
    .magic = nsec->magic,
1095
0
  };
1096
0
  nsec->attributes.keepcase = true;
1097
1098
0
  dns__db_attachnode(node, &(dns_dbnode_t *){ NULL } DNS__DB_FLARG_PASS);
1099
0
  *nsecsig = (dns_rdataset_t){
1100
0
    .methods = &dns_rdataslab_rdatasetmethods,
1101
0
    .rdclass = db->rdclass,
1102
0
    .type = dns_rdatatype_rrsig,
1103
0
    .covers = closest->type,
1104
0
    .ttl = rdataset->ttl,
1105
0
    .trust = rdataset->trust,
1106
0
    .slab.db = db,
1107
0
    .slab.node = node,
1108
0
    .slab.raw = closest->negsig,
1109
0
    .link = nsecsig->link,
1110
0
    .attributes = nsecsig->attributes,
1111
0
    .magic = nsecsig->magic,
1112
0
  };
1113
0
  nsecsig->attributes.keepcase = true;
1114
1115
0
  dns_name_clone(&closest->name, name);
1116
1117
0
  return ISC_R_SUCCESS;
1118
0
}
1119
1120
static void
1121
0
rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
1122
0
  dns_slabheader_t *header = rdataset_getheader(rdataset);
1123
1124
0
  rdataset->trust = trust;
1125
0
  atomic_store(&header->trust, trust);
1126
0
}
1127
1128
static void
1129
0
rdataset_expire(dns_rdataset_t *rdataset DNS__DB_FLARG) {
1130
0
  dns_slabheader_t *header = rdataset_getheader(rdataset);
1131
1132
0
  dns_db_expiredata(header->node, header);
1133
0
}
1134
1135
static void
1136
0
rdataset_clearprefetch(dns_rdataset_t *rdataset) {
1137
0
  dns_slabheader_t *header = rdataset_getheader(rdataset);
1138
1139
0
  DNS_SLABHEADER_CLRATTR(header, DNS_SLABHEADERATTR_PREFETCH);
1140
0
}
1141
1142
static void
1143
0
rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name) {
1144
0
  dns_slabheader_t *header = rdataset_getheader(rdataset);
1145
0
  uint8_t mask = (1 << 7);
1146
0
  uint8_t bits = 0;
1147
1148
0
  if (!CASESET(header)) {
1149
0
    return;
1150
0
  }
1151
1152
0
  if (CASEFULLYLOWER(header)) {
1153
0
    isc_ascii_lowercopy(name->ndata, name->ndata, name->length);
1154
0
    return;
1155
0
  }
1156
1157
0
  uint8_t *nd = name->ndata;
1158
0
  for (size_t i = 0; i < name->length; i++) {
1159
0
    if (mask == (1 << 7)) {
1160
0
      bits = header->upper[i / 8];
1161
0
      mask = 1;
1162
0
    } else {
1163
0
      mask <<= 1;
1164
0
    }
1165
0
    nd[i] = (bits & mask) ? isc_ascii_toupper(nd[i])
1166
0
              : isc_ascii_tolower(nd[i]);
1167
0
  }
1168
0
}
1169
1170
static dns_slabheader_t *
1171
0
rdataset_getheader(const dns_rdataset_t *rdataset) {
1172
0
  dns_slabheader_t *header = (dns_slabheader_t *)rdataset->slab.raw;
1173
0
  return header - 1;
1174
0
}
1175
1176
dns_slabtop_t *
1177
0
dns_slabtop_new(isc_mem_t *mctx, dns_typepair_t typepair) {
1178
0
  dns_slabtop_t *top = isc_mem_get(mctx, sizeof(*top));
1179
0
  *top = (dns_slabtop_t){
1180
0
    .types_link = CDS_LIST_HEAD_INIT(top->types_link),
1181
0
    .headers = CDS_LIST_HEAD_INIT(top->headers),
1182
0
    .typepair = typepair,
1183
0
    .link = ISC_LINK_INITIALIZER,
1184
0
  };
1185
1186
0
  return top;
1187
0
}
1188
1189
void
1190
0
dns_slabtop_destroy(isc_mem_t *mctx, dns_slabtop_t **topp) {
1191
0
  REQUIRE(topp != NULL && *topp != NULL);
1192
0
  dns_slabtop_t *top = *topp;
1193
0
  *topp = NULL;
1194
  isc_mem_put(mctx, top, sizeof(*top));
1195
0
}