Coverage Report

Created: 2026-06-15 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/qpzone.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 <inttypes.h>
17
#include <stdalign.h>
18
#include <stdbool.h>
19
#include <sys/mman.h>
20
21
#include <isc/ascii.h>
22
#include <isc/async.h>
23
#include <isc/atomic.h>
24
#include <isc/file.h>
25
#include <isc/hashmap.h>
26
#include <isc/heap.h>
27
#include <isc/hex.h>
28
#include <isc/log.h>
29
#include <isc/mem.h>
30
#include <isc/mutex.h>
31
#include <isc/os.h>
32
#include <isc/random.h>
33
#include <isc/refcount.h>
34
#include <isc/result.h>
35
#include <isc/rwlock.h>
36
#include <isc/serial.h>
37
#include <isc/slist.h>
38
#include <isc/stdio.h>
39
#include <isc/string.h>
40
#include <isc/time.h>
41
#include <isc/urcu.h>
42
#include <isc/util.h>
43
44
#include <dns/callbacks.h>
45
#include <dns/db.h>
46
#include <dns/dbiterator.h>
47
#include <dns/diff.h>
48
#include <dns/dnssec.h>
49
#include <dns/fixedname.h>
50
#include <dns/masterdump.h>
51
#include <dns/name.h>
52
#include <dns/nsec.h>
53
#include <dns/nsec3.h>
54
#include <dns/qp.h>
55
#include <dns/rdata.h>
56
#include <dns/rdataset.h>
57
#include <dns/rdatasetiter.h>
58
#include <dns/rdatastruct.h>
59
#include <dns/rdatavec.h>
60
#include <dns/stats.h>
61
#include <dns/time.h>
62
#include <dns/types.h>
63
#include <dns/view.h>
64
#include <dns/zone.h>
65
66
#include "db_p.h"
67
#include "qpzone_p.h"
68
#include "rdatavec_p.h"
69
70
17.6k
#define QPDB_ATTR_LOADED  0x01
71
35.2k
#define QPDB_ATTR_LOADING 0x02
72
73
#define QPDBITER_ORIGIN_NODE(qpdb, iterator) \
74
  ((iterator)->node == (qpdb)->origin)
75
#define QPDBITER_NSEC_ORIGIN_NODE(qpdb, iterator) \
76
  ((iterator)->node == (qpdb)->nsec_origin)
77
#define QPDBITER_NSEC3_ORIGIN_NODE(qpdb, iterator) \
78
0
  ((iterator)->node == (qpdb)->nsec3_origin)
79
80
/*%
81
 * Note that "impmagic" is not the first four bytes of the struct, so
82
 * ISC_MAGIC_VALID cannot be used.
83
 */
84
17.6k
#define QPZONE_DB_MAGIC ISC_MAGIC('Q', 'Z', 'D', 'B')
85
#define VALID_QPZONE(qpdb) \
86
  ((qpdb) != NULL && (qpdb)->common.impmagic == QPZONE_DB_MAGIC)
87
88
typedef struct qpzonedb qpzonedb_t;
89
typedef struct qpznode qpznode_t;
90
91
typedef struct qpzone_bucket {
92
  /* Per-bucket lock. */
93
  isc_rwlock_t lock;
94
95
  /* Padding to prevent false sharing between locks. */
96
  uint8_t __padding[ISC_OS_CACHELINE_SIZE -
97
        (sizeof(isc_rwlock_t)) % ISC_OS_CACHELINE_SIZE];
98
} qpzone_bucket_t;
99
100
/*
101
 * Qpzone-specific update context that extends dns_updatectx_t, used in IXFR.
102
 */
103
typedef struct qpzone_updatectx {
104
  dns_updatectx_t base;
105
  dns_qp_t *qp;
106
  dns_qpread_t qpr;
107
} qpzone_updatectx_t;
108
109
static qpzone_bucket_t qpzone_buckets_g[1024];
110
111
typedef struct qpz_changed {
112
  qpznode_t *node;
113
  bool dirty;
114
  ISC_LINK(struct qpz_changed) link;
115
} qpz_changed_t;
116
117
typedef ISC_LIST(qpz_changed_t) qpz_changedlist_t;
118
119
typedef struct qpz_resigned {
120
  qpznode_t *node;
121
  dns_vecheader_t *header;
122
  ISC_LINK(struct qpz_resigned) link;
123
} qpz_resigned_t;
124
125
typedef ISC_LIST(qpz_resigned_t) qpz_resignedlist_t;
126
127
typedef struct qpz_version qpz_version_t;
128
struct qpz_version {
129
  /* Not locked */
130
  uint32_t serial;
131
  qpzonedb_t *qpdb;
132
  isc_refcount_t references;
133
  /* Locked by database lock. */
134
  bool writer;
135
  qpz_changedlist_t changed_list;
136
  qpz_resignedlist_t resigned_list;
137
  ISC_LINK(qpz_version_t) link;
138
  bool secure;
139
  bool havensec3;
140
  /* NSEC3 parameters */
141
  dns_hash_t hash;
142
  uint8_t flags;
143
  uint16_t iterations;
144
  uint8_t salt_length;
145
  unsigned char salt[DNS_NSEC3_SALTSIZE];
146
147
  /*
148
   * records and xfrsize are covered by rwlock.
149
   */
150
  isc_rwlock_t rwlock;
151
  uint64_t records;
152
  uint64_t xfrsize;
153
154
  struct cds_wfs_stack glue_stack;
155
};
156
157
typedef ISC_LIST(qpz_version_t) qpz_versionlist_t;
158
159
/* Resigning heap indirection to allow ref counting */
160
typedef struct qpz_heap {
161
  isc_mem_t *mctx;
162
  isc_refcount_t references;
163
  /* Locks the data in this struct */
164
  isc_mutex_t lock;
165
  isc_heap_t *heap;
166
  isc_hashmap_t *hashmap;
167
} qpz_heap_t;
168
169
typedef struct qpz_resign {
170
  qpznode_t *node;
171
  dns_vecheader_t *header;
172
  unsigned int heap_index;
173
} qpz_resign_t;
174
175
ISC_REFCOUNT_STATIC_DECL(qpz_heap);
176
177
struct qpznode {
178
  DBNODE_FIELDS;
179
  /*
180
   * 'erefs' counts external references held by a caller: for
181
   * example, it could be incremented by dns_db_findnode(),
182
   * and decremented by dns_db_detachnode().
183
   *
184
   * 'references' counts internal references to the node object,
185
   * including the one held by the QP trie so the node won't be
186
   * deleted while it's quiescently stored in the database - even
187
   * though 'erefs' may be zero because no external caller is
188
   * using it at the time.
189
   *
190
   * Generally when 'erefs' is incremented or decremented,
191
   * 'references' is too. When both go to zero (meaning callers
192
   * and the database have both released the object) the object
193
   * is freed.
194
   *
195
   * Whenever 'erefs' is incremented from zero, we also acquire a
196
   * node use reference (see 'qpzonedb->references' below), and
197
   * release it when 'erefs' goes back to zero. This prevents the
198
   * database from being shut down until every caller has released
199
   * all nodes.
200
   */
201
  isc_refcount_t references;
202
  isc_refcount_t erefs;
203
204
  _Atomic(dns_namespace_t) nspace;
205
  atomic_bool havensec;
206
  atomic_bool wild;
207
  atomic_bool delegating;
208
  atomic_bool dirty;
209
210
  ISC_SLIST(dns_vectop_t) next_type;
211
};
212
213
struct qpzonedb {
214
  /* Unlocked. */
215
  dns_db_t common;
216
  /* Locks the data in this struct */
217
  isc_rwlock_t lock;
218
219
  /*
220
   * NOTE: 'references' is NOT the global reference counter for
221
   * the database object handled by dns_db_attach() and _detach();
222
   * that one is 'common.references'.
223
   *
224
   * Instead, 'references' counts the number of nodes being used by
225
   * at least one external caller. (It's called 'references' to
226
   * leverage the ISC_REFCOUNT_STATIC macros, but 'nodes_in_use'
227
   * might be a clearer name.)
228
   *
229
   * One additional reference to this counter is held by the database
230
   * object itself. When 'common.references' goes to zero, that
231
   * reference is released. When in turn 'references' goes to zero,
232
   * the database is shut down and freed.
233
   */
234
235
  qpznode_t *origin;
236
  qpznode_t *nsec_origin;
237
  qpznode_t *nsec3_origin;
238
  isc_stats_t *gluecachestats;
239
  /* Locked by lock. */
240
  unsigned int attributes;
241
  uint32_t current_serial;
242
  uint32_t least_serial;
243
  uint32_t next_serial;
244
  uint32_t maxrrperset;  /* Maximum RRs per RRset */
245
  uint32_t maxtypepername; /* Maximum number of RR types per owner */
246
  qpz_version_t *current_version;
247
  qpz_version_t *future_version;
248
  qpz_versionlist_t open_versions;
249
  struct rcu_head rcu_head;
250
251
  qpz_heap_t *heap; /* Resigning heap */
252
253
  dns_qpmulti_t *tree; /* QP trie for data storage */
254
};
255
256
/*%
257
 * Search Context
258
 */
259
typedef struct {
260
  qpzonedb_t *qpdb;
261
  qpz_version_t *version;
262
  dns_qpread_t qpr;
263
  uint32_t serial;
264
  unsigned int options;
265
  dns_qpchain_t chain;
266
  dns_qpiter_t iter;
267
  bool copy_name;
268
  bool need_cleanup;
269
  bool wild;
270
  qpznode_t *zonecut;
271
  dns_vecheader_t *zonecut_header;
272
  dns_vecheader_t *zonecut_sigheader;
273
  dns_fixedname_t zonecut_name;
274
} qpz_search_t;
275
276
/*%
277
 * Load Context
278
 */
279
typedef struct {
280
  dns_db_t *db;
281
  dns_qp_t *tree;
282
} qpz_load_t;
283
284
static dns_dbmethods_t qpdb_zonemethods;
285
static dns_dbnode_methods_t qpznode_methods;
286
287
#if DNS_DB_NODETRACE
288
#define qpznode_ref(ptr)   qpznode__ref(ptr, __func__, __FILE__, __LINE__)
289
#define qpznode_unref(ptr) qpznode__unref(ptr, __func__, __FILE__, __LINE__)
290
#define qpznode_attach(ptr, ptrp) \
291
  qpznode__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
292
#define qpznode_detach(ptrp) qpznode__detach(ptrp, __func__, __FILE__, __LINE__)
293
ISC_REFCOUNT_STATIC_TRACE_DECL(qpznode);
294
#else
295
ISC_REFCOUNT_STATIC_DECL(qpznode);
296
#endif
297
298
/* Forward declarations for functions used in constructors */
299
static void
300
qpznode_acquire(qpznode_t *node DNS__DB_FLARG);
301
static bool
302
qpznode_release(qpznode_t *node DNS__DB_FLARG);
303
static void
304
qpznode_erefs_increment(qpznode_t *node DNS__DB_FLARG);
305
306
/*%
307
 * Constructor and destructor for qpz_changed_t
308
 */
309
static qpz_changed_t *
310
0
qpz_changed_new(isc_mem_t *mctx, qpznode_t *node DNS__DB_FLARG) {
311
0
  qpz_changed_t *changed = isc_mem_get(mctx, sizeof(qpz_changed_t));
312
0
  *changed = (qpz_changed_t){
313
0
    .node = node,
314
0
    .dirty = false,
315
0
    .link = ISC_LINK_INITIALIZER,
316
0
  };
317
0
  qpznode_acquire(node DNS__DB_FLARG_PASS);
318
0
  return changed;
319
0
}
320
321
/*%
322
 * Constructor and destructor for qpz_resigned_t
323
 */
324
static qpz_resigned_t *
325
0
qpz_resigned_new(isc_mem_t *mctx, qpznode_t *node, dns_vecheader_t *header) {
326
0
  qpz_resigned_t *resigned = isc_mem_get(mctx, sizeof(qpz_resigned_t));
327
0
  *resigned = (qpz_resigned_t){
328
0
    .node = node,
329
0
    .header = header,
330
0
    .link = ISC_LINK_INITIALIZER,
331
0
  };
332
0
  qpznode_ref(node);
333
0
  dns_vecheader_ref(header);
334
0
  return resigned;
335
0
}
336
337
static void
338
0
qpz_resigned_destroy(isc_mem_t *mctx, qpz_resigned_t **resignedp) {
339
0
  qpz_resigned_t *resigned = *resignedp;
340
0
  *resignedp = NULL;
341
0
  qpznode_unref(resigned->node);
342
0
  dns_vecheader_unref(resigned->header);
343
0
  isc_mem_put(mctx, resigned, sizeof(qpz_resigned_t));
344
0
}
345
346
/*%
347
 * Constructor and destructor for qpz_resign_t
348
 */
349
static qpz_resign_t *
350
0
qpz_resign_new(isc_mem_t *mctx, qpznode_t *node, dns_vecheader_t *header) {
351
0
  qpz_resign_t *elem = isc_mem_get(mctx, sizeof(qpz_resign_t));
352
0
  *elem = (qpz_resign_t){
353
0
    .node = node,
354
0
    .header = header,
355
0
    .heap_index = 0,
356
0
  };
357
0
  qpznode_ref(node);
358
0
  dns_vecheader_ref(header);
359
0
  return elem;
360
0
}
361
362
static void
363
0
qpz_resign_destroy(isc_mem_t *mctx, qpz_resign_t **elemp) {
364
0
  qpz_resign_t *elem = *elemp;
365
0
  *elemp = NULL;
366
0
  qpznode_unref(elem->node);
367
0
  dns_vecheader_unref(elem->header);
368
0
  isc_mem_put(mctx, elem, sizeof(qpz_resign_t));
369
0
}
370
371
/* QP trie methods */
372
static void
373
qp_attach(void *uctx, void *pval, uint32_t ival);
374
static void
375
qp_detach(void *uctx, void *pval, uint32_t ival);
376
static size_t
377
qp_makekey(dns_qpkey_t key, void *uctx, void *pval, uint32_t ival);
378
static void
379
qp_triename(void *uctx, char *buf, size_t size);
380
381
static dns_qpmethods_t qpmethods = {
382
  qp_attach,
383
  qp_detach,
384
  qp_makekey,
385
  qp_triename,
386
};
387
388
static void
389
rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG);
390
static isc_result_t
391
rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG);
392
static isc_result_t
393
rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG);
394
static void
395
rdatasetiter_current(dns_rdatasetiter_t *iterator,
396
         dns_rdataset_t *rdataset DNS__DB_FLARG);
397
398
static dns_rdatasetitermethods_t rdatasetiter_methods = {
399
  rdatasetiter_destroy, rdatasetiter_first, rdatasetiter_next,
400
  rdatasetiter_current
401
};
402
403
typedef struct qpdb_rdatasetiter {
404
  dns_rdatasetiter_t common;
405
  dns_vectop_t *currenttop;
406
  dns_vecheader_t *current;
407
} qpdb_rdatasetiter_t;
408
409
/*
410
 * Note that these iterators, unless created with either DNS_DB_NSEC3ONLY
411
 * or DNS_DB_NONSEC3, will transparently move between the last node of the
412
 * "regular" QP trie and the root node of the NSEC3 QP trie of the database
413
 * in question, as if the latter was a successor to the former in lexical
414
 * order.  The "current" field always holds the address of either "iter".
415
 */
416
static void
417
dbiterator_destroy(dns_dbiterator_t **iteratorp DNS__DB_FLARG);
418
static isc_result_t
419
dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG);
420
static isc_result_t
421
dbiterator_last(dns_dbiterator_t *iterator DNS__DB_FLARG);
422
static isc_result_t
423
dbiterator_seek(dns_dbiterator_t *iterator,
424
    const dns_name_t *name DNS__DB_FLARG);
425
static isc_result_t
426
dbiterator_seek3(dns_dbiterator_t *iterator,
427
     const dns_name_t *name DNS__DB_FLARG);
428
static isc_result_t
429
dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG);
430
static isc_result_t
431
dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG);
432
static isc_result_t
433
dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
434
       dns_name_t *name DNS__DB_FLARG);
435
static isc_result_t
436
dbiterator_pause(dns_dbiterator_t *iterator);
437
static isc_result_t
438
dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name);
439
440
static dns_dbiteratormethods_t dbiterator_methods = {
441
  dbiterator_destroy, dbiterator_first, dbiterator_last,
442
  dbiterator_seek,    dbiterator_seek3, dbiterator_prev,
443
  dbiterator_next,    dbiterator_current, dbiterator_pause,
444
  dbiterator_origin
445
};
446
447
typedef struct qpdb_dbiterator {
448
  dns_dbiterator_t common;
449
  isc_result_t result;
450
  dns_qpsnap_t *snap; /* tree snapshot */
451
  dns_qpiter_t iter;  /* tree iterator */
452
  qpznode_t *node;
453
  enum { full, nonsec3, nsec3only } nsec3mode;
454
} qpdb_dbiterator_t;
455
456
/*
457
 * Locking
458
 *
459
 * If a routine is going to lock more than one lock in this module, then
460
 * the locking must be done in the following order:
461
 *
462
 *      Node Lock       (Only one from the set may be locked at one time by
463
 *                       any caller)
464
 *
465
 *      Database Lock
466
 *
467
 * Failure to follow this hierarchy can result in deadlock.
468
 */
469
470
void
471
22
dns__qpzone_initialize(void) {
472
22.5k
  for (size_t idx = 0; idx < ARRAY_SIZE(qpzone_buckets_g); ++idx) {
473
22.5k
    NODE_INITLOCK(&qpzone_buckets_g[idx].lock);
474
22.5k
  }
475
22
}
476
477
void
478
0
dns__qpzone_shutdown(void) {
479
0
  for (size_t idx = 0; idx < ARRAY_SIZE(qpzone_buckets_g); ++idx) {
480
0
    NODE_DESTROYLOCK(&qpzone_buckets_g[idx].lock);
481
0
  }
482
0
}
483
484
static isc_rwlock_t *
485
10.5M
qpzone_get_lock(qpznode_t *node) {
486
10.5M
  return &qpzone_buckets_g[node->locknum].lock;
487
10.5M
}
488
489
static uint16_t
490
10.6M
qpzone_get_locknum(void) {
491
10.6M
  return isc_random_uniform(ARRAY_SIZE(qpzone_buckets_g));
492
10.6M
}
493
494
static void
495
0
free_glue(isc_mem_t *mctx, dns_glue_t *glue) {
496
0
  while (glue != NULL) {
497
0
    dns_glue_t *next = glue->next;
498
499
0
    if (glue->header_a != NULL) {
500
0
      dns_vecheader_unref(glue->header_a);
501
0
    }
502
0
    if (glue->sigheader_a != NULL) {
503
0
      dns_vecheader_unref(glue->sigheader_a);
504
0
    }
505
0
    if (glue->header_aaaa != NULL) {
506
0
      dns_vecheader_unref(glue->header_aaaa);
507
0
    }
508
0
    if (glue->sigheader_aaaa != NULL) {
509
0
      dns_vecheader_unref(glue->sigheader_aaaa);
510
0
    }
511
512
0
    dns_name_free(&glue->name, mctx);
513
514
0
    isc_mem_put(mctx, glue, sizeof(*glue));
515
516
0
    glue = next;
517
0
  }
518
0
}
519
520
static void
521
0
destroy_gluelist(dns_gluelist_t **gluelistp) {
522
0
  REQUIRE(gluelistp != NULL);
523
0
  if (*gluelistp == NULL) {
524
0
    return;
525
0
  }
526
527
0
  dns_gluelist_t *gluelist = *gluelistp;
528
529
0
  free_glue(gluelist->mctx, gluelist->glue);
530
531
0
  isc_mem_putanddetach(&gluelist->mctx, gluelist, sizeof(*gluelist));
532
0
}
533
534
static void
535
0
free_gluelist_rcu(struct rcu_head *rcu_head) {
536
0
  dns_gluelist_t *gluelist = caa_container_of(rcu_head, dns_gluelist_t,
537
0
                rcu_head);
538
0
  destroy_gluelist(&gluelist);
539
0
}
540
541
static void
542
17.6k
cleanup_gluelists(struct cds_wfs_stack *glue_stack) {
543
17.6k
  struct cds_wfs_head *head = __cds_wfs_pop_all(glue_stack);
544
17.6k
  struct cds_wfs_node *node = NULL, *next = NULL;
545
546
17.6k
  rcu_read_lock();
547
17.6k
  cds_wfs_for_each_blocking_safe(head, node, next) {
548
0
    dns_gluelist_t *gluelist =
549
0
      caa_container_of(node, dns_gluelist_t, wfs_node);
550
0
    dns_vecheader_t *header = rcu_xchg_pointer(&gluelist->header,
551
0
                 NULL);
552
0
    (void)rcu_cmpxchg_pointer(&header->gluelist, gluelist, NULL);
553
554
0
    call_rcu(&gluelist->rcu_head, free_gluelist_rcu);
555
0
  }
556
17.6k
  rcu_read_unlock();
557
17.6k
}
558
559
static void
560
17.6k
free_db_rcu(struct rcu_head *rcu_head) {
561
17.6k
  qpzonedb_t *qpdb = caa_container_of(rcu_head, qpzonedb_t, rcu_head);
562
563
17.6k
  if (dns_name_dynamic(&qpdb->common.origin)) {
564
17.6k
    dns_name_free(&qpdb->common.origin, qpdb->common.mctx);
565
17.6k
  }
566
567
17.6k
  qpz_heap_detach(&qpdb->heap);
568
569
17.6k
  if (qpdb->gluecachestats != NULL) {
570
0
    isc_stats_detach(&qpdb->gluecachestats);
571
0
  }
572
573
17.6k
  isc_rwlock_destroy(&qpdb->lock);
574
17.6k
  isc_refcount_destroy(&qpdb->common.references);
575
576
17.6k
  qpdb->common.magic = 0;
577
17.6k
  qpdb->common.impmagic = 0;
578
579
17.6k
  if (qpdb->common.update_listeners != NULL) {
580
17.6k
    INSIST(!cds_lfht_destroy(qpdb->common.update_listeners, NULL));
581
17.6k
  }
582
583
17.6k
  isc_mem_putanddetach(&qpdb->common.mctx, qpdb, sizeof(*qpdb));
584
17.6k
}
585
586
static void
587
17.6k
qpzone_destroy(qpzonedb_t *qpdb) {
588
17.6k
  REQUIRE(qpdb->future_version == NULL);
589
590
17.6k
  isc_refcount_decrementz(&qpdb->current_version->references);
591
592
17.6k
  isc_refcount_destroy(&qpdb->current_version->references);
593
17.6k
  ISC_LIST_UNLINK(qpdb->open_versions, qpdb->current_version, link);
594
17.6k
  cds_wfs_destroy(&qpdb->current_version->glue_stack);
595
17.6k
  isc_rwlock_destroy(&qpdb->current_version->rwlock);
596
17.6k
  isc_mem_put(qpdb->common.mctx, qpdb->current_version,
597
17.6k
        sizeof(*qpdb->current_version));
598
599
17.6k
  dns_qpmulti_destroy(&qpdb->tree);
600
601
17.6k
  char buf[DNS_NAME_FORMATSIZE];
602
17.6k
  if (dns_name_dynamic(&qpdb->common.origin)) {
603
17.6k
    dns_name_format(&qpdb->common.origin, buf, sizeof(buf));
604
17.6k
  } else {
605
0
    strlcpy(buf, "<UNKNOWN>", sizeof(buf));
606
0
  }
607
17.6k
  isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DB,
608
17.6k
          ISC_LOG_DEBUG(1), "called %s(%s)", __func__, buf);
609
610
17.6k
  call_rcu(&qpdb->rcu_head, free_db_rcu);
611
17.6k
}
612
613
static void
614
17.6k
qpdb_destroy(dns_db_t *arg) {
615
17.6k
  qpzonedb_t *qpdb = (qpzonedb_t *)arg;
616
617
17.6k
  if (qpdb->origin != NULL) {
618
17.6k
    qpznode_detach(&qpdb->origin);
619
17.6k
  }
620
17.6k
  if (qpdb->nsec_origin != NULL) {
621
17.6k
    qpznode_detach(&qpdb->nsec_origin);
622
17.6k
  }
623
17.6k
  if (qpdb->nsec3_origin != NULL) {
624
17.6k
    qpznode_detach(&qpdb->nsec3_origin);
625
17.6k
  }
626
627
  /*
628
   * The current version's glue table needs to be freed early
629
   * so the nodes are dereferenced before we check the active
630
   * node count below.
631
   */
632
17.6k
  if (qpdb->current_version != NULL) {
633
17.6k
    cleanup_gluelists(&qpdb->current_version->glue_stack);
634
17.6k
  }
635
636
17.6k
  qpzone_destroy(qpdb);
637
17.6k
}
638
639
/*%
640
 * Compare resign times with SOA priority logic.
641
 * Returns true if lhs should be resigned sooner than rhs.
642
 * When resign times are equal, non-SOA RRsets sort before the SOA RRset.
643
 */
644
static bool
645
resign_sooner_values(int64_t lhs_resign, dns_typepair_t lhs_typepair,
646
0
         int64_t rhs_resign, dns_typepair_t rhs_typepair) {
647
0
  bool lhs_is_soa = lhs_typepair == DNS_SIGTYPEPAIR(dns_rdatatype_soa);
648
0
  bool rhs_is_soa = rhs_typepair == DNS_SIGTYPEPAIR(dns_rdatatype_soa);
649
650
0
  return lhs_resign < rhs_resign ||
651
0
         (lhs_resign == rhs_resign && lhs_is_soa < rhs_is_soa);
652
0
}
653
654
/*%
655
 * Return which RRset should be resigned sooner.  If the RRsets have the
656
 * same signing time, prefer the other RRset over the SOA RRset.
657
 */
658
static bool
659
0
resign_sooner(void *v1, void *v2) {
660
0
  qpz_resign_t *elem1 = v1;
661
0
  qpz_resign_t *elem2 = v2;
662
663
0
  return resign_sooner_values(
664
0
    elem1->header->resign, elem1->header->typepair,
665
0
    elem2->header->resign, elem2->header->typepair);
666
0
}
667
668
/*%
669
 * This function sets the heap index into the qpz_resign_t.
670
 */
671
static void
672
0
set_index(void *what, unsigned int idx) {
673
0
  qpz_resign_t *elem = what;
674
675
0
  elem->heap_index = idx;
676
0
}
677
678
/*%
679
 * Hashmap matching function for qpz_resign_t entries.
680
 * Matches based on header and node pointers.
681
 */
682
static bool
683
0
resign_match(void *elem_ptr, const void *key) {
684
0
  qpz_resign_t *elem = elem_ptr;
685
0
  const qpz_resign_t *search_elem = key;
686
687
0
  return elem->header == search_elem->header &&
688
0
         elem->node == search_elem->node;
689
0
}
690
691
/*%
692
 * Generate hash value for a heap element based on header pointer.
693
 */
694
static uint32_t
695
170k
resign_hash(dns_vecheader_t *header) {
696
170k
  uintptr_t headerptr = (uintptr_t)header;
697
170k
  return isc_hash32(&headerptr, sizeof(headerptr), true);
698
170k
}
699
700
/*%
701
 * Find an element in the heap/hashmap by header and node.
702
 * Returns ISC_R_SUCCESS if found, ISC_R_NOTFOUND if not found.
703
 * If found, *found_elem will point to the element.
704
 * Assumes heap lock is already held.
705
 */
706
static isc_result_t
707
resign_lookup(qpz_heap_t *heap, dns_vecheader_t *header, qpznode_t *node,
708
0
        qpz_resign_t **found_elem) {
709
0
  qpz_resign_t search_elem = {
710
0
    .header = header,
711
0
    .node = node,
712
0
  };
713
0
  uint32_t hashval = resign_hash(header);
714
715
0
  return isc_hashmap_find(heap->hashmap, hashval, resign_match,
716
0
        &search_elem, (void **)found_elem);
717
0
}
718
719
/*%
720
 * Add an element to the heap/hashmap.
721
 * Assumes heap lock is already held.
722
 */
723
static void
724
0
resign_register(qpz_heap_t *heap, qpznode_t *node, dns_vecheader_t *header) {
725
0
  qpz_resign_t *elem = qpz_resign_new(heap->mctx, node, header);
726
0
  uint32_t hashval = resign_hash(header);
727
728
  /* Verify invariant: element should not already be in hashmap */
729
0
  isc_result_t result = isc_hashmap_add(heap->hashmap, hashval,
730
0
                resign_match, elem, elem, NULL);
731
0
  INSIST(result == ISC_R_SUCCESS);
732
733
0
  isc_heap_insert(heap->heap, elem);
734
0
}
735
736
/*%
737
 * Remove an element from the heap/hashmap.
738
 * Assumes heap lock is already held.
739
 */
