Coverage Report

Created: 2026-07-16 07:05

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.4k
#define QPDB_ATTR_LOADED  0x01
71
34.9k
#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.4k
#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
11.6M
qpzone_get_lock(qpznode_t *node) {
486
11.6M
  return &qpzone_buckets_g[node->locknum].lock;
487
11.6M
}
488
489
static uint16_t
490
11.8M
qpzone_get_locknum(void) {
491
11.8M
  return isc_random_uniform(ARRAY_SIZE(qpzone_buckets_g));
492
11.8M
}
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.4k
cleanup_gluelists(struct cds_wfs_stack *glue_stack) {
543
17.4k
  struct cds_wfs_head *head = __cds_wfs_pop_all(glue_stack);
544
17.4k
  struct cds_wfs_node *node = NULL, *next = NULL;
545
546
17.4k
  rcu_read_lock();
547
17.4k
  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.4k
  rcu_read_unlock();
557
17.4k
}
558
559
static void
560
17.4k
free_db_rcu(struct rcu_head *rcu_head) {
561
17.4k
  qpzonedb_t *qpdb = caa_container_of(rcu_head, qpzonedb_t, rcu_head);
562
563
17.4k
  if (dns_name_dynamic(&qpdb->common.origin)) {
564
17.4k
    dns_name_free(&qpdb->common.origin, qpdb->common.mctx);
565
17.4k
  }
566
567
17.4k
  qpz_heap_detach(&qpdb->heap);
568
569
17.4k
  if (qpdb->gluecachestats != NULL) {
570
0
    isc_stats_detach(&qpdb->gluecachestats);
571
0
  }
572
573
17.4k
  isc_rwlock_destroy(&qpdb->lock);
574
17.4k
  isc_refcount_destroy(&qpdb->common.references);
575
576
17.4k
  qpdb->common.magic = 0;
577
17.4k
  qpdb->common.impmagic = 0;
578
579
17.4k
  if (qpdb->common.update_listeners != NULL) {
580
17.4k
    INSIST(!cds_lfht_destroy(qpdb->common.update_listeners, NULL));
581
17.4k
  }
582
583
17.4k
  isc_mem_putanddetach(&qpdb->common.mctx, qpdb, sizeof(*qpdb));
584
17.4k
}
585
586
static void
587
17.4k
qpzone_destroy(qpzonedb_t *qpdb) {
588
17.4k
  REQUIRE(qpdb->future_version == NULL);
589
590
17.4k
  isc_refcount_decrementz(&qpdb->current_version->references);
591
592
17.4k
  isc_refcount_destroy(&qpdb->current_version->references);
593
17.4k
  ISC_LIST_UNLINK(qpdb->open_versions, qpdb->current_version, link);
594
17.4k
  cds_wfs_destroy(&qpdb->current_version->glue_stack);
595
17.4k
  isc_rwlock_destroy(&qpdb->current_version->rwlock);
596
17.4k
  isc_mem_put(qpdb->common.mctx, qpdb->current_version,
597
17.4k
        sizeof(*qpdb->current_version));
598
599
17.4k
  dns_qpmulti_destroy(&qpdb->tree);
600
601
17.4k
  char buf[DNS_NAME_FORMATSIZE];
602
17.4k
  if (dns_name_dynamic(&qpdb->common.origin)) {
603
17.4k
    dns_name_format(&qpdb->common.origin, buf, sizeof(buf));
604
17.4k
  } else {
605
0
    strlcpy(buf, "<UNKNOWN>", sizeof(buf));
606
0
  }
607
17.4k
  isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DB,
608
17.4k
          ISC_LOG_DEBUG(1), "called %s(%s)", __func__, buf);
609
610
17.4k
  call_rcu(&qpdb->rcu_head, free_db_rcu);
611
17.4k
}
612
613
static void
614
17.4k
qpdb_destroy(dns_db_t *arg) {
615
17.4k
  qpzonedb_t *qpdb = (qpzonedb_t *)arg;
616
617
17.4k
  if (qpdb->origin != NULL) {
618
17.4k
    qpznode_detach(&qpdb->origin);
619
17.4k
  }
620
17.4k
  if (qpdb->nsec_origin != NULL) {
621
17.4k
    qpznode_detach(&qpdb->nsec_origin);
622
17.4k
  }
623
17.4k
  if (qpdb->nsec3_origin != NULL) {
624
17.4k
    qpznode_detach(&qpdb->nsec3_origin);
625
17.4k
  }
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.4k
  if (qpdb->current_version != NULL) {
633
17.4k
    cleanup_gluelists(&qpdb->current_version->glue_stack);
634
17.4k
  }
635
636
17.4k
  qpzone_destroy(qpdb);
637
17.4k
}
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
168k
resign_hash(dns_vecheader_t *header) {
696
168k
  uintptr_t headerptr = (uintptr_t)header;
697
168k
  return isc_hash32(&headerptr, sizeof(headerptr), true);
698
168k
}
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
168k
resign_unregister(qpz_heap_t *heap, qpznode_t *node, dns_vecheader_t *header) {
742
168k
  if (header == NULL) {
743
0
    return;
744
0
  }
745
746
168k
  qpz_resign_t *found_elem = NULL;
747
168k
  uint32_t hashval = resign_hash(header);
748
168k
  qpz_resign_t search_elem = {
749
168k
    .header = header,
750
168k
    .node = node,
751
168k
  };
752
168k
  isc_result_t result = isc_hashmap_find(heap->hashmap, hashval,
753
168k
                 resign_match, &search_elem,
754
168k
                 (void **)&found_elem);
755
756
168k
  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
168k
}
763
764
static qpz_heap_t *
765
17.4k
new_qpz_heap(isc_mem_t *mctx) {
766
17.4k
  qpz_heap_t *new_heap = isc_mem_get(mctx, sizeof(*new_heap));
767
17.4k
  *new_heap = (qpz_heap_t){
768
17.4k
    .references = ISC_REFCOUNT_INITIALIZER(1),
769
17.4k
  };
770
771
17.4k
  isc_mutex_init(&new_heap->lock);
772
17.4k
  isc_heap_create(mctx, resign_sooner, set_index, 0, &new_heap->heap);
773
17.4k
  isc_mem_attach(mctx, &new_heap->mctx);
774
775
17.4k
  isc_hashmap_create(mctx, 1u, &new_heap->hashmap);
776
777
17.4k
  return new_heap;
778
17.4k
}
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.4k
qpz_heap_destroy(qpz_heap_t *qpheap) {
797
  /* Clean up hashmap entries */
798
17.4k
  isc_hashmap_iter_t *iter = NULL;
799
17.4k
  isc_hashmap_iter_create(qpheap->hashmap, &iter);
800
17.4k
  for (isc_result_t result = isc_hashmap_iter_first(iter);
801
17.4k
       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.4k
  isc_hashmap_iter_destroy(&iter);
809
17.4k
  isc_hashmap_destroy(&qpheap->hashmap);
810
811
17.4k
  isc_mutex_destroy(&qpheap->lock);
812
17.4k
  isc_heap_destroy(&qpheap->heap);
813
17.4k
  isc_mem_putanddetach(&qpheap->mctx, qpheap, sizeof(*qpheap));
814
17.4k
}
815
816
static qpznode_t *
817
11.8M
new_qpznode(qpzonedb_t *qpdb, const dns_name_t *name, dns_namespace_t nspace) {
818
11.8M
  qpznode_t *newdata = isc_mem_get(qpdb->common.mctx, sizeof(*newdata));
819
11.8M
  *newdata = (qpznode_t){
820
11.8M
    .next_type = ISC_SLIST_INITIALIZER,
821
11.8M
    .methods = &qpznode_methods,
822
11.8M
    .name = DNS_NAME_INITEMPTY,
823
11.8M
    .nspace = nspace,
824
11.8M
    .references = ISC_REFCOUNT_INITIALIZER(1),
825
11.8M
    .locknum = qpzone_get_locknum(),
826
11.8M
  };
827
828
11.8M
  isc_mem_attach(qpdb->common.mctx, &newdata->mctx);
829
11.8M
  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
11.8M
  return newdata;
836
11.8M
}
837
838
static qpz_version_t *
839
allocate_version(isc_mem_t *mctx, uint32_t serial, unsigned int references,
840
17.4k
     bool writer) {
841
17.4k
  qpz_version_t *version = isc_mem_get(mctx, sizeof(*version));
842
17.4k
  *version = (qpz_version_t){
843
17.4k
    .serial = serial,
844
17.4k
    .writer = writer,
845
17.4k
    .changed_list = ISC_LIST_INITIALIZER,
846
17.4k
    .resigned_list = ISC_LIST_INITIALIZER,
847
17.4k
    .link = ISC_LINK_INITIALIZER,
848
17.4k
    .references = ISC_REFCOUNT_INITIALIZER(references),
849
17.4k
  };
850
851
17.4k
  cds_wfs_init(&version->glue_stack);
852
17.4k
  isc_rwlock_init(&version->rwlock);
853
854
17.4k
  return version;
855
17.4k
}
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.4k
       dns_db_t **dbp) {
862
17.4k
  qpzonedb_t *qpdb = NULL;
863
17.4k
  isc_result_t result;
864
17.4k
  dns_qp_t *qp = NULL;
865
866
17.4k
  qpdb = isc_mem_get(mctx, sizeof(*qpdb));
867
17.4k
  *qpdb = (qpzonedb_t){
868
17.4k
    .common.origin = DNS_NAME_INITEMPTY,
869
17.4k
    .common.rdclass = rdclass,
870
17.4k
    .common.references = ISC_REFCOUNT_INITIALIZER(1),
871
17.4k
    .current_serial = 1,
872
17.4k
    .least_serial = 1,
873
17.4k
    .next_serial = 2,
874
17.4k
    .open_versions = ISC_LIST_INITIALIZER,
875
17.4k
  };
876
877
17.4k
  qpdb->common.methods = &qpdb_zonemethods;
878
17.4k
  if (type == dns_dbtype_stub) {
879
0
    qpdb->common.attributes |= DNS_DBATTR_STUB;
880
0
  }
881
882
17.4k
  isc_rwlock_init(&qpdb->lock);
883
884
17.4k
  qpdb->common.update_listeners = cds_lfht_new(16, 16, 0, 0, NULL);
885
886
17.4k
  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.4k
  isc_mem_attach(mctx, &qpdb->common.mctx);
894
895
  /*
896
   * Make a copy of the origin name.
897
   */
898
17.4k
  dns_name_dup(origin, mctx, &qpdb->common.origin);
899
900
17.4k
  dns_qpmulti_create(mctx, &qpmethods, qpdb, &qpdb->tree);
901
902
  /*
903
   * Version initialization.
904
   */
905
17.4k
  qpdb->current_version = allocate_version(mctx, 1, 1, false);
906
17.4k
  qpdb->current_version->qpdb = qpdb;
907
908
17.4k
  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.4k
  qpdb->origin = new_qpznode(qpdb, &qpdb->common.origin,
922
17.4k
           DNS_DBNAMESPACE_NORMAL);
923
924
17.4k
  result = dns_qp_insert(qp, qpdb->origin, 0);
925
17.4k
  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.4k
  qpdb->nsec_origin = new_qpznode(qpdb, &qpdb->common.origin,
932
17.4k
          DNS_DBNAMESPACE_NSEC);
933
17.4k
  result = dns_qp_insert(qp, qpdb->nsec_origin, 0);
934
17.4k
  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.4k
  qpdb->nsec3_origin = new_qpznode(qpdb, &qpdb->common.origin,
942
17.4k
           DNS_DBNAMESPACE_NSEC3);
943
17.4k
  result = dns_qp_insert(qp, qpdb->nsec3_origin, 0);
944
17.4k
  INSIST(result == ISC_R_SUCCESS);
945
946
17.4k
  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.4k
  ISC_LIST_PREPEND(qpdb->open_versions, qpdb->current_version, link);
953
954
17.4k
  qpdb->common.magic = DNS_DB_MAGIC;
955
17.4k
  qpdb->common.impmagic = QPZONE_DB_MAGIC;
956
957
17.4k
  *dbp = (dns_db_t *)qpdb;
958
959
17.4k
  return ISC_R_SUCCESS;
960
17.4k
}
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
125
qpznode_erefs_increment(qpznode_t *node DNS__DB_FLARG) {
972
125
  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
125
  if (refs > 0) {
979
0
    return;
980
0
  }
981
125
}
982
983
static void
984
125
qpznode_acquire(qpznode_t *node DNS__DB_FLARG) {
985
125
  qpznode_ref(node);
986
125
  qpznode_erefs_increment(node DNS__DB_FLARG_PASS);
987
125
}
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
10.4M
first_existing_header(dns_vectop_t *top, uint32_t serial) {
996
10.4M
  ISC_SLIST_FOREACH(header, top->headers, next_header) {
997
10.4M
    if (header->serial <= serial && !IGNORE(header)) {
998
10.4M
      if (EXISTS(header)) {
999
10.4M
        return header;
1000
10.4M
      }
1001
0
      break;
1002
10.4M
    }
1003
10.4M
  }
1004
0
  return NULL;
1005
10.4M
}
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
125
qpznode_erefs_decrement(qpznode_t *node DNS__DB_FLARG) {
1119
125
  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
125
  if (refs > 1) {
1126
0
    return false;
1127
0
  }
1128
1129
125
  return true;
1130
125
}
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
125
qpznode_release(qpznode_t *node DNS__DB_FLARG) {
1146
125
  bool has_erefs = false;
1147
1148
125
  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
125
  if (!node->dirty && !ISC_SLIST_EMPTY(node->next_type)) {
1155
125
    has_erefs = true;
1156
125
    goto unref;
1157
125
  }
1158
1159
125
unref:
1160
125
  qpznode_unref(node);
1161
125
  return has_erefs;
1162
125
}
1163
1164
static void
1165
bindrdataset(qpzonedb_t *qpdb, dns_vecheader_t *header,
1166
10.4M
       dns_rdataset_t *rdataset DNS__DB_FLARG) {
1167
10.4M
  if (rdataset == NULL) {
1168
10.4M
    return;
1169
10.4M
  }
1170
1171
177
  INSIST(rdataset->methods == NULL); /* We must be disassociated. */
1172
1173
177
  rdataset->methods = &dns_rdatavec_rdatasetmethods;
1174
177
  rdataset->rdclass = qpdb->common.rdclass;
1175
177
  rdataset->type = DNS_TYPEPAIR_TYPE(header->typepair);
1176
177
  rdataset->covers = DNS_TYPEPAIR_COVERS(header->typepair);
1177
177
  rdataset->ttl = header->ttl;
1178
177
  rdataset->trust = atomic_load_acquire(&header->trust);
1179
1180
177
  if (OPTOUT(header)) {
1181
0
    rdataset->attributes.optout = true;
1182
0
  }
1183
1184
177
  rdataset->vec.header = header;
1185
177
  dns_vecheader_ref(header);
1186
177
  rdataset->vec.iter.iter_pos = NULL;
1187
177
  rdataset->vec.iter.iter_count = 0;
1188
1189
  /*
1190
   * Add noqname proof.
1191
   */
1192
1193
  /*
1194
   * Copy out re-signing information.
1195
   */
1196
177
  if (RESIGN(header)) {
1197
0
    rdataset->attributes.resign = true;
1198
0
    rdataset->resign = header->resign;
1199
177
  } else {
1200
177
    rdataset->resign = 0;
1201
177
  }
1202
177
}
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.4k
setsecure(dns_db_t *db, qpz_version_t *version, dns_dbnode_t *origin) {
1293
17.4k
  dns_rdataset_t keyset;
1294
17.4k
  dns_rdataset_t nsecset, signsecset;
1295
17.4k
  bool haszonekey = false;
1296
17.4k
  bool hasnsec = false;
1297
17.4k
  isc_result_t result;
1298
1299
17.4k
  version->secure = false;
1300
17.4k
  version->havensec3 = false;
1301
1302
17.4k
  dns_rdataset_init(&keyset);
1303
17.4k
  result = dns_db_findrdataset(db, origin, (dns_dbversion_t *)version,
1304
17.4k
             dns_rdatatype_dnskey, 0, 0, &keyset, NULL);
1305
17.4k
  if (result == ISC_R_SUCCESS) {
1306
46
    haszonekey = dns_dnssec_haszonekey(&keyset);
1307
46
    dns_rdataset_disassociate(&keyset);
1308
46
  }
1309
17.4k
  if (!haszonekey) {
1310
17.4k
    return;
1311
17.4k
  }
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
191
currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
1339
191
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
1340
191
  qpz_version_t *version = NULL;
1341
1342
191
  REQUIRE(VALID_QPZONE(qpdb));
1343
1344
191
  RWLOCK(&qpdb->lock, isc_rwlocktype_read);
1345
191
  version = qpdb->current_version;
1346
191
  isc_refcount_increment(&version->references);
1347
191
  RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
1348
1349
191
  *versionp = (dns_dbversion_t *)version;
1350
191
}
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
191
       bool commit DNS__DB_FLARG) {
1437
191
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
1438
191
  qpz_version_t *version = NULL, *cleanup_version = NULL;
1439
191
  qpz_version_t *least_greater = NULL;
1440
191
  qpznode_t *node = NULL;
1441
191
  bool rollback = false;
1442
191
  qpz_changedlist_t cleanup_list;
1443
191
  qpz_resignedlist_t resigned_list;
1444
191
  uint32_t serial, least_serial;
1445
1446
191
  REQUIRE(VALID_QPZONE(qpdb));
1447
191
  version = (qpz_version_t *)*versionp;
1448
191
  INSIST(version->qpdb == qpdb);
1449
1450
191
  if (isc_refcount_decrement(&version->references) > 1) {
1451
191
    *versionp = NULL;
1452
191
    return;
1453
191
  }
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.5k
        dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
1675
17.5k
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
1676
17.5k
  qpznode_t *node = (qpznode_t *)dbnode;
1677
17.5k
  dns_vecheader_t *found = NULL, *foundsig = NULL;
1678
17.5k
  uint32_t serial;
1679
17.5k
  qpz_version_t *version = (qpz_version_t *)dbversion;
1680
17.5k
  bool close_version = false;
1681
17.5k
  dns_typepair_t typepair, sigpair;
1682
17.5k
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
1683
17.5k
  isc_rwlock_t *nlock = NULL;
1684
1685
17.5k
  REQUIRE(VALID_QPZONE(qpdb));
1686
17.5k
  REQUIRE(type != dns_rdatatype_any);
1687
17.5k
  INSIST(version == NULL || version->qpdb == qpdb);
1688
1689
17.5k
  if (type == dns_rdatatype_none && covers == dns_rdatatype_none) {
1690
0
    return ISC_R_NOTFOUND;
1691
0
  }
1692
1693
17.5k
  if (version == NULL) {
1694
4
    currentversion(db, (dns_dbversion_t **)&version);
1695
4
    close_version = true;
1696
4
  }
1697
17.5k
  serial = version->serial;
1698
1699
17.5k
  nlock = qpzone_get_lock(node);
1700
17.5k
  NODE_RDLOCK(nlock, &nlocktype);
1701
1702
17.5k
  typepair = DNS_TYPEPAIR_VALUE(type, covers);
1703
17.5k
  if (covers == dns_rdatatype_none) {
1704
17.5k
    sigpair = DNS_SIGTYPEPAIR(type);
1705
17.5k
  } else {
1706
0
    sigpair = dns_typepair_none;
1707
0
  }
1708
1709
17.5k
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
1710
862
    dns_vecheader_t *header = first_existing_header(top, serial);
1711
862
    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
862
      if (top->typepair == typepair) {
1717
57
        found = header;
1718
57
        if (foundsig != NULL) {
1719
4
          break;
1720
4
        }
1721
805
      } else if (top->typepair == sigpair) {
1722
10
        foundsig = header;
1723
10
        if (found != NULL) {
1724
3
          break;
1725
3
        }
1726
10
      }
1727
862
    }
1728
862
  }
1729
17.5k
  if (found != NULL) {
1730
57
    bindrdataset(qpdb, found, rdataset DNS__DB_FLARG_PASS);
1731
57
    if (foundsig != NULL) {
1732
7
      bindrdataset(qpdb, foundsig,
1733
7
             sigrdataset DNS__DB_FLARG_PASS);
1734
7
    }
1735
57
  }
1736
1737
17.5k
  NODE_UNLOCK(nlock, &nlocktype);
1738
1739
17.5k
  if (close_version) {
1740
4
    closeversion(db, (dns_dbversion_t **)&version,
1741
4
           false DNS__DB_FLARG_PASS);
1742
4
  }
1743
1744
17.5k
  if (found == NULL) {
1745
17.4k
    return ISC_R_NOTFOUND;
1746
17.4k
  }
1747
1748
57
  return ISC_R_SUCCESS;
