Coverage Report

Created: 2026-10-17 06:52

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