740
static void
741
170k
resign_unregister(qpz_heap_t *heap, qpznode_t *node, dns_vecheader_t *header) {
742
170k
  if (header == NULL) {
743
0
    return;
744
0
  }
745
746
170k
  qpz_resign_t *found_elem = NULL;
747
170k
  uint32_t hashval = resign_hash(header);
748
170k
  qpz_resign_t search_elem = {
749
170k
    .header = header,
750
170k
    .node = node,
751
170k
  };
752
170k
  isc_result_t result = isc_hashmap_find(heap->hashmap, hashval,
753
170k
                 resign_match, &search_elem,
754
170k
                 (void **)&found_elem);
755
756
170k
  if (result == ISC_R_SUCCESS) {
757
0
    isc_heap_delete(heap->heap, found_elem->heap_index);
758
0
    isc_hashmap_delete(heap->hashmap, hashval, resign_match,
759
0
           found_elem);
760
0
    qpz_resign_destroy(heap->mctx, &found_elem);
761
0
  }
762
170k
}
763
764
static qpz_heap_t *
765
17.6k
new_qpz_heap(isc_mem_t *mctx) {
766
17.6k
  qpz_heap_t *new_heap = isc_mem_get(mctx, sizeof(*new_heap));
767
17.6k
  *new_heap = (qpz_heap_t){
768
17.6k
    .references = ISC_REFCOUNT_INITIALIZER(1),
769
17.6k
  };
770
771
17.6k
  isc_mutex_init(&new_heap->lock);
772
17.6k
  isc_heap_create(mctx, resign_sooner, set_index, 0, &new_heap->heap);
773
17.6k
  isc_mem_attach(mctx, &new_heap->mctx);
774
775
17.6k
  isc_hashmap_create(mctx, 1u, &new_heap->hashmap);
776
777
17.6k
  return new_heap;
778
17.6k
}
779
780
static void
781
resign_rollback(qpzonedb_t *qpdb, qpznode_t *node, qpz_version_t *version,
782
0
    dns_vecheader_t *header DNS__DB_FLARG) {
783
0
  if (header == NULL) {
784
0
    return;
785
0
  }
786
787
0
  qpz_resigned_t *resigned = qpz_resigned_new(((dns_db_t *)qpdb)->mctx,
788
0
                node, header);
789
790
0
  RWLOCK(&qpdb->lock, isc_rwlocktype_write);
791
0
  ISC_LIST_APPEND(version->resigned_list, resigned, link);
792
0
  RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
793
0
}
794
795
static void
796
17.6k
qpz_heap_destroy(qpz_heap_t *qpheap) {
797
  /* Clean up hashmap entries */
798
17.6k
  isc_hashmap_iter_t *iter = NULL;
799
17.6k
  isc_hashmap_iter_create(qpheap->hashmap, &iter);
800
17.6k
  for (isc_result_t result = isc_hashmap_iter_first(iter);
801
17.6k
       result == ISC_R_SUCCESS; result = isc_hashmap_iter_next(iter))
802
0
  {
803
0
    qpz_resign_t *elem = NULL;
804
0
    isc_hashmap_iter_current(iter, (void **)&elem);
805
    /* Release the node reference and deallocate */
806
0
    qpz_resign_destroy(qpheap->mctx, &elem);
807
0
  }
808
17.6k
  isc_hashmap_iter_destroy(&iter);
809
17.6k
  isc_hashmap_destroy(&qpheap->hashmap);
810
811
17.6k
  isc_mutex_destroy(&qpheap->lock);
812
17.6k
  isc_heap_destroy(&qpheap->heap);
813
17.6k
  isc_mem_putanddetach(&qpheap->mctx, qpheap, sizeof(*qpheap));
814
17.6k
}
815
816
static qpznode_t *
817
10.6M
new_qpznode(qpzonedb_t *qpdb, const dns_name_t *name, dns_namespace_t nspace) {
818
10.6M
  qpznode_t *newdata = isc_mem_get(qpdb->common.mctx, sizeof(*newdata));
819
10.6M
  *newdata = (qpznode_t){
820
10.6M
    .next_type = ISC_SLIST_INITIALIZER,
821
10.6M
    .methods = &qpznode_methods,
822
10.6M
    .name = DNS_NAME_INITEMPTY,
823
10.6M
    .nspace = nspace,
824
10.6M
    .references = ISC_REFCOUNT_INITIALIZER(1),
825
10.6M
    .locknum = qpzone_get_locknum(),
826
10.6M
  };
827
828
10.6M
  isc_mem_attach(qpdb->common.mctx, &newdata->mctx);
829
10.6M
  dns_name_dup(name, qpdb->common.mctx, &newdata->name);
830
831
#if DNS_DB_NODETRACE
832
  fprintf(stderr, "new_qpznode:%s:%s:%d:%p->references = 1\n", __func__,
833
    __FILE__, __LINE__ + 1, name);
834
#endif
835
10.6M
  return newdata;
836
10.6M
}
837
838
static qpz_version_t *
839
allocate_version(isc_mem_t *mctx, uint32_t serial, unsigned int references,
840
17.6k
     bool writer) {
841
17.6k
  qpz_version_t *version = isc_mem_get(mctx, sizeof(*version));
842
17.6k
  *version = (qpz_version_t){
843
17.6k
    .serial = serial,
844
17.6k
    .writer = writer,
845
17.6k
    .changed_list = ISC_LIST_INITIALIZER,
846
17.6k
    .resigned_list = ISC_LIST_INITIALIZER,
847
17.6k
    .link = ISC_LINK_INITIALIZER,
848
17.6k
    .references = ISC_REFCOUNT_INITIALIZER(references),
849
17.6k
  };
850
851
17.6k
  cds_wfs_init(&version->glue_stack);
852
17.6k
  isc_rwlock_init(&version->rwlock);
853
854
17.6k
  return version;
855
17.6k
}
856
857
isc_result_t
858
dns__qpzone_create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type,
859
       dns_rdataclass_t rdclass, unsigned int argc ISC_ATTR_UNUSED,
860
       char **argv ISC_ATTR_UNUSED, void *driverarg ISC_ATTR_UNUSED,
861
17.6k
       dns_db_t **dbp) {
862
17.6k
  qpzonedb_t *qpdb = NULL;
863
17.6k
  isc_result_t result;
864
17.6k
  dns_qp_t *qp = NULL;
865
866
17.6k
  qpdb = isc_mem_get(mctx, sizeof(*qpdb));
867
17.6k
  *qpdb = (qpzonedb_t){
868
17.6k
    .common.origin = DNS_NAME_INITEMPTY,
869
17.6k
    .common.rdclass = rdclass,
870
17.6k
    .common.references = ISC_REFCOUNT_INITIALIZER(1),
871
17.6k
    .current_serial = 1,
872
17.6k
    .least_serial = 1,
873
17.6k
    .next_serial = 2,
874
17.6k
    .open_versions = ISC_LIST_INITIALIZER,
875
17.6k
  };
876
877
17.6k
  qpdb->common.methods = &qpdb_zonemethods;
878
17.6k
  if (type == dns_dbtype_stub) {
879
0
    qpdb->common.attributes |= DNS_DBATTR_STUB;
880
0
  }
881
882
17.6k
  isc_rwlock_init(&qpdb->lock);
883
884
17.6k
  qpdb->common.update_listeners = cds_lfht_new(16, 16, 0, 0, NULL);
885
886
17.6k
  qpdb->heap = new_qpz_heap(mctx);
887
888
  /*
889
   * Attach to the mctx.  The database will persist so long as there
890
   * are references to it, and attaching to the mctx ensures that our
891
   * mctx won't disappear out from under us.
892
   */
893
17.6k
  isc_mem_attach(mctx, &qpdb->common.mctx);
894
895
  /*
896
   * Make a copy of the origin name.
897
   */
898
17.6k
  dns_name_dup(origin, mctx, &qpdb->common.origin);
899
900
17.6k
  dns_qpmulti_create(mctx, &qpmethods, qpdb, &qpdb->tree);
901
902
  /*
903
   * Version initialization.
904
   */
905
17.6k
  qpdb->current_version = allocate_version(mctx, 1, 1, false);
906
17.6k
  qpdb->current_version->qpdb = qpdb;
907
908
17.6k
  dns_qpmulti_write(qpdb->tree, &qp);
909
910
  /*
911
   * In order to set the node callback bit correctly in zone databases,
912
   * we need to know if the node has the origin name of the zone.
913
   * In loading_addrdataset() we could simply compare the new name
914
   * to the origin name, but this is expensive.  Also, we don't know the
915
   * node name in addrdataset(), so we need another way of knowing the
916
   * zone's top.
917
   *
918
   * We now explicitly create a node for the zone's origin, and then
919
   * we simply remember the node data's address.
920
   */
921
17.6k
  qpdb->origin = new_qpznode(qpdb, &qpdb->common.origin,
922
17.6k
           DNS_DBNAMESPACE_NORMAL);
923
924
17.6k
  result = dns_qp_insert(qp, qpdb->origin, 0);
925
17.6k
  INSIST(result == ISC_R_SUCCESS);
926
927
  /*
928
   * Add an apex node to the NSEC tree so that we can quickly skip over
929
   * the NSEC nodes while iterating over the full tree.
930
   */
931
17.6k
  qpdb->nsec_origin = new_qpznode(qpdb, &qpdb->common.origin,
932
17.6k
          DNS_DBNAMESPACE_NSEC);
933
17.6k
  result = dns_qp_insert(qp, qpdb->nsec_origin, 0);
934
17.6k
  INSIST(result == ISC_R_SUCCESS);
935
936
  /*
937
   * Add an apex node to the NSEC3 tree so that NSEC3 searches
938
   * return partial matches when there is only a single NSEC3
939
   * record in the tree.
940
   */
941
17.6k
  qpdb->nsec3_origin = new_qpznode(qpdb, &qpdb->common.origin,
942
17.6k
           DNS_DBNAMESPACE_NSEC3);
943
17.6k
  result = dns_qp_insert(qp, qpdb->nsec3_origin, 0);
944
17.6k
  INSIST(result == ISC_R_SUCCESS);
945
946
17.6k
  dns_qpmulti_commit(qpdb->tree, &qp);
947
948
  /*
949
   * Keep the current version in the open list so that list operation
950
   * won't happen in normal lookup operations.
951
   */
952
17.6k
  ISC_LIST_PREPEND(qpdb->open_versions, qpdb->current_version, link);
953
954
17.6k
  qpdb->common.magic = DNS_DB_MAGIC;
955
17.6k
  qpdb->common.impmagic = QPZONE_DB_MAGIC;
956
957
17.6k
  *dbp = (dns_db_t *)qpdb;
958
959
17.6k
  return ISC_R_SUCCESS;
960
17.6k
}
961
962
/*
963
 * If incrementing erefs from zero, we also increment the node use counter
964
 * in the qpzonedb object.
965
 *
966
 * This function is called from qpznode_acquire(), so that internal
967
 * and external references are acquired at the same time, and from
968
 * qpznode_release() when we only need to increase the internal references.
969
 */
970
static void
971
140
qpznode_erefs_increment(qpznode_t *node DNS__DB_FLARG) {
972
140
  uint_fast32_t refs = isc_refcount_increment0(&node->erefs);
973
#if DNS_DB_NODETRACE
974
  fprintf(stderr, "incr:node:%s:%s:%u:%p->erefs = %" PRIuFAST32 "\n",
975
    func, file, line, node, refs + 1);
976
#endif
977
978
140
  if (refs > 0) {
979
0
    return;
980
0
  }
981
140
}
982
983
static void
984
140
qpznode_acquire(qpznode_t *node DNS__DB_FLARG) {
985
140
  qpznode_ref(node);
986
140
  qpznode_erefs_increment(node DNS__DB_FLARG_PASS);
987
140
}
988
989
static dns_vecheader_t *
990
0
first_header(dns_vectop_t *top) {
991
0
  return ISC_SLIST_HEAD(top->headers);
992
0
}
993
994
static dns_vecheader_t *
995
9.12M
first_existing_header(dns_vectop_t *top, uint32_t serial) {
996
9.12M
  ISC_SLIST_FOREACH(header, top->headers, next_header) {
997
9.12M
    if (header->serial <= serial && !IGNORE(header)) {
998
9.12M
      if (EXISTS(header)) {
999
9.12M
        return header;
1000
9.12M
      }
1001
0
      break;
1002
9.12M
    }
1003
9.12M
  }
1004
0
  return NULL;
1005
9.12M
}
1006
1007
static void
1008
0
clean_multiple_headers(qpz_heap_t *heap, qpznode_t *node, dns_vectop_t *top) {
1009
0
  uint32_t parent_serial = UINT32_MAX;
1010
1011
0
  REQUIRE(top != NULL);
1012
1013
0
  ISC_SLIST_FOREACH_PTR(p, &ISC_SLIST_HEAD(top->headers)) {
1014
0
    dns_vecheader_t *header = *p;
1015
0
    INSIST(header->serial <= parent_serial);
1016
1017
0
    if (header->serial == parent_serial || IGNORE(header)) {
1018
0
      ISC_SLIST_PTR_REMOVE(p, header, next_header);
1019
0
      LOCK(&heap->lock);
1020
0
      resign_unregister(heap, node, header);
1021
0
      UNLOCK(&heap->lock);
1022
0
      dns_vecheader_unref(header);
1023
0
    } else {
1024
0
      parent_serial = header->serial;
1025
0
      ISC_SLIST_PTR_ADVANCE(p, next_header);
1026
0
    }
1027
0
  }
1028
0
}
1029
1030
static bool
1031
clean_multiple_versions(qpz_heap_t *heap, qpznode_t *node, dns_vectop_t *top,
1032
0
      uint32_t least_serial) {
1033
0
  REQUIRE(top != NULL);
1034
1035
0
  if (ISC_SLIST_EMPTY(top->headers)) {
1036
0
    return false;
1037
0
  }
1038
1039
0
  bool multiple = false;
1040
0
  dns_vecheader_t **pointer_to_second_header =
1041
0
    &ISC_SLIST_NEXT(ISC_SLIST_HEAD(top->headers), next_header);
1042
0
  ISC_SLIST_FOREACH_PTR(p, pointer_to_second_header) {
1043
0
    dns_vecheader_t *header = *p;
1044
0
    if (header->serial < least_serial) {
1045
0
      ISC_SLIST_PTR_REMOVE(p, header, next_header);
1046
0
      LOCK(&heap->lock);
1047
0
      resign_unregister(heap, node, header);
1048
0
      UNLOCK(&heap->lock);
1049
0
      dns_vecheader_unref(header);
1050
0
    } else {
1051
0
      multiple = true;
1052
0
      ISC_SLIST_PTR_ADVANCE(p, next_header);
1053
0
    }
1054
0
  }
1055
0
  return multiple;
1056
0
}
1057
1058
static void
1059
0
clean_zone_node(qpz_heap_t *heap, qpznode_t *node, uint32_t least_serial) {
1060
0
  bool still_dirty = false;
1061
1062
  /*
1063
   * Caller must be holding the node lock.
1064
   */
1065
0
  REQUIRE(node != NULL);
1066
0
  REQUIRE(least_serial != 0);
1067
1068
  /*
1069
   * First pass: clean all header lists
1070
   */
1071
0
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
1072
    /*
1073
     * First, we clean up any instances of multiple rdatasets
1074
     * with the same serial number, or that have the IGNORE
1075
     * attribute.
1076
     */
1077
0
    clean_multiple_headers(heap, node, top);
1078
1079
0
    if (first_header(top) != NULL) {
1080
      /*
1081
       * Try to find the first down node less than the least
1082
       * serial, and if there are such rdatasets, delete it
1083
       * and any older versions.
1084
       *
1085
       * Note: The serial number of the top header might be
1086
       * less than least_serial too, but we cannot delete it
1087
       * because it is the most recent version.
1088
       */
1089
0
      still_dirty = clean_multiple_versions(heap, node, top,
1090
0
                    least_serial);
1091
0
    }
1092
0
  }
1093
1094
  /*
1095
   * Second pass: remove all empty vectops
1096
   */
1097
0
  ISC_SLIST_FOREACH_PTR(iter, &ISC_SLIST_HEAD(node->next_type)) {
1098
0
    dns_vectop_t *next = *iter;
1099
0
    if (ISC_SLIST_EMPTY(next->headers)) {
1100
0
      ISC_SLIST_PTR_REMOVE(iter, next, next_type);
1101
0
      dns_vectop_destroy(node->mctx, &next);
1102
0
    } else {
1103
0
      ISC_SLIST_PTR_ADVANCE(iter, next_type);
1104
0
    }
1105
0
  }
1106
1107
0
  if (!still_dirty) {
1108
0
    node->dirty = false;
1109
0
  }
1110
0
}
1111
1112
/*
1113
 * Decrement the external references to a node. If the counter
1114
 * goes to zero, decrement the node use counter in the qpzonedb object
1115
 * as well, and return true. Otherwise return false.
1116
 */
1117
static bool
1118
140
qpznode_erefs_decrement(qpznode_t *node DNS__DB_FLARG) {
1119
140
  uint_fast32_t refs = isc_refcount_decrement(&node->erefs);
1120
1121
#if DNS_DB_NODETRACE
1122
  fprintf(stderr, "decr:node:%s:%s:%u:%p->erefs = %" PRIuFAST32 "\n",
1123
    func, file, line, node, refs - 1);
1124
#endif
1125
140
  if (refs > 1) {
1126
0
    return false;
1127
0
  }
1128
1129
140
  return true;
1130
140
}
1131
1132
/*
1133
 * Caller must be holding the node lock; either the read or write lock.
1134
 * Note that the lock must be held even when node references are
1135
 * atomically modified; in that case the decrement operation itself does not
1136
 * have to be protected, but we must avoid a race condition where multiple
1137
 * threads are decreasing the reference to zero simultaneously and at least
1138
 * one of them is going to free the node.
1139
 *
1140
 * This calls dec_erefs() to decrement the external node reference counter,
1141
 * (and possibly the node use counter), cleans up and deletes the node
1142
 * if necessary, then decrements the internal reference counter as well.
1143
 */
1144
static bool
1145
140
qpznode_release(qpznode_t *node DNS__DB_FLARG) {
1146
140
  bool has_erefs = false;
1147
1148
140
  if (!qpznode_erefs_decrement(node DNS__DB_FLARG_PASS)) {
1149
0
    has_erefs = true;
1150
0
    goto unref;
1151
0
  }
1152
1153
  /* Handle easy and typical case first. */
1154
140
  if (!node->dirty && !ISC_SLIST_EMPTY(node->next_type)) {
1155
140
    has_erefs = true;
1156
140
    goto unref;
1157
140
  }
1158
1159
140
unref:
1160
140
  qpznode_unref(node);
1161
140
  return has_erefs;
1162
140
}
1163
1164
static void
1165
bindrdataset(qpzonedb_t *qpdb, dns_vecheader_t *header,
1166
9.18M
       dns_rdataset_t *rdataset DNS__DB_FLARG) {
1167
9.18M
  if (rdataset == NULL) {
1168
9.18M
    return;
1169
9.18M
  }
1170
1171
193
  INSIST(rdataset->methods == NULL); /* We must be disassociated. */
1172
1173
193
  rdataset->methods = &dns_rdatavec_rdatasetmethods;
1174
193
  rdataset->rdclass = qpdb->common.rdclass;
1175
193
  rdataset->type = DNS_TYPEPAIR_TYPE(header->typepair);
1176
193
  rdataset->covers = DNS_TYPEPAIR_COVERS(header->typepair);
1177
193
  rdataset->ttl = header->ttl;
1178
193
  rdataset->trust = atomic_load(&header->trust);
1179
1180
193
  if (OPTOUT(header)) {
1181
0
    rdataset->attributes.optout = true;
1182
0
  }
1183
1184
193
  rdataset->vec.header = header;
1185
193
  dns_vecheader_ref(header);
1186
193
  rdataset->vec.iter.iter_pos = NULL;
1187
193
  rdataset->vec.iter.iter_count = 0;
1188
1189
  /*
1190
   * Add noqname proof.
1191
   */
1192
1193
  /*
1194
   * Copy out re-signing information.
1195
   */
1196
193
  if (RESIGN(header)) {
1197
0
    rdataset->attributes.resign = true;
1198
0
    rdataset->resign = header->resign;
1199
193
  } else {
1200
193
    rdataset->resign = 0;
1201
193
  }
1202
193
}
1203
1204
static void
1205
25
setnsec3parameters(dns_db_t *db, qpz_version_t *version) {
1206
25
  qpznode_t *node = NULL;
1207
25
  dns_rdata_nsec3param_t nsec3param;
1208
25
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
1209
25
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
1210
25
  isc_rwlock_t *nlock = NULL;
1211
25
  dns_vecheader_t *found = NULL;
1212
1213
25
  version->havensec3 = false;
1214
25
  node = qpdb->origin;
1215
25
  nlock = qpzone_get_lock(node);
1216
1217
25
  NODE_RDLOCK(nlock, &nlocktype);
1218
1219
142
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
1220
142
    if (top->typepair != dns_rdatatype_nsec3param) {
1221
134
      continue;
1222
134
    }
1223
8
    found = first_existing_header(top, version->serial);
1224
8
  }
1225
1226
25
  if (found != NULL) {
1227
    /*
1228
     * Find an NSEC3PARAM with a supported algorithm.
1229
     */
1230
1231
8
    rdatavec_iter_t iter;
1232
18
    DNS_VECHEADER_FOREACH(&iter, found, qpdb->common.rdclass) {
1233
18
      dns_rdata_t rdata = DNS_RDATA_INIT;
1234
18
      vecheader_current(&iter, &rdata);
1235
1236
18
      isc_result_t result =
1237
18
        dns_rdata_tostruct(&rdata, &nsec3param, NULL);
1238
18
      INSIST(result == ISC_R_SUCCESS);
1239
1240
18
      if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG &&
1241
18
          !dns_nsec3_supportedhash(nsec3param.hash))
1242
16
      {
1243
16
        continue;
1244
16
      }
1245
1246
2
      if (nsec3param.flags != 0) {
1247
1
        continue;
1248
1
      }
1249
1250
1
      memmove(version->salt, nsec3param.salt.base,
1251
1
        nsec3param.salt.length);
1252
1
      version->hash = nsec3param.hash;
1253
1
      version->salt_length = nsec3param.salt.length;
1254
1
      version->iterations = nsec3param.iterations;
1255
1
      version->flags = nsec3param.flags;
1256
1
      version->havensec3 = true;
1257
      /*
1258
       * Look for a better algorithm than the
1259
       * unknown test algorithm.
1260
       */
1261
1
      if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG) {
1262
1
        break;
1263
1
      }
1264
1
    }
1265
8
  }
1266
1267
25
  NODE_UNLOCK(nlock, &nlocktype);
1268
25
}
1269
1270
static void
1271
0
cleanup_nondirty(qpz_version_t *version, qpz_changedlist_t *cleanup_list) {
1272
  /*
1273
   * If the changed record is dirty, then an update created multiple
1274
   * versions of a given rdataset.  We keep this list until we're the
1275
   * least open version, at which point it's safe to get rid of any
1276
   * older versions.
1277
   *
1278
   * If the changed record isn't dirty, then we don't need it anymore
1279
   * since we're committing and not rolling back.
1280
   *
1281
   * The caller must be holding the database lock.
1282
   */
1283
0
  ISC_LIST_FOREACH(version->changed_list, changed, link) {
1284
0
    if (!changed->dirty) {
1285
0
      ISC_LIST_UNLINK(version->changed_list, changed, link);
1286
0
      ISC_LIST_APPEND(*cleanup_list, changed, link);
1287
0
    }
1288
0
  }
1289
0
}
1290
1291
static void
1292
17.6k
setsecure(dns_db_t *db, qpz_version_t *version, dns_dbnode_t *origin) {
1293
17.6k
  dns_rdataset_t keyset;
1294
17.6k
  dns_rdataset_t nsecset, signsecset;
1295
17.6k
  bool haszonekey = false;
1296
17.6k
  bool hasnsec = false;
1297
17.6k
  isc_result_t result;
1298
1299
17.6k
  version->secure = false;
1300
17.6k
  version->havensec3 = false;
1301
1302
17.6k
  dns_rdataset_init(&keyset);
1303
17.6k
  result = dns_db_findrdataset(db, origin, (dns_dbversion_t *)version,
1304
17.6k
             dns_rdatatype_dnskey, 0, 0, &keyset, NULL);
1305
17.6k
  if (result == ISC_R_SUCCESS) {
1306
47
    haszonekey = dns_dnssec_haszonekey(&keyset);
1307
47
    dns_rdataset_disassociate(&keyset);
1308
47
  }
1309
17.6k
  if (!haszonekey) {
1310
17.6k
    return;
1311
17.6k
  }
1312
1313
25
  dns_rdataset_init(&nsecset);
1314
25
  dns_rdataset_init(&signsecset);
1315
25
  result = dns_db_findrdataset(db, origin, (dns_dbversion_t *)version,
1316
25
             dns_rdatatype_nsec, 0, 0, &nsecset,
1317
25
             &signsecset);
1318
25
  if (result == ISC_R_SUCCESS) {
1319
7
    if (dns_rdataset_isassociated(&signsecset)) {
1320
3
      hasnsec = true;
1321
3
      dns_rdataset_disassociate(&signsecset);
1322
3
    }
1323
7
    dns_rdataset_disassociate(&nsecset);
1324
7
  }
1325
1326
25
  setnsec3parameters(db, version);
1327
1328
  /*
1329
   * If we don't have a valid NSEC/NSEC3 chain,
1330
   * clear the secure flag.
1331
   */
1332
25
  if (version->havensec3 || hasnsec) {
1333
4
    version->secure = true;
1334
4
  }
1335
25
}
1336
1337
static void
1338
219
currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
1339
219
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
1340
219
  qpz_version_t *version = NULL;
1341
1342
219
  REQUIRE(VALID_QPZONE(qpdb));
1343
1344
219
  RWLOCK(&qpdb->lock, isc_rwlocktype_read);
1345
219
  version = qpdb->current_version;
1346
219
  isc_refcount_increment(&version->references);
1347
219
  RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
1348
1349
219
  *versionp = (dns_dbversion_t *)version;
1350
219
}
1351
1352
static void
1353
attachversion(dns_db_t *db, dns_dbversion_t *source,
1354
0
        dns_dbversion_t **targetp) {
1355
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
1356
0
  qpz_version_t *version = (qpz_version_t *)source;
1357
1358
0
  REQUIRE(VALID_QPZONE(qpdb));
1359
0
  INSIST(version != NULL && version->qpdb == qpdb);
1360
1361
0
  isc_refcount_increment(&version->references);
1362
1363
0
  *targetp = source;
1364
0
}
1365
1366
static isc_result_t
1367
0
newversion(dns_db_t *db, dns_dbversion_t **versionp) {
1368
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
1369
0
  qpz_version_t *version = NULL;
1370
1371
0
  REQUIRE(VALID_QPZONE(qpdb));
1372
0
  REQUIRE(versionp != NULL && *versionp == NULL);
1373
0
  REQUIRE(qpdb->future_version == NULL);
1374
1375
0
  RWLOCK(&qpdb->lock, isc_rwlocktype_write);
1376
0
  INSIST(qpdb->next_serial != 0);
1377
0
  version = allocate_version(qpdb->common.mctx, qpdb->next_serial, 1,
1378
0
           true);
1379
0
  version->qpdb = qpdb;
1380
0
  version->secure = qpdb->current_version->secure;
1381
0
  version->havensec3 = qpdb->current_version->havensec3;
1382
0
  if (version->havensec3) {
1383
0
    version->flags = qpdb->current_version->flags;
1384
0
    version->iterations = qpdb->current_version->iterations;
1385
0
    version->hash = qpdb->current_version->hash;
1386
0
    version->salt_length = qpdb->current_version->salt_length;
1387
0
    memmove(version->salt, qpdb->current_version->salt,
1388
0
      version->salt_length);
1389
0
  }
1390
1391
0
  version->records = qpdb->current_version->records;
1392
0
  version->xfrsize = qpdb->current_version->xfrsize;
1393
1394
0
  qpdb->next_serial++;
1395
0
  qpdb->future_version = version;
1396
0
  RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
1397
1398
0
  *versionp = (dns_dbversion_t *)version;
1399
1400
0
  return ISC_R_SUCCESS;
1401
0
}
1402
1403
static void
1404
make_least_version(qpzonedb_t *qpdb, qpz_version_t *version,
1405
0
       qpz_changedlist_t *cleanup_list) {
1406
0
  qpdb->least_serial = version->serial;
1407
0
  *cleanup_list = version->changed_list;
1408
0
  ISC_LIST_INIT(version->changed_list);
1409
0
}
1410
1411
static void
1412
0
rollback_node(qpznode_t *node, uint32_t serial) {
1413
0
  bool make_dirty = false;
1414
1415
  /*
1416
   * We set the IGNORE attribute on rdatasets with serial number
1417
   * 'serial'.  When the reference count goes to zero, these rdatasets
1418
   * will be cleaned up; until that time, they will be ignored.
1419
   */
1420
0
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
1421
0
    ISC_SLIST_FOREACH(header, top->headers, next_header) {
1422
0
      if (header->serial == serial) {
1423
0
        DNS_VECHEADER_SETATTR(header,
1424
0
                  DNS_VECHEADERATTR_IGNORE);
1425
0
        make_dirty = true;
1426
0
      }
1427
0
    }
1428
0
  }
1429
0
  if (make_dirty) {
1430
0
    node->dirty = true;
1431
0
  }