1749
17.5k
}
1750
1751
static bool
1752
10.4M
delegating_type(qpzonedb_t *qpdb, qpznode_t *node, dns_typepair_t typepair) {
1753
10.4M
  return typepair == DNS_TYPEPAIR(dns_rdatatype_dname) ||
1754
10.4M
         (typepair == DNS_TYPEPAIR(dns_rdatatype_ns) &&
1755
8.69M
    (node != qpdb->origin || IS_STUB(qpdb)));
1756
10.4M
}
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
11.6M
    qpznode_t **nodep) {
1762
11.6M
  qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
1763
11.6M
  isc_result_t result;
1764
11.6M
  qpznode_t *node = NULL, *nsecnode = NULL;
1765
1766
11.6M
  if (type == dns_rdatatype_nsec3 || covers == dns_rdatatype_nsec3) {
1767
2.17k
    result = dns_qp_getname(loadctx->tree, name,
1768
2.17k
          DNS_DBNAMESPACE_NSEC3, (void **)&node,
1769
2.17k
          NULL);
1770
2.17k
    if (result == ISC_R_SUCCESS) {
1771
1.25k
      *nodep = node;
1772
1.25k
    } else {
1773
914
      node = new_qpznode(qpdb, name, DNS_DBNAMESPACE_NSEC3);
1774
914
      result = dns_qp_insert(loadctx->tree, node, 0);
1775
914
      INSIST(result == ISC_R_SUCCESS);
1776
914
      *nodep = node;
1777
914
      qpznode_detach(&node);
1778
914
    }
1779
2.17k
    return;
1780
2.17k
  }
1781
1782
11.6M
  result = dns_qp_getname(loadctx->tree, name, DNS_DBNAMESPACE_NORMAL,
1783
11.6M
        (void **)&node, NULL);
1784
11.6M
  if (result == ISC_R_SUCCESS) {
1785
1.33M
    if (type == dns_rdatatype_nsec && node->havensec) {
1786
5.22k
      goto done;
1787
5.22k
    }
1788
10.2M
  } else {
1789
10.2M
    INSIST(node == NULL);
1790
10.2M
    node = new_qpznode(qpdb, name, DNS_DBNAMESPACE_NORMAL);
1791
10.2M
    result = dns_qp_insert(loadctx->tree, node, 0);
1792
10.2M
    INSIST(result == ISC_R_SUCCESS);
1793
10.2M
    qpznode_unref(node);
1794
10.2M
  }
1795
11.6M
  if (type != dns_rdatatype_nsec) {
1796
11.5M
    goto done;
1797
11.5M
  }
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
67.0k
  node->havensec = true;
1808
67.0k
  nsecnode = new_qpznode(qpdb, name, DNS_DBNAMESPACE_NSEC);
1809
67.0k
  (void)dns_qp_insert(loadctx->tree, nsecnode, 0);
1810
67.0k
  qpznode_detach(&nsecnode);
1811
1812
11.6M
done:
1813
11.6M
  *nodep = node;
1814
11.6M
}
1815
1816
static bool
1817
10.4M
cname_and_other(qpznode_t *node, uint32_t serial) {
1818
10.4M
  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
10.4M
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
1826
10.4M
    dns_rdatatype_t rdtype = DNS_TYPEPAIR_TYPE(top->typepair);
1827
10.4M
    if (rdtype == dns_rdatatype_cname) {
1828
451
      if (first_existing_header(top, serial) != NULL) {
1829
451
        cname = true;
1830
451
      }
1831
10.4M
    } else if (rdtype != dns_rdatatype_nsec &&
1832
10.4M
         rdtype != dns_rdatatype_rrsig)
1833
10.4M
    {
1834
10.4M
      if (first_existing_header(top, serial) != NULL) {
1835
10.4M
        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.66M
          return cname;
1843
1.66M
        }
1844
8.74M
        other = true;
1845
8.74M
      }
1846
10.4M
    }
1847
1848
8.82M
    if (cname && other) {
1849
15
      return true;
1850
15
    }
1851
8.82M
  }
1852
1853
8.80M
  return false;
1854
10.4M
}
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
10.6M
recordsize(dns_vecheader_t *header, unsigned int namelen) {
1873
10.6M
  return dns_rdatavec_size(header) + sizeof(dns_ttl_t) +
1874
10.6M
         sizeof(dns_rdatatype_t) + sizeof(dns_rdataclass_t) + namelen;
1875
10.6M
}
1876
1877
static void
1878
maybe_update_recordsandsize(bool add, qpz_version_t *version,
1879
10.6M
          dns_vecheader_t *header, unsigned int namelen) {
1880
10.6M
  if (!EXISTS(header)) {
1881
0
    return;
1882
0
  }
1883
1884
21.2M
  RWLOCK(&version->rwlock, isc_rwlocktype_write);
1885
21.2M
  if (add) {
1886
10.4M
    version->records += dns_rdatavec_count(header);
1887
10.4M
    version->xfrsize += recordsize(header, namelen);
1888
10.4M
  } else {
1889
168k
    version->records -= dns_rdatavec_count(header);
1890
168k
    version->xfrsize -= recordsize(header, namelen);
1891
168k
  }
1892
21.2M
  RWUNLOCK(&version->rwlock, isc_rwlocktype_write);
1893
10.6M
}
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
11.6M
    isc_stdtime_t now ISC_ATTR_UNUSED DNS__DB_FLARG) {
1900
11.6M
  qpz_changed_t *changed = NULL;
1901
11.6M
  dns_vectop_t *foundtop = NULL;
1902
11.6M
  dns_vectop_t *priotop = NULL;
1903
11.6M
  dns_vecheader_t *merged = NULL;
1904
11.6M
  isc_result_t result;
1905
11.6M
  bool merge = false;
1906
11.6M
  uint32_t ntypes;
1907
1908
11.6M
  if ((options & DNS_DBADD_MERGE) != 0) {
1909
11.6M
    REQUIRE(version != NULL);
1910
11.6M
    merge = true;
1911
11.6M
  }
1912
1913
11.6M
  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
11.6M
  ntypes = 0;
1923
11.6M
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
1924
1.49M
    ++ntypes;
1925
1.49M
    if (prio_type(top->typepair)) {
1926
307k
      priotop = top;
1927
307k
    }
1928
1.49M
    if (top->typepair == newheader->typepair) {
1929
1.33M
      foundtop = top;
1930
1.33M
      break;
1931
1.33M
    }
1932
1.49M
  }
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
11.6M
  dns_vecheader_t **header_p = NULL;
1940
11.6M
  dns_vecheader_t *header = NULL;
1941
11.6M
  if (foundtop != NULL) {
1942
1.33M
    ISC_SLIST_FOREACH_PTR(p, &ISC_SLIST_HEAD(foundtop->headers)) {
1943
1.33M
      if (!IGNORE(*p)) {
1944
1.33M
        header_p = p;
1945
1.33M
        header = *p;
1946
1.33M
        break;
1947
1.33M
      }
1948
0
      ISC_SLIST_PTR_ADVANCE(p, next_header);
1949
0
    }
1950
1.33M
  }
1951
1952
11.6M
  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.33M
    if (merge && EXISTS(header)) {
1959
1.33M
      unsigned int flags = 0;
1960
1.33M
      INSIST(version->serial >= header->serial);
1961
1.33M
      merged = NULL;
1962
1.33M
      result = ISC_R_SUCCESS;
1963
1964
1.33M
      if ((options & DNS_DBADD_EXACT) != 0) {
1965
0
        flags |= DNS_RDATAVEC_EXACT;
1966
0
      }
1967
1.33M
      if ((options & DNS_DBADD_EXACTTTL) != 0 &&
1968
0
          newheader->ttl != header->ttl)
1969
0
      {
1970
0
        result = DNS_R_NOTEXACT;
1971
1.33M
      } else if (newheader->ttl != header->ttl) {
1972
4.20k
        flags |= DNS_RDATAVEC_FORCE;
1973
4.20k
      }
1974
1.33M
      if (result == ISC_R_SUCCESS) {
1975
1.33M
        result = dns_rdatavec_merge(
1976
1.33M
          header, newheader, qpdb->common.mctx,
1977
1.33M
          qpdb->common.rdclass,
1978
1.33M
          DNS_TYPEPAIR_TYPE(header->typepair),
1979
1.33M
          flags, qpdb->maxrrperset, &merged);
1980
1.33M
      }
1981
1.33M
      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
168k
        dns_vecheader_unref(newheader);
1991
168k
        newheader = merged;
1992
        /*
1993
         * dns_rdatavec_subtract takes the header from
1994
         * the first argument, so it preserves the case
1995
         */
1996
168k
        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.16M
      } else {
2006
1.16M
        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.16M
        dns_vecheader_unref(newheader);
2014
1.16M
        return result;
2015
1.16M
      }
2016
1.33M
    }
2017
2018
168k
    INSIST(version->serial >= header->serial);
2019
168k
    INSIST(foundtop->typepair == newheader->typepair);
2020
2021
168k
    if (loading) {
2022
168k
      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
168k
      ISC_SLIST_PTR_REMOVE(header_p, header, next_header);
2036
168k
      ISC_SLIST_PREPEND(foundtop->headers, newheader,
2037
168k
            next_header);
2038
168k
      maybe_update_recordsandsize(false, version, header,
2039
168k
                nodename->length);
2040
2041
168k
      LOCK(&qpdb->heap->lock);
2042
168k
      resign_unregister(qpdb->heap, node, header);
2043
168k
      UNLOCK(&qpdb->heap->lock);
2044
168k
      dns_vecheader_unref(header);
2045
168k
    } 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
10.2M
  } 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
10.2M
    if (!EXISTS(newheader)) {
2073
0
      dns_vecheader_unref(newheader);
2074
0
      return DNS_R_UNCHANGED;
2075
0
    }
2076
2077
10.2M
    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
10.2M
    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
10.2M
    } else {
2105
      /*
2106
       * No rdatasets of the given type exist at the node.
2107
       */
2108
2109
10.2M
      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
10.2M
      dns_vectop_t *newtop =
2120
10.2M
        dns_vectop_new(node->mctx, newheader->typepair);
2121
2122
10.2M
      ISC_SLIST_PREPEND(newtop->headers, newheader,
2123
10.2M
            next_header);
2124
2125
10.2M
      if (prio_type(newheader->typepair)) {
2126
        /* This is a priority type, prepend it */
2127
8.75M
        ISC_SLIST_PREPEND(node->next_type, newtop,
2128
8.75M
              next_type);
2129
8.75M
      } else if (priotop != NULL) {
2130
        /* Append after the priority headers */
2131
2.20k
        ISC_SLIST_INSERTAFTER(priotop, newtop,
2132
2.20k
                  next_type);
2133
1.53M
      } else {
2134
        /* There were no priority headers */
2135
1.53M
        ISC_SLIST_PREPEND(node->next_type, newtop,
2136
1.53M
              next_type);
2137
1.53M
      }
2138
10.2M
    }
2139
10.2M
  }
2140
2141
10.4M
  maybe_update_recordsandsize(true, version, newheader, nodename->length);
2142
2143
  /*
2144
   * Check if the node now contains CNAME and other data.
2145
   */
2146
10.4M
  if (cname_and_other(node, version->serial)) {
2147
23
    return DNS_R_CNAMEANDOTHER;
2148
23
  }
2149
2150
10.4M
  bindrdataset(qpdb, newheader, addedrdataset DNS__DB_FLARG_PASS);
2151
2152
10.4M
  return ISC_R_SUCCESS;
2153
10.4M
}
2154
2155
static void
2156
wildcardmagic(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name,
2157
2.88M
        dns_namespace_t nspace) {
2158
2.88M
  isc_result_t result;
2159
2.88M
  dns_name_t foundname;
2160
2.88M
  unsigned int n;
2161
2.88M
  qpznode_t *node = NULL;
2162
2163
2.88M
  dns_name_init(&foundname);
2164
2.88M
  n = dns_name_countlabels(name);
2165
2.88M
  INSIST(n >= 2);
2166
2.88M
  n--;
2167
2.88M
  dns_name_getlabelsequence(name, 1, n, &foundname);
2168
2169
  /* insert an empty node, if needed, to hold the wildcard bit */
2170
2.88M
  result = dns_qp_getname(qp, &foundname, nspace, (void **)&node, NULL);
2171
2.88M
  if (result != ISC_R_SUCCESS) {
2172
1.40M
    INSIST(node == NULL);
2173
1.40M
    node = new_qpznode(qpdb, &foundname, nspace);
2174
1.40M
    result = dns_qp_insert(qp, node, 0);
2175
1.40M
    INSIST(result == ISC_R_SUCCESS);
2176
1.40M
    qpznode_unref(node);
2177
1.40M
  }
2178
2179
2.88M
  node->wild = true;
2180
2.88M
}
2181
2182
static void
2183
addwildcards(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name,
2184
11.6M
       dns_namespace_t nspace) {
2185
11.6M
  dns_name_t foundname;
2186
11.6M
  unsigned int n, l, i;
2187
2188
11.6M
  dns_name_init(&foundname);
2189
11.6M
  n = dns_name_countlabels(name);
2190
11.6M
  l = dns_name_countlabels(&qpdb->common.origin);
2191
11.6M
  i = l + 1;
2192
22.3M
  while (i < n) {
2193
10.6M
    dns_name_getlabelsequence(name, n - i, i, &foundname);
2194
10.6M
    if (dns_name_iswildcard(&foundname)) {
2195
2.22M
      wildcardmagic(qpdb, qp, &foundname, nspace);
2196
2.22M
    }
2197
2198
10.6M
    i++;
2199
10.6M
  }
2200
11.6M
}
2201
2202
static isc_result_t
2203
loading_addrdataset(void *arg, const dns_name_t *name, dns_rdataset_t *rdataset,
2204
11.6M
        dns_diffop_t op ISC_ATTR_UNUSED DNS__DB_FLARG) {
2205
11.6M
  qpz_load_t *loadctx = arg;
2206
11.6M
  qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
2207
11.6M
  qpznode_t *node = NULL;
2208
11.6M
  isc_result_t result = ISC_R_SUCCESS;
2209
11.6M
  isc_region_t region;
2210
11.6M
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
2211
11.6M
  isc_rwlock_t *nlock = NULL;
2212
2213
11.6M
  REQUIRE(rdataset->rdclass == qpdb->common.rdclass);
2214
2215
  /*
2216
   * SOA records are only allowed at top of zone.
2217
   */
2218
11.6M
  if (rdataset->type == dns_rdatatype_soa &&
2219
261
      !dns_name_equal(name, &qpdb->common.origin))
2220
1
  {
2221
1
    return DNS_R_NOTZONETOP;
2222
1
  }
2223
2224
11.6M
  if (rdataset->type != dns_rdatatype_nsec3 &&
2225
11.6M
      rdataset->covers != dns_rdatatype_nsec3)
2226
11.6M
  {
2227
11.6M
    addwildcards(qpdb, loadctx->tree, name, DNS_DBNAMESPACE_NORMAL);
2228
11.6M
  }
2229
2230
11.6M
  if (dns_name_iswildcard(name)) {
2231
662k
    if (rdataset->type == dns_rdatatype_ns) {
2232
      /*
2233
       * NS owners cannot legally be wild cards.
2234
       */
2235
154
      return DNS_R_INVALIDNS;
2236
154
    }
2237
2238
662k
    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
662k
    wildcardmagic(qpdb, loadctx->tree, name,
2246
662k
            DNS_DBNAMESPACE_NORMAL);
2247
662k
  }
2248
2249
11.6M
  loading_addnode(loadctx, name, rdataset->type, rdataset->covers, &node);
2250
11.6M
  result = dns_rdatavec_fromrdataset(rdataset, node->mctx, &region,
2251
11.6M
             qpdb->maxrrperset);
2252
11.6M
  if (result != ISC_R_SUCCESS) {
2253
72
    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
72
    return result;
2259
72
  }
2260
2261
11.6M
  dns_vecheader_t *newheader = (dns_vecheader_t *)region.base;
2262
11.6M
  newheader->ttl = rdataset->ttl;
2263
11.6M
  newheader->serial = 1;
2264
11.6M
  atomic_store_release(&newheader->trust, rdataset->trust);
2265
2266
11.6M
  dns_vecheader_setownercase(newheader, name);
2267
2268
11.6M
  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
11.6M
  nlock = qpzone_get_lock(node);
2274
11.6M
  NODE_WRLOCK(nlock, &nlocktype);
2275
11.6M
  result = add(qpdb, node, name, qpdb->current_version, newheader,
2276
11.6M
         DNS_DBADD_MERGE, true, NULL, 0 DNS__DB_FLARG_PASS);
2277
11.6M
  NODE_UNLOCK(nlock, &nlocktype);
2278
2279
11.6M
  if (result == ISC_R_SUCCESS &&
2280
10.4M
      delegating_type(qpdb, node, rdataset->type))
2281
8.68M
  {
2282
8.68M
    node->delegating = true;
2283
8.68M
  } else if (result == DNS_R_UNCHANGED) {
2284
1.16M
    result = ISC_R_SUCCESS;
2285
1.16M
  }
2286
2287
11.6M
  return result;
2288
11.6M
}
2289
2290
static void
2291
17.4k
loading_setup(void *arg) {
2292
17.4k
  qpz_load_t *loadctx = arg;
2293
17.4k
  qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
2294
2295
17.4k
  dns_qpmulti_write(qpdb->tree, &loadctx->tree);
2296
17.4k
}
2297
2298
static void
2299
17.4k
loading_commit(void *arg) {
2300
17.4k
  qpz_load_t *loadctx = arg;
2301
17.4k
  qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
2302
2303
17.4k
  if (loadctx->tree != NULL) {
2304
17.4k
    dns_qp_compact(loadctx->tree, DNS_QPGC_MAYBE);
2305
17.4k
    dns_qpmulti_commit(qpdb->tree, &loadctx->tree);
2306
17.4k
  }
2307
17.4k
}
2308
2309
static isc_result_t
2310
17.4k
beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
2311
17.4k
  qpz_load_t *loadctx = NULL;
2312
17.4k
  qpzonedb_t *qpdb = NULL;
2313
17.4k
  qpdb = (qpzonedb_t *)db;
2314
2315
17.4k
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
2316
17.4k
  REQUIRE(VALID_QPZONE(qpdb));
2317
2318
17.4k
  loadctx = isc_mem_get(qpdb->common.mctx, sizeof(*loadctx));
2319
17.4k
  *loadctx = (qpz_load_t){ .db = db };
2320
2321
17.4k
  RWLOCK(&qpdb->lock, isc_rwlocktype_write);
2322
2323
17.4k
  REQUIRE((qpdb->attributes & (QPDB_ATTR_LOADED | QPDB_ATTR_LOADING)) ==
2324
17.4k
    0);
2325
17.4k
  qpdb->attributes |= QPDB_ATTR_LOADING;
2326
2327
17.4k
  RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
2328
2329
17.4k
  callbacks->update = loading_addrdataset;
2330
17.4k
  callbacks->setup = loading_setup;
2331
17.4k
  callbacks->commit = loading_commit;
2332
17.4k
  callbacks->add_private = loadctx;
2333
2334
17.4k
  return ISC_R_SUCCESS;
2335
17.4k
}
2336
2337
static isc_result_t
2338
17.4k
endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
2339
17.4k
  qpz_load_t *loadctx = NULL;
2340
17.4k
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
2341
2342
17.4k
  REQUIRE(VALID_QPZONE(qpdb));
2343
17.4k
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
2344
17.4k
  loadctx = callbacks->add_private;
2345
17.4k
  REQUIRE(loadctx != NULL);
2346
17.4k
  REQUIRE(loadctx->db == db);
2347
2348
17.4k
  RWLOCK(&qpdb->lock, isc_rwlocktype_write);
2349
2350
17.4k
  REQUIRE((qpdb->attributes & QPDB_ATTR_LOADING) != 0);
2351
17.4k
  REQUIRE((qpdb->attributes & QPDB_ATTR_LOADED) == 0);
2352
2353
17.4k
  qpdb->attributes &= ~QPDB_ATTR_LOADING;
2354
17.4k
  qpdb->attributes |= QPDB_ATTR_LOADED;
2355
2356
17.4k
  if (qpdb->origin != NULL) {
2357
17.4k
    qpz_version_t *version = qpdb->current_version;
2358
17.4k
    RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
2359
17.4k
    setsecure(db, version, (dns_dbnode_t *)qpdb->origin);
2360
17.4k
  } else {
2361
0
    RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
2362
0
  }
2363
2364
17.4k
  callbacks->update = NULL;
2365
17.4k
  callbacks->setup = NULL;
2366
17.4k
  callbacks->commit = NULL;
2367
17.4k
  callbacks->add_private = NULL;
2368
2369
17.4k
  isc_mem_put(qpdb->common.mctx, loadctx, sizeof(*loadctx));
2370
2371
17.4k
  return ISC_R_SUCCESS;
2372
17.4k
}
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
  nlock = qpzone_get_lock(elem->node);
2538
0
  UNLOCK(&qpdb->heap->lock);
2539
2540
0
again:
2541
0
  NODE_RDLOCK(nlock, &nlocktype);
2542
2543
0
  LOCK(&qpdb->heap->lock);
2544
0
  elem = isc_heap_element(qpdb->heap->heap, 1);
2545
2546
0
  isc_rwlock_t *new_nlock = (elem != NULL) ? qpzone_get_lock(elem->node)
2547
0
             : NULL;
2548
0
  if (new_nlock != NULL && new_nlock != nlock) {
2549
0
    UNLOCK(&qpdb->heap->lock);
2550
0
    NODE_UNLOCK(nlock, &nlocktype);
2551
2552
0
    nlock = new_nlock;
2553
0
    goto again;
2554
0
  }
2555
2556
0
  if (elem != NULL) {
2557
0
    header = elem->header;
2558
0
    *resign = RESIGN(header) ? (uint32_t)header->resign : 0;
2559
0
    dns_name_copy(&elem->node->name, foundname);
2560
0
    *typepair = header->typepair;
2561
0
    result = ISC_R_SUCCESS;
2562
0
  }
2563
0
  UNLOCK(&qpdb->heap->lock);
2564
0
  NODE_UNLOCK(nlock, &nlocktype);
2565
2566
0
  return result;