1432
0
}
1433
1434
static void
1435
closeversion(dns_db_t *db, dns_dbversion_t **versionp,
1436
219
       bool commit DNS__DB_FLARG) {
1437
219
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
1438
219
  qpz_version_t *version = NULL, *cleanup_version = NULL;
1439
219
  qpz_version_t *least_greater = NULL;
1440
219
  qpznode_t *node = NULL;
1441
219
  bool rollback = false;
1442
219
  qpz_changedlist_t cleanup_list;
1443
219
  qpz_resignedlist_t resigned_list;
1444
219
  uint32_t serial, least_serial;
1445
1446
219
  REQUIRE(VALID_QPZONE(qpdb));
1447
219
  version = (qpz_version_t *)*versionp;
1448
219
  INSIST(version->qpdb == qpdb);
1449
1450
219
  if (isc_refcount_decrement(&version->references) > 1) {
1451
219
    *versionp = NULL;
1452
219
    return;
1453
219
  }
1454
1455
0
  ISC_LIST_INIT(cleanup_list);
1456
0
  ISC_LIST_INIT(resigned_list);
1457
1458
  /*
1459
   * Update the zone's secure status in version before making
1460
   * it the current version.
1461
   */
1462
0
  if (version->writer && commit) {
1463
0
    setsecure(db, version, (dns_dbnode_t *)qpdb->origin);
1464
0
  }
1465
1466
0
  RWLOCK(&qpdb->lock, isc_rwlocktype_write);
1467
0
  serial = version->serial;
1468
0
  if (version->writer) {
1469
0
    if (commit) {
1470
0
      unsigned int cur_ref;
1471
0
      qpz_version_t *cur_version = NULL;
1472
1473
0
      INSIST(version == qpdb->future_version);
1474
      /*
1475
       * The current version is going to be replaced.
1476
       * Release the (likely last) reference to it from the
1477
       * DB itself and unlink it from the open list.
1478
       */
1479
0
      cur_version = qpdb->current_version;
1480
0
      cur_ref = isc_refcount_decrement(
1481
0
        &cur_version->references);
1482
0
      if (cur_ref == 1) {
1483
0
        (void)isc_refcount_current(
1484
0
          &cur_version->references);
1485
0
        if (cur_version->serial == qpdb->least_serial) {
1486
0
          INSIST(ISC_LIST_EMPTY(
1487
0
            cur_version->changed_list));
1488
0
        }
1489
0
        ISC_LIST_UNLINK(qpdb->open_versions,
1490
0
            cur_version, link);
1491
0
      }
1492
0
      if (ISC_LIST_EMPTY(qpdb->open_versions)) {
1493
        /*
1494
         * We're going to become the least open
1495
         * version.
1496
         */
1497
0
        make_least_version(qpdb, version,
1498
0
               &cleanup_list);
1499
0
      } else {
1500
        /*
1501
         * Some other open version is the
1502
         * least version.  We can't cleanup
1503
         * records that were changed in this
1504
         * version because the older versions
1505
         * may still be in use by an open
1506
         * version.
1507
         *
1508
         * We can, however, discard the
1509
         * changed records for things that
1510
         * we've added that didn't exist in
1511
         * prior versions.
1512
         */
1513
0
        cleanup_nondirty(version, &cleanup_list);
1514
0
      }
1515
      /*
1516
       * If the (soon to be former) current version
1517
       * isn't being used by anyone, we can clean
1518
       * it up.
1519
       */
1520
0
      if (cur_ref == 1) {
1521
0
        cleanup_version = cur_version;
1522
0
        ISC_LIST_APPENDLIST(
1523
0
          version->changed_list,
1524
0
          cleanup_version->changed_list, link);
1525
0
      }
1526
      /*
1527
       * Become the current version.
1528
       */
1529
0
      version->writer = false;
1530
0
      qpdb->current_version = version;
1531
0
      qpdb->current_serial = version->serial;
1532
0
      qpdb->future_version = NULL;
1533
1534
      /*
1535
       * Keep the current version in the open list, and
1536
       * gain a reference for the DB itself (see the DB
1537
       * creation function below).  This must be the only
1538
       * case where we need to increment the counter from
1539
       * zero and need to use isc_refcount_increment0().
1540
       */
1541
0
      INSIST(isc_refcount_increment0(&version->references) ==
1542
0
             0);
1543
0
      ISC_LIST_PREPEND(qpdb->open_versions,
1544
0
           qpdb->current_version, link);
1545
0
      resigned_list = version->resigned_list;
1546
0
      ISC_LIST_INIT(version->resigned_list);
1547
0
    } else {
1548
      /*
1549
       * We're rolling back this transaction.
1550
       */
1551
0
      cleanup_list = version->changed_list;
1552
0
      ISC_LIST_INIT(version->changed_list);
1553
0
      resigned_list = version->resigned_list;
1554
0
      ISC_LIST_INIT(version->resigned_list);
1555
0
      rollback = true;
1556
0
      cleanup_version = version;
1557
0
      qpdb->future_version = NULL;
1558
0
    }
1559
0
  } else {
1560
0
    if (version != qpdb->current_version) {
1561
      /*
1562
       * There are no external or internal references
1563
       * to this version and it can be cleaned up.
1564
       */
1565
0
      cleanup_version = version;
1566
1567
      /*
1568
       * Find the version with the least serial
1569
       * number greater than ours.
1570
       */
1571
0
      least_greater = ISC_LIST_PREV(version, link);
1572
0
      if (least_greater == NULL) {
1573
0
        least_greater = qpdb->current_version;
1574
0
      }
1575
1576
0
      INSIST(version->serial < least_greater->serial);
1577
      /*
1578
       * Is this the least open version?
1579
       */
1580
0
      if (version->serial == qpdb->least_serial) {
1581
        /*
1582
         * Yes.  Install the new least open
1583
         * version.
1584
         */
1585
0
        make_least_version(qpdb, least_greater,
1586
0
               &cleanup_list);
1587
0
      } else {
1588
        /*
1589
         * Add any unexecuted cleanups to
1590
         * those of the least greater version.
1591
         */
1592
0
        ISC_LIST_APPENDLIST(least_greater->changed_list,
1593
0
                version->changed_list,
1594
0
                link);
1595
0
      }
1596
0
    } else if (version->serial == qpdb->least_serial) {
1597
0
      INSIST(ISC_LIST_EMPTY(version->changed_list));
1598
0
    }
1599
0
    ISC_LIST_UNLINK(qpdb->open_versions, version, link);
1600
0
  }
1601
0
  least_serial = qpdb->least_serial;
1602
0
  RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
1603
1604
0
  if (cleanup_version != NULL) {
1605
0
    isc_refcount_destroy(&cleanup_version->references);
1606
0
    INSIST(ISC_LIST_EMPTY(cleanup_version->changed_list));
1607
0
    cleanup_gluelists(&cleanup_version->glue_stack);
1608
0
    cds_wfs_destroy(&cleanup_version->glue_stack);
1609
0
    isc_rwlock_destroy(&cleanup_version->rwlock);
1610
0
    isc_mem_put(qpdb->common.mctx, cleanup_version,
1611
0
          sizeof(*cleanup_version));
1612
0
  }
1613
1614
  /*
1615
   * Commit/rollback re-signed headers.
1616
   */
1617
0
  ISC_LIST_FOREACH(resigned_list, resigned, link) {
1618
0
    isc_rwlock_t *nlock = NULL;
1619
0
    isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
1620
0
    dns_vecheader_t *header = resigned->header;
1621
0
    qpznode_t *resigned_node = resigned->node;
1622
1623
0
    ISC_LIST_UNLINK(resigned_list, resigned, link);
1624
1625
0
    nlock = qpzone_get_lock(resigned_node);
1626
0
    NODE_WRLOCK(nlock, &nlocktype);
1627
0
    if (rollback && !IGNORE(header)) {
1628
0
      LOCK(&qpdb->heap->lock);
1629
0
      resign_register(qpdb->heap, resigned_node, header);
1630
0
      UNLOCK(&qpdb->heap->lock);
1631
0
    }
1632
0
    qpz_resigned_destroy(db->mctx, &resigned);
1633
0
    NODE_UNLOCK(nlock, &nlocktype);
1634
0
  }
1635
1636
0
  if (ISC_LIST_EMPTY(cleanup_list)) {
1637
0
    *versionp = NULL;
1638
0
    return;
1639
0
  }
1640
1641
0
  ISC_LIST_FOREACH(cleanup_list, changed, link) {
1642
0
    isc_rwlock_t *nlock = NULL;
1643
0
    isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
1644
1645
0
    node = changed->node;
1646
0
    nlock = qpzone_get_lock(node);
1647
1648
0
    NODE_WRLOCK(nlock, &nlocktype);
1649
0
    if (rollback) {
1650
0
      rollback_node(node, serial);
1651
0
    }
1652
0
    bool has_erefs = qpznode_release(node DNS__DB_FILELINE);
1653
0
    if (!has_erefs) {
1654
0
      clean_zone_node(qpdb->heap, node, least_serial);
1655
0
    }
1656
1657
0
    NODE_UNLOCK(nlock, &nlocktype);
1658
1659
    /*
1660
     * The node reference is released separately above, so
1661
     * we just free the changed structure here.
1662
     */
1663
0
    isc_mem_put(qpdb->common.mctx, changed, sizeof(*changed));
1664
0
  }
1665
1666
0
  *versionp = NULL;
1667
0
}
1668
1669
static isc_result_t
1670
qpzone_findrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
1671
        dns_dbversion_t *dbversion, dns_rdatatype_t type,
1672
        dns_rdatatype_t covers, isc_stdtime_t now ISC_ATTR_UNUSED,
1673
        dns_rdataset_t *rdataset,
1674
17.6k
        dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
1675
17.6k
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
1676
17.6k
  qpznode_t *node = (qpznode_t *)dbnode;
1677
17.6k
  dns_vecheader_t *found = NULL, *foundsig = NULL;
1678
17.6k
  uint32_t serial;
1679
17.6k
  qpz_version_t *version = (qpz_version_t *)dbversion;
1680
17.6k
  bool close_version = false;
1681
17.6k
  dns_typepair_t typepair, sigpair;
1682
17.6k
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
1683
17.6k
  isc_rwlock_t *nlock = NULL;
1684
1685
17.6k
  REQUIRE(VALID_QPZONE(qpdb));
1686
17.6k
  REQUIRE(type != dns_rdatatype_any);
1687
17.6k
  INSIST(version == NULL || version->qpdb == qpdb);
1688
1689
17.6k
  if (type == dns_rdatatype_none && covers == dns_rdatatype_none) {
1690
0
    return ISC_R_NOTFOUND;
1691
0
  }
1692
1693
17.6k
  if (version == NULL) {
1694
4
    currentversion(db, (dns_dbversion_t **)&version);
1695
4
    close_version = true;
1696
4
  }
1697
17.6k
  serial = version->serial;
1698
1699
17.6k
  nlock = qpzone_get_lock(node);
1700
17.6k
  NODE_RDLOCK(nlock, &nlocktype);
1701
1702
17.6k
  typepair = DNS_TYPEPAIR_VALUE(type, covers);
1703
17.6k
  if (covers == dns_rdatatype_none) {
1704
17.6k
    sigpair = DNS_SIGTYPEPAIR(type);
1705
17.6k
  } else {
1706
0
    sigpair = dns_typepair_none;
1707
0
  }
1708
1709
17.6k
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
1710
873
    dns_vecheader_t *header = first_existing_header(top, serial);
1711
873
    if (header != NULL) {
1712
      /*
1713
       * We have an active, extant rdataset.  If it's a
1714
       * type we're looking for, remember it.
1715
       */
1716
873
      if (top->typepair == typepair) {
1717
58
        found = header;
1718
58
        if (foundsig != NULL) {
1719
4
          break;
1720
4
        }
1721
815
      } else if (top->typepair == sigpair) {
1722
9
        foundsig = header;
1723
9
        if (found != NULL) {
1724
3
          break;
1725
3
        }
1726
9
      }
1727
873
    }
1728
873
  }
1729
17.6k
  if (found != NULL) {
1730
58
    bindrdataset(qpdb, found, rdataset DNS__DB_FLARG_PASS);
1731
58
    if (foundsig != NULL) {
1732
7
      bindrdataset(qpdb, foundsig,
1733
7
             sigrdataset DNS__DB_FLARG_PASS);
1734
7
    }
1735
58
  }
1736
1737
17.6k
  NODE_UNLOCK(nlock, &nlocktype);
1738
1739
17.6k
  if (close_version) {
1740
4
    closeversion(db, (dns_dbversion_t **)&version,
1741
4
           false DNS__DB_FLARG_PASS);
1742
4
  }
1743
1744
17.6k
  if (found == NULL) {
1745
17.6k
    return ISC_R_NOTFOUND;
1746
17.6k
  }
1747
1748
58
  return ISC_R_SUCCESS;
1749
17.6k
}
1750
1751
static bool
1752
9.18M
delegating_type(qpzonedb_t *qpdb, qpznode_t *node, dns_typepair_t typepair) {
1753
9.18M
  return typepair == DNS_TYPEPAIR(dns_rdatatype_dname) ||
1754
9.18M
         (typepair == DNS_TYPEPAIR(dns_rdatatype_ns) &&
1755
7.36M
    (node != qpdb->origin || IS_STUB(qpdb)));
1756
9.18M
}
1757
1758
static void
1759
loading_addnode(qpz_load_t *loadctx, const dns_name_t *name,
1760
    dns_rdatatype_t type, dns_rdatatype_t covers,
1761
10.5M
    qpznode_t **nodep) {
1762
10.5M
  qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
1763
10.5M
  isc_result_t result;
1764
10.5M
  qpznode_t *node = NULL, *nsecnode = NULL;
1765
1766
10.5M
  if (type == dns_rdatatype_nsec3 || covers == dns_rdatatype_nsec3) {
1767
2.22k
    result = dns_qp_getname(loadctx->tree, name,
1768
2.22k
          DNS_DBNAMESPACE_NSEC3, (void **)&node,
1769
2.22k
          NULL);
1770
2.22k
    if (result == ISC_R_SUCCESS) {
1771
1.19k
      *nodep = node;
1772
1.19k
    } else {
1773
1.03k
      node = new_qpznode(qpdb, name, DNS_DBNAMESPACE_NSEC3);
1774
1.03k
      result = dns_qp_insert(loadctx->tree, node, 0);
1775
1.03k
      INSIST(result == ISC_R_SUCCESS);
1776
1.03k
      *nodep = node;
1777
1.03k
      qpznode_detach(&node);
1778
1.03k
    }
1779
2.22k
    return;
1780
2.22k
  }
1781
1782
10.5M
  result = dns_qp_getname(loadctx->tree, name, DNS_DBNAMESPACE_NORMAL,
1783
10.5M
        (void **)&node, NULL);
1784
10.5M
  if (result == ISC_R_SUCCESS) {
1785
1.54M
    if (type == dns_rdatatype_nsec && node->havensec) {
1786
7.95k
      goto done;
1787
7.95k
    }
1788
9.00M
  } else {
1789
9.00M
    INSIST(node == NULL);
1790
9.00M
    node = new_qpznode(qpdb, name, DNS_DBNAMESPACE_NORMAL);
1791
9.00M
    result = dns_qp_insert(loadctx->tree, node, 0);
1792
9.00M
    INSIST(result == ISC_R_SUCCESS);
1793
9.00M
    qpznode_unref(node);
1794
9.00M
  }
1795
10.5M
  if (type != dns_rdatatype_nsec) {
1796
10.4M
    goto done;
1797
10.4M
  }
1798
1799
  /*
1800
   * We're adding an NSEC record, so create a node in the nsec tree
1801
   * too. This tree speeds searches for closest NSECs that would
1802
   * otherwise need to examine many irrelevant nodes in large TLDs.
1803
   * If dns_qp_insert() fails, it means there's already an NSEC
1804
   * node there, so we can just detach the new one we created and
1805
   * move on.
1806
   */
1807
66.9k
  node->havensec = true;
1808
66.9k
  nsecnode = new_qpznode(qpdb, name, DNS_DBNAMESPACE_NSEC);
1809
66.9k
  (void)dns_qp_insert(loadctx->tree, nsecnode, 0);
1810
66.9k
  qpznode_detach(&nsecnode);
1811
1812
10.5M
done:
1813
10.5M
  *nodep = node;
1814
10.5M
}
1815
1816
static bool
1817
9.18M
cname_and_other(qpznode_t *node, uint32_t serial) {
1818
9.18M
  bool cname = false, other = false;
1819
1820
  /*
1821
   * Look for CNAME and "other data" rdatasets active in our version.
1822
   * ("Other data" is any rdataset whose type is not KEY, NSEC, SIG
1823
   * or RRSIG.
1824
   */
1825
9.20M
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
1826
9.20M
    dns_rdatatype_t rdtype = DNS_TYPEPAIR_TYPE(top->typepair);
1827
9.20M
    if (rdtype == dns_rdatatype_cname) {
1828
452
      if (first_existing_header(top, serial) != NULL) {
1829
452
        cname = true;
1830
452
      }
1831
9.20M
    } else if (rdtype != dns_rdatatype_nsec &&
1832
9.13M
         rdtype != dns_rdatatype_rrsig)
1833
9.11M
    {
1834
9.11M
      if (first_existing_header(top, serial) != NULL) {
1835
9.11M
        if (!prio_type(rdtype)) {
1836
          /*
1837
           * CNAME is in the priority list, so if
1838
           * we are done with priority types, we
1839
           * know there will not be a CNAME, and
1840
           * are safe to skip the rest.
1841
           */
1842
1.71M
          return cname;
1843
1.71M
        }
1844
7.40M
        other = true;
1845
7.40M
      }
1846
9.11M
    }
1847
1848
7.49M
    if (cname && other) {
1849
16
      return true;
1850
16
    }
1851
7.49M
  }
1852
1853
7.47M
  return false;
1854
9.18M
}
1855
1856
static qpz_changed_t *
1857
add_changed(qpzonedb_t *qpdb, qpznode_t *node,
1858
0
      qpz_version_t *version DNS__DB_FLARG) {
1859
0
  qpz_changed_t *changed = qpz_changed_new(qpdb->common.mctx,
1860
0
             node DNS__DB_FLARG_PASS);
1861
1862
0
  RWLOCK(&qpdb->lock, isc_rwlocktype_write);
1863
0
  REQUIRE(version->writer);
1864
1865
0
  ISC_LIST_INITANDAPPEND(version->changed_list, changed, link);
1866
0
  RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
1867
1868
0
  return changed;
1869
0
}
1870
1871
static uint64_t
1872
9.35M
recordsize(dns_vecheader_t *header, unsigned int namelen) {
1873
9.35M
  return dns_rdatavec_size(header) + sizeof(dns_ttl_t) +
1874
9.35M
         sizeof(dns_rdatatype_t) + sizeof(dns_rdataclass_t) + namelen;
1875
9.35M
}
1876
1877
static void
1878
maybe_update_recordsandsize(bool add, qpz_version_t *version,
1879
9.35M
          dns_vecheader_t *header, unsigned int namelen) {
1880
9.35M
  if (!EXISTS(header)) {
1881
0
    return;
1882
0
  }
1883
1884
18.7M
  RWLOCK(&version->rwlock, isc_rwlocktype_write);
1885
18.7M
  if (add) {
1886
9.18M
    version->records += dns_rdatavec_count(header);
1887
9.18M
    version->xfrsize += recordsize(header, namelen);
1888
9.18M
  } else {
1889
170k
    version->records -= dns_rdatavec_count(header);
1890
170k
    version->xfrsize -= recordsize(header, namelen);
1891
170k
  }
1892
18.7M
  RWUNLOCK(&version->rwlock, isc_rwlocktype_write);
1893
9.35M
}
1894
1895
static isc_result_t
1896
add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
1897
    qpz_version_t *version, dns_vecheader_t *newheader, unsigned int options,
1898
    bool loading, dns_rdataset_t *addedrdataset,
1899
10.5M
    isc_stdtime_t now ISC_ATTR_UNUSED DNS__DB_FLARG) {
1900
10.5M
  qpz_changed_t *changed = NULL;
1901
10.5M
  dns_vectop_t *foundtop = NULL;
1902
10.5M
  dns_vectop_t *priotop = NULL;
1903
10.5M
  dns_vecheader_t *merged = NULL;
1904
10.5M
  isc_result_t result;
1905
10.5M
  bool merge = false;
1906
10.5M
  uint32_t ntypes;
1907
1908
10.5M
  if ((options & DNS_DBADD_MERGE) != 0) {
1909
10.5M
    REQUIRE(version != NULL);
1910
10.5M
    merge = true;
1911
10.5M
  }
1912
1913
10.5M
  if (!loading) {
1914
    /*
1915
     * We always add a changed record, even if no changes end up
1916
     * being made to this node, because it's harmless and
1917
     * simplifies the code.
1918
     */
1919
0
    changed = add_changed(qpdb, node, version DNS__DB_FLARG_PASS);
1920
0
  }
1921
1922
10.5M
  ntypes = 0;
1923
10.5M
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
1924
1.70M
    ++ntypes;
1925
1.70M
    if (prio_type(top->typepair)) {
1926
311k
      priotop = top;
1927
311k
    }
1928
1.70M
    if (top->typepair == newheader->typepair) {
1929
1.53M
      foundtop = top;
1930
1.53M
      break;
1931
1.53M
    }
1932
1.70M
  }
1933
1934
  /*
1935
   * If topheader isn't NULL, we've found the right type.  There may be
1936
   * IGNORE rdatasets between the top of the chain and the first real
1937
   * data.  We skip over them.
1938
   */
1939
10.5M
  dns_vecheader_t **header_p = NULL;
1940
10.5M
  dns_vecheader_t *header = NULL;
1941
10.5M
  if (foundtop != NULL) {
1942
1.53M
    ISC_SLIST_FOREACH_PTR(p, &ISC_SLIST_HEAD(foundtop->headers)) {
1943
1.53M
      if (!IGNORE(*p)) {
1944
1.53M
        header_p = p;
1945
1.53M
        header = *p;
1946
1.53M
        break;
1947
1.53M
      }
1948
0
      ISC_SLIST_PTR_ADVANCE(p, next_header);
1949
0
    }
1950
1.53M
  }
1951
1952
10.5M
  if (header != NULL) {
1953
    /*
1954
     * If 'merge' is true and header isn't empty/nonexistent,
1955
     * we'll try to create a new rdataset that is the union
1956
     * of 'newheader' and 'header'.
1957
     */
1958
1.53M
    if (merge && EXISTS(header)) {
1959
1.53M
      unsigned int flags = 0;
1960
1.53M
      INSIST(version->serial >= header->serial);
1961
1.53M
      merged = NULL;
1962
1.53M
      result = ISC_R_SUCCESS;
1963
1964
1.53M
      if ((options & DNS_DBADD_EXACT) != 0) {
1965
0
        flags |= DNS_RDATAVEC_EXACT;
1966
0
      }
1967
1.53M
      if ((options & DNS_DBADD_EXACTTTL) != 0 &&
1968
0
          newheader->ttl != header->ttl)
1969
0
      {
1970
0
        result = DNS_R_NOTEXACT;
1971
1.53M
      } else if (newheader->ttl != header->ttl) {
1972
3.92k
        flags |= DNS_RDATAVEC_FORCE;
1973
3.92k
      }
1974
1.53M
      if (result == ISC_R_SUCCESS) {
1975
1.53M
        result = dns_rdatavec_merge(
1976
1.53M
          header, newheader, qpdb->common.mctx,
1977
1.53M
          qpdb->common.rdclass,
1978
1.53M
          DNS_TYPEPAIR_TYPE(header->typepair),
1979
1.53M
          flags, qpdb->maxrrperset, &merged);
1980
1.53M
      }
1981
1.53M
      if (result == ISC_R_SUCCESS) {
1982
        /*
1983
         * If 'header' has the same serial number as
1984
         * we do, we could clean it up now if we knew
1985
         * that our caller had no references to it.
1986
         * We don't know this, however, so we leave it
1987
         * alone.  It will get cleaned up when
1988
         * clean_zone_node() runs.
1989
         */
1990
170k
        dns_vecheader_unref(newheader);
1991
170k
        newheader = merged;
1992
        /*
1993
         * dns_rdatavec_subtract takes the header from
1994
         * the first argument, so it preserves the case
1995
         */
1996
170k
        if (loading && RESIGN(newheader) &&
1997
0
            RESIGN(header) &&
1998
0
            resign_sooner_values(header->resign,
1999
0
               header->typepair,
2000
0
               newheader->resign,
2001
0
               newheader->typepair))
2002
0
        {
2003
0
          newheader->resign = header->resign;
2004
0
        }
2005
1.36M
      } else {
2006
1.36M
        if (result == DNS_R_TOOMANYRECORDS) {
2007
0
          dns__db_logtoomanyrecords(
2008
0
            (dns_db_t *)qpdb, nodename,
2009
0
            DNS_TYPEPAIR_TYPE(
2010
0
              header->typepair),
2011
0
            "updating", qpdb->maxrrperset);
2012
0
        }
2013
1.36M
        dns_vecheader_unref(newheader);
2014
1.36M
        return result;
2015
1.36M
      }
2016
1.53M
    }
2017
2018
170k
    INSIST(version->serial >= header->serial);
2019
170k
    INSIST(foundtop->typepair == newheader->typepair);
2020
2021
170k
    if (loading) {
2022
170k
      if (RESIGN(newheader)) {
2023
0
        LOCK(&qpdb->heap->lock);
2024
0
        resign_register(qpdb->heap, node, newheader);
2025
0
        UNLOCK(&qpdb->heap->lock);
2026
        /* resigndelete not needed here */
2027
0
      }
2028
2029
      /*
2030
       * There are no other references to 'header' when
2031
       * loading, so we MAY clean up 'header' now.
2032
       * Since we don't generate changed records when
2033
       * loading, we MUST clean up 'header' now.
2034
       */
2035
170k
      ISC_SLIST_PTR_REMOVE(header_p, header, next_header);
2036
170k
      ISC_SLIST_PREPEND(foundtop->headers, newheader,
2037
170k
            next_header);
2038
170k
      maybe_update_recordsandsize(false, version, header,
2039
170k
                nodename->length);
2040
2041
170k
      LOCK(&qpdb->heap->lock);
2042
170k
      resign_unregister(qpdb->heap, node, header);
2043
170k
      UNLOCK(&qpdb->heap->lock);
2044
170k
      dns_vecheader_unref(header);
2045
170k
    } else {
2046
0
      if (RESIGN(newheader)) {
2047
0
        LOCK(&qpdb->heap->lock);
2048
0
        resign_register(qpdb->heap, node, newheader);
2049
0
        resign_unregister(qpdb->heap, node, header);
2050
0
        UNLOCK(&qpdb->heap->lock);
2051
0
        resign_rollback(qpdb, node, version,
2052
0
            header DNS__DB_FLARG_PASS);
2053
0
      }
2054
2055
0
      ISC_SLIST_PREPEND(foundtop->headers, newheader,
2056
0
            next_header);
2057
2058
0
      node->dirty = true;
2059
0
      if (changed != NULL) {
2060
0
        changed->dirty = true;
2061
0
      }
2062
0
      maybe_update_recordsandsize(false, version, header,
2063
0
                nodename->length);
2064
0
    }
2065
9.01M
  } else {
2066
    /*
2067
     * No non-IGNORED rdatasets of the given type exist at
2068
     * this node.
2069
     *
2070
     * If we're trying to delete the type, don't bother.
2071
     */
2072
9.01M
    if (!EXISTS(newheader)) {
2073
0
      dns_vecheader_unref(newheader);
2074
0
      return DNS_R_UNCHANGED;
2075
0
    }
2076
2077
9.01M
    if (RESIGN(newheader)) {
2078
0
      LOCK(&qpdb->heap->lock);
2079
0
      resign_register(qpdb->heap, node, newheader);
2080
0
      resign_unregister(qpdb->heap, node, header);
2081
0
      UNLOCK(&qpdb->heap->lock);
2082
0
      resign_rollback(qpdb, node, version,
2083
0
          header DNS__DB_FLARG_PASS);
2084
0
    }
2085
2086
9.01M
    if (foundtop != NULL) {
2087
      /*
2088
       * We have a list of rdatasets of the given type,
2089
       * but they're all marked IGNORE.  We simply insert
2090
       * the new rdataset at the head of the list.
2091
       *
2092
       * Ignored rdatasets cannot occur during loading, so
2093
       * we INSIST on it.
2094
       */
2095
0
      INSIST(!loading);
2096
2097
0
      ISC_SLIST_PREPEND(foundtop->headers, newheader,
2098
0
            next_header);
2099
2100
0
      if (changed != NULL) {
2101
0
        changed->dirty = true;
2102
0
      }
2103
0
      node->dirty = true;
2104
9.01M
    } else {
2105
      /*
2106
       * No rdatasets of the given type exist at the node.
2107
       */
2108
2109
9.01M
      if (qpdb->maxtypepername > 0 &&
2110
0
          ntypes >= qpdb->maxtypepername)
2111
0
      {
2112
0
        LOCK(&qpdb->heap->lock);
2113
0
        resign_unregister(qpdb->heap, node, newheader);
2114
0
        UNLOCK(&qpdb->heap->lock);
2115
0
        dns_vecheader_unref(newheader);
2116
0
        return DNS_R_TOOMANYRECORDS;
2117
0
      }
2118
2119
9.01M
      dns_vectop_t *newtop =
2120
9.01M
        dns_vectop_new(node->mctx, newheader->typepair);
2121
2122
9.01M
      ISC_SLIST_PREPEND(newtop->headers, newheader,
2123
9.01M
            next_header);
2124
2125
9.01M
      if (prio_type(newheader->typepair)) {
2126
        /* This is a priority type, prepend it */
2127
7.42M
        ISC_SLIST_PREPEND(node->next_type, newtop,
2128
7.42M
              next_type);
2129
7.42M
      } else if (priotop != NULL) {
2130
        /* Append after the priority headers */
2131
2.08k
        ISC_SLIST_INSERTAFTER(priotop, newtop,
2132
2.08k
                  next_type);
2133
1.58M
      } else {
2134
        /* There were no priority headers */
2135
1.58M
        ISC_SLIST_PREPEND(node->next_type, newtop,
2136
1.58M
              next_type);
2137
1.58M
      }
2138
9.01M
    }
2139
9.01M
  }
2140
2141
9.18M
  maybe_update_recordsandsize(true, version, newheader, nodename->length);
2142
2143
  /*
2144
   * Check if the node now contains CNAME and other data.
2145
   */
2146
9.18M
  if (cname_and_other(node, version->serial)) {
2147
24
    return DNS_R_CNAMEANDOTHER;
2148
24
  }
2149
2150
9.18M
  bindrdataset(qpdb, newheader, addedrdataset DNS__DB_FLARG_PASS);
2151
2152
9.18M
  return ISC_R_SUCCESS;