2567
0
}
2568
2569
static isc_result_t
2570
2
setgluecachestats(dns_db_t *db, isc_stats_t *stats) {
2571
2
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
2572
2573
2
  REQUIRE(VALID_QPZONE(qpdb));
2574
2
  REQUIRE(!IS_STUB(qpdb));
2575
2
  REQUIRE(stats != NULL);
2576
2577
2
  isc_stats_attach(stats, &qpdb->gluecachestats);
2578
2
  return ISC_R_SUCCESS;
2579
2
}
2580
2581
static dns_qp_t *
2582
6
begin_transaction(qpzonedb_t *qpdb, dns_qpread_t *qprp, bool create) {
2583
6
  dns_qp_t *qp = NULL;
2584
2585
6
  if (create) {
2586
0
    dns_qpmulti_write(qpdb->tree, &qp);
2587
6
  } else {
2588
6
    dns_qpmulti_query(qpdb->tree, qprp);
2589
6
    qp = (dns_qp_t *)qprp;
2590
6
  }
2591
2592
6
  return qp;
2593
6
}
2594
2595
static void
2596
6
end_transaction(qpzonedb_t *qpdb, dns_qp_t *qp, bool create) {
2597
6
  if (create) {
2598
0
    dns_qp_compact(qp, DNS_QPGC_MAYBE);
2599
0
    dns_qpmulti_commit(qpdb->tree, &qp);
2600
6
  } else {
2601
6
    dns_qpread_t *qprp = (dns_qpread_t *)qp;
2602
6
    dns_qpread_destroy(qpdb->tree, qprp);
2603
6
  }
2604
6
}
2605
2606
static isc_result_t
2607
findnodeintree(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name,
2608
6
         bool create, bool nsec3, dns_dbnode_t **nodep DNS__DB_FLARG) {
2609
6
  isc_result_t result;
2610
6
  qpznode_t *node = NULL;
2611
6
  dns_namespace_t nspace = nsec3 ? DNS_DBNAMESPACE_NSEC3
2612
6
               : DNS_DBNAMESPACE_NORMAL;
2613
  /*
2614
   * findnodeintree is a wrapper around dns_qp_getname that does some
2615
   * qpzone-specific bookkeeping before returning the lookup result to the
2616
   * caller.
2617
   *
2618
   * First, we do a lookup ...
2619
   */
2620
6
  result = dns_qp_getname(qp, name, nspace, (void **)&node, NULL);
2621
6
  if (result == ISC_R_SUCCESS) {
2622
    /*
2623
     * ... if the lookup is successful, we need to increase both the
2624
     * internal and external reference count before returning to
2625
     * the caller. qpznode_acquire takes care of that.
2626
     */
2627
6
    qpznode_acquire(node DNS__DB_FLARG_PASS);
2628
2629
6
    INSIST(node->nspace == DNS_DBNAMESPACE_NSEC3 || !nsec3);
2630
6
  } else if (result != ISC_R_SUCCESS && create) {
2631
    /*
2632
     * ... if the lookup is unsuccessful, but the caller asked us to
2633
     * create a new node, create one and insert it into the tree.
2634
     */
2635
0
    node = new_qpznode(qpdb, name, nspace);
2636
0
    result = dns_qp_insert(qp, node, 0);
2637
0
    INSIST(result == ISC_R_SUCCESS);
2638
2639
    /*
2640
     * The new node now has two internal references:
2641
     *  - One from new_qpznode, that initializes references at 1.
2642
     *  - One from attach_leaf, that increases the reference by
2643
     *    one at insertion in the qp-tree.
2644
     * We want the node to have two internal and one external
2645
     * reference:
2646
     *  - One internal reference from the qp-tree.
2647
     *  - One internal and one external reference from the caller.
2648
     *
2649
     * So we increase the external reference count by one.
2650
     */
2651
0
    qpznode_erefs_increment(node DNS__DB_FLARG_PASS);
2652
2653
0
    if (!nsec3) {
2654
      /*
2655
       * Add empty non-terminal nodes to help with wildcards.
2656
       */
2657
0
      addwildcards(qpdb, qp, name, nspace);
2658
0
      if (dns_name_iswildcard(name)) {
2659
0
        wildcardmagic(qpdb, qp, name, nspace);
2660
0
      }
2661
0
    }
2662
2663
0
    INSIST(node->nspace == DNS_DBNAMESPACE_NSEC3 || !nsec3);
2664
0
  }
2665
  /*
2666
   * ... if the lookup is unsuccessful, and the caller didn't ask us
2667
   * to create a new node, there is nothing to do. Return the result
2668
   * of the lookup to the caller, and set *nodep to NULL
2669
   */
2670
2671
6
  *nodep = (dns_dbnode_t *)node;
2672
2673
6
  return result;
2674
6
}
2675
2676
static isc_result_t
2677
qpzone_findnode(dns_db_t *db, const dns_name_t *name, bool create,
2678
    dns_clientinfomethods_t *methods ISC_ATTR_UNUSED,
2679
    dns_clientinfo_t *clientinfo ISC_ATTR_UNUSED,
2680
6
    dns_dbnode_t **nodep DNS__DB_FLARG) {
2681
6
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
2682
2683
6
  REQUIRE(VALID_QPZONE(qpdb));
2684
2685
6
  dns_qpread_t qpr = { 0 };
2686
6
  dns_qp_t *qp = begin_transaction(qpdb, &qpr, create);
2687
2688
6
  isc_result_t result = findnodeintree(qpdb, qp, name, create, false,
2689
6
               nodep DNS__DB_FLARG_PASS);
2690
2691
6
  end_transaction(qpdb, qp, create);
2692
2693
6
  return result;
2694
6
}
2695
2696
static isc_result_t
2697
qpzone_findnsec3node(dns_db_t *db, const dns_name_t *name, bool create,
2698
0
         dns_dbnode_t **nodep DNS__DB_FLARG) {
2699
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
2700
2701
0
  REQUIRE(VALID_QPZONE(qpdb));
2702
2703
0
  dns_qpread_t qpr = { 0 };
2704
0
  dns_qp_t *qp = begin_transaction(qpdb, &qpr, create);
2705
2706
0
  isc_result_t result = findnodeintree(qpdb, qp, name, create, true,
2707
0
               nodep DNS__DB_FLARG_PASS);
2708
2709
0
  end_transaction(qpdb, qp, create);
2710
2711
0
  return result;
2712
0
}
2713
2714
static bool
2715
0
matchparams(dns_vecheader_t *header, qpz_search_t *search) {
2716
0
  dns_rdata_nsec3_t nsec3;
2717
0
  isc_result_t result;
2718
2719
0
  REQUIRE(header->typepair == DNS_TYPEPAIR(dns_rdatatype_nsec3));
2720
2721
0
  rdatavec_iter_t iter;
2722
0
  DNS_VECHEADER_FOREACH(&iter, header, search->qpdb->common.rdclass) {
2723
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
2724
0
    vecheader_current(&iter, &rdata);
2725
2726
0
    result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
2727
0
    INSIST(result == ISC_R_SUCCESS);
2728
2729
0
    if (nsec3.hash == search->version->hash &&
2730
0
        nsec3.iterations == search->version->iterations &&
2731
0
        nsec3.salt.length == search->version->salt_length &&
2732
0
        memcmp(nsec3.salt.base, search->version->salt,
2733
0
         nsec3.salt.length) == 0)
2734
0
    {
2735
0
      return true;
2736
0
    }
2737
0
  }
2738
2739
0
  return false;
2740
0
}
2741
2742
static isc_result_t
2743
qpzone_setup_delegation(qpz_search_t *search, dns_dbnode_t **nodep,
2744
      dns_name_t *foundname, dns_rdataset_t *rdataset,
2745
0
      dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
2746
0
  dns_name_t *zcname = NULL;
2747
0
  dns_typepair_t typepair;
2748
0
  qpznode_t *node = NULL;
2749
2750
0
  REQUIRE(search != NULL);
2751
0
  REQUIRE(search->zonecut != NULL);
2752
0
  REQUIRE(search->zonecut_header != NULL);
2753
2754
  /*
2755
   * The caller MUST NOT be holding any node locks.
2756
   */
2757
2758
0
  node = search->zonecut;
2759
0
  typepair = search->zonecut_header->typepair;
2760
2761
  /*
2762
   * If we have to set foundname, we do it before anything else.
2763
   * If we were to set foundname after we had set nodep or bound the
2764
   * rdataset, then we'd have to undo that work if dns_name_copy()
2765
   * failed.  By setting foundname first, there's nothing to undo if
2766
   * we have trouble.
2767
   */
2768
0
  if (foundname != NULL && search->copy_name) {
2769
0
    zcname = dns_fixedname_name(&search->zonecut_name);
2770
0
    dns_name_copy(zcname, foundname);
2771
0
  }
2772
0
  if (nodep != NULL) {
2773
    /*
2774
     * Note that we don't have to increment the node's reference
2775
     * count here because we're going to use the reference we
2776
     * already have in the search block.
2777
     */
2778
0
    *nodep = (dns_dbnode_t *)node;
2779
0
    search->need_cleanup = false;
2780
0
  }
2781
0
  if (rdataset != NULL) {
2782
0
    isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
2783
0
    isc_rwlock_t *nlock = qpzone_get_lock(node);
2784
0
    NODE_RDLOCK(nlock, &nlocktype);
2785
0
    bindrdataset(search->qpdb, search->zonecut_header,
2786
0
           rdataset DNS__DB_FLARG_PASS);
2787
0
    if (sigrdataset != NULL && search->zonecut_sigheader != NULL) {
2788
0
      bindrdataset(search->qpdb, search->zonecut_sigheader,
2789
0
             sigrdataset DNS__DB_FLARG_PASS);
2790
0
    }
2791
0
    NODE_UNLOCK(nlock, &nlocktype);
2792
0
  }
2793
2794
0
  if (typepair == DNS_TYPEPAIR(dns_rdatatype_dname)) {
2795
0
    return DNS_R_DNAME;
2796
0
  }
2797
0
  return DNS_R_DELEGATION;
2798
0
}
2799
2800
typedef enum { FORWARD, BACK } direction_t;
2801
2802
/*
2803
 * Step backwards or forwards through the database until we find a
2804
 * node with data in it for the desired version. If 'nextname' is not NULL,
2805
 * and we found a predecessor or successor, save the name we found in it.
2806
 * Return true if we found a predecessor or successor.
2807
 */
2808
static bool
2809
step(qpz_search_t *search, dns_qpiter_t *it, direction_t direction,
2810
64
     qpznode_t **node_p) {
2811
64
  REQUIRE(node_p != NULL && *node_p == NULL);
2812
2813
64
  qpznode_t *node = NULL, *previous_node = NULL;
2814
64
  isc_result_t result = ISC_R_SUCCESS;
2815
2816
64
  result = dns_qpiter_current(it, (void **)&node, NULL);
2817
192
  while (result == ISC_R_SUCCESS) {
2818
128
    previous_node = node;
2819
2820
128
    isc_rwlock_t *nlock = qpzone_get_lock(node);
2821
128
    isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
2822
128
    dns_vecheader_t *found = NULL;
2823
2824
128
    NODE_RDLOCK(nlock, &nlocktype);
2825
128
    ISC_SLIST_FOREACH(top, node->next_type, next_type) {
2826
0
      found = first_existing_header(top, search->serial);
2827
0
    }
2828
128
    NODE_UNLOCK(nlock, &nlocktype);
2829
128
    if (found != NULL) {
2830
0
      break;
2831
0
    }
2832
2833
128
    if (direction == FORWARD) {
2834
128
      result = dns_qpiter_next(it, (void **)&node, NULL);
2835
128
    } else {
2836
0
      result = dns_qpiter_prev(it, (void **)&node, NULL);
2837
0
    }
2838
128
  };
2839
2840
64
  if (result == ISC_R_SUCCESS) {
2841
0
    if (previous_node != NULL) {
2842
0
      *node_p = previous_node;
2843
0
    }
2844
0
    return true;
2845
0
  }
2846
2847
64
  return false;
2848
64
}
2849
2850
static bool
2851
64
activeempty(qpz_search_t *search, dns_qpiter_t *it, const dns_name_t *current) {
2852
64
  qpznode_t *next_node = NULL;
2853
2854
  /*
2855
   * The iterator is currently pointed at the predecessor
2856
   * of the name we were searching for. Step the iterator
2857
   * forward, then step() will continue forward until it
2858
   * finds a node with active data. If that node is a
2859
   * subdomain of the one we were looking for, then we're
2860
   * at an active empty nonterminal node.
2861
   */
2862
64
  isc_result_t result = dns_qpiter_next(it, NULL, NULL);
2863
64
  if (result != ISC_R_SUCCESS) {
2864
    /* An ENT at the end of the zone is impossible */
2865
0
    return false;
2866
0
  }
2867
64
  return step(search, it, FORWARD, &next_node) &&
2868
0
         dns_name_issubdomain(&next_node->name, current);
2869
64
}
2870
2871
static bool
2872
wildcard_blocked(qpz_search_t *search, const dns_name_t *qname,
2873
0
     dns_name_t *wname) {
2874
0
  isc_result_t result;
2875
0
  qpznode_t *next_node = NULL, *prev_node = NULL;
2876
0
  dns_name_t name;
2877
0
  dns_name_t rname;
2878
0
  dns_name_t tname;
2879
0
  dns_qpiter_t it;
2880
0
  bool check_next = false;
2881
0
  bool check_prev = false;
2882
0
  unsigned int n;
2883
2884
0
  dns_name_init(&name);
2885
0
  dns_name_init(&tname);
2886
0
  dns_name_init(&rname);
2887
2888
  /*
2889
   * The qname seems to have matched a wildcard, but we
2890
   * need to find out if there's an empty nonterminal node
2891
   * between the wildcard level and the qname.
2892
   *
2893
   * search->iter should now be pointing at the predecessor
2894
   * of the searched-for name. We are using a local copy of the
2895
   * iterator so as not to change the state of search->iter.
2896
   * step() will walk backward until we find a predecessor with
2897
   * data.
2898
   */
2899
0
  it = search->iter;
2900
0
  check_prev = step(search, &it, BACK, &prev_node);
2901
2902
  /* Now reset the iterator and look for a successor with data. */
2903
0
  it = search->iter;
2904
0
  result = dns_qpiter_next(&it, NULL, NULL);
2905
0
  if (result == ISC_R_SUCCESS) {
2906
0
    check_next = step(search, &it, FORWARD, &next_node);
2907
0
  }
2908
2909
0
  if (!check_prev && !check_next) {
2910
    /* No predecessor or successor was found at all? */
2911
0
    return false;
2912
0
  }
2913
2914
0
  dns_name_clone(qname, &rname);
2915
2916
  /*
2917
   * Remove the wildcard label to find the terminal name.
2918
   */
2919
0
  n = dns_name_countlabels(wname);
2920
0
  dns_name_getlabelsequence(wname, 1, n - 1, &tname);
2921
2922
0
  do {
2923
0
    if ((check_prev &&
2924
0
         dns_name_issubdomain(&prev_node->name, &rname)) ||
2925
0
        (check_next &&
2926
0
         dns_name_issubdomain(&next_node->name, &rname)))
2927
0
    {
2928
0
      return true;
2929
0
    }
2930
2931
    /*
2932
     * Remove the leftmost label from the qname and check again.
2933
     */
2934
0
    n = dns_name_countlabels(&rname);
2935
0
    dns_name_getlabelsequence(&rname, 1, n - 1, &rname);
2936
0
  } while (!dns_name_equal(&rname, &tname));
2937
2938
0
  return false;
2939
0
}
2940
2941
static bool
2942
0
node_active(qpz_search_t *search, qpznode_t *node) {
2943
0
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
2944
0
    if (first_existing_header(top, search->serial) != NULL) {
2945
0
      return true;
2946
0
    }
2947
0
  }
2948
0
  return false;
2949
0
}
2950
2951
static isc_result_t
2952
find_wildcard(qpz_search_t *search, qpznode_t **nodep, const dns_name_t *qname,
2953
0
        dns_namespace_t nspace) {
2954
0
  isc_result_t result = ISC_R_NOTFOUND;
2955
2956
  /*
2957
   * Examine each ancestor level.  If the level's wild bit
2958
   * is set, then construct the corresponding wildcard name and
2959
   * search for it.  If the wildcard node exists, and is active in
2960
   * this version, we're done.  If not, then we next check to see
2961
   * if the ancestor is active in this version.  If so, then there
2962
   * can be no possible wildcard match and again we're done.  If not,
2963
   * continue the search.
2964
   */
2965
0
  for (int i = dns_qpchain_length(&search->chain) - 1; i >= 0; i--) {
2966
0
    qpznode_t *node = NULL;
2967
0
    isc_rwlock_t *nlock = NULL;
2968
0
    isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
2969
0
    bool wild, active;
2970
2971
0
    dns_qpchain_node(&search->chain, i, (void **)&node, NULL);
2972
2973
0
    nlock = qpzone_get_lock(node);
2974
0
    NODE_RDLOCK(nlock, &nlocktype);
2975
    /*
2976
     * First we try to figure out if this node is active in
2977
     * the search's version.  We do this now, even though we
2978
     * may not need the information, because it simplifies the
2979
     * locking and code flow.
2980
     */
2981
0
    active = node_active(search, node);
2982
0
    wild = node->wild;
2983
0
    NODE_UNLOCK(nlock, &nlocktype);
2984
2985
0
    if (wild) {
2986
0
      qpznode_t *wnode = NULL;
2987
0
      dns_fixedname_t fwname;
2988
0
      dns_name_t *wname = dns_fixedname_initname(&fwname);
2989
0
      dns_qpiter_t wit;
2990
0
      bool wactive;
2991
2992
      /*
2993
       * Construct the wildcard name for this level.
2994
       */
2995
0
      result = dns_name_concatenate(dns_wildcardname,
2996
0
                  &node->name, wname);
2997
0
      if (result != ISC_R_SUCCESS) {
2998
0
        break;
2999
0
      }
3000
3001
0
      result = dns_qp_lookup(&search->qpr, wname, nspace,
3002
0
                 &wit, NULL, (void **)&wnode,
3003
0
                 NULL);
3004
0
      if (result == ISC_R_SUCCESS) {
3005
        /*
3006
         * We have found the wildcard node.  If it
3007
         * is active in the search's version, we're
3008
         * done.
3009
         */
3010
0
        nlock = qpzone_get_lock(wnode);
3011
0
        NODE_RDLOCK(nlock, &nlocktype);
3012
0
        wactive = node_active(search, wnode);
3013
0
        NODE_UNLOCK(nlock, &nlocktype);
3014
0
        if (wactive || activeempty(search, &wit, wname))
3015
0
        {
3016
0
          if (wildcard_blocked(search, qname,
3017
0
                   wname))
3018
0
          {
3019
0
            return ISC_R_NOTFOUND;
3020
0
          }
3021
3022
          /*
3023
           * The wildcard node is active!
3024
           *
3025
           * Note: result is still ISC_R_SUCCESS
3026
           * so we don't have to set it.
3027
           */
3028
0
          *nodep = wnode;
3029
0
          break;
3030
0
        }
3031
0
      } else if (result != ISC_R_NOTFOUND &&
3032
0
           result != DNS_R_PARTIALMATCH)
3033
0
      {
3034
        /*
3035
         * An error has occurred.  Bail out.
3036
         */
3037
0
        break;
3038
0
      }
3039
0
    }
3040
3041
0
    if (active) {
3042
      /*
3043
       * The level node is active.  Any wildcarding
3044
       * present at higher levels has no
3045
       * effect and we're done.
3046
       */
3047
0
      result = ISC_R_NOTFOUND;
3048
0
      break;
3049
0
    }
3050
0
  }
3051
3052
0
  return result;
3053
0
}
3054
3055
/*
3056
 * Find node of the NSEC/NSEC3 record preceding 'name'.
3057
 */
3058
static isc_result_t
3059
previous_closest_nsec(dns_rdatatype_t type, qpz_search_t *search,
3060
          dns_name_t *name, qpznode_t **nodep, dns_qpiter_t *nit,
3061
0
          bool *firstp) {
3062
0
  isc_result_t result;
3063
3064
0
  REQUIRE(nodep != NULL && *nodep == NULL);
3065
0
  REQUIRE(type == dns_rdatatype_nsec3 || firstp != NULL);
3066
3067
0
  if (type == dns_rdatatype_nsec3) {
3068
0
    result = dns_qpiter_prev(&search->iter, (void **)nodep, NULL);
3069
0
    if (result == ISC_R_SUCCESS) {
3070
0
      dns_name_copy(&(*nodep)->name, name);
3071
0
    }
3072
0
    return result;
3073
0
  }
3074
3075
0
  for (;;) {
3076
0
    qpznode_t *nsec_node = NULL;
3077
3078
0
    if (*firstp) {
3079
      /*
3080
       * This is the first attempt to find 'name' in the
3081
       * NSEC namespace.
3082
       */
3083
0
      *firstp = false;
3084
0
      result = dns_qp_lookup(&search->qpr, name,
3085
0
                 DNS_DBNAMESPACE_NSEC, nit, NULL,
3086
0
                 NULL, NULL);
3087
3088
0
      INSIST(result != ISC_R_NOTFOUND);
3089
0
      if (result == ISC_R_SUCCESS) {
3090
        /*
3091
         * If we find an exact match in the NSEC
3092
         * namespace on our first attempt, it
3093
         * implies that the corresponding node in
3094
         * the normal namespace had an unacceptable
3095
         * NSEC record; we want the previous node
3096
         * in the NSEC tree.
3097
         */
3098
0
        result = dns_qpiter_prev(
3099
0
          nit, (void **)&nsec_node, NULL);
3100
0
      } else if (result == DNS_R_PARTIALMATCH) {
3101
        /*
3102
         * This was a partial match, so the
3103
         * iterator is already at the previous
3104
         * node in the NSEC namespace, which is
3105
         * what we want.
3106
         */
3107
0
        isc_result_t iresult = dns_qpiter_current(
3108
0
          nit, (void **)&nsec_node, NULL);
3109
0
        REQUIRE(iresult == ISC_R_SUCCESS);
3110
0
        result = ISC_R_SUCCESS;
3111
0
      }
3112
0
    } else {
3113
      /*
3114
       * We've taken at least two steps back through the
3115
       * NSEC namespace. The previous steps must have
3116
       * found nodes with NSEC records, but they didn't
3117
       * work; perhaps they lacked signature records.
3118
       * Keep searching.
3119
       */
3120
0
      result = dns_qpiter_prev(nit, (void **)&nsec_node,
3121
0
             NULL);
3122
0
    }
3123
3124
0
    if (result != ISC_R_SUCCESS) {
3125
0
      break;
3126
0
    }
3127
3128
0
    *nodep = NULL;
3129
0
    result = dns_qp_lookup(&search->qpr, &nsec_node->name,
3130
0
               DNS_DBNAMESPACE_NORMAL, &search->iter,
3131
0
               &search->chain, (void **)nodep, NULL);
3132
0
    if (result == ISC_R_SUCCESS) {
3133
0
      dns_name_copy(&nsec_node->name, name);
3134
0
      break;
3135
0
    }
3136
3137
    /*
3138
     * There should always be a node in the normal namespace
3139
     * with the same name as the node in the NSEC namespace,
3140
     * except when nodes in the NSEC namespace are awaiting
3141
     * deletion.
3142
     */
3143
0
    if (result != DNS_R_PARTIALMATCH && result != ISC_R_NOTFOUND) {
3144
0
      isc_log_write(DNS_LOGCATEGORY_DATABASE,
3145
0
              DNS_LOGMODULE_DB, ISC_LOG_ERROR,
3146
0
              "previous_closest_nsec(): %s",
3147
0
              isc_result_totext(result));
3148
0
      result = DNS_R_BADDB;
3149
0
      break;
3150
0
    }
3151
0
  }
3152
3153
0
  return result;
3154
0
}
3155
3156
/*
3157
 * Find the NSEC/NSEC3 which is at or before the name being sought.
3158
 * For NSEC3 records only NSEC3 records that match the
3159
 * current NSEC3PARAM record are considered.
3160
 */