2153
9.18M
}
2154
2155
static void
2156
wildcardmagic(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name,
2157
3.09M
        dns_namespace_t nspace) {
2158
3.09M
  isc_result_t result;
2159
3.09M
  dns_name_t foundname;
2160
3.09M
  unsigned int n;
2161
3.09M
  qpznode_t *node = NULL;
2162
2163
3.09M
  dns_name_init(&foundname);
2164
3.09M
  n = dns_name_countlabels(name);
2165
3.09M
  INSIST(n >= 2);
2166
3.09M
  n--;
2167
3.09M
  dns_name_getlabelsequence(name, 1, n, &foundname);
2168
2169
  /* insert an empty node, if needed, to hold the wildcard bit */
2170
3.09M
  result = dns_qp_getname(qp, &foundname, nspace, (void **)&node, NULL);
2171
3.09M
  if (result != ISC_R_SUCCESS) {
2172
1.53M
    INSIST(node == NULL);
2173
1.53M
    node = new_qpznode(qpdb, &foundname, nspace);
2174
1.53M
    result = dns_qp_insert(qp, node, 0);
2175
1.53M
    INSIST(result == ISC_R_SUCCESS);
2176
1.53M
    qpznode_unref(node);
2177
1.53M
  }
2178
2179
3.09M
  node->wild = true;
2180
3.09M
}
2181
2182
static void
2183
addwildcards(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name,
2184
10.5M
       dns_namespace_t nspace) {
2185
10.5M
  dns_name_t foundname;
2186
10.5M
  unsigned int n, l, i;
2187
2188
10.5M
  dns_name_init(&foundname);
2189
10.5M
  n = dns_name_countlabels(name);
2190
10.5M
  l = dns_name_countlabels(&qpdb->common.origin);
2191
10.5M
  i = l + 1;
2192
21.5M
  while (i < n) {
2193
10.9M
    dns_name_getlabelsequence(name, n - i, i, &foundname);
2194
10.9M
    if (dns_name_iswildcard(&foundname)) {
2195
2.38M
      wildcardmagic(qpdb, qp, &foundname, nspace);
2196
2.38M
    }
2197
2198
10.9M
    i++;
2199
10.9M
  }
2200
10.5M
}
2201
2202
static isc_result_t
2203
loading_addrdataset(void *arg, const dns_name_t *name, dns_rdataset_t *rdataset,
2204
10.5M
        dns_diffop_t op ISC_ATTR_UNUSED DNS__DB_FLARG) {
2205
10.5M
  qpz_load_t *loadctx = arg;
2206
10.5M
  qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
2207
10.5M
  qpznode_t *node = NULL;
2208
10.5M
  isc_result_t result = ISC_R_SUCCESS;
2209
10.5M
  isc_region_t region;
2210
10.5M
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
2211
10.5M
  isc_rwlock_t *nlock = NULL;
2212
2213
10.5M
  REQUIRE(rdataset->rdclass == qpdb->common.rdclass);
2214
2215
  /*
2216
   * SOA records are only allowed at top of zone.
2217
   */
2218
10.5M
  if (rdataset->type == dns_rdatatype_soa &&
2219
266
      !dns_name_equal(name, &qpdb->common.origin))
2220
1
  {
2221
1
    return DNS_R_NOTZONETOP;
2222
1
  }
2223
2224
10.5M
  if (rdataset->type != dns_rdatatype_nsec3 &&
2225
10.5M
      rdataset->covers != dns_rdatatype_nsec3)
2226
10.5M
  {
2227
10.5M
    addwildcards(qpdb, loadctx->tree, name, DNS_DBNAMESPACE_NORMAL);
2228
10.5M
  }
2229
2230
10.5M
  if (dns_name_iswildcard(name)) {
2231
705k
    if (rdataset->type == dns_rdatatype_ns) {
2232
      /*
2233
       * NS owners cannot legally be wild cards.
2234
       */
2235
152
      return DNS_R_INVALIDNS;
2236
152
    }
2237
2238
705k
    if (rdataset->type == dns_rdatatype_nsec3) {
2239
      /*
2240
       * NSEC3 owners cannot legally be wild cards.
2241
       */
2242
1
      return DNS_R_INVALIDNSEC3;
2243
1
    }
2244
2245
705k
    wildcardmagic(qpdb, loadctx->tree, name,
2246
705k
            DNS_DBNAMESPACE_NORMAL);
2247
705k
  }
2248
2249
10.5M
  loading_addnode(loadctx, name, rdataset->type, rdataset->covers, &node);
2250
10.5M
  result = dns_rdatavec_fromrdataset(rdataset, node->mctx, &region,
2251
10.5M
             qpdb->maxrrperset);
2252
10.5M
  if (result != ISC_R_SUCCESS) {
2253
71
    if (result == DNS_R_TOOMANYRECORDS) {
2254
0
      dns__db_logtoomanyrecords((dns_db_t *)qpdb, name,
2255
0
              rdataset->type, "adding",
2256
0
              qpdb->maxrrperset);
2257
0
    }
2258
71
    return result;
2259
71
  }
2260
2261
10.5M
  dns_vecheader_t *newheader = (dns_vecheader_t *)region.base;
2262
10.5M
  newheader->ttl = rdataset->ttl;
2263
10.5M
  newheader->serial = 1;
2264
10.5M
  atomic_store(&newheader->trust, rdataset->trust);
2265
2266
10.5M
  dns_vecheader_setownercase(newheader, name);
2267
2268
10.5M
  if (rdataset->attributes.resign) {
2269
0
    DNS_VECHEADER_SETATTR(newheader, DNS_VECHEADERATTR_RESIGN);
2270
0
    newheader->resign = dns_time64_from32(rdataset->resign);
2271
0
  }
2272
2273
10.5M
  nlock = qpzone_get_lock(node);
2274
10.5M
  NODE_WRLOCK(nlock, &nlocktype);
2275
10.5M
  result = add(qpdb, node, name, qpdb->current_version, newheader,
2276
10.5M
         DNS_DBADD_MERGE, true, NULL, 0 DNS__DB_FLARG_PASS);
2277
10.5M
  NODE_UNLOCK(nlock, &nlocktype);
2278
2279
10.5M
  if (result == ISC_R_SUCCESS &&
2280
9.18M
      delegating_type(qpdb, node, rdataset->type))
2281
7.35M
  {
2282
7.35M
    node->delegating = true;
2283
7.35M
  } else if (result == DNS_R_UNCHANGED) {
2284
1.36M
    result = ISC_R_SUCCESS;
2285
1.36M
  }
2286
2287
10.5M
  return result;
2288
10.5M
}
2289
2290
static void
2291
17.6k
loading_setup(void *arg) {
2292
17.6k
  qpz_load_t *loadctx = arg;
2293
17.6k
  qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
2294
2295
17.6k
  dns_qpmulti_write(qpdb->tree, &loadctx->tree);
2296
17.6k
}
2297
2298
static void
2299
17.6k
loading_commit(void *arg) {
2300
17.6k
  qpz_load_t *loadctx = arg;
2301
17.6k
  qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
2302
2303
17.6k
  if (loadctx->tree != NULL) {
2304
17.6k
    dns_qp_compact(loadctx->tree, DNS_QPGC_MAYBE);
2305
17.6k
    dns_qpmulti_commit(qpdb->tree, &loadctx->tree);
2306
17.6k
  }
2307
17.6k
}
2308
2309
static isc_result_t
2310
17.6k
beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
2311
17.6k
  qpz_load_t *loadctx = NULL;
2312
17.6k
  qpzonedb_t *qpdb = NULL;
2313
17.6k
  qpdb = (qpzonedb_t *)db;
2314
2315
17.6k
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
2316
17.6k
  REQUIRE(VALID_QPZONE(qpdb));
2317
2318
17.6k
  loadctx = isc_mem_get(qpdb->common.mctx, sizeof(*loadctx));
2319
17.6k
  *loadctx = (qpz_load_t){ .db = db };
2320
2321
17.6k
  RWLOCK(&qpdb->lock, isc_rwlocktype_write);
2322
2323
17.6k
  REQUIRE((qpdb->attributes & (QPDB_ATTR_LOADED | QPDB_ATTR_LOADING)) ==
2324
17.6k
    0);
2325
17.6k
  qpdb->attributes |= QPDB_ATTR_LOADING;
2326
2327
17.6k
  RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
2328
2329
17.6k
  callbacks->update = loading_addrdataset;
2330
17.6k
  callbacks->setup = loading_setup;
2331
17.6k
  callbacks->commit = loading_commit;
2332
17.6k
  callbacks->add_private = loadctx;
2333
2334
17.6k
  return ISC_R_SUCCESS;
2335
17.6k
}
2336
2337
static isc_result_t
2338
17.6k
endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
2339
17.6k
  qpz_load_t *loadctx = NULL;
2340
17.6k
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
2341
2342
17.6k
  REQUIRE(VALID_QPZONE(qpdb));
2343
17.6k
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
2344
17.6k
  loadctx = callbacks->add_private;
2345
17.6k
  REQUIRE(loadctx != NULL);
2346
17.6k
  REQUIRE(loadctx->db == db);
2347
2348
17.6k
  RWLOCK(&qpdb->lock, isc_rwlocktype_write);
2349
2350
17.6k
  REQUIRE((qpdb->attributes & QPDB_ATTR_LOADING) != 0);
2351
17.6k
  REQUIRE((qpdb->attributes & QPDB_ATTR_LOADED) == 0);
2352
2353
17.6k
  qpdb->attributes &= ~QPDB_ATTR_LOADING;
2354
17.6k
  qpdb->attributes |= QPDB_ATTR_LOADED;
2355
2356
17.6k
  if (qpdb->origin != NULL) {
2357
17.6k
    qpz_version_t *version = qpdb->current_version;
2358
17.6k
    RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
2359
17.6k
    setsecure(db, version, (dns_dbnode_t *)qpdb->origin);
2360
17.6k
  } else {
2361
0
    RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
2362
0
  }
2363
2364
17.6k
  callbacks->update = NULL;
2365
17.6k
  callbacks->setup = NULL;
2366
17.6k
  callbacks->commit = NULL;
2367
17.6k
  callbacks->add_private = NULL;
2368
2369
17.6k
  isc_mem_put(qpdb->common.mctx, loadctx, sizeof(*loadctx));
2370
2371
17.6k
  return ISC_R_SUCCESS;
2372
17.6k
}
2373
2374
static bool
2375
2
issecure(dns_db_t *db) {
2376
2
  qpzonedb_t *qpdb = NULL;
2377
2
  bool secure;
2378
2379
2
  qpdb = (qpzonedb_t *)db;
2380
2381
2
  REQUIRE(VALID_QPZONE(qpdb));
2382
2383
2
  RWLOCK(&qpdb->lock, isc_rwlocktype_read);
2384
2
  secure = qpdb->current_version->secure;
2385
2
  RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
2386
2387
2
  return secure;
2388
2
}
2389
2390
static isc_result_t
2391
getnsec3parameters(dns_db_t *db, dns_dbversion_t *dbversion, dns_hash_t *hash,
2392
       uint8_t *flags, uint16_t *iterations, unsigned char *salt,
2393
0
       size_t *salt_length) {
2394
0
  qpzonedb_t *qpdb = NULL;
2395
0
  isc_result_t result = ISC_R_NOTFOUND;
2396
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
2397
2398
0
  qpdb = (qpzonedb_t *)db;
2399
2400
0
  REQUIRE(VALID_QPZONE(qpdb));
2401
0
  INSIST(version == NULL || version->qpdb == qpdb);
2402
2403
0
  RWLOCK(&qpdb->lock, isc_rwlocktype_read);
2404
0
  if (version == NULL) {
2405
0
    version = qpdb->current_version;
2406
0
  }
2407
2408
0
  if (version->havensec3) {
2409
0
    SET_IF_NOT_NULL(hash, version->hash);
2410
0
    if (salt != NULL && salt_length != NULL) {
2411
0
      REQUIRE(*salt_length >= version->salt_length);
2412
0
      memmove(salt, version->salt, version->salt_length);
2413
0
    }
2414
0
    SET_IF_NOT_NULL(salt_length, version->salt_length);
2415
0
    SET_IF_NOT_NULL(iterations, version->iterations);
2416
0
    SET_IF_NOT_NULL(flags, version->flags);
2417
0
    result = ISC_R_SUCCESS;
2418
0
  }
2419
0
  RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
2420
2421
0
  return result;
2422
0
}
2423
2424
static isc_result_t
2425
getsize(dns_db_t *db, dns_dbversion_t *dbversion, uint64_t *records,
2426
0
  uint64_t *xfrsize) {
2427
0
  qpzonedb_t *qpdb = NULL;
2428
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
2429
0
  isc_result_t result = ISC_R_SUCCESS;
2430
2431
0
  qpdb = (qpzonedb_t *)db;
2432
2433
0
  REQUIRE(VALID_QPZONE(qpdb));
2434
0
  INSIST(version == NULL || version->qpdb == qpdb);
2435
2436
0
  RWLOCK(&qpdb->lock, isc_rwlocktype_read);
2437
0
  if (version == NULL) {
2438
0
    version = qpdb->current_version;
2439
0
  }
2440
2441
0
  RWLOCK(&version->rwlock, isc_rwlocktype_read);
2442
0
  SET_IF_NOT_NULL(records, version->records);
2443
2444
0
  SET_IF_NOT_NULL(xfrsize, version->xfrsize);
2445
0
  RWUNLOCK(&version->rwlock, isc_rwlocktype_read);
2446
0
  RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
2447
2448
0
  return result;
2449
0
}
2450
2451
static isc_result_t
2452
setsigningtime(dns_db_t *db, dns_dbnode_t *dbnode, dns_rdataset_t *rdataset,
2453
0
         isc_stdtime_t resign) {
2454
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
2455
0
  qpznode_t *node = (qpznode_t *)dbnode;
2456
0
  dns_vecheader_t *header = NULL;
2457
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
2458
0
  isc_rwlock_t *nlock = NULL;
2459
2460
0
  REQUIRE(VALID_QPZONE(qpdb));
2461
0
  REQUIRE(rdataset != NULL);
2462
0
  REQUIRE(rdataset->methods == &dns_rdatavec_rdatasetmethods);
2463
2464
0
  header = dns_vecheader_getheader(rdataset);
2465
2466
0
  nlock = qpzone_get_lock(node);
2467
0
  NODE_WRLOCK(nlock, &nlocktype);
2468
2469
  /*
2470
   * Check if element is in the heap using hashmap lookup.
2471
   */
2472
0
  qpz_resign_t *found_elem = NULL;
2473
0
  isc_result_t find_result;
2474
2475
0
  LOCK(&qpdb->heap->lock);
2476
0
  find_result = resign_lookup(qpdb->heap, header, node, &found_elem);
2477
2478
0
  if (find_result == ISC_R_SUCCESS) {
2479
    /* Element is in heap */
2480
0
    INSIST(RESIGN(header));
2481
0
    if (resign == 0) {
2482
0
      resign_unregister(qpdb->heap, node, header);
2483
0
    } else {
2484
0
      int64_t old_resign = header->resign;
2485
0
      int64_t new_resign = dns_time64_from32(resign);
2486
2487
0
      header->resign = new_resign;
2488
2489
0
      if (resign_sooner_values(new_resign, header->typepair,
2490
0
             old_resign, header->typepair))
2491
0
      {
2492
0
        isc_heap_increased(qpdb->heap->heap,
2493
0
               found_elem->heap_index);
2494
0
      } else if (resign_sooner_values(
2495
0
             old_resign, header->typepair,
2496
0
             new_resign, header->typepair))
2497
0
      {
2498
0
        isc_heap_decreased(qpdb->heap->heap,
2499
0
               found_elem->heap_index);
2500
0
      }
2501
      /* No heap adjustment needed if neither direction
2502
       * indicates sooner */
2503
0
    }
2504
0
  } else if (resign != 0) {
2505
    /* Element not in heap, add it */
2506
0
    header->resign = dns_time64_from32(resign);
2507
0
    DNS_VECHEADER_SETATTR(header, DNS_VECHEADERATTR_RESIGN);
2508
2509
0
    resign_register(qpdb->heap, node, header);
2510
0
  }
2511
0
  UNLOCK(&qpdb->heap->lock);
2512
0
  NODE_UNLOCK(nlock, &nlocktype);
2513
0
  return ISC_R_SUCCESS;
2514
0
}
2515
2516
static isc_result_t
2517
getsigningtime(dns_db_t *db, isc_stdtime_t *resign, dns_name_t *foundname,
2518
0
         dns_typepair_t *typepair) {
2519
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
2520
0
  qpz_resign_t *elem = NULL;
2521
0
  dns_vecheader_t *header = NULL;
2522
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
2523
0
  isc_rwlock_t *nlock = NULL;
2524
0
  isc_result_t result = ISC_R_NOTFOUND;
2525
2526
0
  REQUIRE(VALID_QPZONE(qpdb));
2527
0
  REQUIRE(resign != NULL);
2528
0
  REQUIRE(foundname != NULL);
2529
0
  REQUIRE(typepair != NULL);
2530
2531
0
  LOCK(&qpdb->heap->lock);
2532
0
  elem = isc_heap_element(qpdb->heap->heap, 1);
2533
0
  if (elem == NULL) {
2534
0
    UNLOCK(&qpdb->heap->lock);
2535
0
    return ISC_R_NOTFOUND;
2536
0
  }
2537
0
  header = elem->header;
2538
0
  nlock = qpzone_get_lock(elem->node);
2539
0
  UNLOCK(&qpdb->heap->lock);
2540
2541
0
again:
2542
0
  NODE_RDLOCK(nlock, &nlocktype);
2543
2544
0
  LOCK(&qpdb->heap->lock);
2545
0
  elem = isc_heap_element(qpdb->heap->heap, 1);
2546
2547
0
  isc_rwlock_t *new_nlock = (elem != NULL) ? qpzone_get_lock(elem->node)
2548
0
             : NULL;
2549
0
  if (new_nlock != NULL && new_nlock != nlock) {
2550
0
    UNLOCK(&qpdb->heap->lock);
2551
0
    NODE_UNLOCK(nlock, &nlocktype);
2552
2553
0
    nlock = new_nlock;
2554
0
    goto again;
2555
0
  }
2556
2557
0
  if (elem != NULL) {
2558
0
    header = elem->header;
2559
0
    *resign = RESIGN(header) ? (uint32_t)header->resign : 0;
2560
0
    dns_name_copy(&elem->node->name, foundname);
2561
0
    *typepair = header->typepair;
2562
0
    result = ISC_R_SUCCESS;
2563
0
  }
2564
0
  UNLOCK(&qpdb->heap->lock);
2565
0
  NODE_UNLOCK(nlock, &nlocktype);
2566
2567
0
  return result;
2568
0
}
2569
2570
static isc_result_t
2571
2
setgluecachestats(dns_db_t *db, isc_stats_t *stats) {
2572
2
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
2573
2574
2
  REQUIRE(VALID_QPZONE(qpdb));
2575
2
  REQUIRE(!IS_STUB(qpdb));
2576
2
  REQUIRE(stats != NULL);
2577
2578
2
  isc_stats_attach(stats, &qpdb->gluecachestats);
2579
2
  return ISC_R_SUCCESS;
2580
2
}
2581
2582
static dns_qp_t *
2583
6
begin_transaction(qpzonedb_t *qpdb, dns_qpread_t *qprp, bool create) {
2584
6
  dns_qp_t *qp = NULL;
2585
2586
6
  if (create) {
2587
0
    dns_qpmulti_write(qpdb->tree, &qp);
2588
6
  } else {
2589
6
    dns_qpmulti_query(qpdb->tree, qprp);
2590
6
    qp = (dns_qp_t *)qprp;
2591
6
  }
2592
2593
6
  return qp;
2594
6
}
2595
2596
static void
2597
6
end_transaction(qpzonedb_t *qpdb, dns_qp_t *qp, bool create) {
2598
6
  if (create) {
2599
0
    dns_qp_compact(qp, DNS_QPGC_MAYBE);
2600
0
    dns_qpmulti_commit(qpdb->tree, &qp);
2601
6
  } else {
2602
6
    dns_qpread_t *qprp = (dns_qpread_t *)qp;
2603
6
    dns_qpread_destroy(qpdb->tree, qprp);
2604
6
  }
2605
6
}
2606
2607
static isc_result_t
2608
findnodeintree(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name,
2609
6
         bool create, bool nsec3, dns_dbnode_t **nodep DNS__DB_FLARG) {
2610
6
  isc_result_t result;
2611
6
  qpznode_t *node = NULL;
2612
6
  dns_namespace_t nspace = nsec3 ? DNS_DBNAMESPACE_NSEC3
2613
6
               : DNS_DBNAMESPACE_NORMAL;
2614
  /*
2615
   * findnodeintree is a wrapper around dns_qp_getname that does some
2616
   * qpzone-specific bookkeeping before returning the lookup result to the
2617
   * caller.
2618
   *
2619
   * First, we do a lookup ...
2620
   */
2621
6
  result = dns_qp_getname(qp, name, nspace, (void **)&node, NULL);
2622
6
  if (result == ISC_R_SUCCESS) {
2623
    /*
2624
     * ... if the lookup is successful, we need to increase both the
2625
     * internal and external reference count before returning to
2626
     * the caller. qpznode_acquire takes care of that.
2627
     */
2628
6
    qpznode_acquire(node DNS__DB_FLARG_PASS);
2629
2630
6
    INSIST(node->nspace == DNS_DBNAMESPACE_NSEC3 || !nsec3);
2631
6
  } else if (result != ISC_R_SUCCESS && create) {
2632
    /*
2633
     * ... if the lookup is unsuccessful, but the caller asked us to
2634
     * create a new node, create one and insert it into the tree.
2635
     */
2636
0
    node = new_qpznode(qpdb, name, nspace);
2637
0
    result = dns_qp_insert(qp, node, 0);
2638
0
    INSIST(result == ISC_R_SUCCESS);
2639
2640
    /*
2641
     * The new node now has two internal references:
2642
     *  - One from new_qpznode, that initializes references at 1.
2643
     *  - One from attach_leaf, that increases the reference by
2644
     *    one at insertion in the qp-tree.
2645
     * We want the node to have two internal and one external
2646
     * reference:
2647
     *  - One internal reference from the qp-tree.
2648
     *  - One internal and one external reference from the caller.
2649
     *
2650
     * So we increase the external reference count by one.
2651
     */
2652
0
    qpznode_erefs_increment(node DNS__DB_FLARG_PASS);
2653
2654
0
    if (!nsec3) {
2655
      /*
2656
       * Add empty non-terminal nodes to help with wildcards.
2657
       */
2658
0
      addwildcards(qpdb, qp, name, nspace);
2659
0
      if (dns_name_iswildcard(name)) {
2660
0
        wildcardmagic(qpdb, qp, name, nspace);
2661
0
      }
2662
0
    }
2663
2664
0
    INSIST(node->nspace == DNS_DBNAMESPACE_NSEC3 || !nsec3);
2665
0
  }
2666
  /*
2667
   * ... if the lookup is unsuccessful, and the caller didn't ask us
2668
   * to create a new node, there is nothing to do. Return the result
2669
   * of the lookup to the caller, and set *nodep to NULL
2670
   */
2671
2672
6
  *nodep = (dns_dbnode_t *)node;
2673
2674
6
  return result;
2675
6
}
2676
2677
static isc_result_t
2678
qpzone_findnode(dns_db_t *db, const dns_name_t *name, bool create,
2679
    dns_clientinfomethods_t *methods ISC_ATTR_UNUSED,
2680
    dns_clientinfo_t *clientinfo ISC_ATTR_UNUSED,
2681
6
    dns_dbnode_t **nodep DNS__DB_FLARG) {
2682
6
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
2683
2684
6
  REQUIRE(VALID_QPZONE(qpdb));
2685
2686
6
  dns_qpread_t qpr = { 0 };
2687
6
  dns_qp_t *qp = begin_transaction(qpdb, &qpr, create);
2688
2689
6
  isc_result_t result = findnodeintree(qpdb, qp, name, create, false,
2690
6
               nodep DNS__DB_FLARG_PASS);
2691
2692
6
  end_transaction(qpdb, qp, create);
2693
2694
6
  return result;
2695
6
}
2696
2697
static isc_result_t
2698
qpzone_findnsec3node(dns_db_t *db, const dns_name_t *name, bool create,
2699
0
         dns_dbnode_t **nodep DNS__DB_FLARG) {
2700
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
2701
2702
0
  REQUIRE(VALID_QPZONE(qpdb));
2703
2704
0
  dns_qpread_t qpr = { 0 };
2705
0
  dns_qp_t *qp = begin_transaction(qpdb, &qpr, create);
2706
2707
0
  isc_result_t result = findnodeintree(qpdb, qp, name, create, true,
2708
0
               nodep DNS__DB_FLARG_PASS);
2709
2710
0
  end_transaction(qpdb, qp, create);
2711
2712
0
  return result;
2713
0
}
2714
2715
static bool
2716
0
matchparams(dns_vecheader_t *header, qpz_search_t *search) {
2717
0
  dns_rdata_nsec3_t nsec3;
2718
0
  isc_result_t result;
2719
2720
0
  REQUIRE(header->typepair == DNS_TYPEPAIR(dns_rdatatype_nsec3));
2721
2722
0
  rdatavec_iter_t iter;
2723
0
  DNS_VECHEADER_FOREACH(&iter, header, search->qpdb->common.rdclass) {
2724
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
2725
0
    vecheader_current(&iter, &rdata);
2726
2727
0
    result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
2728
0
    INSIST(result == ISC_R_SUCCESS);
2729
2730
0
    if (nsec3.hash == search->version->hash &&
2731
0
        nsec3.iterations == search->version->iterations &&
2732
0
        nsec3.salt.length == search->version->salt_length &&
2733
0
        memcmp(nsec3.salt.base, search->version->salt,
2734
0
         nsec3.salt.length) == 0)
2735
0
    {
2736
0
      return true;
2737
0
    }
2738
0
  }
2739
2740
0
  return false;
2741
0
}
2742
2743
static isc_result_t
2744
qpzone_setup_delegation(qpz_search_t *search, dns_dbnode_t **nodep,
2745
      dns_name_t *foundname, dns_rdataset_t *rdataset,
2746
0
      dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
2747
0
  dns_name_t *zcname = NULL;
2748
0
  dns_typepair_t typepair;
2749
0
  qpznode_t *node = NULL;
2750
2751
0
  REQUIRE(search != NULL);
2752
0
  REQUIRE(search->zonecut != NULL);
2753
0
  REQUIRE(search->zonecut_header != NULL);
2754
2755
  /*
2756
   * The caller MUST NOT be holding any node locks.
2757
   */
2758
2759
0
  node = search->zonecut;
2760
0
  typepair = search->zonecut_header->typepair;
2761
2762
  /*
2763
   * If we have to set foundname, we do it before anything else.
2764
   * If we were to set foundname after we had set nodep or bound the
2765
   * rdataset, then we'd have to undo that work if dns_name_copy()
2766
   * failed.  By setting foundname first, there's nothing to undo if
2767
   * we have trouble.
2768
   */
2769
0
  if (foundname != NULL && search->copy_name) {
2770
0
    zcname = dns_fixedname_name(&search->zonecut_name);
2771
0
    dns_name_copy(zcname, foundname);
2772
0
  }
2773
0
  if (nodep != NULL) {
2774
    /*
2775
     * Note that we don't have to increment the node's reference
2776
     * count here because we're going to use the reference we
2777
     * already have in the search block.
2778
     */
2779
0
    *nodep = (dns_dbnode_t *)node;
2780
0
    search->need_cleanup = false;
2781
0
  }
2782
0
  if (rdataset != NULL) {
2783
0
    isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
2784
0
    isc_rwlock_t *nlock = qpzone_get_lock(node);
2785
0
    NODE_RDLOCK(nlock, &nlocktype);
2786
0
    bindrdataset(search->qpdb, search->zonecut_header,
2787
0
           rdataset DNS__DB_FLARG_PASS);
2788
0
    if (sigrdataset != NULL && search->zonecut_sigheader != NULL) {
2789
0
      bindrdataset(search->qpdb, search->zonecut_sigheader,
2790
0
             sigrdataset DNS__DB_FLARG_PASS);
2791
0
    }
2792
0
    NODE_UNLOCK(nlock, &nlocktype);
2793
0
  }
2794
2795
0
  if (typepair == DNS_TYPEPAIR(dns_rdatatype_dname)) {
2796
0
    return DNS_R_DNAME;
2797
0
  }
2798
0
  return DNS_R_DELEGATION;
2799
0
}
2800
2801
typedef enum { FORWARD, BACK } direction_t;
2802
2803
/*
2804
 * Step backwards or forwards through the database until we find a
2805
 * node with data in it for the desired version. If 'nextname' is not NULL,
2806
 * and we found a predecessor or successor, save the name we found in it.
2807
 * Return true if we found a predecessor or successor.
2808
 */
2809
static bool
2810
step(qpz_search_t *search, dns_qpiter_t *it, direction_t direction,
2811
77
     qpznode_t **node_p) {
2812
77
  REQUIRE(node_p != NULL && *node_p == NULL);
2813
2814
77
  qpznode_t *node = NULL, *previous_node = NULL;
2815
77
  isc_result_t result = ISC_R_SUCCESS;
2816
2817
77
  result = dns_qpiter_current(it, (void **)&node, NULL);
2818
231
  while (result == ISC_R_SUCCESS) {
2819
154
    previous_node = node;
2820
2821
154
    isc_rwlock_t *nlock = qpzone_get_lock(node);
2822
154
    isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
2823
154
    dns_vecheader_t *found = NULL;
2824
2825
154
    NODE_RDLOCK(nlock, &nlocktype);
2826
154
    ISC_SLIST_FOREACH(top, node->next_type, next_type) {
2827
0
      found = first_existing_header(top, search->serial);
2828
0
    }
2829
154
    NODE_UNLOCK(nlock, &nlocktype);
2830
154
    if (found != NULL) {
2831
0
      break;
2832
0
    }
2833
2834
154
    if (direction == FORWARD) {
2835
154
      result = dns_qpiter_next(it, (void **)&node, NULL);
2836
154
    } else {
2837
0
      result = dns_qpiter_prev(it, (void **)&node, NULL);
2838
0
    }
2839
154
  };
2840
2841
77
  if (result == ISC_R_SUCCESS) {
2842
0
    if (previous_node != NULL) {
2843
0
      *node_p = previous_node;
2844
0
    }
2845
0
    return true;
2846
0
  }
2847
2848
77
  return false;
2849
77
}
2850
2851
static bool
2852
77
activeempty(qpz_search_t *search, dns_qpiter_t *it, const dns_name_t *current) {
2853
77
  qpznode_t *next_node = NULL;
2854
2855
  /*
2856
   * The iterator is currently pointed at the predecessor
2857
   * of the name we were searching for. Step the iterator
2858
   * forward, then step() will continue forward until it
2859
   * finds a node with active data. If that node is a
2860
   * subdomain of the one we were looking for, then we're
2861
   * at an active empty nonterminal node.
2862
   */
2863
77
  isc_result_t result = dns_qpiter_next(it, NULL, NULL);
2864
77
  if (result != ISC_R_SUCCESS) {
2865
    /* An ENT at the end of the zone is impossible */
2866
0
    return false;
2867
0
  }
2868
77
  return step(search, it, FORWARD, &next_node) &&
2869
0
         dns_name_issubdomain(&next_node->name, current);
2870
77
}
2871
2872
static bool
2873
wildcard_blocked(qpz_search_t *search, const dns_name_t *qname,
2874
0
     dns_name_t *wname) {
2875
0
  isc_result_t result;
2876
0
  qpznode_t *next_node = NULL, *prev_node = NULL;
2877
0
  dns_name_t name;
2878
0
  dns_name_t rname;
2879
0
  dns_name_t tname;
2880
0
  dns_qpiter_t it;
2881
0
  bool check_next = false;
2882
0
  bool check_prev = false;
2883
0
  unsigned int n;
2884
2885
0
  dns_name_init(&name);
2886
0
  dns_name_init(&tname);
2887
0
  dns_name_init(&rname);
2888
2889
  /*
2890
   * The qname seems to have matched a wildcard, but we
2891
   * need to find out if there's an empty nonterminal node
2892
   * between the wildcard level and the qname.
2893
   *
2894
   * search->iter should now be pointing at the predecessor
2895
   * of the searched-for name. We are using a local copy of the
2896
   * iterator so as not to change the state of search->iter.
2897
   * step() will walk backward until we find a predecessor with
2898
   * data.
2899
   */
2900
0
  it = search->iter;
2901
0
  check_prev = step(search, &it, BACK, &prev_node);
2902
2903
  /* Now reset the iterator and look for a successor with data. */
2904
0
  it = search->iter;
2905
0
  result = dns_qpiter_next(&it, NULL, NULL);
2906
0
  if (result == ISC_R_SUCCESS) {
2907
0
    check_next = step(search, &it, FORWARD, &next_node);
2908
0
  }
2909
2910
0
  if (!check_prev && !check_next) {
2911
    /* No predecessor or successor was found at all? */
2912
0
    return false;
2913
0
  }
2914
2915
0
  dns_name_clone(qname, &rname);
2916
2917
  /*
2918
   * Remove the wildcard label to find the terminal name.
2919
   */
2920
0
  n = dns_name_countlabels(wname);
2921
0
  dns_name_getlabelsequence(wname, 1, n - 1, &tname);
2922
2923
0
  do {
2924
0
    if ((check_prev &&
2925
0
         dns_name_issubdomain(&prev_node->name, &rname)) ||
2926
0
        (check_next &&
2927
0
         dns_name_issubdomain(&next_node->name, &rname)))
2928
0
    {
2929
0
      return true;
2930
0
    }
2931
2932
    /*
2933
     * Remove the leftmost label from the qname and check again.
2934
     */
2935
0
    n = dns_name_countlabels(&rname);
2936
0
    dns_name_getlabelsequence(&rname, 1, n - 1, &rname);
2937
0
  } while (!dns_name_equal(&rname, &tname));
2938
2939
0
  return false;
2940
0
}
2941
2942
static bool
2943
0
node_active(qpz_search_t *search, qpznode_t *node) {
2944
0
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
2945
0
    if (first_existing_header(top, search->serial) != NULL) {
2946
0
      return true;
2947
0
    }
2948
0
  }
2949
0
  return false;
2950
0
}
2951
2952
static isc_result_t
2953
find_wildcard(qpz_search_t *search, qpznode_t **nodep, const dns_name_t *qname,
2954
0
        dns_namespace_t nspace) {
2955
0
  isc_result_t result = ISC_R_NOTFOUND;
2956
2957
  /*
2958
   * Examine each ancestor level.  If the level's wild bit
2959
   * is set, then construct the corresponding wildcard name and
2960
   * search for it.  If the wildcard node exists, and is active in
2961
   * this version, we're done.  If not, then we next check to see
2962
   * if the ancestor is active in this version.  If so, then there
2963
   * can be no possible wildcard match and again we're done.  If not,
2964
   * continue the search.
2965
   */
2966
0
  for (int i = dns_qpchain_length(&search->chain) - 1; i >= 0; i--) {
2967
0
    qpznode_t *node = NULL;
2968
0
    isc_rwlock_t *nlock = NULL;
2969
0
    isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
2970
0
    bool wild, active;
2971
2972
0
    dns_qpchain_node(&search->chain, i, (void **)&node, NULL);
2973
2974
0
    nlock = qpzone_get_lock(node);
2975
0
    NODE_RDLOCK(nlock, &nlocktype);
2976
    /*
2977
     * First we try to figure out if this node is active in
2978
     * the search's version.  We do this now, even though we
2979
     * may not need the information, because it simplifies the
2980
     * locking and code flow.
2981
     */
2982
0
    active = node_active(search, node);
2983
0
    wild = node->wild;
2984
0
    NODE_UNLOCK(nlock, &nlocktype);
2985
2986
0
    if (wild) {
2987
0
      qpznode_t *wnode = NULL;
2988
0
      dns_fixedname_t fwname;
2989
0
      dns_name_t *wname = dns_fixedname_initname(&fwname);
2990
0
      dns_qpiter_t wit;
2991
0
      bool wactive;
2992
2993
      /*
2994
       * Construct the wildcard name for this level.
2995
       */
2996
0
      result = dns_name_concatenate(dns_wildcardname,
2997
0
                  &node->name, wname);
2998
0
      if (result != ISC_R_SUCCESS) {
2999
0
        break;
3000
0
      }
3001
3002
0
      result = dns_qp_lookup(&search->qpr, wname, nspace,
3003
0
                 &wit, NULL, (void **)&wnode,
3004
0
                 NULL);
3005
0
      if (result == ISC_R_SUCCESS) {
3006
        /*
3007
         * We have found the wildcard node.  If it
3008
         * is active in the search's version, we're
3009
         * done.
3010
         */
3011
0
        nlock = qpzone_get_lock(wnode);
3012
0
        NODE_RDLOCK(nlock, &nlocktype);
3013
0
        wactive = node_active(search, wnode);
3014
0
        NODE_UNLOCK(nlock, &nlocktype);
3015
0
        if (wactive || activeempty(search, &wit, wname))
3016
0
        {
3017
0
          if (wildcard_blocked(search, qname,
3018
0
                   wname))
3019
0
          {
3020
0
            return ISC_R_NOTFOUND;
3021
0
          }
3022
3023
          /*
3024
           * The wildcard node is active!
3025
           *
3026
           * Note: result is still ISC_R_SUCCESS
3027
           * so we don't have to set it.
3028
           */
3029
0
          *nodep = wnode;
3030
0
          break;
3031
0
        }
3032
0
      } else if (result != ISC_R_NOTFOUND &&
3033
0
           result != DNS_R_PARTIALMATCH)
3034
0
      {
3035
        /*
3036
         * An error has occurred.  Bail out.
3037
         */
3038
0
        break;
3039
0
      }
3040
0
    }
3041
3042
0
    if (active) {
3043
      /*
3044
       * The level node is active.  Any wildcarding
3045
       * present at higher levels has no
3046
       * effect and we're done.
3047
       */
3048
0
      result = ISC_R_NOTFOUND;
3049
0
      break;
3050
0
    }
3051
0
  }
3052
3053
0
  return result;
3054
0
}
3055
3056
/*
3057
 * Find node of the NSEC/NSEC3 record preceding 'name'.
3058
 */
3059
static isc_result_t
3060
previous_closest_nsec(dns_rdatatype_t type, qpz_search_t *search,
3061
          dns_name_t *name, qpznode_t **nodep, dns_qpiter_t *nit,
3062
0
          bool *firstp) {
3063
0
  isc_result_t result;
3064
3065
0
  REQUIRE(nodep != NULL && *nodep == NULL);
3066
0
  REQUIRE(type == dns_rdatatype_nsec3 || firstp != NULL);
3067
3068
0
  if (type == dns_rdatatype_nsec3) {
3069
0
    result = dns_qpiter_prev(&search->iter, (void **)nodep, NULL);
3070
0
    if (result == ISC_R_SUCCESS) {
3071
0
      dns_name_copy(&(*nodep)->name, name);
3072
0
    }
3073
0
    return result;
3074
0
  }
3075
3076
0
  for (;;) {
3077
0
    qpznode_t *nsec_node = NULL;
3078
3079
0
    if (*firstp) {
3080
      /*
3081
       * This is the first attempt to find 'name' in the
3082
       * NSEC namespace.
3083
       */
3084
0
      *firstp = false;
3085
0
      result = dns_qp_lookup(&search->qpr, name,
3086
0
                 DNS_DBNAMESPACE_NSEC, nit, NULL,
3087
0
                 NULL, NULL);
3088
3089
0
      INSIST(result != ISC_R_NOTFOUND);
3090
0
      if (result == ISC_R_SUCCESS) {
3091
        /*
3092
         * If we find an exact match in the NSEC
3093
         * namespace on our first attempt, it
3094
         * implies that the corresponding node in
3095
         * the normal namespace had an unacceptable
3096
         * NSEC record; we want the previous node
3097
         * in the NSEC tree.
3098
         */
3099
0
        result = dns_qpiter_prev(
3100
0
          nit, (void **)&nsec_node, NULL);
3101
0
      } else if (result == DNS_R_PARTIALMATCH) {
3102
        /*
3103
         * This was a partial match, so the
3104
         * iterator is already at the previous
3105
         * node in the NSEC namespace, which is
3106
         * what we want.
3107
         */
3108
0
        isc_result_t iresult = dns_qpiter_current(
3109
0
          nit, (void **)&nsec_node, NULL);
3110
0
        REQUIRE(iresult == ISC_R_SUCCESS);
3111
0
        result = ISC_R_SUCCESS;
3112
0
      }
3113
0
    } else {
3114
      /*
3115
       * We've taken at least two steps back through the
3116
       * NSEC namespace. The previous steps must have
3117
       * found nodes with NSEC records, but they didn't
3118
       * work; perhaps they lacked signature records.
3119
       * Keep searching.
3120
       */
3121
0
      result = dns_qpiter_prev(nit, (void **)&nsec_node,
3122
0
             NULL);
3123
0
    }
3124
3125
0
    if (result != ISC_R_SUCCESS) {
3126
0
      break;
3127
0
    }
3128
3129
0
    *nodep = NULL;
3130
0
    result = dns_qp_lookup(&search->qpr, &nsec_node->name,
3131
0
               DNS_DBNAMESPACE_NORMAL, &search->iter,
3132
0
               &search->chain, (void **)nodep, NULL);
3133
0
    if (result == ISC_R_SUCCESS) {
3134
0
      dns_name_copy(&nsec_node->name, name);
3135
0
      break;
3136
0
    }
3137
3138
    /*
3139
     * There should always be a node in the normal namespace
3140
     * with the same name as the node in the NSEC namespace,
3141
     * except when nodes in the NSEC namespace are awaiting
3142
     * deletion.
3143
     */
3144
0
    if (result != DNS_R_PARTIALMATCH && result != ISC_R_NOTFOUND) {
3145
0
      isc_log_write(DNS_LOGCATEGORY_DATABASE,
3146
0
              DNS_LOGMODULE_DB, ISC_LOG_ERROR,
3147
0
              "previous_closest_nsec(): %s",
3148
0
              isc_result_totext(result));
3149
0
      result = DNS_R_BADDB;
3150
0
      break;
3151
0
    }
3152
0
  }
3153
3154
0
  return result;
3155
0
}
3156
3157
/*
3158
 * Find the NSEC/NSEC3 which is at or before the name being sought.
3159
 * For NSEC3 records only NSEC3 records that match the
3160
 * current NSEC3PARAM record are considered.
3161
 */
3162
static isc_result_t
3163
find_closest_nsec(qpz_search_t *search, dns_dbnode_t **nodep,
3164
      dns_name_t *foundname, dns_rdataset_t *rdataset,
3165
      dns_rdataset_t *sigrdataset, bool nsec3,
3166
0
      bool secure DNS__DB_FLARG) {
3167
0
  qpznode_t *node = NULL, *prevnode = NULL;
3168
0
  dns_qpiter_t nseciter;
3169
0
  bool empty_node;
3170
0
  isc_result_t result;
3171
0
  dns_fixedname_t fname;
3172
0
  dns_name_t *name = dns_fixedname_initname(&fname);
3173
0
  dns_rdatatype_t matchtype = nsec3 ? dns_rdatatype_nsec3
3174
0
            : dns_rdatatype_nsec;
3175
0
  dns_typepair_t typepair = DNS_TYPEPAIR(matchtype);
3176
0
  dns_typepair_t sigpair = DNS_SIGTYPEPAIR(matchtype);
3177
0
  bool wraps = nsec3;
3178
0
  bool first = true;
3179
0
  bool need_sig = secure;
3180
3181
  /*
3182
   * When a lookup is unsuccessful, the QP iterator will already
3183
   * be pointing at the node preceding the searched-for name in
3184
   * the normal namespace. We'll check there first, assuming it will
3185
   * be right much of the time. If we don't find an NSEC there,
3186
   * then we start using the auxiliary NSEC namespace to find
3187
   * the next predecessor.
3188
   */
3189
0
  result = dns_qpiter_current(&search->iter, (void **)&node, NULL);
3190
0
  if (result != ISC_R_SUCCESS) {
3191
0
    return result;
3192
0
  }
3193
0
  dns_name_copy(&node->name, name);
3194
0
again:
3195
0
  do {
3196
0
    dns_vecheader_t *found = NULL, *foundsig = NULL;
3197
0
    isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
3198
0
    isc_rwlock_t *nlock = qpzone_get_lock(node);
3199
0
    NODE_RDLOCK(nlock, &nlocktype);
3200
0
    empty_node = true;
3201
0
    ISC_SLIST_FOREACH(top, node->next_type, next_type) {
3202
      /*
3203
       * Look for an active, extant NSEC or RRSIG NSEC.
3204
       */
3205
0
      dns_vecheader_t *header =
3206
0
        first_existing_header(top, search->serial);
3207
0
      if (header != NULL) {
3208
        /*
3209
         * We now know that there is at least one
3210
         * active rdataset at this node.
3211
         */
3212
0
        empty_node = false;
3213
0
        if (top->typepair == typepair) {
3214
0
          found = header;
3215
0
          if (foundsig != NULL) {
3216
0
            break;
3217
0
          }
3218
0
        } else if (top->typepair == sigpair) {
3219
0
          foundsig = header;
3220
0
          if (found != NULL) {
3221
0
            break;
3222
0
          }
3223
0
        }
3224
0
      }
3225
0
    }
3226
0
    if (!empty_node) {
3227
0
      if (found != NULL && search->version->havensec3 &&
3228
0
          found->typepair ==
3229
0
            DNS_TYPEPAIR(dns_rdatatype_nsec3) &&
3230
0
          !matchparams(found, search))
3231
0
      {
3232
0
        empty_node = true;
3233
0
        found = NULL;
3234
0
        foundsig = NULL;
3235
0
        result = previous_closest_nsec(typepair, search,
3236
0
                     name, &prevnode,
3237
0
                     NULL, NULL);
3238
0
      } else if (found != NULL &&
3239
0
           (foundsig != NULL || !need_sig))
3240
0
      {
3241
        /*
3242
         * We've found the right NSEC/NSEC3 record.
3243
         *
3244
         * Note: for this to really be the right
3245
         * NSEC record, it's essential that the NSEC
3246
         * records of any nodes obscured by a zone
3247
         * cut have been removed; we assume this is
3248
         * the case.
3249
         */
3250
0
        dns_name_copy(name, foundname);
3251
0
        if (nodep != NULL) {
3252
0
          qpznode_acquire(
3253
0
            node DNS__DB_FLARG_PASS);
3254
0
          *nodep = (dns_dbnode_t *)node;
3255
0
        }
3256
0
        bindrdataset(search->qpdb, found,
3257
0
               rdataset DNS__DB_FLARG_PASS);
3258
0
        if (foundsig != NULL) {
3259
0
          bindrdataset(
3260
0
            search->qpdb, foundsig,
3261
0
            sigrdataset DNS__DB_FLARG_PASS);
3262
0
        }
3263
0
      } else if (found == NULL && foundsig == NULL) {
3264
        /*
3265
         * This node is active, but has no NSEC or
3266
         * RRSIG NSEC.  That means it's glue or
3267
         * other obscured zone data that isn't
3268
         * relevant for our search.  Treat the
3269
         * node as if it were empty and keep looking.
3270
         */
3271
0
        empty_node = true;
3272
0
        result = previous_closest_nsec(
3273
0
          typepair, search, name, &prevnode,
3274
0
          &nseciter, &first);
3275
0
      } else {
3276
        /*
3277
         * We found an active node, but either the
3278
         * NSEC or the RRSIG NSEC is missing.  This
3279
         * shouldn't happen.
3280
         */
3281
0
        result = DNS_R_BADDB;
3282
0
      }
3283
0
    } else {
3284
      /*
3285
       * This node isn't active.  We've got to keep
3286
       * looking.
3287
       */
3288
0
      result = previous_closest_nsec(typepair, search, name,
3289
0
                   &prevnode, &nseciter,
3290
0
                   &first);
3291
0
    }
3292
0
    NODE_UNLOCK(nlock, &nlocktype);
3293
0
    node = prevnode;
3294
0
    prevnode = NULL;
3295
0
  } while (empty_node && result == ISC_R_SUCCESS);
3296
3297
0
  if (result == ISC_R_NOMORE && wraps) {
3298
0
    result = dns_qpiter_prev(&search->iter, (void **)&node, NULL);
3299
0
    if (result == ISC_R_SUCCESS) {
3300
0
      dns_name_copy(&node->name, name);
3301
0
      wraps = false;
3302
0
      goto again;
3303
0
    }
3304
0
  }
3305
3306
  /*
3307
   * If the result is ISC_R_NOMORE, then we got to the beginning of
3308
   * the database and didn't find a NSEC record.  This shouldn't
3309
   * happen.
3310
   */
3311
0
  if (result == ISC_R_NOMORE) {
3312
0
    result = DNS_R_BADDB;
3313
0
  }
3314
3315
0
  return result;
3316
0
}
3317
3318
static isc_result_t
3319
77
qpzone_check_zonecut(qpznode_t *node, void *arg DNS__DB_FLARG) {
3320
77
  qpz_search_t *search = arg;
3321
77
  dns_vecheader_t *dname_header = NULL, *sigdname_header = NULL;
3322
77
  dns_vecheader_t *ns_header = NULL;
3323
77
  dns_vecheader_t *found = NULL;
3324
77
  isc_result_t result = DNS_R_CONTINUE;
3325
77
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
3326
77
  isc_rwlock_t *nlock = qpzone_get_lock(node);
3327
3328
77
  NODE_RDLOCK(nlock, &nlocktype);
3329
3330
  /*
3331
   * Look for an NS or DNAME rdataset active in our version.
3332
   */
3333
231
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
3334
231
    if (top->typepair == DNS_TYPEPAIR(dns_rdatatype_ns) ||
3335
231
        top->typepair == DNS_TYPEPAIR(dns_rdatatype_dname) ||
3336
154
        top->typepair == DNS_SIGTYPEPAIR(dns_rdatatype_dname))
3337
77
    {
3338
77
      dns_vecheader_t *header =
3339
77
        first_existing_header(top, search->serial);
3340
77
      if (header != NULL) {
3341
77
        if (top->typepair ==
3342
77
            DNS_TYPEPAIR(dns_rdatatype_dname))
3343
0
        {
3344
0
          dname_header = header;
3345
77
        } else if (top->typepair ==
3346
77
             DNS_SIGTYPEPAIR(dns_rdatatype_dname))
3347
0
        {
3348
0
          sigdname_header = header;
3349
77
        } else if (node != search->qpdb->origin ||
3350
77
             IS_STUB(search->qpdb))
3351
0
        {
3352
          /*
3353
           * We've found an NS rdataset that
3354
           * isn't at the origin node.
3355
           */
3356
0
          ns_header = header;
3357
0
        }
3358
77
      }
3359
77
    }
3360
231
  }
3361
3362
  /*
3363
   * Did we find anything?
3364
   */
3365
77
  if (!IS_STUB(search->qpdb) && ns_header != NULL) {
3366
    /*
3367
     * Note that NS has precedence over DNAME if both exist
3368
     * in a zone.  Otherwise DNAME take precedence over NS.
3369
     */
3370
0
    found = ns_header;
3371
0
    search->zonecut_sigheader = NULL;
3372
77
  } else if (dname_header != NULL) {
3373
0
    found = dname_header;
3374
0
    search->zonecut_sigheader = sigdname_header;
3375
77
  } else if (ns_header != NULL) {
3376
0
    found = ns_header;
3377
0
    search->zonecut_sigheader = NULL;
3378
0
  }
3379
3380
77
  if (found != NULL) {
3381
    /*
3382
     * We increment the reference count on node to ensure that
3383
     * search->zonecut_header will still be valid later.
3384
     */
3385
0
    qpznode_acquire(node DNS__DB_FLARG_PASS);
3386
0
    search->zonecut = node;
3387
0
    search->zonecut_header = found;
3388
0
    search->need_cleanup = true;
3389
    /*
3390
     * Since we've found a zonecut, anything beneath it is
3391
     * glue and is not subject to wildcard matching, so we
3392
     * may clear search->wild.
3393
     */
3394
0
    search->wild = false;
3395
0
    if ((search->options & DNS_DBFIND_GLUEOK) == 0) {
3396
      /*
3397
       * If the caller does not want to find glue, then
3398
       * this is the best answer and the search should
3399
       * stop now.
3400
       */
3401
0
      result = DNS_R_PARTIALMATCH;
3402
0
    } else {
3403
0
      dns_name_t *zcname = NULL;
3404
3405
      /*
3406
       * The search will continue beneath the zone cut.
3407
       * This may or may not be the best match.  In case it
3408
       * is, we need to remember the node name.
3409
       */
3410
0
      zcname = dns_fixedname_name(&search->zonecut_name);
3411
0
      dns_name_copy(&node->name, zcname);
3412
0
      search->copy_name = true;
3413
0
    }
3414
77
  } else {
3415
    /*
3416
     * There is no zonecut at this node which is active in this
3417
     * version.
3418
     *
3419
     * If this is a "wild" node and the caller hasn't disabled
3420
     * wildcard matching, remember that we've seen a wild node
3421
     * in case we need to go searching for wildcard matches
3422
     * later on.
3423
     */
3424
77
    if (node->wild && (search->options & DNS_DBFIND_NOWILD) == 0) {
3425
0
      search->wild = true;
3426
0
    }
3427
77
  }
3428
3429
77
  NODE_UNLOCK(nlock, &nlocktype);
3430
3431
77
  return result;
3432
77
}
3433
3434
static void
3435
qpz_search_init(qpz_search_t *search, qpzonedb_t *db, qpz_version_t *version,
3436
209
    unsigned int options) {
3437
  /*
3438
   * qpz_search_t contains two structures with large buffers (dns_qpiter_t
3439
   * and dns_qpchain_t). Those two structures will be initialized later by
3440
   * dns_qp_lookup anyway.
3441
   * To avoid the overhead of zero initialization, we avoid designated
3442
   * initializers and initialize all "small" fields manually.
3443
   */
3444
209
  search->qpdb = db;
3445
209
  search->version = version;
3446
209
  search->qpr = (dns_qpread_t){};
3447
209
  search->serial = version->serial;
3448
209
  search->options = options;
3449
  /*
3450
   * qpch->in -- init in dns_qp_lookup
3451
   * qpiter -- init in dns_qp_lookup
3452
   */
3453
209
  search->copy_name = false;
3454
209
  search->need_cleanup = false;
3455
209
  search->wild = false;
3456
209
  search->zonecut = NULL;
3457
209
  search->zonecut_header = NULL;
3458
209
  search->zonecut_sigheader = NULL;
3459
209
  dns_fixedname_init(&search->zonecut_name);
3460
209
}
3461
3462
static isc_result_t
3463
qpzone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
3464
      dns_rdatatype_t type, unsigned int options,
3465
      isc_stdtime_t now ISC_ATTR_UNUSED, dns_dbnode_t **nodep,
3466
      dns_name_t *foundname,
3467
      dns_clientinfomethods_t *methods ISC_ATTR_UNUSED,
3468
      dns_clientinfo_t *clientinfo ISC_ATTR_UNUSED,
3469
      dns_rdataset_t *rdataset,
3470
209
      dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
3471
209
  isc_result_t result;
3472
209
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
3473
209
  qpznode_t *node = NULL;
3474
209
  bool cname_ok = true, close_version = false;
3475
209
  bool maybe_zonecut = false, at_zonecut = false;
3476
209
  bool wild = false, empty_node = false;
3477
209
  bool nsec3 = false;
3478
209
  dns_vecheader_t *found = NULL, *nsecheader = NULL;
3479
209
  dns_vecheader_t *foundsig = NULL, *cnamesig = NULL, *nsecsig = NULL;
3480
209
  dns_typepair_t sigpair;
3481
209
  bool active;
3482
209
  isc_rwlock_t *nlock = NULL;
3483
209
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
3484
3485
209
  REQUIRE(VALID_QPZONE((qpzonedb_t *)db));
3486
209
  INSIST(version == NULL ||
3487
209
         ((qpz_version_t *)version)->qpdb == (qpzonedb_t *)db);
3488
3489
  /*
3490
   * If the caller didn't supply a version, attach to the current
3491
   * version.
3492
   */
3493
209
  if (version == NULL) {
3494
209
    currentversion(db, &version);
3495
209
    close_version = true;
3496
209
  }
3497
3498
209
  dns_namespace_t nspace;
3499
209
  qpz_search_t search;
3500
209
  qpz_search_init(&search, (qpzonedb_t *)db, (qpz_version_t *)version,
3501
209
      options);
3502
3503
209
  if ((options & DNS_DBFIND_FORCENSEC3) != 0) {
3504
0
    nsec3 = true;
3505
0
    nspace = DNS_DBNAMESPACE_NSEC3;
3506
209
  } else {
3507
209
    nspace = DNS_DBNAMESPACE_NORMAL;
3508
209
  }
3509
209
  dns_qpmulti_query(qpdb->tree, &search.qpr);
3510
3511
  /*
3512
   * Search down from the root of the tree.
3513
   */
3514
209
  result = dns_qp_lookup(&search.qpr, name, nspace, &search.iter,
3515
209
             &search.chain, (void **)&node, NULL);
3516
209
  if (result != ISC_R_NOTFOUND) {
3517
209
    dns_name_copy(&node->name, foundname);
3518
209
  }
3519
3520
  /*
3521
   * Check the QP chain to see if there's a node above us with a
3522
   * active DNAME or NS rdatasets.
3523
   *
3524
   * We're only interested in nodes above QNAME, so if the result
3525
   * was success, then we skip the last item in the chain.
3526
   */
3527
209
  unsigned int clen = dns_qpchain_length(&search.chain);
3528
209
  if (result == ISC_R_SUCCESS) {
3529
132
    clen--;
3530
132
  }
3531
286
  for (unsigned int i = 0; i < clen && search.zonecut == NULL; i++) {
3532
77
    qpznode_t *n = NULL;
3533
77
    isc_result_t tresult;
3534
3535
77
    dns_qpchain_node(&search.chain, i, (void **)&n, NULL);
3536
77
    tresult = qpzone_check_zonecut(n, &search DNS__DB_FLARG_PASS);
3537
77
    if (tresult != DNS_R_CONTINUE) {
3538
0
      result = tresult;
3539
0
      search.chain.len = i - 1;
3540
0
      dns_name_copy(&n->name, foundname);
3541
0
      node = n;
3542
0
    }
3543
77
  }
3544
3545
209
  if (result == DNS_R_PARTIALMATCH) {
3546
77
  partial_match:
3547
77
    if (search.zonecut != NULL) {
3548
0
      result = qpzone_setup_delegation(
3549
0
        &search, nodep, foundname, rdataset,
3550
0
        sigrdataset DNS__DB_FLARG_PASS);
3551
0
      goto tree_exit;
3552
0
    }
3553
3554
77
    if (search.wild) {
3555
      /*
3556
       * At least one of the levels in the search chain
3557
       * potentially has a wildcard.  For each such level,
3558
       * we must see if there's a matching wildcard active
3559
       * in the current version.
3560
       */
3561
0
      result = find_wildcard(&search, &node, name, nspace);
3562
0
      if (result == ISC_R_SUCCESS) {
3563
0
        dns_name_copy(name, foundname);
3564
0
        wild = true;
3565
0
        goto found;
3566
0
      } else if (result != ISC_R_NOTFOUND) {
3567
0
        goto tree_exit;
3568
0
      }
3569
0
    }
3570
3571
77
    active = false;
3572
77
    if (!nsec3) {
3573
      /*
3574
       * The NSEC3 tree won't have empty nodes,
3575
       * so it isn't necessary to check for them.
3576
       */
3577
77
      dns_qpiter_t iter = search.iter;
3578
77
      active = activeempty(&search, &iter, name);
3579
77
    }
3580
3581
    /*
3582
     * If we're here, then the name does not exist, is not
3583
     * beneath a zonecut, and there's no matching wildcard.
3584
     */
3585
77
    if ((search.version->secure && !search.version->havensec3) ||
3586
77
        nsec3)
3587
0
    {
3588
0
      result = find_closest_nsec(
3589
0
        &search, nodep, foundname, rdataset,
3590
0
        sigrdataset, nsec3,
3591
0
        search.version->secure DNS__DB_FLARG_PASS);
3592
0
      if (result == ISC_R_SUCCESS) {
3593
0
        result = active ? DNS_R_EMPTYNAME
3594
0
            : DNS_R_NXDOMAIN;
3595
0
      }
3596
77
    } else {
3597
77
      result = active ? DNS_R_EMPTYNAME : DNS_R_NXDOMAIN;
3598
77
    }
3599
77
    goto tree_exit;
3600
132
  } else if (result != ISC_R_SUCCESS) {
3601
0
    goto tree_exit;
3602
0
  }