3161
static isc_result_t
3162
find_closest_nsec(qpz_search_t *search, dns_dbnode_t **nodep,
3163
      dns_name_t *foundname, dns_rdataset_t *rdataset,
3164
      dns_rdataset_t *sigrdataset, bool nsec3,
3165
0
      bool secure DNS__DB_FLARG) {
3166
0
  qpznode_t *node = NULL, *prevnode = NULL;
3167
0
  dns_qpiter_t nseciter;
3168
0
  bool empty_node;
3169
0
  isc_result_t result;
3170
0
  dns_fixedname_t fname;
3171
0
  dns_name_t *name = dns_fixedname_initname(&fname);
3172
0
  dns_rdatatype_t matchtype = nsec3 ? dns_rdatatype_nsec3
3173
0
            : dns_rdatatype_nsec;
3174
0
  dns_typepair_t typepair = DNS_TYPEPAIR(matchtype);
3175
0
  dns_typepair_t sigpair = DNS_SIGTYPEPAIR(matchtype);
3176
0
  bool wraps = nsec3;
3177
0
  bool first = true;
3178
0
  bool need_sig = secure;
3179
3180
  /*
3181
   * When a lookup is unsuccessful, the QP iterator will already
3182
   * be pointing at the node preceding the searched-for name in
3183
   * the normal namespace. We'll check there first, assuming it will
3184
   * be right much of the time. If we don't find an NSEC there,
3185
   * then we start using the auxiliary NSEC namespace to find
3186
   * the next predecessor.
3187
   */
3188
0
  result = dns_qpiter_current(&search->iter, (void **)&node, NULL);
3189
0
  if (result != ISC_R_SUCCESS) {
3190
0
    return result;
3191
0
  }
3192
0
  dns_name_copy(&node->name, name);
3193
0
again:
3194
0
  do {
3195
0
    dns_vecheader_t *found = NULL, *foundsig = NULL;
3196
0
    isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
3197
0
    isc_rwlock_t *nlock = qpzone_get_lock(node);
3198
0
    NODE_RDLOCK(nlock, &nlocktype);
3199
0
    empty_node = true;
3200
0
    ISC_SLIST_FOREACH(top, node->next_type, next_type) {
3201
      /*
3202
       * Look for an active, extant NSEC or RRSIG NSEC.
3203
       */
3204
0
      dns_vecheader_t *header =
3205
0
        first_existing_header(top, search->serial);
3206
0
      if (header != NULL) {
3207
        /*
3208
         * We now know that there is at least one
3209
         * active rdataset at this node.
3210
         */
3211
0
        empty_node = false;
3212
0
        if (top->typepair == typepair) {
3213
0
          found = header;
3214
0
          if (foundsig != NULL) {
3215
0
            break;
3216
0
          }
3217
0
        } else if (top->typepair == sigpair) {
3218
0
          foundsig = header;
3219
0
          if (found != NULL) {
3220
0
            break;
3221
0
          }
3222
0
        }
3223
0
      }
3224
0
    }
3225
0
    if (!empty_node) {
3226
0
      if (found != NULL && search->version->havensec3 &&
3227
0
          found->typepair ==
3228
0
            DNS_TYPEPAIR(dns_rdatatype_nsec3) &&
3229
0
          !matchparams(found, search))
3230
0
      {
3231
0
        empty_node = true;
3232
0
        found = NULL;
3233
0
        foundsig = NULL;
3234
0
        result = previous_closest_nsec(typepair, search,
3235
0
                     name, &prevnode,
3236
0
                     NULL, NULL);
3237
0
      } else if (found != NULL &&
3238
0
           (foundsig != NULL || !need_sig))
3239
0
      {
3240
        /*
3241
         * We've found the right NSEC/NSEC3 record.
3242
         *
3243
         * Note: for this to really be the right
3244
         * NSEC record, it's essential that the NSEC
3245
         * records of any nodes obscured by a zone
3246
         * cut have been removed; we assume this is
3247
         * the case.
3248
         */
3249
0
        dns_name_copy(name, foundname);
3250
0
        if (nodep != NULL) {
3251
0
          qpznode_acquire(
3252
0
            node DNS__DB_FLARG_PASS);
3253
0
          *nodep = (dns_dbnode_t *)node;
3254
0
        }
3255
0
        bindrdataset(search->qpdb, found,
3256
0
               rdataset DNS__DB_FLARG_PASS);
3257
0
        if (foundsig != NULL) {
3258
0
          bindrdataset(
3259
0
            search->qpdb, foundsig,
3260
0
            sigrdataset DNS__DB_FLARG_PASS);
3261
0
        }
3262
0
      } else if (found == NULL && foundsig == NULL) {
3263
        /*
3264
         * This node is active, but has no NSEC or
3265
         * RRSIG NSEC.  That means it's glue or
3266
         * other obscured zone data that isn't
3267
         * relevant for our search.  Treat the
3268
         * node as if it were empty and keep looking.
3269
         */
3270
0
        empty_node = true;
3271
0
        result = previous_closest_nsec(
3272
0
          typepair, search, name, &prevnode,
3273
0
          &nseciter, &first);
3274
0
      } else {
3275
        /*
3276
         * We found an active node, but either the
3277
         * NSEC or the RRSIG NSEC is missing.  This
3278
         * shouldn't happen.
3279
         */
3280
0
        result = DNS_R_BADDB;
3281
0
      }
3282
0
    } else {
3283
      /*
3284
       * This node isn't active.  We've got to keep
3285
       * looking.
3286
       */
3287
0
      result = previous_closest_nsec(typepair, search, name,
3288
0
                   &prevnode, &nseciter,
3289
0
                   &first);
3290
0
    }
3291
0
    NODE_UNLOCK(nlock, &nlocktype);
3292
0
    node = prevnode;
3293
0
    prevnode = NULL;
3294
0
  } while (empty_node && result == ISC_R_SUCCESS);
3295
3296
0
  if (result == ISC_R_NOMORE && wraps) {
3297
0
    result = dns_qpiter_prev(&search->iter, (void **)&node, NULL);
3298
0
    if (result == ISC_R_SUCCESS) {
3299
0
      dns_name_copy(&node->name, name);
3300
0
      wraps = false;
3301
0
      goto again;
3302
0
    }
3303
0
  }
3304
3305
  /*
3306
   * If the result is ISC_R_NOMORE, then we got to the beginning of
3307
   * the database and didn't find a NSEC record.  This shouldn't
3308
   * happen.
3309
   */
3310
0
  if (result == ISC_R_NOMORE) {
3311
0
    result = DNS_R_BADDB;
3312
0
  }
3313
3314
0
  return result;
3315
0
}
3316
3317
static isc_result_t
3318
64
qpzone_check_zonecut(qpznode_t *node, void *arg DNS__DB_FLARG) {
3319
64
  qpz_search_t *search = arg;
3320
64
  dns_vecheader_t *dname_header = NULL, *sigdname_header = NULL;
3321
64
  dns_vecheader_t *ns_header = NULL;
3322
64
  dns_vecheader_t *found = NULL;
3323
64
  isc_result_t result = DNS_R_CONTINUE;
3324
64
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
3325
64
  isc_rwlock_t *nlock = qpzone_get_lock(node);
3326
3327
64
  NODE_RDLOCK(nlock, &nlocktype);
3328
3329
  /*
3330
   * Look for an NS or DNAME rdataset active in our version.
3331
   */
3332
192
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
3333
192
    if (top->typepair == DNS_TYPEPAIR(dns_rdatatype_ns) ||
3334
192
        top->typepair == DNS_TYPEPAIR(dns_rdatatype_dname) ||
3335
128
        top->typepair == DNS_SIGTYPEPAIR(dns_rdatatype_dname))
3336
64
    {
3337
64
      dns_vecheader_t *header =
3338
64
        first_existing_header(top, search->serial);
3339
64
      if (header != NULL) {
3340
64
        if (top->typepair ==
3341
64
            DNS_TYPEPAIR(dns_rdatatype_dname))
3342
0
        {
3343
0
          dname_header = header;
3344
64
        } else if (top->typepair ==
3345
64
             DNS_SIGTYPEPAIR(dns_rdatatype_dname))
3346
0
        {
3347
0
          sigdname_header = header;
3348
64
        } else if (node != search->qpdb->origin ||
3349
64
             IS_STUB(search->qpdb))
3350
0
        {
3351
          /*
3352
           * We've found an NS rdataset that
3353
           * isn't at the origin node.
3354
           */
3355
0
          ns_header = header;
3356
0
        }
3357
64
      }
3358
64
    }
3359
192
  }
3360
3361
  /*
3362
   * Did we find anything?
3363
   */
3364
64
  if (!IS_STUB(search->qpdb) && ns_header != NULL) {
3365
    /*
3366
     * Note that NS has precedence over DNAME if both exist
3367
     * in a zone.  Otherwise DNAME take precedence over NS.
3368
     */
3369
0
    found = ns_header;
3370
0
    search->zonecut_sigheader = NULL;
3371
64
  } else if (dname_header != NULL) {
3372
0
    found = dname_header;
3373
0
    search->zonecut_sigheader = sigdname_header;
3374
64
  } else if (ns_header != NULL) {
3375
0
    found = ns_header;
3376
0
    search->zonecut_sigheader = NULL;
3377
0
  }
3378
3379
64
  if (found != NULL) {
3380
    /*
3381
     * We increment the reference count on node to ensure that
3382
     * search->zonecut_header will still be valid later.
3383
     */
3384
0
    qpznode_acquire(node DNS__DB_FLARG_PASS);
3385
0
    search->zonecut = node;
3386
0
    search->zonecut_header = found;
3387
0
    search->need_cleanup = true;
3388
    /*
3389
     * Since we've found a zonecut, anything beneath it is
3390
     * glue and is not subject to wildcard matching, so we
3391
     * may clear search->wild.
3392
     */
3393
0
    search->wild = false;
3394
0
    if ((search->options & DNS_DBFIND_GLUEOK) == 0) {
3395
      /*
3396
       * If the caller does not want to find glue, then
3397
       * this is the best answer and the search should
3398
       * stop now.
3399
       */
3400
0
      result = DNS_R_PARTIALMATCH;
3401
0
    } else {
3402
0
      dns_name_t *zcname = NULL;
3403
3404
      /*
3405
       * The search will continue beneath the zone cut.
3406
       * This may or may not be the best match.  In case it
3407
       * is, we need to remember the node name.
3408
       */
3409
0
      zcname = dns_fixedname_name(&search->zonecut_name);
3410
0
      dns_name_copy(&node->name, zcname);
3411
0
      search->copy_name = true;
3412
0
    }
3413
64
  } else {
3414
    /*
3415
     * There is no zonecut at this node which is active in this
3416
     * version.
3417
     *
3418
     * If this is a "wild" node and the caller hasn't disabled
3419
     * wildcard matching, remember that we've seen a wild node
3420
     * in case we need to go searching for wildcard matches
3421
     * later on.
3422
     */
3423
64
    if (node->wild && (search->options & DNS_DBFIND_NOWILD) == 0) {
3424
0
      search->wild = true;
3425
0
    }
3426
64
  }
3427
3428
64
  NODE_UNLOCK(nlock, &nlocktype);
3429
3430
64
  return result;
3431
64
}
3432
3433
static void
3434
qpz_search_init(qpz_search_t *search, qpzonedb_t *db, qpz_version_t *version,
3435
181
    unsigned int options) {
3436
  /*
3437
   * qpz_search_t contains two structures with large buffers (dns_qpiter_t
3438
   * and dns_qpchain_t). Those two structures will be initialized later by
3439
   * dns_qp_lookup anyway.
3440
   * To avoid the overhead of zero initialization, we avoid designated
3441
   * initializers and initialize all "small" fields manually.
3442
   */
3443
181
  search->qpdb = db;
3444
181
  search->version = version;
3445
181
  search->qpr = (dns_qpread_t){};
3446
181
  search->serial = version->serial;
3447
181
  search->options = options;
3448
  /*
3449
   * qpch->in -- init in dns_qp_lookup
3450
   * qpiter -- init in dns_qp_lookup
3451
   */
3452
181
  search->copy_name = false;
3453
181
  search->need_cleanup = false;
3454
181
  search->wild = false;
3455
181
  search->zonecut = NULL;
3456
181
  search->zonecut_header = NULL;
3457
181
  search->zonecut_sigheader = NULL;
3458
181
  dns_fixedname_init(&search->zonecut_name);
3459
181
}
3460
3461
static isc_result_t
3462
qpzone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
3463
      dns_rdatatype_t type, unsigned int options,
3464
      isc_stdtime_t now ISC_ATTR_UNUSED, dns_dbnode_t **nodep,
3465
      dns_name_t *foundname,
3466
      dns_clientinfomethods_t *methods ISC_ATTR_UNUSED,
3467
      dns_clientinfo_t *clientinfo ISC_ATTR_UNUSED,
3468
      dns_rdataset_t *rdataset,
3469
181
      dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
3470
181
  isc_result_t result;
3471
181
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
3472
181
  qpznode_t *node = NULL;
3473
181
  bool cname_ok = true, close_version = false;
3474
181
  bool maybe_zonecut = false, at_zonecut = false;
3475
181
  bool wild = false, empty_node = false;
3476
181
  bool nsec3 = false;
3477
181
  dns_vecheader_t *found = NULL, *nsecheader = NULL;
3478
181
  dns_vecheader_t *foundsig = NULL, *cnamesig = NULL, *nsecsig = NULL;
3479
181
  dns_typepair_t sigpair;
3480
181
  bool active;
3481
181
  isc_rwlock_t *nlock = NULL;
3482
181
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
3483
3484
181
  REQUIRE(VALID_QPZONE((qpzonedb_t *)db));
3485
181
  INSIST(version == NULL ||
3486
181
         ((qpz_version_t *)version)->qpdb == (qpzonedb_t *)db);
3487
3488
  /*
3489
   * If the caller didn't supply a version, attach to the current
3490
   * version.
3491
   */
3492
181
  if (version == NULL) {
3493
181
    currentversion(db, &version);
3494
181
    close_version = true;
3495
181
  }
3496
3497
181
  dns_namespace_t nspace;
3498
181
  qpz_search_t search;
3499
181
  qpz_search_init(&search, (qpzonedb_t *)db, (qpz_version_t *)version,
3500
181
      options);
3501
3502
181
  if ((options & DNS_DBFIND_FORCENSEC3) != 0) {
3503
0
    nsec3 = true;
3504
0
    nspace = DNS_DBNAMESPACE_NSEC3;
3505
181
  } else {
3506
181
    nspace = DNS_DBNAMESPACE_NORMAL;
3507
181
  }
3508
181
  dns_qpmulti_query(qpdb->tree, &search.qpr);
3509
3510
  /*
3511
   * Search down from the root of the tree.
3512
   */
3513
181
  result = dns_qp_lookup(&search.qpr, name, nspace, &search.iter,
3514
181
             &search.chain, (void **)&node, NULL);
3515
181
  if (result != ISC_R_NOTFOUND) {
3516
181
    dns_name_copy(&node->name, foundname);
3517
181
  }
3518
3519
  /*
3520
   * Check the QP chain to see if there's a node above us with a
3521
   * active DNAME or NS rdatasets.
3522
   *
3523
   * We're only interested in nodes above QNAME, so if the result
3524
   * was success, then we skip the last item in the chain.
3525
   */
3526
181
  unsigned int clen = dns_qpchain_length(&search.chain);
3527
181
  if (result == ISC_R_SUCCESS) {
3528
117
    clen--;
3529
117
  }
3530
245
  for (unsigned int i = 0; i < clen && search.zonecut == NULL; i++) {
3531
64
    qpznode_t *n = NULL;
3532
64
    isc_result_t tresult;
3533
3534
64
    dns_qpchain_node(&search.chain, i, (void **)&n, NULL);
3535
64
    tresult = qpzone_check_zonecut(n, &search DNS__DB_FLARG_PASS);
3536
64
    if (tresult != DNS_R_CONTINUE) {
3537
0
      result = tresult;
3538
0
      search.chain.len = i - 1;
3539
0
      dns_name_copy(&n->name, foundname);
3540
0
      node = n;
3541
0
    }
3542
64
  }
3543
3544
181
  if (result == DNS_R_PARTIALMATCH) {
3545
64
  partial_match:
3546
64
    if (search.zonecut != NULL) {
3547
0
      result = qpzone_setup_delegation(
3548
0
        &search, nodep, foundname, rdataset,
3549
0
        sigrdataset DNS__DB_FLARG_PASS);
3550
0
      goto tree_exit;
3551
0
    }
3552
3553
64
    if (search.wild) {
3554
      /*
3555
       * At least one of the levels in the search chain
3556
       * potentially has a wildcard.  For each such level,
3557
       * we must see if there's a matching wildcard active
3558
       * in the current version.
3559
       */
3560
0
      result = find_wildcard(&search, &node, name, nspace);
3561
0
      if (result == ISC_R_SUCCESS) {
3562
0
        dns_name_copy(name, foundname);
3563
0
        wild = true;
3564
0
        goto found;
3565
0
      } else if (result != ISC_R_NOTFOUND) {
3566
0
        goto tree_exit;
3567
0
      }
3568
0
    }
3569
3570
64
    active = false;
3571
64
    if (!nsec3) {
3572
      /*
3573
       * The NSEC3 tree won't have empty nodes,
3574
       * so it isn't necessary to check for them.
3575
       */
3576
64
      dns_qpiter_t iter = search.iter;
3577
64
      active = activeempty(&search, &iter, name);
3578
64
    }
3579
3580
    /*
3581
     * If we're here, then the name does not exist, is not
3582
     * beneath a zonecut, and there's no matching wildcard.
3583
     */
3584
64
    if ((search.version->secure && !search.version->havensec3) ||
3585
64
        nsec3)
3586
0
    {
3587
0
      result = find_closest_nsec(
3588
0
        &search, nodep, foundname, rdataset,
3589
0
        sigrdataset, nsec3,
3590
0
        search.version->secure DNS__DB_FLARG_PASS);
3591
0
      if (result == ISC_R_SUCCESS) {
3592
0
        result = active ? DNS_R_EMPTYNAME
3593
0
            : DNS_R_NXDOMAIN;
3594
0
      }
3595
64
    } else {
3596
64
      result = active ? DNS_R_EMPTYNAME : DNS_R_NXDOMAIN;
3597
64
    }
3598
64
    goto tree_exit;
3599
117
  } else if (result != ISC_R_SUCCESS) {
3600
0
    goto tree_exit;
3601
0
  }
3602
3603
117
found:
3604
  /*
3605
   * We have found a node whose name is the desired name, or we
3606
   * have matched a wildcard.
3607
   */
3608
3609
117
  nlock = qpzone_get_lock(node);
3610
117
  NODE_RDLOCK(nlock, &nlocktype);
3611
3612
117
  if (search.zonecut != NULL) {
3613
    /*
3614
     * If we're beneath a zone cut, we don't want to look for
3615
     * CNAMEs because they're not legitimate zone glue.
3616
     */
3617
0
    cname_ok = false;
3618
117
  } else {
3619
    /*
3620
     * The node may be a zone cut itself.  If it might be one,
3621
     * make sure we check for it later.
3622
     *
3623
     * DS records live above the zone cut in ordinary zone so
3624
     * we want to ignore any referral.
3625
     *
3626
     * Stub zones don't have anything "above" the delegation so
3627
     * we always return a referral.
3628
     */
3629
117
    if (node->delegating && ((node != search.qpdb->origin &&
3630
0
            !dns_rdatatype_atparent(type)) ||
3631
0
           IS_STUB(search.qpdb)))
3632
0
    {
3633
0
      maybe_zonecut = true;
3634
0
    }
3635
117
  }
3636
3637
  /*
3638
   * Certain DNSSEC types are not subject to CNAME matching
3639
   * (RFC4035, section 2.5).
3640
   *
3641
   * We don't check for RRSIG, because we don't store RRSIG records
3642
   * directly.
3643
   */
3644
117
  if (type == dns_rdatatype_nsec) {
3645
0
    cname_ok = false;
3646
0
  }
3647
3648
  /*
3649
   * We now go looking for rdata...
3650
   */
3651
3652
117
  sigpair = DNS_SIGTYPEPAIR(type);
3653
117
  empty_node = true;
3654
351
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
3655
    /*
3656
     * Look for an active, extant rdataset.
3657
     */
3658
351
    dns_vecheader_t *header = first_existing_header(top,
3659
351
                search.serial);