3603
3604
132
found:
3605
  /*
3606
   * We have found a node whose name is the desired name, or we
3607
   * have matched a wildcard.
3608
   */
3609
3610
132
  nlock = qpzone_get_lock(node);
3611
132
  NODE_RDLOCK(nlock, &nlocktype);
3612
3613
132
  if (search.zonecut != NULL) {
3614
    /*
3615
     * If we're beneath a zone cut, we don't want to look for
3616
     * CNAMEs because they're not legitimate zone glue.
3617
     */
3618
0
    cname_ok = false;
3619
132
  } else {
3620
    /*
3621
     * The node may be a zone cut itself.  If it might be one,
3622
     * make sure we check for it later.
3623
     *
3624
     * DS records live above the zone cut in ordinary zone so
3625
     * we want to ignore any referral.
3626
     *
3627
     * Stub zones don't have anything "above" the delegation so
3628
     * we always return a referral.
3629
     */
3630
132
    if (node->delegating && ((node != search.qpdb->origin &&
3631
0
            !dns_rdatatype_atparent(type)) ||
3632
0
           IS_STUB(search.qpdb)))
3633
0
    {
3634
0
      maybe_zonecut = true;
3635
0
    }
3636
132
  }
3637
3638
  /*
3639
   * Certain DNSSEC types are not subject to CNAME matching
3640
   * (RFC4035, section 2.5).
3641
   *
3642
   * We don't check for RRSIG, because we don't store RRSIG records
3643
   * directly.
3644
   */
3645
132
  if (type == dns_rdatatype_nsec) {
3646
0
    cname_ok = false;
3647
0
  }
3648
3649
  /*
3650
   * We now go looking for rdata...
3651
   */
3652
3653
132
  sigpair = DNS_SIGTYPEPAIR(type);
3654
132
  empty_node = true;
3655
396
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
3656
    /*
3657
     * Look for an active, extant rdataset.
3658
     */
3659
396
    dns_vecheader_t *header = first_existing_header(top,
3660
396
                search.serial);
3661
396
    if (header != NULL) {
3662
      /*
3663
       * We now know that there is at least one active
3664
       * rdataset at this node.
3665
       */
3666
396
      empty_node = false;
3667
3668
      /*
3669
       * Do special zone cut handling, if requested.
3670
       */
3671
396
      if (maybe_zonecut &&
3672
0
          top->typepair == DNS_TYPEPAIR(dns_rdatatype_ns))
3673
0
      {
3674
        /*
3675
         * We increment the reference count on node to
3676
         * ensure that search->zonecut_header will
3677
         * still be valid later.
3678
         */
3679
0
        qpznode_acquire(node DNS__DB_FLARG_PASS);
3680
0
        search.zonecut = node;
3681
0
        search.zonecut_header = header;
3682
0
        search.zonecut_sigheader = NULL;
3683
0
        search.need_cleanup = true;
3684
0
        maybe_zonecut = false;
3685
0
        at_zonecut = true;
3686
3687
0
        if ((search.options & DNS_DBFIND_GLUEOK) == 0 &&
3688
0
            type != dns_rdatatype_nsec)
3689
0
        {
3690
          /*
3691
           * Glue is not OK, but any answer we
3692
           * could return would be glue.  Return
3693
           * the delegation.
3694
           */
3695
0
          found = NULL;
3696
0
          break;
3697
0
        }
3698
0
        if (found != NULL && foundsig != NULL) {
3699
0
          break;
3700
0
        }
3701
0
      }
3702
3703
      /*
3704
       * If the NSEC3 record doesn't match the chain
3705
       * we are using behave as if it isn't here.
3706
       */
3707
396
      if (top->typepair ==
3708
396
            DNS_TYPEPAIR(dns_rdatatype_nsec3) &&
3709
0
          !matchparams(header, &search))
3710
0
      {
3711
0
        NODE_UNLOCK(nlock, &nlocktype);
3712
0
        goto partial_match;
3713
0
      }
3714
      /*
3715
       * If we found a type we were looking for,
3716
       * remember it.
3717
       */
3718
396
      if (top->typepair == type ||
3719
264
          type == dns_rdatatype_any ||
3720
264
          (top->typepair ==
3721
264
             DNS_TYPEPAIR(dns_rdatatype_cname) &&
3722
0
           cname_ok))
3723
132
      {
3724
        /*
3725
         * We've found the answer!
3726
         */
3727
132
        found = header;
3728
132
        if (top->typepair ==
3729
132
              DNS_TYPEPAIR(dns_rdatatype_cname) &&
3730
0
            cname_ok)
3731
0
        {
3732
          /*
3733
           * We may be finding a CNAME instead
3734
           * of the desired type.
3735
           *
3736
           * If we've already got the CNAME RRSIG,
3737
           * use it, otherwise change sigtype
3738
           * so that we find it.
3739
           */
3740
0
          if (cnamesig != NULL) {
3741
0
            foundsig = cnamesig;
3742
0
          } else {
3743
0
            sigpair = DNS_SIGTYPEPAIR(
3744
0
              dns_rdatatype_cname);
3745
0
          }
3746
0
        }
3747
        /*
3748
         * If we've got all we need, end the search.
3749
         */
3750
132
        if (!maybe_zonecut && foundsig != NULL) {
3751
0
          break;
3752
0
        }
3753
264
      } else if (top->typepair == sigpair) {
3754
        /*
3755
         * We've found the RRSIG rdataset for our
3756
         * target type.  Remember it.
3757
         */
3758
0
        foundsig = header;
3759
        /*
3760
         * If we've got all we need, end the search.
3761
         */
3762
0
        if (!maybe_zonecut && found != NULL) {
3763
0
          break;
3764
0
        }
3765
264
      } else if (top->typepair ==
3766
264
             DNS_TYPEPAIR(dns_rdatatype_nsec) &&
3767
0
           !search.version->havensec3)
3768
0
      {
3769
        /*
3770
         * Remember a NSEC rdataset even if we're
3771
         * not specifically looking for it, because
3772
         * we might need it later.
3773
         */
3774
0
        nsecheader = header;
3775
264
      } else if (top->typepair ==
3776
264
             DNS_SIGTYPEPAIR(
3777
0
               dns_rdatatype_nsec) &&
3778
0
           !search.version->havensec3)
3779
0
      {
3780
        /*
3781
         * If we need the NSEC rdataset, we'll also
3782
         * need its signature.
3783
         */
3784
0
        nsecsig = header;
3785
264
      } else if (cname_ok &&
3786
264
           top->typepair ==
3787
264
             DNS_SIGTYPEPAIR(dns_rdatatype_cname))
3788
0
      {
3789
        /*
3790
         * If we get a CNAME match, we'll also need
3791
         * its signature.
3792
         */
3793
0
        cnamesig = header;
3794
0
      }
3795
396
    }
3796
396
  }
3797
3798
132
  if (empty_node) {
3799
    /*
3800
     * We have an exact match for the name, but there are no
3801
     * active rdatasets in the desired version.  That means that
3802
     * this node doesn't exist in the desired version.
3803
     * If there's a node above this one, reassign the
3804
     * foundname to the parent and treat this as a partial
3805
     * match.
3806
     */
3807
0
    if (!wild) {
3808
0
      unsigned int len = search.chain.len - 1;
3809
0
      if (len > 0) {
3810
0
        NODE_UNLOCK(nlock, &nlocktype);
3811
0
        dns_qpchain_node(&search.chain, len - 1,
3812
0
             (void **)&node, NULL);
3813
0
        dns_name_copy(&node->name, foundname);
3814
0
        goto partial_match;
3815
0
      }
3816
0
    }
3817
0
  }
3818
3819
  /*
3820
   * If we didn't find what we were looking for...
3821
   */
3822
132
  if (found == NULL) {
3823
0
    if (search.zonecut != NULL) {
3824
      /*
3825
       * We were trying to find glue at a node beneath a
3826
       * zone cut, but didn't.
3827
       *
3828
       * Return the delegation.
3829
       */
3830
0
      NODE_UNLOCK(nlock, &nlocktype);
3831
0
      result = qpzone_setup_delegation(
3832
0
        &search, nodep, foundname, rdataset,
3833
0
        sigrdataset DNS__DB_FLARG_PASS);
3834
0
      goto tree_exit;
3835
0
    }
3836
    /*
3837
     * The desired type doesn't exist.
3838
     */
3839
0
    result = DNS_R_NXRRSET;
3840
0
    if (search.version->secure && !search.version->havensec3 &&
3841
0
        (nsecheader == NULL || nsecsig == NULL))
3842
0
    {
3843
      /*
3844
       * The zone is secure but there's no NSEC,
3845
       * or the NSEC has no signature!
3846
       */
3847
0
      if (!wild) {
3848
0
        result = DNS_R_BADDB;
3849
0
        goto node_exit;
3850
0
      }
3851
3852
0
      NODE_UNLOCK(nlock, &nlocktype);
3853
0
      result = find_closest_nsec(
3854
0
        &search, nodep, foundname, rdataset,
3855
0
        sigrdataset, false,
3856
0
        search.version->secure DNS__DB_FLARG_PASS);
3857
0
      if (result == ISC_R_SUCCESS) {
3858
0
        result = DNS_R_EMPTYWILD;
3859
0
      }
3860
0
      goto tree_exit;
3861
0
    }
3862
0
    if (nodep != NULL) {
3863
0
      qpznode_acquire(node DNS__DB_FLARG_PASS);
3864
0
      *nodep = (dns_dbnode_t *)node;
3865
0
    }
3866
0
    if (search.version->secure && !search.version->havensec3) {
3867
0
      bindrdataset(search.qpdb, nsecheader,
3868
0
             rdataset DNS__DB_FLARG_PASS);
3869
0
      if (nsecsig != NULL) {
3870
0
        bindrdataset(search.qpdb, nsecsig,
3871
0
               sigrdataset DNS__DB_FLARG_PASS);
3872
0
      }
3873
0
    }
3874
0
    if (wild) {
3875
0
      foundname->attributes.wildcard = true;
3876
0
    }
3877
0
    goto node_exit;
3878
0
  }
3879
3880
  /*
3881
   * We found what we were looking for, or we found a CNAME.
3882
   */
3883
132
  if (type != found->typepair && type != dns_rdatatype_any &&
3884
0
      found->typepair == DNS_TYPEPAIR(dns_rdatatype_cname))
3885
0
  {
3886
    /*
3887
     * We weren't doing an ANY query and we found a CNAME instead
3888
     * of the type we were looking for, so we need to indicate
3889
     * that result to the caller.
3890
     */
3891
0
    result = DNS_R_CNAME;
3892
132
  } else if (search.zonecut != NULL) {
3893
    /*
3894
     * If we're beneath a zone cut, we must indicate that the
3895
     * result is glue, unless we're actually at the zone cut
3896
     * and the type is NSEC.
3897
     */
3898
0
    if (search.zonecut == node) {
3899
0
      if (dns_rdatatype_isnsec(type)) {
3900
0
        result = ISC_R_SUCCESS;
3901
0
      } else if (type == dns_rdatatype_any) {
3902
0
        result = DNS_R_ZONECUT;
3903
0
      } else {
3904
0
        result = DNS_R_GLUE;
3905
0
      }
3906
0
    } else {
3907
0
      result = DNS_R_GLUE;
3908
0
    }
3909
132
  } else {
3910
    /*
3911
     * An ordinary successful query!
3912
     */
3913
132
    result = ISC_R_SUCCESS;
3914
132
  }
3915
3916
132
  if (nodep != NULL) {
3917
132
    if (!at_zonecut) {
3918
132
      qpznode_acquire(node DNS__DB_FLARG_PASS);
3919
132
    } else {
3920
0
      search.need_cleanup = false;
3921
0
    }
3922
132
    *nodep = (dns_dbnode_t *)node;
3923
132
  }
3924
3925
132
  if (type != dns_rdatatype_any) {
3926
132
    bindrdataset(search.qpdb, found, rdataset DNS__DB_FLARG_PASS);
3927
132
    if (foundsig != NULL) {
3928
0
      bindrdataset(search.qpdb, foundsig,
3929
0
             sigrdataset DNS__DB_FLARG_PASS);
3930
0
    }
3931
132
  }
3932
3933
132
  if (wild) {
3934
0
    foundname->attributes.wildcard = true;
3935
0
  }
3936
3937
132
node_exit:
3938
132
  NODE_UNLOCK(nlock, &nlocktype);
3939
3940
209
tree_exit:
3941
209
  dns_qpread_destroy(qpdb->tree, &search.qpr);
3942
3943
  /*
3944
   * If we found a zonecut but aren't going to use it, we have to
3945
   * let go of it.
3946
   */
3947
209
  if (search.need_cleanup) {
3948
0
    node = search.zonecut;
3949
0
    INSIST(node != NULL);
3950
0
    nlock = qpzone_get_lock(node);
3951
3952
0
    NODE_RDLOCK(nlock, &nlocktype);
3953
0
    (void)qpznode_release(node DNS__DB_FLARG_PASS);
3954
0
    NODE_UNLOCK(nlock, &nlocktype);
3955
0
  }
3956
3957
209
  if (close_version) {
3958
209
    closeversion(db, &version, false DNS__DB_FLARG_PASS);
3959
209
  }
3960
3961
209
  return result;
3962
209
}
3963
3964
static isc_result_t
3965
qpzone_allrdatasets(dns_db_t *db, dns_dbnode_t *dbnode,
3966
        dns_dbversion_t *dbversion, unsigned int options,
3967
        isc_stdtime_t now ISC_ATTR_UNUSED,
3968
0
        dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) {
3969
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
3970
0
  qpznode_t *node = (qpznode_t *)dbnode;
3971
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
3972
0
  qpdb_rdatasetiter_t *iterator = NULL;
3973
3974
0
  REQUIRE(VALID_QPZONE(qpdb));
3975
3976
0
  if (version == NULL) {
3977
0
    currentversion(db, (dns_dbversion_t **)(void *)(&version));
3978
0
  } else {
3979
0
    INSIST(version->qpdb == qpdb);
3980
0
    isc_refcount_increment(&version->references);
3981
0
  }
3982
3983
0
  iterator = isc_mem_get(qpdb->common.mctx, sizeof(*iterator));
3984
0
  *iterator = (qpdb_rdatasetiter_t){
3985
0
    .common.methods = &rdatasetiter_methods,
3986
0
    .common.db = db,
3987
0
    .common.node = (dns_dbnode_t *)node,
3988
0
    .common.version = (dns_dbversion_t *)version,
3989
0
    .common.options = options,
3990
0
    .common.magic = DNS_RDATASETITER_MAGIC,
3991
0
  };
3992
3993
0
  qpznode_acquire(node DNS__DB_FLARG_PASS);
3994
3995
0
  *iteratorp = (dns_rdatasetiter_t *)iterator;
3996
0
  return ISC_R_SUCCESS;
3997
0
}
3998
3999
static void
4000
0
qpzone_attachnode(dns_dbnode_t *source, dns_dbnode_t **targetp DNS__DB_FLARG) {
4001
0
  qpznode_t *node = (qpznode_t *)source;
4002
4003
0
  REQUIRE(targetp != NULL && *targetp == NULL);
4004
4005
0
  qpznode_acquire(node DNS__DB_FLARG_PASS);
4006
4007
0
  *targetp = source;
4008
0
}
4009
4010
static void
4011
140
qpzone_detachnode(dns_dbnode_t **nodep DNS__DB_FLARG) {
4012
140
  qpznode_t *node = NULL;
4013
140
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4014
140
  isc_rwlock_t *nlock = NULL;
4015
4016
140
  REQUIRE(nodep != NULL && *nodep != NULL);
4017
4018
140
  node = (qpznode_t *)(*nodep);
4019
4020
140
  *nodep = NULL;
4021
140
  nlock = qpzone_get_lock(node);
4022
4023
  /*
4024
   * qpzone_destroy() uses call_rcu() API to destroy the node locks, so it
4025
   * is safe to call it in the middle of NODE_LOCK, but we need to acquire
4026
   * the database reference to prevent destroying the database while the
4027
   * NODE_LOCK is locked.
4028
   */
4029
4030
140
  rcu_read_lock();
4031
140
  NODE_RDLOCK(nlock, &nlocktype);
4032
140
  (void)qpznode_release(node DNS__DB_FLARG_PASS);
4033
140
  NODE_UNLOCK(nlock, &nlocktype);
4034
140
  rcu_read_unlock();
4035
140
}
4036
4037
static unsigned int
4038
2
nodecount(dns_db_t *db) {
4039
2
  qpzonedb_t *qpdb = qpdb = (qpzonedb_t *)db;
4040
2
  dns_qp_memusage_t mu;
4041
4042
2
  REQUIRE(VALID_QPZONE(qpdb));
4043
4044
2
  mu = dns_qpmulti_memusage(qpdb->tree);
4045
4046
2
  return mu.leaves;
4047
2
}
4048
4049
static isc_result_t
4050
2
getoriginnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) {
4051
2
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
4052
4053
2
  REQUIRE(VALID_QPZONE(qpdb));
4054
2
  REQUIRE(nodep != NULL && *nodep == NULL);
4055
4056
  /* Note that the access to the origin node doesn't require a DB lock */
4057
2
  INSIST(qpdb->origin != NULL);
4058
2
  qpznode_acquire(qpdb->origin DNS__DB_FLARG_PASS);
4059
2
  *nodep = (dns_dbnode_t *)qpdb->origin;
4060
4061
2
  return ISC_R_SUCCESS;
4062
2
}
4063
4064
/*
4065
 * Rdataset Iterator Methods
4066
 */
4067
4068
static void
4069
0
rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) {
4070
0
  qpdb_rdatasetiter_t *qrditer = NULL;
4071
4072
0
  qrditer = (qpdb_rdatasetiter_t *)(*iteratorp);
4073
4074
0
  if (qrditer->common.version != NULL) {
4075
0
    closeversion(qrditer->common.db, &qrditer->common.version,
4076
0
           false DNS__DB_FLARG_PASS);
4077
0
  }
4078
0
  dns__db_detachnode(&qrditer->common.node DNS__DB_FLARG_PASS);
4079
0
  isc_mem_put(qrditer->common.db->mctx, qrditer, sizeof(*qrditer));
4080
4081
0
  *iteratorp = NULL;
4082
0
}
4083
4084
static isc_result_t
4085
0
rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG) {
4086
0
  qpdb_rdatasetiter_t *qrditer = (qpdb_rdatasetiter_t *)iterator;
4087
0
  qpznode_t *node = (qpznode_t *)qrditer->common.node;
4088
0
  qpz_version_t *version = (qpz_version_t *)qrditer->common.version;
4089
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4090
0
  isc_rwlock_t *nlock = qpzone_get_lock(node);
4091
4092
0
  qrditer->currenttop = NULL;
4093
0
  qrditer->current = NULL;
4094
4095
0
  NODE_RDLOCK(nlock, &nlocktype);
4096
4097
0
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
4098
0
    qrditer->current = first_existing_header(top, version->serial);
4099
4100
0
    if (qrditer->current != NULL) {
4101
0
      qrditer->currenttop = top;
4102
0
      break;
4103
0
    }
4104
0
  }
4105
4106
0
  NODE_UNLOCK(nlock, &nlocktype);
4107
4108
0
  if (qrditer->currenttop == NULL) {
4109
0
    return ISC_R_NOMORE;
4110
0
  }
4111
4112
0
  return ISC_R_SUCCESS;
4113
0
}
4114
4115
static isc_result_t
4116
0
rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG) {
4117
0
  qpdb_rdatasetiter_t *qrditer = (qpdb_rdatasetiter_t *)iterator;
4118
0
  qpznode_t *node = (qpznode_t *)qrditer->common.node;
4119
0
  qpz_version_t *version = (qpz_version_t *)qrditer->common.version;
4120
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4121
0
  isc_rwlock_t *nlock = qpzone_get_lock(node);
4122
0
  dns_vectop_t *from = NULL;
4123
4124
0
  if (qrditer->currenttop == NULL) {
4125
0
    return ISC_R_NOMORE;
4126
0
  }
4127
4128
0
  NODE_RDLOCK(nlock, &nlocktype);
4129
4130
0
  from = ISC_SLIST_NEXT(qrditer->currenttop, next_type);
4131
0
  qrditer->currenttop = NULL;
4132
0
  qrditer->current = NULL;
4133
4134
  /*
4135
   * Find the start of the header chain for the next type.
4136
   */
4137
0
  if (from != NULL) {
4138
0
    ISC_SLIST_FOREACH_FROM(top, node->next_type, next_type, from) {
4139
0
      qrditer->current =
4140
0
        first_existing_header(top, version->serial);
4141
0
      if (qrditer->current != NULL) {
4142
0
        qrditer->currenttop = top;
4143
0
        break;
4144
0
      }
4145
0
    }
4146
0
  }
4147
4148
0
  NODE_UNLOCK(nlock, &nlocktype);
4149
4150
0
  if (qrditer->currenttop == NULL) {
4151
0
    return ISC_R_NOMORE;
4152
0
  }
4153
4154
0
  return ISC_R_SUCCESS;
4155
0
}
4156
4157
static void
4158
rdatasetiter_current(dns_rdatasetiter_t *iterator,
4159
0
         dns_rdataset_t *rdataset DNS__DB_FLARG) {
4160
0
  qpdb_rdatasetiter_t *qrditer = (qpdb_rdatasetiter_t *)iterator;
4161
0
  qpzonedb_t *qpdb = (qpzonedb_t *)(qrditer->common.db);
4162
0
  qpznode_t *qpnode = (qpznode_t *)qrditer->common.node;
4163
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4164
0
  isc_rwlock_t *nlock = qpzone_get_lock(qpnode);
4165
0
  dns_vecheader_t *header = qrditer->current;
4166
4167
0
  REQUIRE(header != NULL);
4168
4169
0
  NODE_RDLOCK(nlock, &nlocktype);
4170
4171
0
  bindrdataset(qpdb, header, rdataset DNS__DB_FLARG_PASS);
4172
4173
0
  NODE_UNLOCK(nlock, &nlocktype);
4174
0
}
4175
4176
/*
4177
 * Database Iterator Methods
4178
 */
4179
static void
4180
0
reference_iter_node(qpdb_dbiterator_t *iter DNS__DB_FLARG) {
4181
0
  qpznode_t *node = iter->node;
4182
4183
0
  if (node == NULL) {
4184
0
    return;
4185
0
  }
4186
4187
0
  qpznode_acquire(node DNS__DB_FLARG_PASS);
4188
0
}
4189
4190
static void
4191
0
dereference_iter_node(qpdb_dbiterator_t *iter DNS__DB_FLARG) {
4192
0
  qpznode_t *node = iter->node;
4193
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4194
0
  isc_rwlock_t *nlock = NULL;
4195
4196
0
  if (node == NULL) {
4197
0
    return;
4198
0
  }
4199
4200
0
  iter->node = NULL;
4201
0
  nlock = qpzone_get_lock(node);
4202
4203
0
  NODE_RDLOCK(nlock, &nlocktype);
4204
0
  (void)qpznode_release(node DNS__DB_FLARG_PASS);
4205
0
  NODE_UNLOCK(nlock, &nlocktype);
4206
0
}
4207
4208
static void
4209
0
dbiterator_destroy(dns_dbiterator_t **iteratorp DNS__DB_FLARG) {
4210
0
  qpdb_dbiterator_t *iter = (qpdb_dbiterator_t *)(*iteratorp);
4211
0
  dns_db_t *db = NULL;
4212
4213
0
  dereference_iter_node(iter DNS__DB_FLARG_PASS);
4214
4215
0
  dns_db_attach(iter->common.db, &db);
4216
0
  dns_db_detach(&iter->common.db);
4217
4218
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
4219
0
  dns_qpsnap_destroy(qpdb->tree, &iter->snap);
4220
4221
0
  isc_mem_put(db->mctx, iter, sizeof(*iter));
4222
0
  dns_db_detach(&db);
4223
4224
0
  *iteratorp = NULL;
4225
0
}
4226
4227
static isc_result_t
4228
0
dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG) {
4229
0
  isc_result_t result;
4230
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4231
0
  qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
4232
4233
0
  if (qpdbiter->result != ISC_R_SUCCESS &&
4234
0
      qpdbiter->result != ISC_R_NOTFOUND &&
4235
0
      qpdbiter->result != DNS_R_PARTIALMATCH &&
4236
0
      qpdbiter->result != ISC_R_NOMORE)
4237
0
  {
4238
0
    return qpdbiter->result;
4239
0
  }
4240
4241
0
  dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4242
4243
0
  dns_qpiter_init(qpdbiter->snap, &qpdbiter->iter);
4244
0
  result = dns_qpiter_next(&qpdbiter->iter, (void **)&qpdbiter->node,
4245
0
         NULL);
4246
4247
0
  switch (qpdbiter->nsec3mode) {
4248
0
  case nonsec3:
4249
0
    if (result == ISC_R_SUCCESS) {
4250
      /*
4251
       * If we immediately hit an NSEC/NSEC3 node,
4252
       * we don't have any non-nsec nodes.
4253
       */
4254
0
      if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NORMAL) {
4255
0
        qpdbiter->node = NULL;
4256
0
        result = ISC_R_NOMORE;
4257
0
      }
4258
0
    }
4259
0
    break;
4260
0
  case full:
4261
    /* skip the NSEC3 origin node. */
4262
0
    if (result == ISC_R_SUCCESS &&
4263
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4264
0
    {
4265
0
      result = dns_qpiter_next(&qpdbiter->iter,
4266
0
             (void **)&qpdbiter->node,
4267
0
             NULL);
4268
0
    }
4269
0
    if (result != ISC_R_SUCCESS) {
4270
0
      qpdbiter->node = NULL;
4271
0
      break;
4272
0
    }
4273
4274
    /*
4275
     * If we hit an NSEC node, we need to start at the NSEC3 part of
4276
     * the tree.
4277
     */
4278
0
    if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NSEC) {
4279
0
      break;
4280
0
    }
4281
0
    INSIST(qpdbiter->node->nspace == DNS_DBNAMESPACE_NSEC);
4282
4283
    /* FALLTHROUGH */
4284
0
  case nsec3only:
4285
    /*
4286
     * NSEC3 follows after all non-nsec3 nodes, seek the NSEC3
4287
     * origin node.
4288
     */
4289
0
    result = dns_qp_lookup(qpdbiter->snap, &qpdb->common.origin,
4290
0
               DNS_DBNAMESPACE_NSEC3, &qpdbiter->iter,
4291
0
               NULL, (void **)&qpdbiter->node, NULL);
4292
0
    if (result != ISC_R_SUCCESS ||
4293
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4294
0
    {
4295
      /* skip the NSEC3 origin node (or its predecessor) */
4296
0
      result = dns_qpiter_next(&qpdbiter->iter,
4297
0
             (void **)&qpdbiter->node,
4298
0
             NULL);
4299
0
    }
4300
0
    break;
4301
0
  default:
4302
0
    UNREACHABLE();
4303
0
  }
4304
4305
0
  if (result == ISC_R_SUCCESS) {
4306
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4307
0
  } else {
4308
0
    qpdbiter->node = NULL;
4309
0
  }
4310
0
  qpdbiter->result = result;
4311
0
  return result;
4312
0
}
4313
4314
static isc_result_t
4315
0
dbiterator_last(dns_dbiterator_t *iterator DNS__DB_FLARG) {
4316
0
  isc_result_t result;
4317
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4318
0
  qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
4319
4320
0
  if (qpdbiter->result != ISC_R_SUCCESS &&
4321
0
      qpdbiter->result != ISC_R_NOTFOUND &&
4322
0
      qpdbiter->result != DNS_R_PARTIALMATCH &&
4323
0
      qpdbiter->result != ISC_R_NOMORE)
4324
0
  {
4325
0
    return qpdbiter->result;
4326
0
  }
4327
4328
0
  dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4329
4330
0
  dns_qpiter_init(qpdbiter->snap, &qpdbiter->iter);
4331
0
  result = dns_qpiter_prev(&qpdbiter->iter, (void **)&qpdbiter->node,
4332
0
         NULL);
4333
4334
0
  switch (qpdbiter->nsec3mode) {
4335
0
  case nsec3only:
4336
0
    if (result == ISC_R_SUCCESS) {
4337
0
      if (QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter)) {
4338
        /* tree only has NSEC3 origin node. */
4339
0
        qpdbiter->node = NULL;
4340
0
        result = ISC_R_NOMORE;
4341
0
      } else if (qpdbiter->node->nspace !=
4342
0
           DNS_DBNAMESPACE_NSEC3)
4343
0
      {
4344
        /* tree has no NSEC3 nodes at all. */
4345
0
        qpdbiter->node = NULL;
4346
0
        result = ISC_R_NOMORE;
4347
0
      }
4348
0
    }
4349
0
    break;
4350
0
  case full:
4351
    /* skip the NSEC3 origin node. */
4352
0
    if (result == ISC_R_SUCCESS &&
4353
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4354
0
    {
4355
0
      result = dns_qpiter_prev(&qpdbiter->iter,
4356
0
             (void **)&qpdbiter->node,
4357
0
             NULL);
4358
0
    }
4359
0
    if (result != ISC_R_SUCCESS) {
4360
0
      qpdbiter->node = NULL;
4361
0
      break;
4362
0
    }
4363
4364
    /*
4365
     * If we hit an NSEC node, we need to seek the final normal node
4366
     * of the tree.
4367
     */
4368
0
    if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NSEC) {
4369
0
      break;
4370
0
    }
4371
0
    INSIST(qpdbiter->node->nspace == DNS_DBNAMESPACE_NSEC);
4372
4373
    /* FALLTHROUGH */
4374
0
  case nonsec3:
4375
    /*
4376
     * The final non-nsec node is before the NSEC origin node.
4377
     */
4378
0
    result = dns_qp_lookup(qpdbiter->snap, &qpdb->common.origin,
4379
0
               DNS_DBNAMESPACE_NSEC, &qpdbiter->iter,
4380
0
               NULL, (void **)&qpdbiter->node, NULL);
4381
0
    if (result == ISC_R_SUCCESS) {
4382
0
      INSIST(QPDBITER_NSEC_ORIGIN_NODE(qpdb, qpdbiter));
4383
      /* skip the NSEC origin node */
4384
0
      result = dns_qpiter_prev(&qpdbiter->iter,
4385
0
             (void **)&qpdbiter->node,
4386
0
             NULL);
4387
0
    } else {
4388
      /*
4389
       * The NSEC origin node was not found, but the iterator
4390
       * should point to its predecessor, which is the node we
4391
       * want.
4392
       */
4393
0
      result = dns_qpiter_current(&qpdbiter->iter,
4394
0
                (void **)&qpdbiter->node,
4395
0
                NULL);
4396
0
      INSIST(result == ISC_R_SUCCESS);
4397
0
      INSIST(qpdbiter->node->nspace ==
4398
0
             DNS_DBNAMESPACE_NORMAL);
4399
0
    }
4400
0
    break;
4401
0
  default:
4402
0
    UNREACHABLE();
4403
0
  }
4404
4405
0
  if (result == ISC_R_SUCCESS) {
4406
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4407
0
  } else {
4408
0
    qpdbiter->node = NULL;
4409
0
  }
4410
0
  qpdbiter->result = result;
4411
0
  return result;
4412
0
}
4413
4414
static isc_result_t
4415
dbiterator_seek(dns_dbiterator_t *iterator,
4416
0
    const dns_name_t *name DNS__DB_FLARG) {
4417
0
  isc_result_t result, tresult;
4418
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4419
4420
0
  if (qpdbiter->result != ISC_R_SUCCESS &&
4421
0
      qpdbiter->result != ISC_R_NOTFOUND &&
4422
0
      qpdbiter->result != DNS_R_PARTIALMATCH &&
4423
0
      qpdbiter->result != ISC_R_NOMORE)
4424
0
  {
4425
0
    return qpdbiter->result;
4426
0
  }
4427
4428
0
  dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4429
4430
0
  switch (qpdbiter->nsec3mode) {
4431
0
  case nsec3only:
4432
0
    result = dns_qp_lookup(qpdbiter->snap, name,
4433
0
               DNS_DBNAMESPACE_NSEC3, &qpdbiter->iter,
4434
0
               NULL, (void **)&qpdbiter->node, NULL);
4435
0
    break;
4436
0
  case nonsec3:
4437
0
    result = dns_qp_lookup(qpdbiter->snap, name,
4438
0
               DNS_DBNAMESPACE_NORMAL, &qpdbiter->iter,
4439
0
               NULL, (void **)&qpdbiter->node, NULL);
4440
0
    break;
4441
0
  case full:
4442
0
    result = dns_qp_lookup(qpdbiter->snap, name,
4443
0
               DNS_DBNAMESPACE_NORMAL, &qpdbiter->iter,
4444
0
               NULL, (void **)&qpdbiter->node, NULL);
4445
0
    if (result != ISC_R_SUCCESS) {
4446
0
      tresult = dns_qp_lookup(qpdbiter->snap, name,
4447
0
            DNS_DBNAMESPACE_NSEC3,
4448
0
            &qpdbiter->iter, NULL,
4449
0
            (void **)&qpdbiter->node, NULL);
4450
0
      if (tresult == ISC_R_SUCCESS) {
4451
0
        result = tresult;
4452
0
      }
4453
0
    }
4454
0
    break;
4455
0
  default:
4456
0
    UNREACHABLE();
4457
0
  }
4458
4459
0
  if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
4460
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4461
0
  } else {
4462
0
    qpdbiter->node = NULL;
4463
0
  }
4464
4465
0
  qpdbiter->result = (result == DNS_R_PARTIALMATCH) ? ISC_R_SUCCESS
4466
0
                : result;
4467
0
  return result;
4468
0
}
4469
4470
static isc_result_t
4471
dbiterator_seek3(dns_dbiterator_t *iterator,
4472
0
     const dns_name_t *name DNS__DB_FLARG) {
4473
0
  isc_result_t result;
4474
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4475
4476
0
  if (qpdbiter->result != ISC_R_SUCCESS &&
4477
0
      qpdbiter->result != ISC_R_NOTFOUND &&
4478
0
      qpdbiter->result != DNS_R_PARTIALMATCH &&
4479
0
      qpdbiter->result != ISC_R_NOMORE)
4480
0
  {
4481
0
    return qpdbiter->result;
4482
0
  }
4483
4484
0
  if (qpdbiter->nsec3mode != nsec3only) {
4485
0
    return ISC_R_NOTIMPLEMENTED;
4486
0
  }
4487
4488
0
  dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4489
4490
0
  result = dns_qp_lookup(qpdbiter->snap, name, DNS_DBNAMESPACE_NSEC3,
4491
0
             &qpdbiter->iter, NULL, (void **)&qpdbiter->node,
4492
0
             NULL);
4493
4494
0
  switch (result) {
4495
0
  case ISC_R_SUCCESS:
4496
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4497
0
    break;
4498
0
  case DNS_R_PARTIALMATCH:
4499
    /* dbiterator_next() will dereference the node */
4500
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4501
4502
0
    result = dbiterator_next(iterator);
4503
0
    if (result == ISC_R_NOMORE) {
4504
0
      result = dbiterator_first(iterator);
4505
0
    }
4506
0
    break;
4507
0
  case ISC_R_NOTFOUND:
4508
0
  default:
4509
0
    break;
4510
0
  }
4511
4512
0
  qpdbiter->result = result;
4513
4514
0
  return qpdbiter->result;
4515
0
}
4516
4517
static isc_result_t
4518
0
dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) {
4519
0
  isc_result_t result;
4520
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4521
0
  qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
4522
0
  qpznode_t *node = NULL;
4523
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4524
0
  isc_rwlock_t *nlock = NULL;
4525
4526
0
  REQUIRE(qpdbiter->node != NULL);
4527
4528
0
  if (qpdbiter->result != ISC_R_SUCCESS) {
4529
0
    return qpdbiter->result;
4530
0
  }
4531
4532
  /*
4533
   * Defer the release of the current node until we have the prev node
4534
   * from the QP tree.
4535
   */
4536
0
  node = qpdbiter->node;
4537
0
  qpdbiter->node = NULL;
4538
4539
0
  result = dns_qpiter_prev(&qpdbiter->iter, (void **)&qpdbiter->node,
4540
0
         NULL);
4541
4542
0
  switch (qpdbiter->nsec3mode) {
4543
0
  case nsec3only:
4544
0
    if (result == ISC_R_SUCCESS) {
4545
0
      if (QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter)) {
4546
        /* we hit the NSEC3 origin node. */
4547
0
        qpdbiter->node = NULL;
4548
0
        result = ISC_R_NOMORE;
4549
0
      } else if (qpdbiter->node->nspace !=
4550
0
           DNS_DBNAMESPACE_NSEC3)
4551
0
      {
4552
        /* we hit a non-NSEC3 node. */
4553
0
        qpdbiter->node = NULL;
4554
0
        result = ISC_R_NOMORE;
4555
0
      }
4556
0
    }
4557
0
    break;
4558
0
  case full:
4559
    /* skip the NSEC3 origin node. */
4560
0
    if (result == ISC_R_SUCCESS &&
4561
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4562
0
    {
4563
0
      result = dns_qpiter_prev(&qpdbiter->iter,
4564
0
             (void **)&qpdbiter->node,
4565
0
             NULL);
4566
0
    }
4567
0
    if (result != ISC_R_SUCCESS) {
4568
0
      qpdbiter->node = NULL;
4569
0
      break;
4570
0
    }
4571
4572
    /*
4573
     * If we hit an NSEC node, we need to seek the final normal node
4574
     * of the tree.
4575
     */
4576
0
    if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NSEC) {
4577
0
      break;
4578
0
    }
4579
4580
0
    INSIST(qpdbiter->node->nspace == DNS_DBNAMESPACE_NSEC);
4581
0
    result = dns_qp_lookup(qpdbiter->snap, &qpdb->common.origin,
4582
0
               DNS_DBNAMESPACE_NSEC, &qpdbiter->iter,
4583
0
               NULL, (void **)&qpdbiter->node, NULL);
4584
4585
0
    if (result == ISC_R_SUCCESS) {
4586
0
      INSIST(QPDBITER_NSEC_ORIGIN_NODE(qpdb, qpdbiter));
4587
      /* skip the NSEC origin node */
4588
0
      result = dns_qpiter_prev(&qpdbiter->iter,
4589
0
             (void **)&qpdbiter->node,
4590
0
             NULL);
4591
0
    } else {
4592
      /*
4593
       * The NSEC origin node was not found, but the iterator
4594
       * should point to its predecessor, which is the node we
4595
       * want.
4596
       */
4597
0
      result = dns_qpiter_current(&qpdbiter->iter,
4598
0
                (void **)&qpdbiter->node,
4599
0
                NULL);
4600
0
      INSIST(result == ISC_R_SUCCESS);
4601
0
      INSIST(qpdbiter->node->nspace ==
4602
0
             DNS_DBNAMESPACE_NORMAL);
4603
0
    }
4604
0
    break;
4605
0
  case nonsec3:
4606
0
    break;
4607
0
  default:
4608
0
    UNREACHABLE();
4609
0
  }
4610
4611
  /*
4612
   * We have the prev node, we can release the previous current.
4613
   */
4614
0
  nlock = qpzone_get_lock(node);
4615
0
  NODE_RDLOCK(nlock, &nlocktype);
4616
0
  (void)qpznode_release(node DNS__DB_FLARG_PASS);
4617
0
  NODE_UNLOCK(nlock, &nlocktype);
4618
4619
0
  if (result == ISC_R_SUCCESS) {
4620
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4621
0
  } else {
4622
0
    qpdbiter->node = NULL;
4623
0
  }
4624
4625
0
  qpdbiter->result = result;
4626
0
  return result;
4627
0
}
4628
4629
static isc_result_t
4630
0
dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG) {
4631
0
  isc_result_t result;
4632
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4633
0
  qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
4634
0
  qpznode_t *node = NULL;
4635
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4636
0
  isc_rwlock_t *nlock = NULL;
4637
4638
0
  REQUIRE(qpdbiter->node != NULL);
4639
4640
0
  if (qpdbiter->result != ISC_R_SUCCESS) {
4641
0
    return qpdbiter->result;
4642
0
  }
4643
4644
  /*
4645
   * Defer the release of the current node until we have the next node
4646
   * from the QP tree.
4647
   */
4648
0
  node = qpdbiter->node;
4649
0
  qpdbiter->node = NULL;
4650
4651
0
  result = dns_qpiter_next(&qpdbiter->iter, (void **)&qpdbiter->node,
4652
0
         NULL);
4653
4654
0
  switch (qpdbiter->nsec3mode) {
4655
0
  case nonsec3:
4656
0
    if (result == ISC_R_SUCCESS) {
4657
      /* we hit an NSEC or NSEC3 node. */
4658
0
      if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NORMAL) {
4659
0
        qpdbiter->node = NULL;
4660
0
        result = ISC_R_NOMORE;
4661
0
      }
4662
0
    }
4663
0
    break;
4664
0
  case full:
4665
    /* skip the NSEC3 origin node. */
4666
0
    if (result == ISC_R_SUCCESS &&
4667
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4668
0
    {
4669
0
      result = dns_qpiter_next(&qpdbiter->iter,
4670
0
             (void **)&qpdbiter->node,
4671
0
             NULL);
4672
0
    }
4673
0
    if (result != ISC_R_SUCCESS) {
4674
0
      qpdbiter->node = NULL;
4675
0
      break;
4676
0
    }
4677
4678
    /*
4679
     * If we hit an NSEC node, we need to start at the NSEC3 part of
4680
     * the tree.
4681
     */
4682
0
    if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NSEC) {
4683
0
      break;
4684
0
    }
4685
0
    INSIST(qpdbiter->node->nspace == DNS_DBNAMESPACE_NSEC);
4686
4687
0
    result = dns_qp_lookup(qpdbiter->snap, &qpdb->common.origin,
4688
0
               DNS_DBNAMESPACE_NSEC3, &qpdbiter->iter,
4689
0
               NULL, (void **)&qpdbiter->node, NULL);
4690
0
    if (result != ISC_R_SUCCESS ||
4691
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4692
0
    {
4693
      /* skip the NSEC3 origin node (or its predecessor). */
4694
0
      result = dns_qpiter_next(&qpdbiter->iter,
4695
0
             (void **)&qpdbiter->node,
4696
0
             NULL);
4697
0
    }
4698
0
    break;
4699
0
  case nsec3only:
4700
0
    break;
4701
0
  default:
4702
0
    UNREACHABLE();
4703
0
  }
4704
4705
  /*
4706
   * We have the next node, we can release the previous current.
4707
   */
4708
0
  nlock = qpzone_get_lock(node);
4709
0
  NODE_RDLOCK(nlock, &nlocktype);
4710
0
  (void)qpznode_release(node DNS__DB_FLARG_PASS);
4711
0
  NODE_UNLOCK(nlock, &nlocktype);
4712
4713
0
  if (result == ISC_R_SUCCESS) {
4714
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4715
0
  } else {
4716
0
    qpdbiter->node = NULL;
4717
0
  }
4718
4719
0
  qpdbiter->result = result;
4720
0
  return result;
4721
0
}
4722
4723
static isc_result_t
4724
dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
4725
0
       dns_name_t *name DNS__DB_FLARG) {
4726
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4727
0
  qpznode_t *node = qpdbiter->node;
4728
4729
0
  REQUIRE(qpdbiter->result == ISC_R_SUCCESS);
4730
0
  REQUIRE(qpdbiter->node != NULL);
4731
4732
0
  if (name != NULL) {
4733
0
    dns_name_copy(&qpdbiter->node->name, name);
4734
0
  }
4735
4736
0
  qpznode_acquire(node DNS__DB_FLARG_PASS);
4737
4738
0
  *nodep = (dns_dbnode_t *)qpdbiter->node;
4739
4740
0
  return ISC_R_SUCCESS;
4741
0
}
4742
4743
static isc_result_t
4744
0
dbiterator_pause(dns_dbiterator_t *iterator ISC_ATTR_UNUSED) {
4745
0
  return ISC_R_SUCCESS;
4746
0
}
4747
4748
static isc_result_t
4749
0
dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) {
4750
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4751
4752
0
  if (qpdbiter->result != ISC_R_SUCCESS) {
4753
0
    return qpdbiter->result;
4754
0
  }
4755
4756
0
  dns_name_copy(dns_rootname, name);
4757
0
  return ISC_R_SUCCESS;
4758
0
}
4759
4760
static isc_result_t
4761
qpzone_createiterator(dns_db_t *db, unsigned int options,
4762
0
          dns_dbiterator_t **iteratorp) {
4763
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
4764
0
  qpdb_dbiterator_t *iter = NULL;
4765
0
  isc_result_t result;
4766
4767
0
  REQUIRE(VALID_QPZONE(qpdb));
4768
4769
0
  iter = isc_mem_get(qpdb->common.mctx, sizeof(*iter));
4770
0
  *iter = (qpdb_dbiterator_t){
4771
0
    .common.magic = DNS_DBITERATOR_MAGIC,
4772
0
    .common.methods = &dbiterator_methods,
4773
0
    .common.relative_names = ((options & DNS_DB_RELATIVENAMES) !=
4774
0
            0),
4775
0
  };
4776
4777
0
  if ((options & DNS_DB_NSEC3ONLY) != 0) {
4778
0
    iter->nsec3mode = nsec3only;
4779
0
  } else if ((options & DNS_DB_NONSEC3) != 0) {
4780
0
    iter->nsec3mode = nonsec3;
4781
0
  } else {
4782
0
    iter->nsec3mode = full;
4783
0
  }
4784
4785
0
  dns_db_attach(db, &iter->common.db);
4786
4787
0
  dns_qpmulti_snapshot(qpdb->tree, &iter->snap);
4788
4789
0
  switch (iter->nsec3mode) {
4790
0
  case nonsec3:
4791
0
  case full:
4792
0
    dns_qpiter_init(iter->snap, &iter->iter);
4793
0
    break;
4794
0
  case nsec3only:
4795
    /*
4796
     * NSEC3 follows after all non-nsec3 nodes,
4797
     * seek the NSEC3 origin node.
4798
     */
4799
0
    result = dns_qp_lookup(iter->snap, &qpdb->common.origin,
4800
0
               DNS_DBNAMESPACE_NSEC3, &iter->iter, NULL,
4801
0
               NULL, NULL);
4802
0
    INSIST(result == ISC_R_SUCCESS);
4803
0
    break;
4804
0
  default:
4805
0
    UNREACHABLE();
4806
0
  }
4807
4808
0
  *iteratorp = (dns_dbiterator_t *)iter;
4809
0
  return ISC_R_SUCCESS;
4810
0
}
4811
4812
/*
4813
 * Main logic to add a new rdataset to a node.
4814
 *
4815
 * The reason this is split from qpzone_addrdataset is to allow the reuse of
4816
 * the same qp transaction for multiple adds.
4817
 *
4818
 * If the rdataset is of type NSEC, 'nsec' must point to the qp trie for the
4819
 * zone, otherwise it must be NULL.
4820
 *
4821
 * qpzone_subtractrdataset doesn't have the same problem since it cannot delete
4822
 * nodes, only rdatasets.
4823
 */
4824
static isc_result_t
4825
qpzone_addrdataset_inner(qpzonedb_t *qpdb, qpznode_t *node,
4826
       dns_dbversion_t *dbversion, dns_rdataset_t *rdataset,
4827
       unsigned int options, dns_rdataset_t *addedrdataset,
4828
0
       dns_qp_t *nsec) {
4829
0
  isc_result_t result;
4830
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
4831
0
  isc_region_t region;
4832
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4833
0
  isc_rwlock_t *nlock = NULL;
4834
0
  dns_fixedname_t fn;
4835
0
  dns_name_t *name = dns_fixedname_initname(&fn);
4836
4837
0
  REQUIRE(VALID_QPZONE(qpdb));
4838
0
  REQUIRE(version != NULL && version->qpdb == qpdb);
4839
4840
  /*
4841
   * SOA records are only allowed at top of zone.
4842
   */
4843
0
  if (rdataset->type == dns_rdatatype_soa && node != qpdb->origin) {
4844
0
    return DNS_R_NOTZONETOP;
4845
0
  }
4846
4847
0
  REQUIRE((node->nspace == DNS_DBNAMESPACE_NSEC3 &&
4848
0
     (rdataset->type == dns_rdatatype_nsec3 ||
4849
0
      rdataset->covers == dns_rdatatype_nsec3)) ||
4850
0
    (node->nspace != DNS_DBNAMESPACE_NSEC3 &&
4851
0
     rdataset->type != dns_rdatatype_nsec3 &&
4852
0
     rdataset->covers != dns_rdatatype_nsec3));
4853
4854
0
  result = dns_rdatavec_fromrdataset(rdataset, node->mctx, &region,
4855
0
             qpdb->maxrrperset);
4856
0
  if (result != ISC_R_SUCCESS) {
4857
0
    if (result == DNS_R_TOOMANYRECORDS) {
4858
0
      dns__db_logtoomanyrecords((dns_db_t *)qpdb, &node->name,
4859
0
              rdataset->type, "adding",
4860
0
              qpdb->maxrrperset);
4861
0
    }
4862
0
    return result;
4863
0
  }
4864
4865
0
  dns_name_copy(&node->name, name);
4866
0
  dns_rdataset_getownercase(rdataset, name);
4867
4868
0
  dns_vecheader_t *newheader = (dns_vecheader_t *)region.base;
4869
4870
0
  dns_vecheader_setownercase(newheader, name);
4871
4872
0
  newheader->ttl = rdataset->ttl;
4873
0
  if (rdataset->ttl == 0U) {
4874
0
    DNS_VECHEADER_SETATTR(newheader, DNS_VECHEADERATTR_ZEROTTL);
4875
0
  }
4876
4877
0
  newheader->serial = version->serial;
4878
0
  if (rdataset->attributes.resign) {
4879
0
    DNS_VECHEADER_SETATTR(newheader, DNS_VECHEADERATTR_RESIGN);
4880
0
    newheader->resign = dns_time64_from32(rdataset->resign);
4881
0
  }
4882
4883
  /*
4884
   * If we're adding a delegation type or adding to the auxiliary NSEC
4885
   * namespace, hold an exclusive lock on the tree.  In the latter case
4886
   * the lock does not necessarily have to be acquired but it will help
4887
   * purge ancient entries more effectively.
4888
   *
4889
   * (Note: node lock must be acquired after starting
4890
   * the QPDB transaction and released before committing.)
4891
   */
4892
0
  nlock = qpzone_get_lock(node);
4893
4894
0
  NODE_WRLOCK(nlock, &nlocktype);
4895
4896
0
  result = ISC_R_SUCCESS;
4897
0
  if (nsec != NULL) {
4898
0
    node->havensec = true;
4899
4900
    /*
4901
     * If it fails, there was already an NSEC node,
4902
     * so we can detach the new one we created and
4903
     * move on.
4904
     */
4905
0
    qpznode_t *nsecnode = new_qpznode(qpdb, name,
4906
0
              DNS_DBNAMESPACE_NSEC);
4907
    /*
4908
     * We don't need a separate transaction since the NSEC tree and
4909
     * the normal tree are part of the same qp-tree.
4910
     */
4911
0
    (void)dns_qp_insert(nsec, nsecnode, 0);
4912
0
    qpznode_detach(&nsecnode);
4913
0
  }
4914
4915
0
  if (result == ISC_R_SUCCESS) {
4916
0
    result = add(qpdb, node, name, version, newheader, options,
4917
0
           false, addedrdataset, 0 DNS__DB_FLARG_PASS);
4918
0
  }
4919
4920
  /*
4921
   * If we're adding a delegation type (e.g. NS or DNAME),
4922
   * then we need to set the callback bit on the node.
4923
   */
4924
0
  if (result == ISC_R_SUCCESS &&
4925
0
      delegating_type(qpdb, node, rdataset->type))
4926
0
  {
4927
0
    node->delegating = true;
4928
0
  }
4929
4930
0
  NODE_UNLOCK(nlock, &nlocktype);
4931
4932
0
  return result;
4933
0
}
4934
4935
static isc_result_t
4936
qpzone_addrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
4937
       dns_dbversion_t *dbversion,
4938
       isc_stdtime_t now ISC_ATTR_UNUSED, dns_rdataset_t *rdataset,
4939
       unsigned int options,
4940
0
       dns_rdataset_t *addedrdataset DNS__DB_FLARG) {
4941
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
4942
0
  qpznode_t *node = (qpznode_t *)dbnode;
4943
0
  dns_qp_t *nsec = NULL;
4944
4945
0
  REQUIRE(VALID_QPZONE(qpdb));
4946
4947
  /*
4948
   * Open a new write transaction if we're adding to the auxiliary
4949
   * NSEC namespace.
4950
   */
4951
0
  if (!node->havensec && rdataset->type == dns_rdatatype_nsec) {
4952
0
    dns_qpmulti_write(qpdb->tree, &nsec);
4953
0
  }
4954
4955
0
  isc_result_t result = qpzone_addrdataset_inner(
4956
0
    qpdb, node, dbversion, rdataset, options, addedrdataset, nsec);
4957
4958
0
  if (nsec != NULL) {
4959
0
    dns_qpmulti_commit(qpdb->tree, &nsec);
4960
0
  }
4961
4962
0
  return result;
4963
0
}
4964
4965
static isc_result_t
4966
qpzone_subtractrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
4967
      dns_dbversion_t *dbversion, dns_rdataset_t *rdataset,
4968
      unsigned int options,
4969
0
      dns_rdataset_t *newrdataset DNS__DB_FLARG) {
4970
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
4971
0
  qpznode_t *node = (qpznode_t *)dbnode;
4972
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
4973
0
  dns_fixedname_t fname;
4974
0
  dns_name_t *nodename = dns_fixedname_initname(&fname);
4975
0
  dns_vectop_t *foundtop = NULL;
4976
0
  dns_vecheader_t *newheader = NULL;
4977
0
  dns_vecheader_t *subresult = NULL;
4978
0
  isc_region_t region;
4979
0
  isc_result_t result;
4980
0
  qpz_changed_t *changed = NULL;
4981
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4982
0
  isc_rwlock_t *nlock;
4983
4984
0
  REQUIRE(VALID_QPZONE(qpdb));
4985
0
  REQUIRE(version != NULL && version->qpdb == qpdb);
4986
4987
0
  REQUIRE((node->nspace == DNS_DBNAMESPACE_NSEC3 &&
4988
0
     (rdataset->type == dns_rdatatype_nsec3 ||
4989
0
      rdataset->covers == dns_rdatatype_nsec3)) ||
4990
0
    (node->nspace != DNS_DBNAMESPACE_NSEC3 &&
4991
0
     rdataset->type != dns_rdatatype_nsec3 &&
4992
0
     rdataset->covers != dns_rdatatype_nsec3));
4993
4994
0
  dns_name_copy(&node->name, nodename);
4995
0
  result = dns_rdatavec_fromrdataset(rdataset, node->mctx, &region, 0);
4996
0
  if (result != ISC_R_SUCCESS) {
4997
0
    return result;
4998
0
  }
4999
5000
0
  newheader = (dns_vecheader_t *)region.base;
5001
0
  newheader->ttl = rdataset->ttl;
5002
0
  atomic_init(&newheader->attributes, 0);
5003
0
  newheader->serial = version->serial;
5004
0
  if (rdataset->attributes.resign) {
5005
0
    DNS_VECHEADER_SETATTR(newheader, DNS_VECHEADERATTR_RESIGN);
5006
0
    newheader->resign = dns_time64_from32(rdataset->resign);
5007
0
  }
5008
5009
0
  nlock = qpzone_get_lock(node);
5010
0
  NODE_WRLOCK(nlock, &nlocktype);
5011
5012
0
  changed = add_changed(qpdb, node, version DNS__DB_FLARG_PASS);
5013
0
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
5014
0
    if (top->typepair == newheader->typepair) {
5015
0
      foundtop = top;
5016
0
      break;
5017
0
    }
5018
0
  }
5019
  /*
5020
   * If header isn't NULL, we've found the right type.  There may be
5021
   * IGNORE rdatasets between the top of the chain and the first real
5022
   * data.  We skip over them.
5023
   */
5024
0
  dns_vecheader_t *header = NULL;
5025
0
  if (foundtop != NULL) {
5026
0
    ISC_SLIST_FOREACH(tmp, foundtop->headers, next_header) {
5027
0
      if (!IGNORE(tmp)) {
5028
0
        header = tmp;
5029
0
        break;
5030
0
      }
5031
0
    }
5032
0
  }
5033
0
  if (header != NULL && EXISTS(header)) {
5034
0
    unsigned int flags = 0;
5035
0
    subresult = NULL;
5036
0
    result = ISC_R_SUCCESS;
5037
0
    if ((options & DNS_DBSUB_EXACT) != 0) {
5038
0
      flags |= DNS_RDATAVEC_EXACT;
5039
0
      if (newheader->ttl != header->ttl) {
5040
0
        result = DNS_R_NOTEXACT;
5041
0
      }
5042
0
    }
5043
0
    if (result == ISC_R_SUCCESS) {
5044
0
      result = dns_rdatavec_subtract(
5045
0
        header, newheader, qpdb->common.mctx,
5046
0
        qpdb->common.rdclass,
5047
0
        DNS_TYPEPAIR_TYPE(foundtop->typepair), flags,
5048
0
        &subresult);
5049
0
    }
5050
0
    if (result == ISC_R_SUCCESS) {
5051
0
      LOCK(&qpdb->heap->lock);
5052
0
      resign_unregister(qpdb->heap, node, newheader);
5053
0
      UNLOCK(&qpdb->heap->lock);
5054
0
      dns_vecheader_unref(newheader);
5055
0
      newheader = subresult;
5056
      /*
5057
       * dns_rdatavec_subtract takes the header from the first
5058
       * argument, so it preserves the case
5059
       */
5060
0
      if (RESIGN(header)) {
5061
0
        DNS_VECHEADER_SETATTR(newheader,
5062
0
                  DNS_VECHEADERATTR_RESIGN);
5063
0
        newheader->resign = header->resign;
5064
0
        LOCK(&qpdb->heap->lock);
5065
0
        resign_register(qpdb->heap, node, newheader);
5066
0
        UNLOCK(&qpdb->heap->lock);
5067
0
      }
5068
      /*
5069
       * We have to set the serial since the rdatavec
5070
       * subtraction routine copies the reserved portion of
5071
       * header, not newheader.
5072
       */
5073
0
      newheader->serial = version->serial;
5074
      /*
5075
       * XXXJT: dns_rdatavec_subtract() copied the pointers
5076
       * to additional info.  We need to clear these fields
5077
       * to avoid having duplicated references.
5078
       */
5079
0
      maybe_update_recordsandsize(true, version, newheader,
5080
0
                nodename->length);
5081
0
    } else if (result == DNS_R_NXRRSET) {
5082
      /*
5083
       * This subtraction would remove all of the rdata;
5084
       * add a nonexistent header instead.
5085
       */
5086
0
      LOCK(&qpdb->heap->lock);
5087
0
      resign_unregister(qpdb->heap, node, newheader);
5088
0
      UNLOCK(&qpdb->heap->lock);
5089
0
      dns_vecheader_unref(newheader);
5090
0
      newheader = dns_vecheader_new(db->mctx);
5091
0
      newheader->ttl = 0;
5092
0
      newheader->typepair = foundtop->typepair;
5093
0
      atomic_init(&newheader->attributes,
5094
0
            DNS_VECHEADERATTR_NONEXISTENT);
5095
0
      newheader->serial = version->serial;
5096
0
    } else {
5097
0
      LOCK(&qpdb->heap->lock);
5098
0
      resign_unregister(qpdb->heap, node, newheader);
5099
0
      UNLOCK(&qpdb->heap->lock);
5100
0
      dns_vecheader_unref(newheader);
5101
0
      goto unlock;
5102
0
    }
5103
5104
    /*
5105
     * If we're here, we want to link newheader at the top.
5106
     */
5107
0
    maybe_update_recordsandsize(false, version, header,
5108
0
              nodename->length);
5109
5110
0
    ISC_SLIST_PREPEND(foundtop->headers, newheader, next_header);
5111
5112
0
    node->dirty = true;
5113
0
    changed->dirty = true;
5114
0
    LOCK(&qpdb->heap->lock);
5115
0
    resign_unregister(qpdb->heap, node, header);
5116
0
    UNLOCK(&qpdb->heap->lock);
5117
0
    resign_rollback(qpdb, node, version, header DNS__DB_FLARG_PASS);
5118
0
  } else {
5119
    /*
5120
     * The rdataset doesn't exist, so we don't need to do anything
5121
     * to satisfy the deletion request.
5122
     */
5123
0
    LOCK(&qpdb->heap->lock);
5124
0
    resign_unregister(qpdb->heap, node, newheader);
5125
0
    UNLOCK(&qpdb->heap->lock);
5126
0
    dns_vecheader_unref(newheader);
5127
0
    if ((options & DNS_DBSUB_EXACT) != 0) {
5128
0
      result = DNS_R_NOTEXACT;
5129
0
    } else {
5130
0
      result = DNS_R_UNCHANGED;
5131
0
    }
5132
0
  }