3660
351
    if (header != NULL) {
3661
      /*
3662
       * We now know that there is at least one active
3663
       * rdataset at this node.
3664
       */
3665
351
      empty_node = false;
3666
3667
      /*
3668
       * Do special zone cut handling, if requested.
3669
       */
3670
351
      if (maybe_zonecut &&
3671
0
          top->typepair == DNS_TYPEPAIR(dns_rdatatype_ns))
3672
0
      {
3673
        /*
3674
         * We increment the reference count on node to
3675
         * ensure that search->zonecut_header will
3676
         * still be valid later.
3677
         */
3678
0
        qpznode_acquire(node DNS__DB_FLARG_PASS);
3679
0
        search.zonecut = node;
3680
0
        search.zonecut_header = header;
3681
0
        search.zonecut_sigheader = NULL;
3682
0
        search.need_cleanup = true;
3683
0
        maybe_zonecut = false;
3684
0
        at_zonecut = true;
3685
3686
0
        if ((search.options & DNS_DBFIND_GLUEOK) == 0 &&
3687
0
            type != dns_rdatatype_nsec)
3688
0
        {
3689
          /*
3690
           * Glue is not OK, but any answer we
3691
           * could return would be glue.  Return
3692
           * the delegation.
3693
           */
3694
0
          found = NULL;
3695
0
          break;
3696
0
        }
3697
0
        if (found != NULL && foundsig != NULL) {
3698
0
          break;
3699
0
        }
3700
0
      }
3701
3702
      /*
3703
       * If the NSEC3 record doesn't match the chain
3704
       * we are using behave as if it isn't here.
3705
       */
3706
351
      if (top->typepair ==
3707
351
            DNS_TYPEPAIR(dns_rdatatype_nsec3) &&
3708
0
          !matchparams(header, &search))
3709
0
      {
3710
0
        NODE_UNLOCK(nlock, &nlocktype);
3711
0
        goto partial_match;
3712
0
      }
3713
      /*
3714
       * If we found a type we were looking for,
3715
       * remember it.
3716
       */
3717
351
      if (top->typepair == type ||
3718
234
          type == dns_rdatatype_any ||
3719
234
          (top->typepair ==
3720
234
             DNS_TYPEPAIR(dns_rdatatype_cname) &&
3721
0
           cname_ok))
3722
117
      {
3723
        /*
3724
         * We've found the answer!
3725
         */
3726
117
        found = header;
3727
117
        if (top->typepair ==
3728
117
              DNS_TYPEPAIR(dns_rdatatype_cname) &&
3729
0
            cname_ok)
3730
0
        {
3731
          /*
3732
           * We may be finding a CNAME instead
3733
           * of the desired type.
3734
           *
3735
           * If we've already got the CNAME RRSIG,
3736
           * use it, otherwise change sigtype
3737
           * so that we find it.
3738
           */
3739
0
          if (cnamesig != NULL) {
3740
0
            foundsig = cnamesig;
3741
0
          } else {
3742
0
            sigpair = DNS_SIGTYPEPAIR(
3743
0
              dns_rdatatype_cname);
3744
0
          }
3745
0
        }
3746
        /*
3747
         * If we've got all we need, end the search.
3748
         */
3749
117
        if (!maybe_zonecut && foundsig != NULL) {
3750
0
          break;
3751
0
        }
3752
234
      } else if (top->typepair == sigpair) {
3753
        /*
3754
         * We've found the RRSIG rdataset for our
3755
         * target type.  Remember it.
3756
         */
3757
0
        foundsig = header;
3758
        /*
3759
         * If we've got all we need, end the search.
3760
         */
3761
0
        if (!maybe_zonecut && found != NULL) {
3762
0
          break;
3763
0
        }
3764
234
      } else if (top->typepair ==
3765
234
             DNS_TYPEPAIR(dns_rdatatype_nsec) &&
3766
0
           !search.version->havensec3)
3767
0
      {
3768
        /*
3769
         * Remember a NSEC rdataset even if we're
3770
         * not specifically looking for it, because
3771
         * we might need it later.
3772
         */
3773
0
        nsecheader = header;
3774
234
      } else if (top->typepair ==
3775
234
             DNS_SIGTYPEPAIR(
3776
0
               dns_rdatatype_nsec) &&
3777
0
           !search.version->havensec3)
3778
0
      {
3779
        /*
3780
         * If we need the NSEC rdataset, we'll also
3781
         * need its signature.
3782
         */
3783
0
        nsecsig = header;
3784
234
      } else if (cname_ok &&
3785
234
           top->typepair ==
3786
234
             DNS_SIGTYPEPAIR(dns_rdatatype_cname))
3787
0
      {
3788
        /*
3789
         * If we get a CNAME match, we'll also need
3790
         * its signature.
3791
         */
3792
0
        cnamesig = header;
3793
0
      }
3794
351
    }
3795
351
  }
3796
3797
117
  if (empty_node) {
3798
    /*
3799
     * We have an exact match for the name, but there are no
3800
     * active rdatasets in the desired version.  That means that
3801
     * this node doesn't exist in the desired version.
3802
     * If there's a node above this one, reassign the
3803
     * foundname to the parent and treat this as a partial
3804
     * match.
3805
     */
3806
0
    if (!wild) {
3807
0
      unsigned int len = search.chain.len - 1;
3808
0
      if (len > 0) {
3809
0
        NODE_UNLOCK(nlock, &nlocktype);
3810
0
        dns_qpchain_node(&search.chain, len - 1,
3811
0
             (void **)&node, NULL);
3812
0
        dns_name_copy(&node->name, foundname);
3813
0
        goto partial_match;
3814
0
      }
3815
0
    }
3816
0
  }
3817
3818
  /*
3819
   * If we didn't find what we were looking for...
3820
   */
3821
117
  if (found == NULL) {
3822
0
    if (search.zonecut != NULL) {
3823
      /*
3824
       * We were trying to find glue at a node beneath a
3825
       * zone cut, but didn't.
3826
       *
3827
       * Return the delegation.
3828
       */
3829
0
      NODE_UNLOCK(nlock, &nlocktype);
3830
0
      result = qpzone_setup_delegation(
3831
0
        &search, nodep, foundname, rdataset,
3832
0
        sigrdataset DNS__DB_FLARG_PASS);
3833
0
      goto tree_exit;
3834
0
    }
3835
    /*
3836
     * The desired type doesn't exist.
3837
     */
3838
0
    result = DNS_R_NXRRSET;
3839
0
    if (search.version->secure && !search.version->havensec3 &&
3840
0
        (nsecheader == NULL || nsecsig == NULL))
3841
0
    {
3842
      /*
3843
       * The zone is secure but there's no NSEC,
3844
       * or the NSEC has no signature!
3845
       */
3846
0
      if (!wild) {
3847
0
        result = DNS_R_BADDB;
3848
0
        goto node_exit;
3849
0
      }
3850
3851
0
      NODE_UNLOCK(nlock, &nlocktype);
3852
0
      result = find_closest_nsec(
3853
0
        &search, nodep, foundname, rdataset,
3854
0
        sigrdataset, false,
3855
0
        search.version->secure DNS__DB_FLARG_PASS);
3856
0
      if (result == ISC_R_SUCCESS) {
3857
0
        result = DNS_R_EMPTYWILD;
3858
0
      }
3859
0
      goto tree_exit;
3860
0
    }
3861
0
    if (nodep != NULL) {
3862
0
      qpznode_acquire(node DNS__DB_FLARG_PASS);
3863
0
      *nodep = (dns_dbnode_t *)node;
3864
0
    }
3865
0
    if (search.version->secure && !search.version->havensec3) {
3866
0
      bindrdataset(search.qpdb, nsecheader,
3867
0
             rdataset DNS__DB_FLARG_PASS);
3868
0
      if (nsecsig != NULL) {
3869
0
        bindrdataset(search.qpdb, nsecsig,
3870
0
               sigrdataset DNS__DB_FLARG_PASS);
3871
0
      }
3872
0
    }
3873
0
    if (wild) {
3874
0
      foundname->attributes.wildcard = true;
3875
0
    }
3876
0
    goto node_exit;
3877
0
  }
3878
3879
  /*
3880
   * We found what we were looking for, or we found a CNAME.
3881
   */
3882
117
  if (type != found->typepair && type != dns_rdatatype_any &&
3883
0
      found->typepair == DNS_TYPEPAIR(dns_rdatatype_cname))
3884
0
  {
3885
    /*
3886
     * We weren't doing an ANY query and we found a CNAME instead
3887
     * of the type we were looking for, so we need to indicate
3888
     * that result to the caller.
3889
     */
3890
0
    result = DNS_R_CNAME;
3891
117
  } else if (search.zonecut != NULL) {
3892
    /*
3893
     * If we're beneath a zone cut, we must indicate that the
3894
     * result is glue, unless we're actually at the zone cut
3895
     * and the type is NSEC.
3896
     */
3897
0
    if (search.zonecut == node) {
3898
0
      if (dns_rdatatype_isnsec(type)) {
3899
0
        result = ISC_R_SUCCESS;
3900
0
      } else if (type == dns_rdatatype_any) {
3901
0
        result = DNS_R_ZONECUT;
3902
0
      } else {
3903
0
        result = DNS_R_GLUE;
3904
0
      }
3905
0
    } else {
3906
0
      result = DNS_R_GLUE;
3907
0
    }
3908
117
  } else {
3909
    /*
3910
     * An ordinary successful query!
3911
     */
3912
117
    result = ISC_R_SUCCESS;
3913
117
  }
3914
3915
117
  if (nodep != NULL) {
3916
117
    if (!at_zonecut) {
3917
117
      qpznode_acquire(node DNS__DB_FLARG_PASS);
3918
117
    } else {
3919
0
      search.need_cleanup = false;
3920
0
    }
3921
117
    *nodep = (dns_dbnode_t *)node;
3922
117
  }
3923
3924
117
  if (type != dns_rdatatype_any) {
3925
117
    bindrdataset(search.qpdb, found, rdataset DNS__DB_FLARG_PASS);
3926
117
    if (foundsig != NULL) {
3927
0
      bindrdataset(search.qpdb, foundsig,
3928
0
             sigrdataset DNS__DB_FLARG_PASS);
3929
0
    }
3930
117
  }
3931
3932
117
  if (wild) {
3933
0
    foundname->attributes.wildcard = true;
3934
0
  }
3935
3936
117
node_exit:
3937
117
  NODE_UNLOCK(nlock, &nlocktype);
3938
3939
181
tree_exit:
3940
181
  dns_qpread_destroy(qpdb->tree, &search.qpr);
3941
3942
  /*
3943
   * If we found a zonecut but aren't going to use it, we have to
3944
   * let go of it.
3945
   */
3946
181
  if (search.need_cleanup) {
3947
0
    node = search.zonecut;
3948
0
    INSIST(node != NULL);
3949
0
    nlock = qpzone_get_lock(node);
3950
3951
0
    NODE_RDLOCK(nlock, &nlocktype);
3952
0
    (void)qpznode_release(node DNS__DB_FLARG_PASS);
3953
0
    NODE_UNLOCK(nlock, &nlocktype);
3954
0
  }
3955
3956
181
  if (close_version) {
3957
181
    closeversion(db, &version, false DNS__DB_FLARG_PASS);
3958
181
  }
3959
3960
181
  return result;
3961
181
}
3962
3963
static isc_result_t
3964
qpzone_allrdatasets(dns_db_t *db, dns_dbnode_t *dbnode,
3965
        dns_dbversion_t *dbversion, unsigned int options,
3966
        isc_stdtime_t now ISC_ATTR_UNUSED,
3967
0
        dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) {
3968
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
3969
0
  qpznode_t *node = (qpznode_t *)dbnode;
3970
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
3971
0
  qpdb_rdatasetiter_t *iterator = NULL;
3972
3973
0
  REQUIRE(VALID_QPZONE(qpdb));
3974
3975
0
  if (version == NULL) {
3976
0
    currentversion(db, (dns_dbversion_t **)(void *)(&version));
3977
0
  } else {
3978
0
    INSIST(version->qpdb == qpdb);
3979
0
    isc_refcount_increment(&version->references);
3980
0
  }
3981
3982
0
  iterator = isc_mem_get(qpdb->common.mctx, sizeof(*iterator));
3983
0
  *iterator = (qpdb_rdatasetiter_t){
3984
0
    .common.methods = &rdatasetiter_methods,
3985
0
    .common.db = db,
3986
0
    .common.node = (dns_dbnode_t *)node,
3987
0
    .common.version = (dns_dbversion_t *)version,
3988
0
    .common.options = options,
3989
0
    .common.magic = DNS_RDATASETITER_MAGIC,
3990
0
  };
3991
3992
0
  qpznode_acquire(node DNS__DB_FLARG_PASS);
3993
3994
0
  *iteratorp = (dns_rdatasetiter_t *)iterator;
3995
0
  return ISC_R_SUCCESS;
3996
0
}
3997
3998
static void
3999
0
qpzone_attachnode(dns_dbnode_t *source, dns_dbnode_t **targetp DNS__DB_FLARG) {
4000
0
  qpznode_t *node = (qpznode_t *)source;
4001
4002
0
  REQUIRE(targetp != NULL && *targetp == NULL);
4003
4004
0
  qpznode_acquire(node DNS__DB_FLARG_PASS);
4005
4006
0
  *targetp = source;
4007
0
}
4008
4009
static void
4010
125
qpzone_detachnode(dns_dbnode_t **nodep DNS__DB_FLARG) {
4011
125
  qpznode_t *node = NULL;
4012
125
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4013
125
  isc_rwlock_t *nlock = NULL;
4014
4015
125
  REQUIRE(nodep != NULL && *nodep != NULL);
4016
4017
125
  node = (qpznode_t *)(*nodep);
4018
4019
125
  *nodep = NULL;
4020
125
  nlock = qpzone_get_lock(node);
4021
4022
  /*
4023
   * qpzone_destroy() uses call_rcu() API to destroy the node locks, so it
4024
   * is safe to call it in the middle of NODE_LOCK, but we need to acquire
4025
   * the database reference to prevent destroying the database while the
4026
   * NODE_LOCK is locked.
4027
   */
4028
4029
125
  rcu_read_lock();
4030
125
  NODE_RDLOCK(nlock, &nlocktype);
4031
125
  (void)qpznode_release(node DNS__DB_FLARG_PASS);
4032
125
  NODE_UNLOCK(nlock, &nlocktype);
4033
125
  rcu_read_unlock();
4034
125
}
4035
4036
static unsigned int
4037
2
nodecount(dns_db_t *db) {
4038
2
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
4039
2
  dns_qp_memusage_t mu;
4040
4041
2
  REQUIRE(VALID_QPZONE(qpdb));
4042
4043
2
  mu = dns_qpmulti_memusage(qpdb->tree);
4044
4045
2
  return mu.leaves;
4046
2
}
4047
4048
static isc_result_t
4049
2
getoriginnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) {
4050
2
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
4051
4052
2
  REQUIRE(VALID_QPZONE(qpdb));
4053
2
  REQUIRE(nodep != NULL && *nodep == NULL);
4054
4055
  /* Note that the access to the origin node doesn't require a DB lock */
4056
2
  INSIST(qpdb->origin != NULL);
4057
2
  qpznode_acquire(qpdb->origin DNS__DB_FLARG_PASS);
4058
2
  *nodep = (dns_dbnode_t *)qpdb->origin;
4059
4060
2
  return ISC_R_SUCCESS;
4061
2
}
4062
4063
/*
4064
 * Rdataset Iterator Methods
4065
 */
4066
4067
static void
4068
0
rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) {
4069
0
  qpdb_rdatasetiter_t *qrditer = NULL;
4070
4071
0
  qrditer = (qpdb_rdatasetiter_t *)(*iteratorp);
4072
4073
0
  if (qrditer->common.version != NULL) {
4074
0
    closeversion(qrditer->common.db, &qrditer->common.version,
4075
0
           false DNS__DB_FLARG_PASS);
4076
0
  }
4077
0
  dns__db_detachnode(&qrditer->common.node DNS__DB_FLARG_PASS);
4078
0
  isc_mem_put(qrditer->common.db->mctx, qrditer, sizeof(*qrditer));
4079
4080
0
  *iteratorp = NULL;
4081
0
}
4082
4083
static isc_result_t
4084
0
rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG) {
4085
0
  qpdb_rdatasetiter_t *qrditer = (qpdb_rdatasetiter_t *)iterator;
4086
0
  qpznode_t *node = (qpznode_t *)qrditer->common.node;
4087
0
  qpz_version_t *version = (qpz_version_t *)qrditer->common.version;
4088
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4089
0
  isc_rwlock_t *nlock = qpzone_get_lock(node);
4090
4091
0
  qrditer->currenttop = NULL;
4092
0
  qrditer->current = NULL;
4093
4094
0
  NODE_RDLOCK(nlock, &nlocktype);
4095
4096
0
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
4097
0
    qrditer->current = first_existing_header(top, version->serial);
4098
4099
0
    if (qrditer->current != NULL) {
4100
0
      qrditer->currenttop = top;
4101
0
      break;
4102
0
    }
4103
0
  }
4104
4105
0
  NODE_UNLOCK(nlock, &nlocktype);
4106
4107
0
  if (qrditer->currenttop == NULL) {
4108
0
    return ISC_R_NOMORE;
4109
0
  }
4110
4111
0
  return ISC_R_SUCCESS;
4112
0
}
4113
4114
static isc_result_t
4115
0
rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG) {
4116
0
  qpdb_rdatasetiter_t *qrditer = (qpdb_rdatasetiter_t *)iterator;
4117
0
  qpznode_t *node = (qpznode_t *)qrditer->common.node;
4118
0
  qpz_version_t *version = (qpz_version_t *)qrditer->common.version;
4119
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4120
0
  isc_rwlock_t *nlock = qpzone_get_lock(node);
4121
0
  dns_vectop_t *from = NULL;
4122
4123
0
  if (qrditer->currenttop == NULL) {
4124
0
    return ISC_R_NOMORE;
4125
0
  }
4126
4127
0
  NODE_RDLOCK(nlock, &nlocktype);
4128
4129
0
  from = ISC_SLIST_NEXT(qrditer->currenttop, next_type);
4130
0
  qrditer->currenttop = NULL;
4131
0
  qrditer->current = NULL;
4132
4133
  /*
4134
   * Find the start of the header chain for the next type.
4135
   */
4136
0
  if (from != NULL) {
4137
0
    ISC_SLIST_FOREACH_FROM(top, node->next_type, next_type, from) {
4138
0
      qrditer->current =
4139
0
        first_existing_header(top, version->serial);
4140
0
      if (qrditer->current != NULL) {
4141
0
        qrditer->currenttop = top;
4142
0
        break;
4143
0
      }
4144
0
    }
4145
0
  }
4146
4147
0
  NODE_UNLOCK(nlock, &nlocktype);
4148
4149
0
  if (qrditer->currenttop == NULL) {
4150
0
    return ISC_R_NOMORE;
4151
0
  }
4152
4153
0
  return ISC_R_SUCCESS;
4154
0
}
4155
4156
static void
4157
rdatasetiter_current(dns_rdatasetiter_t *iterator,
4158
0
         dns_rdataset_t *rdataset DNS__DB_FLARG) {
4159
0
  qpdb_rdatasetiter_t *qrditer = (qpdb_rdatasetiter_t *)iterator;
4160
0
  qpzonedb_t *qpdb = (qpzonedb_t *)(qrditer->common.db);
4161
0
  qpznode_t *qpnode = (qpznode_t *)qrditer->common.node;
4162
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4163
0
  isc_rwlock_t *nlock = qpzone_get_lock(qpnode);
4164
0
  dns_vecheader_t *header = qrditer->current;
4165
4166
0
  REQUIRE(header != NULL);
4167
4168
0
  NODE_RDLOCK(nlock, &nlocktype);
4169
4170
0
  bindrdataset(qpdb, header, rdataset DNS__DB_FLARG_PASS);
4171
4172
0
  NODE_UNLOCK(nlock, &nlocktype);
4173
0
}
4174
4175
/*
4176
 * Database Iterator Methods
4177
 */
4178
static void
4179
0
reference_iter_node(qpdb_dbiterator_t *iter DNS__DB_FLARG) {
4180
0
  qpznode_t *node = iter->node;
4181
4182
0
  if (node == NULL) {
4183
0
    return;
4184
0
  }
4185
4186
0
  qpznode_acquire(node DNS__DB_FLARG_PASS);
4187
0
}
4188
4189
static void
4190
0
dereference_iter_node(qpdb_dbiterator_t *iter DNS__DB_FLARG) {
4191
0
  qpznode_t *node = iter->node;
4192
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4193
0
  isc_rwlock_t *nlock = NULL;
4194
4195
0
  if (node == NULL) {
4196
0
    return;
4197
0
  }
4198
4199
0
  iter->node = NULL;
4200
0
  nlock = qpzone_get_lock(node);
4201
4202
0
  NODE_RDLOCK(nlock, &nlocktype);
4203
0
  (void)qpznode_release(node DNS__DB_FLARG_PASS);
4204
0
  NODE_UNLOCK(nlock, &nlocktype);
4205
0
}
4206
4207
static void
4208
0
dbiterator_destroy(dns_dbiterator_t **iteratorp DNS__DB_FLARG) {
4209
0
  qpdb_dbiterator_t *iter = (qpdb_dbiterator_t *)(*iteratorp);
4210
0
  dns_db_t *db = NULL;
4211
4212
0
  dereference_iter_node(iter DNS__DB_FLARG_PASS);
4213
4214
0
  dns_db_attach(iter->common.db, &db);
4215
0
  dns_db_detach(&iter->common.db);
4216
4217
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
4218
0
  dns_qpsnap_destroy(qpdb->tree, &iter->snap);
4219
4220
0
  isc_mem_put(db->mctx, iter, sizeof(*iter));
4221
0
  dns_db_detach(&db);
4222
4223
0
  *iteratorp = NULL;
4224
0
}
4225
4226
static isc_result_t
4227
0
dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG) {
4228
0
  isc_result_t result;
4229
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4230
0
  qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
4231
4232
0
  if (qpdbiter->result != ISC_R_SUCCESS &&
4233
0
      qpdbiter->result != ISC_R_NOTFOUND &&
4234
0
      qpdbiter->result != DNS_R_PARTIALMATCH &&
4235
0
      qpdbiter->result != ISC_R_NOMORE)
4236
0
  {
4237
0
    return qpdbiter->result;
4238
0
  }
4239
4240
0
  dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4241
4242
0
  dns_qpiter_init(qpdbiter->snap, &qpdbiter->iter);
4243
0
  result = dns_qpiter_next(&qpdbiter->iter, (void **)&qpdbiter->node,
4244
0
         NULL);
4245
4246
0
  switch (qpdbiter->nsec3mode) {
4247
0
  case nonsec3:
4248
0
    if (result == ISC_R_SUCCESS) {
4249
      /*
4250
       * If we immediately hit an NSEC/NSEC3 node,
4251
       * we don't have any non-nsec nodes.
4252
       */
4253
0
      if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NORMAL) {
4254
0
        qpdbiter->node = NULL;
4255
0
        result = ISC_R_NOMORE;
4256
0
      }
4257
0
    }
4258
0
    break;
4259
0
  case full:
4260
    /* skip the NSEC3 origin node. */
4261
0
    if (result == ISC_R_SUCCESS &&
4262
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4263
0
    {
4264
0
      result = dns_qpiter_next(&qpdbiter->iter,
4265
0
             (void **)&qpdbiter->node,
4266
0
             NULL);
4267
0
    }
4268
0
    if (result != ISC_R_SUCCESS) {
4269
0
      qpdbiter->node = NULL;
4270
0
      break;
4271
0
    }
4272
4273
    /*
4274
     * If we hit an NSEC node, we need to start at the NSEC3 part of
4275
     * the tree.
4276
     */
4277
0
    if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NSEC) {
4278
0
      break;
4279
0
    }
4280
0
    INSIST(qpdbiter->node->nspace == DNS_DBNAMESPACE_NSEC);
4281
4282
    /* FALLTHROUGH */
4283
0
  case nsec3only:
4284
    /*
4285
     * NSEC3 follows after all non-nsec3 nodes, seek the NSEC3
4286
     * origin node.
4287
     */
4288
0
    result = dns_qp_lookup(qpdbiter->snap, &qpdb->common.origin,
4289
0
               DNS_DBNAMESPACE_NSEC3, &qpdbiter->iter,
4290
0
               NULL, (void **)&qpdbiter->node, NULL);
4291
0
    if (result != ISC_R_SUCCESS ||
4292
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4293
0
    {
4294
      /* skip the NSEC3 origin node (or its predecessor) */
4295
0
      result = dns_qpiter_next(&qpdbiter->iter,
4296
0
             (void **)&qpdbiter->node,
4297
0
             NULL);
4298
0
    }
4299
0
    break;
4300
0
  default:
4301
0
    UNREACHABLE();
4302
0
  }
4303
4304
0
  if (result == ISC_R_SUCCESS) {
4305
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4306
0
  } else {
4307
0
    qpdbiter->node = NULL;
4308
0
  }
4309
0
  qpdbiter->result = result;
4310
0
  return result;
4311
0
}
4312
4313
static isc_result_t
4314
0
dbiterator_last(dns_dbiterator_t *iterator DNS__DB_FLARG) {
4315
0
  isc_result_t result;
4316
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4317
0
  qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
4318
4319
0
  if (qpdbiter->result != ISC_R_SUCCESS &&
4320
0
      qpdbiter->result != ISC_R_NOTFOUND &&
4321
0
      qpdbiter->result != DNS_R_PARTIALMATCH &&
4322
0
      qpdbiter->result != ISC_R_NOMORE)
4323
0
  {
4324
0
    return qpdbiter->result;
4325
0
  }
4326
4327
0
  dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4328
4329
0
  dns_qpiter_init(qpdbiter->snap, &qpdbiter->iter);
4330
0
  result = dns_qpiter_prev(&qpdbiter->iter, (void **)&qpdbiter->node,
4331
0
         NULL);
4332
4333
0
  switch (qpdbiter->nsec3mode) {
4334
0
  case nsec3only:
4335
0
    if (result == ISC_R_SUCCESS) {
4336
0
      if (QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter)) {
4337
        /* tree only has NSEC3 origin node. */
4338
0
        qpdbiter->node = NULL;
4339
0
        result = ISC_R_NOMORE;
4340
0
      } else if (qpdbiter->node->nspace !=
4341
0
           DNS_DBNAMESPACE_NSEC3)
4342
0
      {
4343
        /* tree has no NSEC3 nodes at all. */
4344
0
        qpdbiter->node = NULL;
4345
0
        result = ISC_R_NOMORE;
4346
0
      }
4347
0
    }
4348
0
    break;
4349
0
  case full:
4350
    /* skip the NSEC3 origin node. */
4351
0
    if (result == ISC_R_SUCCESS &&
4352
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4353
0
    {
4354
0
      result = dns_qpiter_prev(&qpdbiter->iter,
4355
0
             (void **)&qpdbiter->node,
4356
0
             NULL);
4357
0
    }
4358
0
    if (result != ISC_R_SUCCESS) {
4359
0
      qpdbiter->node = NULL;
4360
0
      break;
4361
0
    }
4362
4363
    /*
4364
     * If we hit an NSEC node, we need to seek the final normal node
4365
     * of the tree.
4366
     */
4367
0
    if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NSEC) {
4368
0
      break;
4369
0
    }
4370
0
    INSIST(qpdbiter->node->nspace == DNS_DBNAMESPACE_NSEC);
4371
4372
    /* FALLTHROUGH */
4373
0
  case nonsec3:
4374
    /*
4375
     * The final non-nsec node is before the NSEC origin node.
4376
     */
4377
0
    result = dns_qp_lookup(qpdbiter->snap, &qpdb->common.origin,
4378
0
               DNS_DBNAMESPACE_NSEC, &qpdbiter->iter,
4379
0
               NULL, (void **)&qpdbiter->node, NULL);
4380
0
    if (result == ISC_R_SUCCESS) {
4381
0
      INSIST(QPDBITER_NSEC_ORIGIN_NODE(qpdb, qpdbiter));
4382
      /* skip the NSEC origin node */
4383
0
      result = dns_qpiter_prev(&qpdbiter->iter,
4384
0
             (void **)&qpdbiter->node,
4385
0
             NULL);
4386
0
    } else {
4387
      /*
4388
       * The NSEC origin node was not found, but the iterator
4389
       * should point to its predecessor, which is the node we
4390
       * want.
4391
       */
4392
0
      result = dns_qpiter_current(&qpdbiter->iter,
4393
0
                (void **)&qpdbiter->node,
4394
0
                NULL);
4395
0
      INSIST(result == ISC_R_SUCCESS);
4396
0
      INSIST(qpdbiter->node->nspace ==
4397
0
             DNS_DBNAMESPACE_NORMAL);
4398
0
    }
4399
0
    break;
4400
0
  default:
4401
0
    UNREACHABLE();
4402
0
  }
4403
4404
0
  if (result == ISC_R_SUCCESS) {
4405
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4406
0
  } else {
4407
0
    qpdbiter->node = NULL;
4408
0
  }
4409
0
  qpdbiter->result = result;
4410
0
  return result;
4411
0
}
4412
4413
static isc_result_t
4414
dbiterator_seek(dns_dbiterator_t *iterator,
4415
0
    const dns_name_t *name DNS__DB_FLARG) {
4416
0
  isc_result_t result, tresult;
4417
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4418
4419
0
  if (qpdbiter->result != ISC_R_SUCCESS &&
4420
0
      qpdbiter->result != ISC_R_NOTFOUND &&
4421
0
      qpdbiter->result != DNS_R_PARTIALMATCH &&
4422
0
      qpdbiter->result != ISC_R_NOMORE)
4423
0
  {
4424
0
    return qpdbiter->result;
4425
0
  }
4426
4427
0
  dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4428
4429
0
  switch (qpdbiter->nsec3mode) {
4430
0
  case nsec3only:
4431
0
    result = dns_qp_lookup(qpdbiter->snap, name,
4432
0
               DNS_DBNAMESPACE_NSEC3, &qpdbiter->iter,
4433
0
               NULL, (void **)&qpdbiter->node, NULL);
4434
0
    break;
4435
0
  case nonsec3:
4436
0
    result = dns_qp_lookup(qpdbiter->snap, name,
4437
0
               DNS_DBNAMESPACE_NORMAL, &qpdbiter->iter,
4438
0
               NULL, (void **)&qpdbiter->node, NULL);
4439
0
    break;
4440
0
  case full:
4441
0
    result = dns_qp_lookup(qpdbiter->snap, name,
4442
0
               DNS_DBNAMESPACE_NORMAL, &qpdbiter->iter,
4443
0
               NULL, (void **)&qpdbiter->node, NULL);
4444
0
    if (result != ISC_R_SUCCESS) {
4445
0
      tresult = dns_qp_lookup(qpdbiter->snap, name,
4446
0
            DNS_DBNAMESPACE_NSEC3,
4447
0
            &qpdbiter->iter, NULL,
4448
0
            (void **)&qpdbiter->node, NULL);
4449
0
      if (tresult == ISC_R_SUCCESS) {
4450
0
        result = tresult;
4451
0
      }
4452
0
    }
4453
0
    break;
4454
0
  default:
4455
0
    UNREACHABLE();
4456
0
  }
4457
4458
0
  if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
4459
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4460
0
  } else {
4461
0
    qpdbiter->node = NULL;
4462
0
  }
4463
4464
0
  qpdbiter->result = (result == DNS_R_PARTIALMATCH) ? ISC_R_SUCCESS
4465
0
                : result;
4466
0
  return result;
4467
0
}
4468
4469
static isc_result_t
4470
dbiterator_seek3(dns_dbiterator_t *iterator,
4471
0
     const dns_name_t *name DNS__DB_FLARG) {
4472
0
  isc_result_t result;
4473
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4474
4475
0
  if (qpdbiter->result != ISC_R_SUCCESS &&
4476
0
      qpdbiter->result != ISC_R_NOTFOUND &&
4477
0
      qpdbiter->result != DNS_R_PARTIALMATCH &&
4478
0
      qpdbiter->result != ISC_R_NOMORE)
4479
0
  {
4480
0
    return qpdbiter->result;
4481
0
  }
4482
4483
0
  if (qpdbiter->nsec3mode != nsec3only) {
4484
0
    return ISC_R_NOTIMPLEMENTED;
4485
0
  }
4486
4487
0
  dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4488
4489
0
  result = dns_qp_lookup(qpdbiter->snap, name, DNS_DBNAMESPACE_NSEC3,
4490
0
             &qpdbiter->iter, NULL, (void **)&qpdbiter->node,
4491
0
             NULL);
4492
4493
0
  switch (result) {
4494
0
  case ISC_R_SUCCESS:
4495
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4496
0
    break;
4497
0
  case DNS_R_PARTIALMATCH:
4498
    /* dbiterator_next() will dereference the node */
4499
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4500
4501
0
    result = dbiterator_next(iterator);
4502
0
    if (result == ISC_R_NOMORE) {
4503
0
      result = dbiterator_first(iterator);
4504
0
    }
4505
0
    break;
4506
0
  case ISC_R_NOTFOUND:
4507
0
  default:
4508
0
    break;
4509
0
  }
4510
4511
0
  qpdbiter->result = result;
4512
4513
0
  return qpdbiter->result;
4514
0
}
4515
4516
static isc_result_t
4517
0
dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) {
4518
0
  isc_result_t result;
4519
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4520
0
  qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
4521
0
  qpznode_t *node = NULL;
4522
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4523
0
  isc_rwlock_t *nlock = NULL;
4524
4525
0
  REQUIRE(qpdbiter->node != NULL);
4526
4527
0
  if (qpdbiter->result != ISC_R_SUCCESS) {
4528
0
    return qpdbiter->result;
4529
0
  }
4530
4531
  /*
4532
   * Defer the release of the current node until we have the prev node
4533
   * from the QP tree.
4534
   */
4535
0
  node = qpdbiter->node;
4536
0
  qpdbiter->node = NULL;
4537
4538
0
  result = dns_qpiter_prev(&qpdbiter->iter, (void **)&qpdbiter->node,
4539
0
         NULL);
4540
4541
0
  switch (qpdbiter->nsec3mode) {
4542
0
  case nsec3only:
4543
0
    if (result == ISC_R_SUCCESS) {
4544
0
      if (QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter)) {
4545
        /* we hit the NSEC3 origin node. */
4546
0
        qpdbiter->node = NULL;
4547
0
        result = ISC_R_NOMORE;
4548
0
      } else if (qpdbiter->node->nspace !=
4549
0
           DNS_DBNAMESPACE_NSEC3)
4550
0
      {
4551
        /* we hit a non-NSEC3 node. */
4552
0
        qpdbiter->node = NULL;
4553
0
        result = ISC_R_NOMORE;
4554
0
      }
4555
0
    }
4556
0
    break;
4557
0
  case full:
4558
    /* skip the NSEC3 origin node. */
4559
0
    if (result == ISC_R_SUCCESS &&
4560
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4561
0
    {
4562
0
      result = dns_qpiter_prev(&qpdbiter->iter,
4563
0
             (void **)&qpdbiter->node,
4564
0
             NULL);
4565
0
    }
4566
0
    if (result != ISC_R_SUCCESS) {
4567
0
      qpdbiter->node = NULL;
4568
0
      break;
4569
0
    }
4570
4571
    /*
4572
     * If we hit an NSEC node, we need to seek the final normal node
4573
     * of the tree.
4574
     */
4575
0
    if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NSEC) {
4576
0
      break;
4577
0
    }
4578
4579
0
    INSIST(qpdbiter->node->nspace == DNS_DBNAMESPACE_NSEC);
4580
0
    result = dns_qp_lookup(qpdbiter->snap, &qpdb->common.origin,
4581
0
               DNS_DBNAMESPACE_NSEC, &qpdbiter->iter,
4582
0
               NULL, (void **)&qpdbiter->node, NULL);
4583
4584
0
    if (result == ISC_R_SUCCESS) {
4585
0
      INSIST(QPDBITER_NSEC_ORIGIN_NODE(qpdb, qpdbiter));
4586
      /* skip the NSEC origin node */
4587
0
      result = dns_qpiter_prev(&qpdbiter->iter,
4588
0
             (void **)&qpdbiter->node,
4589
0
             NULL);
4590
0
    } else {
4591
      /*
4592
       * The NSEC origin node was not found, but the iterator
4593
       * should point to its predecessor, which is the node we
4594
       * want.
4595
       */
4596
0
      result = dns_qpiter_current(&qpdbiter->iter,
4597
0
                (void **)&qpdbiter->node,
4598
0
                NULL);
4599
0
      INSIST(result == ISC_R_SUCCESS);
4600
0
      INSIST(qpdbiter->node->nspace ==
4601
0
             DNS_DBNAMESPACE_NORMAL);
4602
0
    }
4603
0
    break;
4604
0
  case nonsec3:
4605
0
    break;
4606
0
  default:
4607
0
    UNREACHABLE();
4608
0
  }
4609
4610
  /*
4611
   * We have the prev node, we can release the previous current.
4612
   */
4613
0
  nlock = qpzone_get_lock(node);
4614
0
  NODE_RDLOCK(nlock, &nlocktype);
4615
0
  (void)qpznode_release(node DNS__DB_FLARG_PASS);
4616
0
  NODE_UNLOCK(nlock, &nlocktype);
4617
4618
0
  if (result == ISC_R_SUCCESS) {
4619
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4620
0
  } else {
4621
0
    qpdbiter->node = NULL;
4622
0
  }
4623
4624
0
  qpdbiter->result = result;
4625
0
  return result;
4626
0
}
4627
4628
static isc_result_t
4629
0
dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG) {
4630
0
  isc_result_t result;
4631
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4632
0
  qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
4633
0
  qpznode_t *node = NULL;
4634
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4635
0
  isc_rwlock_t *nlock = NULL;
4636
4637
0
  REQUIRE(qpdbiter->node != NULL);
4638
4639
0
  if (qpdbiter->result != ISC_R_SUCCESS) {
4640
0
    return qpdbiter->result;
4641
0
  }
4642
4643
  /*
4644
   * Defer the release of the current node until we have the next node
4645
   * from the QP tree.
4646
   */
4647
0
  node = qpdbiter->node;
4648
0
  qpdbiter->node = NULL;
4649
4650
0
  result = dns_qpiter_next(&qpdbiter->iter, (void **)&qpdbiter->node,
4651
0
         NULL);
4652
4653
0
  switch (qpdbiter->nsec3mode) {
4654
0
  case nonsec3:
4655
0
    if (result == ISC_R_SUCCESS) {
4656
      /* we hit an NSEC or NSEC3 node. */
4657
0
      if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NORMAL) {
4658
0
        qpdbiter->node = NULL;
4659
0
        result = ISC_R_NOMORE;
4660
0
      }
4661
0
    }
4662
0
    break;
4663
0
  case full:
4664
    /* skip the NSEC3 origin node. */
4665
0
    if (result == ISC_R_SUCCESS &&
4666
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4667
0
    {
4668
0
      result = dns_qpiter_next(&qpdbiter->iter,
4669
0
             (void **)&qpdbiter->node,
4670
0
             NULL);
4671
0
    }
4672
0
    if (result != ISC_R_SUCCESS) {
4673
0
      qpdbiter->node = NULL;
4674
0
      break;
4675
0
    }
4676
4677
    /*
4678
     * If we hit an NSEC node, we need to start at the NSEC3 part of
4679
     * the tree.
4680
     */
4681
0
    if (qpdbiter->node->nspace != DNS_DBNAMESPACE_NSEC) {
4682
0
      break;
4683
0
    }
4684
0
    INSIST(qpdbiter->node->nspace == DNS_DBNAMESPACE_NSEC);
4685
4686
0
    result = dns_qp_lookup(qpdbiter->snap, &qpdb->common.origin,
4687
0
               DNS_DBNAMESPACE_NSEC3, &qpdbiter->iter,
4688
0
               NULL, (void **)&qpdbiter->node, NULL);
4689
0
    if (result != ISC_R_SUCCESS ||
4690
0
        QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
4691
0
    {
4692
      /* skip the NSEC3 origin node (or its predecessor). */
4693
0
      result = dns_qpiter_next(&qpdbiter->iter,
4694
0
             (void **)&qpdbiter->node,
4695
0
             NULL);
4696
0
    }
4697
0
    break;
4698
0
  case nsec3only:
4699
0
    break;
4700
0
  default:
4701
0
    UNREACHABLE();
4702
0
  }
4703
4704
  /*
4705
   * We have the next node, we can release the previous current.
4706
   */
4707
0
  nlock = qpzone_get_lock(node);
4708
0
  NODE_RDLOCK(nlock, &nlocktype);
4709
0
  (void)qpznode_release(node DNS__DB_FLARG_PASS);
4710
0
  NODE_UNLOCK(nlock, &nlocktype);
4711
4712
0
  if (result == ISC_R_SUCCESS) {
4713
0
    reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
4714
0
  } else {
4715
0
    qpdbiter->node = NULL;
4716
0
  }
4717
4718
0
  qpdbiter->result = result;
4719
0
  return result;
4720
0
}
4721
4722
static isc_result_t
4723
dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
4724
0
       dns_name_t *name DNS__DB_FLARG) {
4725
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4726
0
  qpznode_t *node = qpdbiter->node;
4727
4728
0
  REQUIRE(qpdbiter->result == ISC_R_SUCCESS);
4729
0
  REQUIRE(qpdbiter->node != NULL);
4730
4731
0
  if (name != NULL) {
4732
0
    dns_name_copy(&qpdbiter->node->name, name);
4733
0
  }
4734
4735
0
  qpznode_acquire(node DNS__DB_FLARG_PASS);
4736
4737
0
  *nodep = (dns_dbnode_t *)qpdbiter->node;
4738
4739
0
  return ISC_R_SUCCESS;
4740
0
}
4741
4742
static isc_result_t
4743
0
dbiterator_pause(dns_dbiterator_t *iterator ISC_ATTR_UNUSED) {
4744
0
  return ISC_R_SUCCESS;
4745
0
}
4746
4747
static isc_result_t
4748
0
dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) {
4749
0
  qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
4750
4751
0
  if (qpdbiter->result != ISC_R_SUCCESS) {
4752
0
    return qpdbiter->result;
4753
0
  }
4754
4755
0
  dns_name_copy(dns_rootname, name);
4756
0
  return ISC_R_SUCCESS;
4757
0
}
4758
4759
static isc_result_t
4760
qpzone_createiterator(dns_db_t *db, unsigned int options,
4761
0
          dns_dbiterator_t **iteratorp) {
4762
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
4763
0
  qpdb_dbiterator_t *iter = NULL;
4764
0
  isc_result_t result;
4765
4766
0
  REQUIRE(VALID_QPZONE(qpdb));
4767
4768
0
  iter = isc_mem_get(qpdb->common.mctx, sizeof(*iter));
4769
0
  *iter = (qpdb_dbiterator_t){
4770
0
    .common.magic = DNS_DBITERATOR_MAGIC,
4771
0
    .common.methods = &dbiterator_methods,
4772
0
    .common.relative_names = ((options & DNS_DB_RELATIVENAMES) !=
4773
0
            0),
4774
0
  };
4775
4776
0
  if ((options & DNS_DB_NSEC3ONLY) != 0) {
4777
0
    iter->nsec3mode = nsec3only;
4778
0
  } else if ((options & DNS_DB_NONSEC3) != 0) {
4779
0
    iter->nsec3mode = nonsec3;
4780
0
  } else {
4781
0
    iter->nsec3mode = full;
4782
0
  }
4783
4784
0
  dns_db_attach(db, &iter->common.db);
4785
4786
0
  dns_qpmulti_snapshot(qpdb->tree, &iter->snap);
4787
4788
0
  switch (iter->nsec3mode) {
4789
0
  case nonsec3:
4790
0
  case full:
4791
0
    dns_qpiter_init(iter->snap, &iter->iter);
4792
0
    break;
4793
0
  case nsec3only:
4794
    /*
4795
     * NSEC3 follows after all non-nsec3 nodes,
4796
     * seek the NSEC3 origin node.
4797
     */
4798
0
    result = dns_qp_lookup(iter->snap, &qpdb->common.origin,
4799
0
               DNS_DBNAMESPACE_NSEC3, &iter->iter, NULL,
4800
0
               NULL, NULL);
4801
0
    INSIST(result == ISC_R_SUCCESS);
4802
0
    break;
4803
0
  default:
4804
0
    UNREACHABLE();
4805
0
  }
4806
4807
0
  *iteratorp = (dns_dbiterator_t *)iter;
4808
0
  return ISC_R_SUCCESS;
4809
0
}
4810
4811
/*
4812
 * Main logic to add a new rdataset to a node.
4813
 *
4814
 * The reason this is split from qpzone_addrdataset is to allow the reuse of
4815
 * the same qp transaction for multiple adds.
4816
 *
4817
 * If the rdataset is of type NSEC, 'nsec' must point to the qp trie for the
4818
 * zone, otherwise it must be NULL.
4819
 *
4820
 * qpzone_subtractrdataset doesn't have the same problem since it cannot delete
4821
 * nodes, only rdatasets.
4822
 */