5133
5134
0
  if (result == ISC_R_SUCCESS && newrdataset != NULL) {
5135
0
    bindrdataset(qpdb, newheader, newrdataset DNS__DB_FLARG_PASS);
5136
0
  }
5137
5138
0
  if (result == DNS_R_NXRRSET && newrdataset != NULL &&
5139
0
      (options & DNS_DBSUB_WANTOLD) != 0)
5140
0
  {
5141
0
    bindrdataset(qpdb, header, newrdataset DNS__DB_FLARG_PASS);
5142
0
  }
5143
5144
0
unlock:
5145
0
  NODE_UNLOCK(nlock, &nlocktype);
5146
0
  return result;
5147
0
}
5148
5149
static isc_result_t
5150
qpzone_deleterdataset(dns_db_t *db, dns_dbnode_t *dbnode,
5151
          dns_dbversion_t *dbversion, dns_rdatatype_t type,
5152
0
          dns_rdatatype_t covers DNS__DB_FLARG) {
5153
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5154
0
  qpznode_t *node = (qpznode_t *)dbnode;
5155
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
5156
0
  dns_fixedname_t fname;
5157
0
  dns_name_t *nodename = dns_fixedname_initname(&fname);
5158
0
  isc_result_t result;
5159
0
  dns_vecheader_t *newheader = NULL;
5160
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
5161
0
  isc_rwlock_t *nlock = NULL;
5162
5163
0
  REQUIRE(VALID_QPZONE(qpdb));
5164
0
  REQUIRE(version != NULL && version->qpdb == qpdb);
5165
5166
0
  if (type == dns_rdatatype_any) {
5167
0
    return ISC_R_NOTIMPLEMENTED;
5168
0
  }
5169
0
  if (type == dns_rdatatype_rrsig && covers == dns_rdatatype_none) {
5170
0
    return ISC_R_NOTIMPLEMENTED;
5171
0
  }
5172
5173
0
  newheader = dns_vecheader_new(db->mctx);
5174
0
  newheader->typepair = DNS_TYPEPAIR_VALUE(type, covers);
5175
0
  newheader->ttl = 0;
5176
0
  atomic_init(&newheader->attributes, DNS_VECHEADERATTR_NONEXISTENT);
5177
0
  newheader->serial = version->serial;
5178
5179
0
  dns_name_copy(&node->name, nodename);
5180
5181
0
  nlock = qpzone_get_lock(node);
5182
0
  NODE_WRLOCK(nlock, &nlocktype);
5183
0
  result = add(qpdb, node, nodename, version, newheader, DNS_DBADD_FORCE,
5184
0
         false, NULL, 0 DNS__DB_FLARG_PASS);
5185
0
  NODE_UNLOCK(nlock, &nlocktype);
5186
0
  return result;
5187
0
}
5188
5189
static dns_glue_t *
5190
0
new_glue(isc_mem_t *mctx, const dns_name_t *name) {
5191
0
  dns_glue_t *glue = isc_mem_get(mctx, sizeof(*glue));
5192
0
  *glue = (dns_glue_t){
5193
0
    .name = DNS_NAME_INITEMPTY,
5194
0
  };
5195
5196
0
  dns_name_dup(name, mctx, &glue->name);
5197
5198
0
  return glue;
5199
0
}
5200
5201
static dns_gluelist_t *
5202
new_gluelist(dns_db_t *db, dns_vecheader_t *header,
5203
0
       const dns_dbversion_t *dbversion) {
5204
0
  dns_gluelist_t *gluelist = isc_mem_get(db->mctx, sizeof(*gluelist));
5205
0
  *gluelist = (dns_gluelist_t){
5206
0
    .version = dbversion,
5207
0
    .header = header,
5208
0
  };
5209
5210
0
  isc_mem_attach(db->mctx, &gluelist->mctx);
5211
5212
0
  cds_wfs_node_init(&gluelist->wfs_node);
5213
5214
0
  return gluelist;
5215
0
}
5216
5217
static isc_result_t
5218
glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
5219
0
    dns_rdataset_t *rdataset ISC_ATTR_UNUSED DNS__DB_FLARG) {
5220
0
  dns_glue_additionaldata_ctx_t *ctx = arg;
5221
0
  isc_result_t result;
5222
0
  dns_fixedname_t fixedname_a;
5223
0
  dns_name_t *name_a = NULL;
5224
0
  dns_rdataset_t rdataset_a, sigrdataset_a;
5225
0
  dns_fixedname_t fixedname_aaaa;
5226
0
  dns_name_t *name_aaaa = NULL;
5227
0
  dns_rdataset_t rdataset_aaaa, sigrdataset_aaaa;
5228
0
  dns_glue_t *glue = NULL;
5229
5230
  /*
5231
   * NS records want addresses in additional records.
5232
   */
5233
0
  INSIST(qtype == dns_rdatatype_a);
5234
5235
0
  name_a = dns_fixedname_initname(&fixedname_a);
5236
0
  dns_rdataset_init(&rdataset_a);
5237
0
  dns_rdataset_init(&sigrdataset_a);
5238
5239
0
  name_aaaa = dns_fixedname_initname(&fixedname_aaaa);
5240
0
  dns_rdataset_init(&rdataset_aaaa);
5241
0
  dns_rdataset_init(&sigrdataset_aaaa);
5242
5243
0
  result = qpzone_find(ctx->db, name, ctx->version, dns_rdatatype_a,
5244
0
           DNS_DBFIND_GLUEOK, 0, NULL, name_a, NULL, NULL,
5245
0
           &rdataset_a, &sigrdataset_a DNS__DB_FLARG_PASS);
5246
0
  if (result == DNS_R_GLUE) {
5247
0
    glue = new_glue(ctx->db->mctx, name_a);
5248
5249
    /*
5250
     * Move the header out of the rdataset, transferring
5251
     * ownership of the reference to the glue structure.
5252
     */
5253
0
    glue->header_a = dns_vecheader_moveheader(&rdataset_a);
5254
0
    if (dns_rdataset_isassociated(&sigrdataset_a)) {
5255
0
      glue->sigheader_a =
5256
0
        dns_vecheader_moveheader(&sigrdataset_a);
5257
0
    }
5258
0
  }
5259
5260
0
  result = qpzone_find(ctx->db, name, ctx->version, dns_rdatatype_aaaa,
5261
0
           DNS_DBFIND_GLUEOK, 0, NULL, name_aaaa, NULL, NULL,
5262
0
           &rdataset_aaaa,
5263
0
           &sigrdataset_aaaa DNS__DB_FLARG_PASS);
5264
0
  if (result == DNS_R_GLUE) {
5265
0
    if (glue == NULL) {
5266
0
      glue = new_glue(ctx->db->mctx, name_aaaa);
5267
0
    } else {
5268
0
      INSIST(dns_name_equal(name_a, name_aaaa));
5269
0
    }
5270
5271
    /*
5272
     * Move the header out of the rdataset, transferring
5273
     * ownership of the reference to the glue structure.
5274
     */
5275
0
    glue->header_aaaa = dns_vecheader_moveheader(&rdataset_aaaa);
5276
0
    if (dns_rdataset_isassociated(&sigrdataset_aaaa)) {
5277
0
      glue->sigheader_aaaa =
5278
0
        dns_vecheader_moveheader(&sigrdataset_aaaa);
5279
0
    }
5280
0
  }
5281
5282
  /*
5283
   * If the currently processed NS record is in-bailiwick, mark the
5284
   * glue as 'required'.  The flag is later propagated to the rdataset
5285
   * attributes when the glue is bound.  Note that for simplicity, glue
5286
   * for all in-bailiwick NS records is marked this way, even though
5287
   * dns_message_rendersection() only checks the attributes for the
5288
   * first rdataset associated with the first name added to the
5289
   * ADDITIONAL section.
5290
   */
5291
0
  if (glue != NULL && dns_name_issubdomain(name, ctx->owner_name)) {
5292
0
    glue->required = true;
5293
0
  }
5294
5295
0
  if (glue != NULL) {
5296
0
    glue->next = ctx->glue;
5297
0
    ctx->glue = glue;
5298
0
  }
5299
5300
  /*
5301
   * Clean up any rdatasets that were not consumed by moveheader().
5302
   * This handles the ISC_R_SUCCESS (in-zone, not glue) case where
5303
   * the rdataset is associated but its header was not moved into
5304
   * the glue structure.
5305
   */
5306
0
  dns_rdataset_cleanup(&rdataset_a);
5307
0
  dns_rdataset_cleanup(&sigrdataset_a);
5308
0
  dns_rdataset_cleanup(&rdataset_aaaa);
5309
0
  dns_rdataset_cleanup(&sigrdataset_aaaa);
5310
5311
0
  return ISC_R_SUCCESS;
5312
0
}
5313
5314
/*
5315
 * This calls bindrdataset, so it must be protected by an rcu read lock.
5316
 */
5317
static void
5318
0
addglue_to_message(qpzonedb_t *qpdb, dns_glue_t *ge, dns_message_t *msg) {
5319
0
  for (; ge != NULL; ge = ge->next) {
5320
0
    dns_name_t *name = NULL;
5321
0
    dns_rdataset_t *rdataset_a = NULL;
5322
0
    dns_rdataset_t *sigrdataset_a = NULL;
5323
0
    dns_rdataset_t *rdataset_aaaa = NULL;
5324
0
    dns_rdataset_t *sigrdataset_aaaa = NULL;
5325
0
    bool prepend_name = false;
5326
5327
0
    dns_message_gettempname(msg, &name);
5328
5329
0
    dns_name_copy(&ge->name, name);
5330
5331
0
    if (ge->header_a != NULL) {
5332
0
      dns_message_gettemprdataset(msg, &rdataset_a);
5333
0
    }
5334
5335
0
    if (ge->sigheader_a != NULL) {
5336
0
      dns_message_gettemprdataset(msg, &sigrdataset_a);
5337
0
    }
5338
5339
0
    if (ge->header_aaaa != NULL) {
5340
0
      dns_message_gettemprdataset(msg, &rdataset_aaaa);
5341
0
    }
5342
5343
0
    if (ge->sigheader_aaaa != NULL) {
5344
0
      dns_message_gettemprdataset(msg, &sigrdataset_aaaa);
5345
0
    }
5346
5347
0
    if (rdataset_a != NULL) {
5348
0
      bindrdataset(qpdb, ge->header_a, rdataset_a);
5349
0
      ISC_LIST_APPEND(name->list, rdataset_a, link);
5350
0
      if (ge->required) {
5351
0
        rdataset_a->attributes.required = true;
5352
0
        prepend_name = true;
5353
0
      }
5354
0
    }
5355
5356
0
    if (sigrdataset_a != NULL) {
5357
0
      bindrdataset(qpdb, ge->sigheader_a, sigrdataset_a);
5358
0
      ISC_LIST_APPEND(name->list, sigrdataset_a, link);
5359
0
    }
5360
5361
0
    if (rdataset_aaaa != NULL) {
5362
0
      bindrdataset(qpdb, ge->header_aaaa, rdataset_aaaa);
5363
0
      ISC_LIST_APPEND(name->list, rdataset_aaaa, link);
5364
0
      if (ge->required) {
5365
0
        rdataset_aaaa->attributes.required = true;
5366
0
        prepend_name = true;
5367
0
      }
5368
0
    }
5369
0
    if (sigrdataset_aaaa != NULL) {
5370
0
      bindrdataset(qpdb, ge->sigheader_aaaa,
5371
0
             sigrdataset_aaaa);
5372
0
      ISC_LIST_APPEND(name->list, sigrdataset_aaaa, link);
5373
0
    }
5374
5375
0
    dns_message_addname(msg, name, DNS_SECTION_ADDITIONAL);
5376
5377
    /*
5378
     * When looking for required glue, dns_message_rendersection()
5379
     * only processes the first rdataset associated with the first
5380
     * name added to the ADDITIONAL section.  dns_message_addname()
5381
     * performs an append on the list of names in a given section,
5382
     * so if any glue record was marked as required, we need to
5383
     * move the name it is associated with to the beginning of the
5384
     * list for the ADDITIONAL section or else required glue might
5385
     * not be rendered.
5386
     */
5387
0
    if (prepend_name) {
5388
0
      ISC_LIST_UNLINK(msg->sections[DNS_SECTION_ADDITIONAL],
5389
0
          name, link);
5390
0
      ISC_LIST_PREPEND(msg->sections[DNS_SECTION_ADDITIONAL],
5391
0
           name, link);
5392
0
    }
5393
0
  }
5394
0
}
5395
5396
static dns_gluelist_t *
5397
create_gluelist(qpzonedb_t *qpdb, qpz_version_t *version,
5398
0
    const dns_name_t *owner_name, dns_rdataset_t *rdataset) {
5399
0
  dns_vecheader_t *header = dns_vecheader_getheader(rdataset);
5400
0
  dns_glue_additionaldata_ctx_t ctx = {
5401
0
    .db = (dns_db_t *)qpdb,
5402
0
    .version = (dns_dbversion_t *)version,
5403
0
    .owner_name = owner_name,
5404
0
  };
5405
0
  dns_gluelist_t *gluelist = new_gluelist(ctx.db, header, ctx.version);
5406
5407
  /*
5408
   * Get the owner name of the NS RRset - it will be necessary for
5409
   * identifying required glue in glue_nsdname_cb() (by
5410
   * determining which NS records in the delegation are
5411
   * in-bailiwick).
5412
   */
5413
5414
0
  (void)dns_rdataset_additionaldata(rdataset, dns_rootname,
5415
0
            glue_nsdname_cb, &ctx, 0);
5416
5417
0
  CMM_STORE_SHARED(gluelist->glue, ctx.glue);
5418
5419
0
  return gluelist;
5420
0
}
5421
5422
static void
5423
addglue(dns_db_t *db, dns_dbversion_t *dbversion, const dns_name_t *owner_name,
5424
0
  dns_rdataset_t *rdataset, dns_message_t *msg) {
5425
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5426
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
5427
0
  dns_vecheader_t *header = dns_vecheader_getheader(rdataset);
5428
0
  dns_glue_t *glue = NULL;
5429
0
  isc_statscounter_t counter = dns_gluecachestatscounter_hits_absent;
5430
5431
0
  REQUIRE(rdataset->type == dns_rdatatype_ns);
5432
0
  REQUIRE(qpdb == version->qpdb);
5433
0
  REQUIRE(!IS_STUB(qpdb));
5434
5435
0
  rcu_read_lock();
5436
5437
0
  dns_gluelist_t *gluelist = rcu_dereference(header->gluelist);
5438
0
  if (gluelist == NULL || gluelist->version != dbversion) {
5439
    /* No or old glue list was found in the table. */
5440
5441
0
    dns_gluelist_t *xchg_gluelist = gluelist;
5442
0
    dns_gluelist_t *old_gluelist = (void *)-1;
5443
0
    dns_gluelist_t *new_gluelist =
5444
0
      create_gluelist(qpdb, version, owner_name, rdataset);
5445
5446
0
    while (old_gluelist != xchg_gluelist &&
5447
0
           (xchg_gluelist == NULL ||
5448
0
      xchg_gluelist->version != dbversion))
5449
0
    {
5450
0
      old_gluelist = xchg_gluelist;
5451
0
      xchg_gluelist = rcu_cmpxchg_pointer(
5452
0
        &header->gluelist, old_gluelist, new_gluelist);
5453
0
    }
5454
5455
0
    if (old_gluelist == xchg_gluelist) {
5456
      /* CAS was successful */
5457
0
      cds_wfs_push(&version->glue_stack,
5458
0
             &new_gluelist->wfs_node);
5459
0
      gluelist = new_gluelist;
5460
0
    } else {
5461
0
      destroy_gluelist(&new_gluelist);
5462
0
      gluelist = xchg_gluelist;
5463
0
    }
5464
0
  }
5465
5466
0
  glue = CMM_LOAD_SHARED(gluelist->glue);
5467
5468
0
  if (glue != NULL) {
5469
0
    addglue_to_message(qpdb, glue, msg);
5470
0
    counter = dns_gluecachestatscounter_hits_present;
5471
0
  }
5472
5473
0
  rcu_read_unlock();
5474
5475
  /* We have a cached result. Add it to the message and return. */
5476
0
  if (qpdb->gluecachestats != NULL) {
5477
0
    isc_stats_increment(qpdb->gluecachestats, counter);
5478
0
  }
5479
0
}
5480
5481
static void
5482
2
setmaxrrperset(dns_db_t *db, uint32_t value) {
5483
2
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5484
5485
2
  REQUIRE(VALID_QPZONE(qpdb));
5486
5487
2
  qpdb->maxrrperset = value;
5488
2
}
5489
5490
static void
5491
2
setmaxtypepername(dns_db_t *db, uint32_t value) {
5492
2
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5493
5494
2
  REQUIRE(VALID_QPZONE(qpdb));
5495
5496
2
  qpdb->maxtypepername = value;
5497
2
}
5498
5499
/*
5500
 * Qpzone specialization of the update function from dns_rdatacallbacks_t,
5501
 * meant to reuse the same qp transaction for multiple operations.
5502
 */
5503
static isc_result_t
5504
qpzone_update_rdataset(qpzonedb_t *qpdb, qpz_version_t *version, dns_qp_t *qp,
5505
0
           dns_name_t *name, dns_rdataset_t *rds, dns_diffop_t op) {
5506
0
  isc_result_t result;
5507
0
  unsigned int options;
5508
0
  dns_rdataset_t ardataset;
5509
0
  qpznode_t *node = NULL;
5510
0
  dns_qp_t *nsec = NULL;
5511
0
  bool is_nsec3;
5512
5513
0
  dns_rdataset_init(&ardataset);
5514
5515
0
  is_nsec3 = (rds->type == dns_rdatatype_nsec3 ||
5516
0
        rds->covers == dns_rdatatype_nsec3);
5517
5518
0
  result = findnodeintree(qpdb, qp, name, true, is_nsec3,
5519
0
        (dns_dbnode_t **)&node DNS__DB_FLARG_PASS);
5520
5521
0
  if (result != ISC_R_SUCCESS) {
5522
0
    goto failure;
5523
0
  }
5524
5525
0
  switch (op) {
5526
0
  case DNS_DIFFOP_ADD:
5527
0
  case DNS_DIFFOP_ADDRESIGN:
5528
    /*
5529
     * See the comment on qpzone_addrdataset_inner for why we
5530
     * cannot use qpzone_addrdataset directly.
5531
     */
5532
0
    options = DNS_DBADD_MERGE | DNS_DBADD_EXACT |
5533
0
        DNS_DBADD_EXACTTTL;
5534
0
    if (!node->havensec && rds->type == dns_rdatatype_nsec) {
5535
0
      nsec = qp;
5536
0
    }
5537
0
    result = qpzone_addrdataset_inner(
5538
0
      qpdb, node, (dns_dbversion_t *)version, rds, options,
5539
0
      &ardataset, nsec DNS__DB_FLARG_PASS);
5540
0
    break;
5541
0
  case DNS_DIFFOP_DEL:
5542
0
  case DNS_DIFFOP_DELRESIGN:
5543
0
    options = DNS_DBSUB_EXACT | DNS_DBSUB_WANTOLD;
5544
0
    result = qpzone_subtractrdataset(
5545
0
      (dns_db_t *)qpdb, (dns_dbnode_t *)node,
5546
0
      (dns_dbversion_t *)version, rds, options,
5547
0
      &ardataset DNS__DB_FLARG_PASS);
5548
0
    break;
5549
0
  default:
5550
0
    UNREACHABLE();
5551
0
  }
5552
5553
0
  bool is_resign = rds->type == dns_rdatatype_rrsig &&
5554
0
       (op == DNS_DIFFOP_DELRESIGN ||
5555
0
        op == DNS_DIFFOP_ADDRESIGN);
5556
5557
0
  if (result == ISC_R_SUCCESS && is_resign) {
5558
0
    isc_stdtime_t resign;
5559
0
    resign = dns_rdataset_minresign(&ardataset);
5560
0
    dns_db_setsigningtime((dns_db_t *)qpdb, (dns_dbnode_t *)node,
5561
0
              &ardataset, resign);
5562
0
  }
5563
5564
0
failure:
5565
0
  if (node != NULL) {
5566
0
    dns_db_detachnode((dns_dbnode_t **)&node);
5567
0
  }
5568
0
  dns_rdataset_cleanup(&ardataset);
5569
0
  return result;
5570
0
}
5571
5572
static isc_result_t
5573
qpzone_update_callback(void *arg, const dns_name_t *name, dns_rdataset_t *rds,
5574
0
           dns_diffop_t op DNS__DB_FLARG) {
5575
0
  qpzone_updatectx_t *ctx = arg;
5576
0
  qpzonedb_t *qpdb = (qpzonedb_t *)ctx->base.db;
5577
0
  qpz_version_t *version = (qpz_version_t *)ctx->base.ver;
5578
5579
0
  return qpzone_update_rdataset(qpdb, version, ctx->qp,
5580
0
              (dns_name_t *)name, rds, op);
5581
0
}
5582
5583
static isc_result_t
5584
qpzone_beginupdate(dns_db_t *db, dns_dbversion_t *ver,
5585
0
       dns_rdatacallbacks_t *callbacks) {
5586
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5587
5588
0
  REQUIRE(VALID_QPZONE(qpdb));
5589
0
  REQUIRE(ver != NULL);
5590
0
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
5591
5592
0
  qpzone_updatectx_t *ctx = isc_mem_get(qpdb->common.mctx, sizeof(*ctx));
5593
0
  *ctx = (qpzone_updatectx_t){
5594
0
    .base.db = db,
5595
0
    .base.ver = ver,
5596
0
    .base.warn = true,
5597
0
    .qpr = (dns_qpread_t){ 0 },
5598
0
  };
5599
0
  ctx->qp = begin_transaction(qpdb, &ctx->qpr, true);
5600
5601
0
  callbacks->update = qpzone_update_callback;
5602
0
  callbacks->add_private = ctx;
5603
5604
0
  return ISC_R_SUCCESS;
5605
0
}
5606
5607
static isc_result_t
5608
0
qpzone_commitupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
5609
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5610
0
  qpzone_updatectx_t *ctx;
5611
5612
0
  REQUIRE(VALID_QPZONE(qpdb));
5613
0
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
5614
5615
0
  ctx = (qpzone_updatectx_t *)callbacks->add_private;
5616
0
  if (ctx != NULL) {
5617
0
    end_transaction(qpdb, ctx->qp, true);
5618
5619
0
    isc_mem_put(qpdb->common.mctx, ctx, sizeof(*ctx));
5620
    /*
5621
     * We need to allow the context to be committed or aborted
5622
     * multiple times, so we set the callback data to NULL
5623
     * to signal that nothing needs to be done with this context.
5624
     */
5625
0
    callbacks->add_private = NULL;
5626
0
  }
5627
5628
0
  return ISC_R_SUCCESS;
5629
0
}
5630
5631
/*
5632
 * For now, abortupdate needs to *commit* the results, so that closeversion
5633
 * cleanup might work.
5634
 */
5635
static isc_result_t
5636
0
qpzone_abortupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
5637
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5638
0
  qpzone_updatectx_t *ctx;
5639
5640
0
  REQUIRE(VALID_QPZONE(qpdb));
5641
0
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
5642
5643
0
  ctx = (qpzone_updatectx_t *)callbacks->add_private;
5644
0
  if (ctx != NULL) {
5645
0
    end_transaction(qpdb, ctx->qp, true);
5646
5647
0
    isc_mem_put(qpdb->common.mctx, ctx, sizeof(*ctx));
5648
    /*
5649
     * See qpzone_commitupdate.
5650
     */
5651
0
    callbacks->add_private = NULL;
5652
0
  }
5653
5654
0
  return ISC_R_SUCCESS;
5655
0
}
5656
5657
static dns_dbmethods_t qpdb_zonemethods = {
5658
  .destroy = qpdb_destroy,
5659
  .beginload = beginload,
5660
  .endload = endload,
5661
  .beginupdate = qpzone_beginupdate,
5662
  .commitupdate = qpzone_commitupdate,
5663
  .abortupdate = qpzone_abortupdate,
5664
  .currentversion = currentversion,
5665
  .newversion = newversion,
5666
  .attachversion = attachversion,
5667
  .closeversion = closeversion,
5668
  .findnode = qpzone_findnode,
5669
  .find = qpzone_find,
5670
  .createiterator = qpzone_createiterator,
5671
  .findrdataset = qpzone_findrdataset,
5672
  .allrdatasets = qpzone_allrdatasets,
5673
  .addrdataset = qpzone_addrdataset,
5674
  .subtractrdataset = qpzone_subtractrdataset,
5675
  .deleterdataset = qpzone_deleterdataset,
5676
  .issecure = issecure,
5677
  .nodecount = nodecount,
5678
  .getoriginnode = getoriginnode,
5679
  .getnsec3parameters = getnsec3parameters,
5680
  .findnsec3node = qpzone_findnsec3node,
5681
  .setsigningtime = setsigningtime,
5682
  .getsigningtime = getsigningtime,
5683
  .getsize = getsize,
5684
  .setgluecachestats = setgluecachestats,
5685
  .addglue = addglue,
5686
  .setmaxrrperset = setmaxrrperset,
5687
  .setmaxtypepername = setmaxtypepername,
5688
};
5689
5690
static dns_dbnode_methods_t qpznode_methods = (dns_dbnode_methods_t){
5691
  .attachnode = qpzone_attachnode,
5692
  .detachnode = qpzone_detachnode,
5693
};
5694
5695
static void
5696
10.6M
destroy_qpznode(qpznode_t *node) {
5697
10.6M
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
5698
9.01M
    ISC_SLIST_FOREACH(header, top->headers, next_header) {
5699
9.01M
      dns_vecheader_unref(header);
5700
9.01M
    }
5701
5702
9.01M
    dns_vectop_destroy(node->mctx, &top);
5703
9.01M
  }
5704
5705
10.6M
  dns_name_free(&node->name, node->mctx);
5706
10.6M
  isc_mem_putanddetach(&node->mctx, node, sizeof(qpznode_t));
5707
10.6M
}
5708
5709
#if DNS_DB_NODETRACE
5710
ISC_REFCOUNT_STATIC_TRACE_IMPL(qpznode, destroy_qpznode);
5711
#else
5712
42.7M
ISC_REFCOUNT_STATIC_IMPL(qpznode, destroy_qpznode);
qpzone.c:qpznode_ref
Line
Count
Source
5712
ISC_REFCOUNT_STATIC_IMPL(qpznode, destroy_qpznode);
qpzone.c:qpznode_detach
Line
Count
Source
5712
ISC_REFCOUNT_STATIC_IMPL(qpznode, destroy_qpznode);
qpzone.c:qpznode_unref
Line
Count
Source
5712
ISC_REFCOUNT_STATIC_IMPL(qpznode, destroy_qpznode);
5713
42.7M
#endif
5714
42.7M
5715
42.7M
ISC_REFCOUNT_STATIC_IMPL(qpz_heap, qpz_heap_destroy);
qpzone.c:qpz_heap_detach
Line
Count
Source
5715
ISC_REFCOUNT_STATIC_IMPL(qpz_heap, qpz_heap_destroy);
qpzone.c:qpz_heap_unref
Line
Count
Source
5715
ISC_REFCOUNT_STATIC_IMPL(qpz_heap, qpz_heap_destroy);
5716
35.2k
5717
35.2k
static void
5718
35.2k
qp_attach(void *uctx ISC_ATTR_UNUSED, void *pval,
5719
10.6M
    uint32_t ival ISC_ATTR_UNUSED) {
5720
10.6M
  qpznode_t *data = pval;
5721
10.6M
  qpznode_ref(data);
5722
10.6M
}
5723
5724
static void
5725
qp_detach(void *uctx ISC_ATTR_UNUSED, void *pval,
5726
10.6M
    uint32_t ival ISC_ATTR_UNUSED) {
5727
10.6M
  qpznode_t *data = pval;
5728
10.6M
  qpznode_detach(&data);
5729
10.6M
}
5730
5731
static size_t
5732
qp_makekey(dns_qpkey_t key, void *uctx ISC_ATTR_UNUSED, void *pval,
5733
27.0M
     uint32_t ival ISC_ATTR_UNUSED) {
5734
27.0M
  qpznode_t *data = pval;
5735
27.0M
  return dns_qpkey_fromname(key, &data->name, data->nspace);
5736
27.0M
}
5737
5738
static void
5739
0
qp_triename(void *uctx ISC_ATTR_UNUSED, char *buf, size_t size) {
5740
0
  snprintf(buf, size, "QPDB");
5741
0
}