4823
static isc_result_t
4824
qpzone_addrdataset_inner(qpzonedb_t *qpdb, qpznode_t *node,
4825
       dns_dbversion_t *dbversion, dns_rdataset_t *rdataset,
4826
       unsigned int options, dns_rdataset_t *addedrdataset,
4827
0
       dns_qp_t *nsec) {
4828
0
  isc_result_t result;
4829
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
4830
0
  isc_region_t region;
4831
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4832
0
  isc_rwlock_t *nlock = NULL;
4833
0
  dns_fixedname_t fn;
4834
0
  dns_name_t *name = dns_fixedname_initname(&fn);
4835
4836
0
  REQUIRE(VALID_QPZONE(qpdb));
4837
0
  REQUIRE(version != NULL && version->qpdb == qpdb);
4838
4839
  /*
4840
   * SOA records are only allowed at top of zone.
4841
   */
4842
0
  if (rdataset->type == dns_rdatatype_soa && node != qpdb->origin) {
4843
0
    return DNS_R_NOTZONETOP;
4844
0
  }
4845
4846
0
  REQUIRE((node->nspace == DNS_DBNAMESPACE_NSEC3 &&
4847
0
     (rdataset->type == dns_rdatatype_nsec3 ||
4848
0
      rdataset->covers == dns_rdatatype_nsec3)) ||
4849
0
    (node->nspace != DNS_DBNAMESPACE_NSEC3 &&
4850
0
     rdataset->type != dns_rdatatype_nsec3 &&
4851
0
     rdataset->covers != dns_rdatatype_nsec3));
4852
4853
0
  result = dns_rdatavec_fromrdataset(rdataset, node->mctx, &region,
4854
0
             qpdb->maxrrperset);
4855
0
  if (result != ISC_R_SUCCESS) {
4856
0
    if (result == DNS_R_TOOMANYRECORDS) {
4857
0
      dns__db_logtoomanyrecords((dns_db_t *)qpdb, &node->name,
4858
0
              rdataset->type, "adding",
4859
0
              qpdb->maxrrperset);
4860
0
    }
4861
0
    return result;
4862
0
  }
4863
4864
0
  dns_name_copy(&node->name, name);
4865
0
  dns_rdataset_getownercase(rdataset, name);
4866
4867
0
  dns_vecheader_t *newheader = (dns_vecheader_t *)region.base;
4868
4869
0
  dns_vecheader_setownercase(newheader, name);
4870
4871
0
  newheader->ttl = rdataset->ttl;
4872
0
  if (rdataset->ttl == 0U) {
4873
0
    DNS_VECHEADER_SETATTR(newheader, DNS_VECHEADERATTR_ZEROTTL);
4874
0
  }
4875
4876
0
  newheader->serial = version->serial;
4877
0
  if (rdataset->attributes.resign) {
4878
0
    DNS_VECHEADER_SETATTR(newheader, DNS_VECHEADERATTR_RESIGN);
4879
0
    newheader->resign = dns_time64_from32(rdataset->resign);
4880
0
  }
4881
4882
  /*
4883
   * If we're adding a delegation type or adding to the auxiliary NSEC
4884
   * namespace, hold an exclusive lock on the tree.  In the latter case
4885
   * the lock does not necessarily have to be acquired but it will help
4886
   * purge ancient entries more effectively.
4887
   *
4888
   * (Note: node lock must be acquired after starting
4889
   * the QPDB transaction and released before committing.)
4890
   */
4891
0
  nlock = qpzone_get_lock(node);
4892
4893
0
  NODE_WRLOCK(nlock, &nlocktype);
4894
4895
0
  result = ISC_R_SUCCESS;
4896
0
  if (nsec != NULL) {
4897
0
    node->havensec = true;
4898
4899
    /*
4900
     * If it fails, there was already an NSEC node,
4901
     * so we can detach the new one we created and
4902
     * move on.
4903
     */
4904
0
    qpznode_t *nsecnode = new_qpznode(qpdb, name,
4905
0
              DNS_DBNAMESPACE_NSEC);
4906
    /*
4907
     * We don't need a separate transaction since the NSEC tree and
4908
     * the normal tree are part of the same qp-tree.
4909
     */
4910
0
    (void)dns_qp_insert(nsec, nsecnode, 0);
4911
0
    qpznode_detach(&nsecnode);
4912
0
  }
4913
4914
0
  if (result == ISC_R_SUCCESS) {
4915
0
    result = add(qpdb, node, name, version, newheader, options,
4916
0
           false, addedrdataset, 0 DNS__DB_FLARG_PASS);
4917
0
  }
4918
4919
  /*
4920
   * If we're adding a delegation type (e.g. NS or DNAME),
4921
   * then we need to set the callback bit on the node.
4922
   */
4923
0
  if (result == ISC_R_SUCCESS &&
4924
0
      delegating_type(qpdb, node, rdataset->type))
4925
0
  {
4926
0
    node->delegating = true;
4927
0
  }
4928
4929
0
  NODE_UNLOCK(nlock, &nlocktype);
4930
4931
0
  return result;
4932
0
}
4933
4934
static isc_result_t
4935
qpzone_addrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
4936
       dns_dbversion_t *dbversion,
4937
       isc_stdtime_t now ISC_ATTR_UNUSED, dns_rdataset_t *rdataset,
4938
       unsigned int options,
4939
0
       dns_rdataset_t *addedrdataset DNS__DB_FLARG) {
4940
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
4941
0
  qpznode_t *node = (qpznode_t *)dbnode;
4942
0
  dns_qp_t *nsec = NULL;
4943
4944
0
  REQUIRE(VALID_QPZONE(qpdb));
4945
4946
  /*
4947
   * Open a new write transaction if we're adding to the auxiliary
4948
   * NSEC namespace.
4949
   */
4950
0
  if (!node->havensec && rdataset->type == dns_rdatatype_nsec) {
4951
0
    dns_qpmulti_write(qpdb->tree, &nsec);
4952
0
  }
4953
4954
0
  isc_result_t result = qpzone_addrdataset_inner(
4955
0
    qpdb, node, dbversion, rdataset, options, addedrdataset, nsec);
4956
4957
0
  if (nsec != NULL) {
4958
0
    dns_qpmulti_commit(qpdb->tree, &nsec);
4959
0
  }
4960
4961
0
  return result;
4962
0
}
4963
4964
static isc_result_t
4965
qpzone_subtractrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
4966
      dns_dbversion_t *dbversion, dns_rdataset_t *rdataset,
4967
      unsigned int options,
4968
0
      dns_rdataset_t *newrdataset DNS__DB_FLARG) {
4969
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
4970
0
  qpznode_t *node = (qpznode_t *)dbnode;
4971
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
4972
0
  dns_fixedname_t fname;
4973
0
  dns_name_t *nodename = dns_fixedname_initname(&fname);
4974
0
  dns_vectop_t *foundtop = NULL;
4975
0
  dns_vecheader_t *newheader = NULL;
4976
0
  dns_vecheader_t *subresult = NULL;
4977
0
  isc_region_t region;
4978
0
  isc_result_t result;
4979
0
  qpz_changed_t *changed = NULL;
4980
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4981
0
  isc_rwlock_t *nlock;
4982
4983
0
  REQUIRE(VALID_QPZONE(qpdb));
4984
0
  REQUIRE(version != NULL && version->qpdb == qpdb);
4985
4986
0
  REQUIRE((node->nspace == DNS_DBNAMESPACE_NSEC3 &&
4987
0
     (rdataset->type == dns_rdatatype_nsec3 ||
4988
0
      rdataset->covers == dns_rdatatype_nsec3)) ||
4989
0
    (node->nspace != DNS_DBNAMESPACE_NSEC3 &&
4990
0
     rdataset->type != dns_rdatatype_nsec3 &&
4991
0
     rdataset->covers != dns_rdatatype_nsec3));
4992
4993
0
  dns_name_copy(&node->name, nodename);
4994
0
  result = dns_rdatavec_fromrdataset(rdataset, node->mctx, &region, 0);
4995
0
  if (result != ISC_R_SUCCESS) {
4996
0
    return result;
4997
0
  }
4998
4999
0
  newheader = (dns_vecheader_t *)region.base;
5000
0
  newheader->ttl = rdataset->ttl;
5001
0
  atomic_init(&newheader->attributes, 0);
5002
0
  newheader->serial = version->serial;
5003
0
  if (rdataset->attributes.resign) {
5004
0
    DNS_VECHEADER_SETATTR(newheader, DNS_VECHEADERATTR_RESIGN);
5005
0
    newheader->resign = dns_time64_from32(rdataset->resign);
5006
0
  }
5007
5008
0
  nlock = qpzone_get_lock(node);
5009
0
  NODE_WRLOCK(nlock, &nlocktype);
5010
5011
0
  changed = add_changed(qpdb, node, version DNS__DB_FLARG_PASS);
5012
0
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
5013
0
    if (top->typepair == newheader->typepair) {
5014
0
      foundtop = top;
5015
0
      break;
5016
0
    }
5017
0
  }
5018
  /*
5019
   * If header isn't NULL, we've found the right type.  There may be
5020
   * IGNORE rdatasets between the top of the chain and the first real
5021
   * data.  We skip over them.
5022
   */
5023
0
  dns_vecheader_t *header = NULL;
5024
0
  if (foundtop != NULL) {
5025
0
    ISC_SLIST_FOREACH(tmp, foundtop->headers, next_header) {
5026
0
      if (!IGNORE(tmp)) {
5027
0
        header = tmp;
5028
0
        break;
5029
0
      }
5030
0
    }
5031
0
  }
5032
0
  if (header != NULL && EXISTS(header)) {
5033
0
    unsigned int flags = 0;
5034
0
    subresult = NULL;
5035
0
    result = ISC_R_SUCCESS;
5036
0
    if ((options & DNS_DBSUB_EXACT) != 0) {
5037
0
      flags |= DNS_RDATAVEC_EXACT;
5038
0
      if (newheader->ttl != header->ttl) {
5039
0
        result = DNS_R_NOTEXACT;
5040
0
      }
5041
0
    }
5042
0
    if (result == ISC_R_SUCCESS) {
5043
0
      result = dns_rdatavec_subtract(
5044
0
        header, newheader, qpdb->common.mctx,
5045
0
        qpdb->common.rdclass,
5046
0
        DNS_TYPEPAIR_TYPE(foundtop->typepair), flags,
5047
0
        &subresult);
5048
0
    }
5049
0
    if (result == ISC_R_SUCCESS) {
5050
0
      LOCK(&qpdb->heap->lock);
5051
0
      resign_unregister(qpdb->heap, node, newheader);
5052
0
      UNLOCK(&qpdb->heap->lock);
5053
0
      dns_vecheader_unref(newheader);
5054
0
      newheader = subresult;
5055
      /*
5056
       * dns_rdatavec_subtract takes the header from the first
5057
       * argument, so it preserves the case
5058
       */
5059
0
      if (RESIGN(header)) {
5060
0
        DNS_VECHEADER_SETATTR(newheader,
5061
0
                  DNS_VECHEADERATTR_RESIGN);
5062
0
        newheader->resign = header->resign;
5063
0
        LOCK(&qpdb->heap->lock);
5064
0
        resign_register(qpdb->heap, node, newheader);
5065
0
        UNLOCK(&qpdb->heap->lock);
5066
0
      }
5067
      /*
5068
       * We have to set the serial since the rdatavec
5069
       * subtraction routine copies the reserved portion of
5070
       * header, not newheader.
5071
       */
5072
0
      newheader->serial = version->serial;
5073
      /*
5074
       * XXXJT: dns_rdatavec_subtract() copied the pointers
5075
       * to additional info.  We need to clear these fields
5076
       * to avoid having duplicated references.
5077
       */
5078
0
      maybe_update_recordsandsize(true, version, newheader,
5079
0
                nodename->length);
5080
0
    } else if (result == DNS_R_NXRRSET) {
5081
      /*
5082
       * This subtraction would remove all of the rdata;
5083
       * add a nonexistent header instead.
5084
       */
5085
0
      LOCK(&qpdb->heap->lock);
5086
0
      resign_unregister(qpdb->heap, node, newheader);
5087
0
      UNLOCK(&qpdb->heap->lock);
5088
0
      dns_vecheader_unref(newheader);
5089
0
      newheader = dns_vecheader_new(db->mctx);
5090
0
      newheader->ttl = 0;
5091
0
      newheader->typepair = foundtop->typepair;
5092
0
      atomic_init(&newheader->attributes,
5093
0
            DNS_VECHEADERATTR_NONEXISTENT);
5094
0
      newheader->serial = version->serial;
5095
0
    } else {
5096
0
      LOCK(&qpdb->heap->lock);
5097
0
      resign_unregister(qpdb->heap, node, newheader);
5098
0
      UNLOCK(&qpdb->heap->lock);
5099
0
      dns_vecheader_unref(newheader);
5100
0
      goto unlock;
5101
0
    }
5102
5103
    /*
5104
     * If we're here, we want to link newheader at the top.
5105
     */
5106
0
    maybe_update_recordsandsize(false, version, header,
5107
0
              nodename->length);
5108
5109
0
    ISC_SLIST_PREPEND(foundtop->headers, newheader, next_header);
5110
5111
0
    node->dirty = true;
5112
0
    changed->dirty = true;
5113
0
    LOCK(&qpdb->heap->lock);
5114
0
    resign_unregister(qpdb->heap, node, header);
5115
0
    UNLOCK(&qpdb->heap->lock);
5116
0
    resign_rollback(qpdb, node, version, header DNS__DB_FLARG_PASS);
5117
0
  } else {
5118
    /*
5119
     * The rdataset doesn't exist, so we don't need to do anything
5120
     * to satisfy the deletion request.
5121
     */
5122
0
    LOCK(&qpdb->heap->lock);
5123
0
    resign_unregister(qpdb->heap, node, newheader);
5124
0
    UNLOCK(&qpdb->heap->lock);
5125
0
    dns_vecheader_unref(newheader);
5126
0
    if ((options & DNS_DBSUB_EXACT) != 0) {
5127
0
      result = DNS_R_NOTEXACT;
5128
0
    } else {
5129
0
      result = DNS_R_UNCHANGED;
5130
0
    }
5131
0
  }
5132
5133
0
  if (result == ISC_R_SUCCESS && newrdataset != NULL) {
5134
0
    bindrdataset(qpdb, newheader, newrdataset DNS__DB_FLARG_PASS);
5135
0
  }
5136
5137
0
  if (result == DNS_R_NXRRSET && newrdataset != NULL &&
5138
0
      (options & DNS_DBSUB_WANTOLD) != 0)
5139
0
  {
5140
0
    bindrdataset(qpdb, header, newrdataset DNS__DB_FLARG_PASS);
5141
0
  }
5142
5143
0
unlock:
5144
0
  NODE_UNLOCK(nlock, &nlocktype);
5145
0
  return result;
5146
0
}
5147
5148
static isc_result_t
5149
qpzone_deleterdataset(dns_db_t *db, dns_dbnode_t *dbnode,
5150
          dns_dbversion_t *dbversion, dns_rdatatype_t type,
5151
0
          dns_rdatatype_t covers DNS__DB_FLARG) {
5152
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5153
0
  qpznode_t *node = (qpznode_t *)dbnode;
5154
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
5155
0
  dns_fixedname_t fname;
5156
0
  dns_name_t *nodename = dns_fixedname_initname(&fname);
5157
0
  isc_result_t result;
5158
0
  dns_vecheader_t *newheader = NULL;
5159
0
  isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
5160
0
  isc_rwlock_t *nlock = NULL;
5161
5162
0
  REQUIRE(VALID_QPZONE(qpdb));
5163
0
  REQUIRE(version != NULL && version->qpdb == qpdb);
5164
5165
0
  if (type == dns_rdatatype_any) {
5166
0
    return ISC_R_NOTIMPLEMENTED;
5167
0
  }
5168
0
  if (type == dns_rdatatype_rrsig && covers == dns_rdatatype_none) {
5169
0
    return ISC_R_NOTIMPLEMENTED;
5170
0
  }
5171
5172
0
  newheader = dns_vecheader_new(db->mctx);
5173
0
  newheader->typepair = DNS_TYPEPAIR_VALUE(type, covers);
5174
0
  newheader->ttl = 0;
5175
0
  atomic_init(&newheader->attributes, DNS_VECHEADERATTR_NONEXISTENT);
5176
0
  newheader->serial = version->serial;
5177
5178
0
  dns_name_copy(&node->name, nodename);
5179
5180
0
  nlock = qpzone_get_lock(node);
5181
0
  NODE_WRLOCK(nlock, &nlocktype);
5182
0
  result = add(qpdb, node, nodename, version, newheader, DNS_DBADD_FORCE,
5183
0
         false, NULL, 0 DNS__DB_FLARG_PASS);
5184
0
  NODE_UNLOCK(nlock, &nlocktype);
5185
0
  return result;
5186
0
}
5187
5188
static dns_glue_t *
5189
0
new_glue(isc_mem_t *mctx, const dns_name_t *name) {
5190
0
  dns_glue_t *glue = isc_mem_get(mctx, sizeof(*glue));
5191
0
  *glue = (dns_glue_t){
5192
0
    .name = DNS_NAME_INITEMPTY,
5193
0
  };
5194
5195
0
  dns_name_dup(name, mctx, &glue->name);
5196
5197
0
  return glue;
5198
0
}
5199
5200
static dns_gluelist_t *
5201
new_gluelist(dns_db_t *db, dns_vecheader_t *header,
5202
0
       const dns_dbversion_t *dbversion) {
5203
0
  dns_gluelist_t *gluelist = isc_mem_get(db->mctx, sizeof(*gluelist));
5204
0
  *gluelist = (dns_gluelist_t){
5205
0
    .version = dbversion,
5206
0
    .header = header,
5207
0
  };
5208
5209
0
  isc_mem_attach(db->mctx, &gluelist->mctx);
5210
5211
0
  cds_wfs_node_init(&gluelist->wfs_node);
5212
5213
0
  return gluelist;
5214
0
}
5215
5216
static isc_result_t
5217
glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
5218
0
    dns_rdataset_t *rdataset ISC_ATTR_UNUSED DNS__DB_FLARG) {
5219
0
  dns_glue_additionaldata_ctx_t *ctx = arg;
5220
0
  isc_result_t result;
5221
0
  dns_fixedname_t fixedname_a;
5222
0
  dns_name_t *name_a = NULL;
5223
0
  dns_rdataset_t rdataset_a, sigrdataset_a;
5224
0
  dns_fixedname_t fixedname_aaaa;
5225
0
  dns_name_t *name_aaaa = NULL;
5226
0
  dns_rdataset_t rdataset_aaaa, sigrdataset_aaaa;
5227
0
  dns_glue_t *glue = NULL;
5228
5229
  /*
5230
   * NS records want addresses in additional records.
5231
   */
5232
0
  INSIST(qtype == dns_rdatatype_a);
5233
5234
0
  name_a = dns_fixedname_initname(&fixedname_a);
5235
0
  dns_rdataset_init(&rdataset_a);
5236
0
  dns_rdataset_init(&sigrdataset_a);
5237
5238
0
  name_aaaa = dns_fixedname_initname(&fixedname_aaaa);
5239
0
  dns_rdataset_init(&rdataset_aaaa);
5240
0
  dns_rdataset_init(&sigrdataset_aaaa);
5241
5242
0
  result = qpzone_find(ctx->db, name, ctx->version, dns_rdatatype_a,
5243
0
           DNS_DBFIND_GLUEOK, 0, NULL, name_a, NULL, NULL,
5244
0
           &rdataset_a, &sigrdataset_a DNS__DB_FLARG_PASS);
5245
0
  if (result == DNS_R_GLUE) {
5246
0
    glue = new_glue(ctx->db->mctx, name_a);
5247
5248
    /*
5249
     * Move the header out of the rdataset, transferring
5250
     * ownership of the reference to the glue structure.
5251
     */
5252
0
    glue->header_a = dns_vecheader_moveheader(&rdataset_a);
5253
0
    if (dns_rdataset_isassociated(&sigrdataset_a)) {
5254
0
      glue->sigheader_a =
5255
0
        dns_vecheader_moveheader(&sigrdataset_a);
5256
0
    }
5257
0
  }
5258
5259
0
  result = qpzone_find(ctx->db, name, ctx->version, dns_rdatatype_aaaa,
5260
0
           DNS_DBFIND_GLUEOK, 0, NULL, name_aaaa, NULL, NULL,
5261
0
           &rdataset_aaaa,
5262
0
           &sigrdataset_aaaa DNS__DB_FLARG_PASS);
5263
0
  if (result == DNS_R_GLUE) {
5264
0
    if (glue == NULL) {
5265
0
      glue = new_glue(ctx->db->mctx, name_aaaa);
5266
0
    } else {
5267
0
      INSIST(dns_name_equal(name_a, name_aaaa));
5268
0
    }
5269
5270
    /*
5271
     * Move the header out of the rdataset, transferring
5272
     * ownership of the reference to the glue structure.
5273
     */
5274
0
    glue->header_aaaa = dns_vecheader_moveheader(&rdataset_aaaa);
5275
0
    if (dns_rdataset_isassociated(&sigrdataset_aaaa)) {
5276
0
      glue->sigheader_aaaa =
5277
0
        dns_vecheader_moveheader(&sigrdataset_aaaa);
5278
0
    }
5279
0
  }
5280
5281
  /*
5282
   * If the currently processed NS record is in-bailiwick, mark the
5283
   * glue as 'required'.  The flag is later propagated to the rdataset
5284
   * attributes when the glue is bound.  Note that for simplicity, glue
5285
   * for all in-bailiwick NS records is marked this way, even though
5286
   * dns_message_rendersection() only checks the attributes for the
5287
   * first rdataset associated with the first name added to the
5288
   * ADDITIONAL section.
5289
   */
5290
0
  if (glue != NULL && dns_name_issubdomain(name, ctx->owner_name)) {
5291
0
    glue->required = true;
5292
0
  }
5293
5294
0
  if (glue != NULL) {
5295
0
    glue->next = ctx->glue;
5296
0
    ctx->glue = glue;
5297
0
  }
5298
5299
  /*
5300
   * Clean up any rdatasets that were not consumed by moveheader().
5301
   * This handles the ISC_R_SUCCESS (in-zone, not glue) case where
5302
   * the rdataset is associated but its header was not moved into
5303
   * the glue structure.
5304
   */
5305
0
  dns_rdataset_cleanup(&rdataset_a);
5306
0
  dns_rdataset_cleanup(&sigrdataset_a);
5307
0
  dns_rdataset_cleanup(&rdataset_aaaa);
5308
0
  dns_rdataset_cleanup(&sigrdataset_aaaa);
5309
5310
0
  return ISC_R_SUCCESS;
5311
0
}
5312
5313
/*
5314
 * This calls bindrdataset, so it must be protected by an rcu read lock.
5315
 */
5316
static void
5317
0
addglue_to_message(qpzonedb_t *qpdb, dns_glue_t *ge, dns_message_t *msg) {
5318
0
  for (; ge != NULL; ge = ge->next) {
5319
0
    dns_name_t *name = NULL;
5320
0
    dns_rdataset_t *rdataset_a = NULL;
5321
0
    dns_rdataset_t *sigrdataset_a = NULL;
5322
0
    dns_rdataset_t *rdataset_aaaa = NULL;
5323
0
    dns_rdataset_t *sigrdataset_aaaa = NULL;
5324
0
    bool prepend_name = false;
5325
5326
0
    dns_message_gettempname(msg, &name);
5327
5328
0
    dns_name_copy(&ge->name, name);
5329
5330
0
    if (ge->header_a != NULL) {
5331
0
      dns_message_gettemprdataset(msg, &rdataset_a);
5332
0
    }
5333
5334
0
    if (ge->sigheader_a != NULL) {
5335
0
      dns_message_gettemprdataset(msg, &sigrdataset_a);
5336
0
    }
5337
5338
0
    if (ge->header_aaaa != NULL) {
5339
0
      dns_message_gettemprdataset(msg, &rdataset_aaaa);
5340
0
    }
5341
5342
0
    if (ge->sigheader_aaaa != NULL) {
5343
0
      dns_message_gettemprdataset(msg, &sigrdataset_aaaa);
5344
0
    }
5345
5346
0
    if (rdataset_a != NULL) {
5347
0
      bindrdataset(qpdb, ge->header_a, rdataset_a);
5348
0
      ISC_LIST_APPEND(name->list, rdataset_a, link);
5349
0
      if (ge->required) {
5350
0
        rdataset_a->attributes.required = true;
5351
0
        prepend_name = true;
5352
0
      }
5353
0
    }
5354
5355
0
    if (sigrdataset_a != NULL) {
5356
0
      bindrdataset(qpdb, ge->sigheader_a, sigrdataset_a);
5357
0
      ISC_LIST_APPEND(name->list, sigrdataset_a, link);
5358
0
    }
5359
5360
0
    if (rdataset_aaaa != NULL) {
5361
0
      bindrdataset(qpdb, ge->header_aaaa, rdataset_aaaa);
5362
0
      ISC_LIST_APPEND(name->list, rdataset_aaaa, link);
5363
0
      if (ge->required) {
5364
0
        rdataset_aaaa->attributes.required = true;
5365
0
        prepend_name = true;
5366
0
      }
5367
0
    }
5368
0
    if (sigrdataset_aaaa != NULL) {
5369
0
      bindrdataset(qpdb, ge->sigheader_aaaa,
5370
0
             sigrdataset_aaaa);
5371
0
      ISC_LIST_APPEND(name->list, sigrdataset_aaaa, link);
5372
0
    }
5373
5374
0
    dns_message_addname(msg, name, DNS_SECTION_ADDITIONAL);
5375
5376
    /*
5377
     * When looking for required glue, dns_message_rendersection()
5378
     * only processes the first rdataset associated with the first
5379
     * name added to the ADDITIONAL section.  dns_message_addname()
5380
     * performs an append on the list of names in a given section,
5381
     * so if any glue record was marked as required, we need to
5382
     * move the name it is associated with to the beginning of the
5383
     * list for the ADDITIONAL section or else required glue might
5384
     * not be rendered.
5385
     */
5386
0
    if (prepend_name) {
5387
0
      ISC_LIST_UNLINK(msg->sections[DNS_SECTION_ADDITIONAL],
5388
0
          name, link);
5389
0
      ISC_LIST_PREPEND(msg->sections[DNS_SECTION_ADDITIONAL],
5390
0
           name, link);
5391
0
    }
5392
0
  }
5393
0
}
5394
5395
static dns_gluelist_t *
5396
create_gluelist(qpzonedb_t *qpdb, qpz_version_t *version,
5397
0
    const dns_name_t *owner_name, dns_rdataset_t *rdataset) {
5398
0
  dns_vecheader_t *header = dns_vecheader_getheader(rdataset);
5399
0
  dns_glue_additionaldata_ctx_t ctx = {
5400
0
    .db = (dns_db_t *)qpdb,
5401
0
    .version = (dns_dbversion_t *)version,
5402
0
    .owner_name = owner_name,
5403
0
  };
5404
0
  dns_gluelist_t *gluelist = new_gluelist(ctx.db, header, ctx.version);
5405
5406
  /*
5407
   * Get the owner name of the NS RRset - it will be necessary for
5408
   * identifying required glue in glue_nsdname_cb() (by
5409
   * determining which NS records in the delegation are
5410
   * in-bailiwick).
5411
   */
5412
5413
0
  (void)dns_rdataset_additionaldata(rdataset, dns_rootname,
5414
0
            glue_nsdname_cb, &ctx, 0);
5415
5416
0
  CMM_STORE_SHARED(gluelist->glue, ctx.glue);
5417
5418
0
  return gluelist;
5419
0
}
5420
5421
static void
5422
addglue(dns_db_t *db, dns_dbversion_t *dbversion, const dns_name_t *owner_name,
5423
0
  dns_rdataset_t *rdataset, dns_message_t *msg) {
5424
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5425
0
  qpz_version_t *version = (qpz_version_t *)dbversion;
5426
0
  dns_vecheader_t *header = dns_vecheader_getheader(rdataset);
5427
0
  dns_glue_t *glue = NULL;
5428
0
  isc_statscounter_t counter = dns_gluecachestatscounter_hits_absent;
5429
5430
0
  REQUIRE(rdataset->type == dns_rdatatype_ns);
5431
0
  REQUIRE(qpdb == version->qpdb);
5432
0
  REQUIRE(!IS_STUB(qpdb));
5433
5434
0
  rcu_read_lock();
5435
5436
0
  dns_gluelist_t *gluelist = rcu_dereference(header->gluelist);
5437
0
  if (gluelist == NULL || gluelist->version != dbversion) {
5438
    /* No or old glue list was found in the table. */
5439
5440
0
    dns_gluelist_t *xchg_gluelist = gluelist;
5441
0
    dns_gluelist_t *old_gluelist = (void *)-1;
5442
0
    dns_gluelist_t *new_gluelist =
5443
0
      create_gluelist(qpdb, version, owner_name, rdataset);
5444
5445
0
    while (old_gluelist != xchg_gluelist &&
5446
0
           (xchg_gluelist == NULL ||
5447
0
      xchg_gluelist->version != dbversion))
5448
0
    {
5449
0
      old_gluelist = xchg_gluelist;
5450
0
      xchg_gluelist = rcu_cmpxchg_pointer(
5451
0
        &header->gluelist, old_gluelist, new_gluelist);
5452
0
    }
5453
5454
0
    if (old_gluelist == xchg_gluelist) {
5455
      /* CAS was successful */
5456
0
      cds_wfs_push(&version->glue_stack,
5457
0
             &new_gluelist->wfs_node);
5458
0
      gluelist = new_gluelist;
5459
0
    } else {
5460
0
      destroy_gluelist(&new_gluelist);
5461
0
      gluelist = xchg_gluelist;
5462
0
    }
5463
0
  }
5464
5465
0
  glue = CMM_LOAD_SHARED(gluelist->glue);
5466
5467
0
  if (glue != NULL) {
5468
0
    addglue_to_message(qpdb, glue, msg);
5469
0
    counter = dns_gluecachestatscounter_hits_present;
5470
0
  }
5471
5472
0
  rcu_read_unlock();
5473
5474
  /* We have a cached result. Add it to the message and return. */
5475
0
  if (qpdb->gluecachestats != NULL) {
5476
0
    isc_stats_increment(qpdb->gluecachestats, counter);
5477
0
  }
5478
0
}
5479
5480
static void
5481
2
setmaxrrperset(dns_db_t *db, uint32_t value) {
5482
2
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5483
5484
2
  REQUIRE(VALID_QPZONE(qpdb));
5485
5486
2
  qpdb->maxrrperset = value;
5487
2
}
5488
5489
static void
5490
2
setmaxtypepername(dns_db_t *db, uint32_t value) {
5491
2
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5492
5493
2
  REQUIRE(VALID_QPZONE(qpdb));
5494
5495
2
  qpdb->maxtypepername = value;
5496
2
}
5497
5498
/*
5499
 * Qpzone specialization of the update function from dns_rdatacallbacks_t,
5500
 * meant to reuse the same qp transaction for multiple operations.
5501
 */
5502
static isc_result_t
5503
qpzone_update_rdataset(qpzonedb_t *qpdb, qpz_version_t *version, dns_qp_t *qp,
5504
0
           dns_name_t *name, dns_rdataset_t *rds, dns_diffop_t op) {
5505
0
  isc_result_t result;
5506
0
  unsigned int options;
5507
0
  dns_rdataset_t ardataset;
5508
0
  qpznode_t *node = NULL;
5509
0
  dns_qp_t *nsec = NULL;
5510
0
  bool is_nsec3;
5511
5512
0
  dns_rdataset_init(&ardataset);
5513
5514
0
  is_nsec3 = (rds->type == dns_rdatatype_nsec3 ||
5515
0
        rds->covers == dns_rdatatype_nsec3);
5516
5517
0
  result = findnodeintree(qpdb, qp, name, true, is_nsec3,
5518
0
        (dns_dbnode_t **)&node DNS__DB_FLARG_PASS);
5519
5520
0
  if (result != ISC_R_SUCCESS) {
5521
0
    goto failure;
5522
0
  }
5523
5524
0
  switch (op) {
5525
0
  case DNS_DIFFOP_ADD:
5526
0
  case DNS_DIFFOP_ADDRESIGN:
5527
    /*
5528
     * See the comment on qpzone_addrdataset_inner for why we
5529
     * cannot use qpzone_addrdataset directly.
5530
     */
5531
0
    options = DNS_DBADD_MERGE | DNS_DBADD_EXACT |
5532
0
        DNS_DBADD_EXACTTTL;
5533
0
    if (!node->havensec && rds->type == dns_rdatatype_nsec) {
5534
0
      nsec = qp;
5535
0
    }
5536
0
    result = qpzone_addrdataset_inner(
5537
0
      qpdb, node, (dns_dbversion_t *)version, rds, options,
5538
0
      &ardataset, nsec DNS__DB_FLARG_PASS);
5539
0
    break;
5540
0
  case DNS_DIFFOP_DEL:
5541
0
  case DNS_DIFFOP_DELRESIGN:
5542
0
    options = DNS_DBSUB_EXACT | DNS_DBSUB_WANTOLD;
5543
0
    result = qpzone_subtractrdataset(
5544
0
      (dns_db_t *)qpdb, (dns_dbnode_t *)node,
5545
0
      (dns_dbversion_t *)version, rds, options,
5546
0
      &ardataset DNS__DB_FLARG_PASS);
5547
0
    break;
5548
0
  default:
5549
0
    UNREACHABLE();
5550
0
  }
5551
5552
0
  bool is_resign = rds->type == dns_rdatatype_rrsig &&
5553
0
       (op == DNS_DIFFOP_DELRESIGN ||
5554
0
        op == DNS_DIFFOP_ADDRESIGN);
5555
5556
0
  if (result == ISC_R_SUCCESS && is_resign) {
5557
0
    isc_stdtime_t resign;
5558
0
    resign = dns_rdataset_minresign(&ardataset);
5559
0
    dns_db_setsigningtime((dns_db_t *)qpdb, (dns_dbnode_t *)node,
5560
0
              &ardataset, resign);
5561
0
  }
5562
5563
0
failure:
5564
0
  if (node != NULL) {
5565
0
    dns_db_detachnode((dns_dbnode_t **)&node);
5566
0
  }
5567
0
  dns_rdataset_cleanup(&ardataset);
5568
0
  return result;
5569
0
}
5570
5571
static isc_result_t
5572
qpzone_update_callback(void *arg, const dns_name_t *name, dns_rdataset_t *rds,
5573
0
           dns_diffop_t op DNS__DB_FLARG) {
5574
0
  qpzone_updatectx_t *ctx = arg;
5575
0
  qpzonedb_t *qpdb = (qpzonedb_t *)ctx->base.db;
5576
0
  qpz_version_t *version = (qpz_version_t *)ctx->base.ver;
5577
5578
0
  return qpzone_update_rdataset(qpdb, version, ctx->qp,
5579
0
              (dns_name_t *)name, rds, op);
5580
0
}
5581
5582
static isc_result_t
5583
qpzone_beginupdate(dns_db_t *db, dns_dbversion_t *ver,
5584
0
       dns_rdatacallbacks_t *callbacks) {
5585
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5586
5587
0
  REQUIRE(VALID_QPZONE(qpdb));
5588
0
  REQUIRE(ver != NULL);
5589
0
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
5590
5591
0
  qpzone_updatectx_t *ctx = isc_mem_get(qpdb->common.mctx, sizeof(*ctx));
5592
0
  *ctx = (qpzone_updatectx_t){
5593
0
    .base.db = db,
5594
0
    .base.ver = ver,
5595
0
    .base.warn = true,
5596
0
    .qpr = (dns_qpread_t){ 0 },
5597
0
  };
5598
0
  ctx->qp = begin_transaction(qpdb, &ctx->qpr, true);
5599
5600
0
  callbacks->update = qpzone_update_callback;
5601
0
  callbacks->add_private = ctx;
5602
5603
0
  return ISC_R_SUCCESS;
5604
0
}
5605
5606
static isc_result_t
5607
0
qpzone_commitupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
5608
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5609
0
  qpzone_updatectx_t *ctx;
5610
5611
0
  REQUIRE(VALID_QPZONE(qpdb));
5612
0
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
5613
5614
0
  ctx = (qpzone_updatectx_t *)callbacks->add_private;
5615
0
  if (ctx != NULL) {
5616
0
    end_transaction(qpdb, ctx->qp, true);
5617
5618
0
    isc_mem_put(qpdb->common.mctx, ctx, sizeof(*ctx));
5619
    /*
5620
     * We need to allow the context to be committed or aborted
5621
     * multiple times, so we set the callback data to NULL
5622
     * to signal that nothing needs to be done with this context.
5623
     */
5624
0
    callbacks->add_private = NULL;
5625
0
  }
5626
5627
0
  return ISC_R_SUCCESS;
5628
0
}
5629
5630
/*
5631
 * For now, abortupdate needs to *commit* the results, so that closeversion
5632
 * cleanup might work.
5633
 */
5634
static isc_result_t
5635
0
qpzone_abortupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
5636
0
  qpzonedb_t *qpdb = (qpzonedb_t *)db;
5637
0
  qpzone_updatectx_t *ctx;
5638
5639
0
  REQUIRE(VALID_QPZONE(qpdb));
5640
0
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
5641
5642
0
  ctx = (qpzone_updatectx_t *)callbacks->add_private;
5643
0
  if (ctx != NULL) {
5644
0
    end_transaction(qpdb, ctx->qp, true);
5645
5646
0
    isc_mem_put(qpdb->common.mctx, ctx, sizeof(*ctx));
5647
    /*
5648
     * See qpzone_commitupdate.
5649
     */
5650
0
    callbacks->add_private = NULL;
5651
0
  }
5652
5653
0
  return ISC_R_SUCCESS;
5654
0
}
5655
5656
static dns_dbmethods_t qpdb_zonemethods = {
5657
  .destroy = qpdb_destroy,
5658
  .beginload = beginload,
5659
  .endload = endload,
5660
  .beginupdate = qpzone_beginupdate,
5661
  .commitupdate = qpzone_commitupdate,
5662
  .abortupdate = qpzone_abortupdate,
5663
  .currentversion = currentversion,
5664
  .newversion = newversion,
5665
  .attachversion = attachversion,
5666
  .closeversion = closeversion,
5667
  .findnode = qpzone_findnode,
5668
  .find = qpzone_find,
5669
  .createiterator = qpzone_createiterator,
5670
  .findrdataset = qpzone_findrdataset,
5671
  .allrdatasets = qpzone_allrdatasets,
5672
  .addrdataset = qpzone_addrdataset,
5673
  .subtractrdataset = qpzone_subtractrdataset,
5674
  .deleterdataset = qpzone_deleterdataset,
5675
  .issecure = issecure,
5676
  .nodecount = nodecount,
5677
  .getoriginnode = getoriginnode,
5678
  .getnsec3parameters = getnsec3parameters,
5679
  .findnsec3node = qpzone_findnsec3node,
5680
  .setsigningtime = setsigningtime,
5681
  .getsigningtime = getsigningtime,
5682
  .getsize = getsize,
5683
  .setgluecachestats = setgluecachestats,
5684
  .addglue = addglue,
5685
  .setmaxrrperset = setmaxrrperset,
5686
  .setmaxtypepername = setmaxtypepername,
5687
};
5688
5689
static dns_dbnode_methods_t qpznode_methods = (dns_dbnode_methods_t){
5690
  .attachnode = qpzone_attachnode,
5691
  .detachnode = qpzone_detachnode,
5692
};
5693
5694
static void
5695
11.8M
destroy_qpznode(qpznode_t *node) {
5696
11.8M
  ISC_SLIST_FOREACH(top, node->next_type, next_type) {
5697
10.2M
    ISC_SLIST_FOREACH(header, top->headers, next_header) {
5698
10.2M
      dns_vecheader_unref(header);
5699
10.2M
    }
5700
5701
10.2M
    dns_vectop_destroy(node->mctx, &top);
5702
10.2M
  }
5703
5704
11.8M
  dns_name_free(&node->name, node->mctx);
5705
11.8M
  isc_mem_putanddetach(&node->mctx, node, sizeof(qpznode_t));
5706
11.8M
}
5707
5708
#if DNS_DB_NODETRACE
5709
ISC_REFCOUNT_STATIC_TRACE_IMPL(qpznode, destroy_qpznode);
5710
#else
5711
47.3M
ISC_REFCOUNT_STATIC_IMPL(qpznode, destroy_qpznode);
qpzone.c:qpznode_ref
Line
Count
Source
5711
ISC_REFCOUNT_STATIC_IMPL(qpznode, destroy_qpznode);
qpzone.c:qpznode_detach
Line
Count
Source
5711
ISC_REFCOUNT_STATIC_IMPL(qpznode, destroy_qpznode);
qpzone.c:qpznode_unref
Line
Count
Source
5711
ISC_REFCOUNT_STATIC_IMPL(qpznode, destroy_qpznode);
5712
47.3M
#endif
5713
47.3M
5714
47.3M
ISC_REFCOUNT_STATIC_IMPL(qpz_heap, qpz_heap_destroy);
qpzone.c:qpz_heap_detach
Line
Count
Source
5714
ISC_REFCOUNT_STATIC_IMPL(qpz_heap, qpz_heap_destroy);
qpzone.c:qpz_heap_unref
Line
Count
Source
5714
ISC_REFCOUNT_STATIC_IMPL(qpz_heap, qpz_heap_destroy);
5715
34.9k
5716
34.9k
static void
5717
34.9k
qp_attach(void *uctx ISC_ATTR_UNUSED, void *pval,
5718
11.8M
    uint32_t ival ISC_ATTR_UNUSED) {
5719
11.8M
  qpznode_t *data = pval;
5720
11.8M
  qpznode_ref(data);
5721
11.8M
}
5722
5723
static void
5724
qp_detach(void *uctx ISC_ATTR_UNUSED, void *pval,
5725
11.8M
    uint32_t ival ISC_ATTR_UNUSED) {
5726
11.8M
  qpznode_t *data = pval;
5727
11.8M
  qpznode_detach(&data);
5728
11.8M
}
5729
5730
static size_t
5731
qp_makekey(dns_qpkey_t key, void *uctx ISC_ATTR_UNUSED, void *pval,
5732
29.1M
     uint32_t ival ISC_ATTR_UNUSED) {
5733
29.1M
  qpznode_t *data = pval;
5734
29.1M
  return dns_qpkey_fromname(key, &data->name, data->nspace);
5735
29.1M
}
5736
5737
static void
5738
0
qp_triename(void *uctx ISC_ATTR_UNUSED, char *buf, size_t size) {
5739
0
  snprintf(buf, size, "QPDB");
5740
0
}