Coverage Report

Created: 2026-09-03 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/db.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
/***
17
 *** Imports
18
 ***/
19
20
#include <inttypes.h>
21
#include <stdbool.h>
22
23
#include <isc/buffer.h>
24
#include <isc/hash.h>
25
#include <isc/log.h>
26
#include <isc/mem.h>
27
#include <isc/result.h>
28
#include <isc/rwlock.h>
29
#include <isc/string.h>
30
#include <isc/tid.h>
31
#include <isc/urcu.h>
32
#include <isc/util.h>
33
34
#include <dns/callbacks.h>
35
#include <dns/clientinfo.h>
36
#include <dns/db.h>
37
#include <dns/master.h>
38
#include <dns/rdata.h>
39
#include <dns/rdataclass.h>
40
#include <dns/rdataset.h>
41
#include <dns/rdatasetiter.h>
42
43
/***
44
 *** Private Types
45
 ***/
46
47
struct dns_dbimplementation {
48
  const char *name;
49
  dns_dbcreatefunc_t create;
50
  isc_mem_t *mctx;
51
  void *driverarg;
52
  ISC_LINK(dns_dbimplementation_t) link;
53
};
54
55
/***
56
 *** Supported DB Implementations Registry
57
 ***/
58
59
/*
60
 * Built in database implementations are registered here.
61
 */
62
63
#include "db_p.h"
64
#include "qpcache_p.h"
65
#include "qpzone_p.h"
66
67
unsigned int dns_pps = 0U;
68
69
static ISC_LIST(dns_dbimplementation_t) implementations;
70
static isc_rwlock_t implock;
71
72
static dns_dbimplementation_t qpimp;
73
static dns_dbimplementation_t qpzoneimp;
74
75
void
76
22
dns__db_initialize(void) {
77
22
  isc_rwlock_init(&implock);
78
79
22
  ISC_LIST_INIT(implementations);
80
81
22
  qpimp = (dns_dbimplementation_t){
82
22
    .name = "qpcache",
83
22
    .create = dns__qpcache_create,
84
22
    .link = ISC_LINK_INITIALIZER,
85
22
  };
86
87
22
  qpzoneimp = (dns_dbimplementation_t){
88
22
    .name = "qpzone",
89
22
    .create = dns__qpzone_create,
90
22
    .link = ISC_LINK_INITIALIZER,
91
22
  };
92
93
22
  ISC_LIST_APPEND(implementations, &qpimp, link);
94
22
  ISC_LIST_APPEND(implementations, &qpzoneimp, link);
95
22
}
96
97
void
98
0
dns__db_shutdown(void) {
99
0
  isc_rwlock_destroy(&implock);
100
0
}
101
102
static dns_dbimplementation_t *
103
17.6k
impfind(const char *name) {
104
35.2k
  ISC_LIST_FOREACH(implementations, imp, link) {
105
35.2k
    if (strcasecmp(name, imp->name) == 0) {
106
17.6k
      return imp;
107
17.6k
    }
108
35.2k
  }
109
0
  return NULL;
110
17.6k
}
111
112
static void
113
call_updatenotify(dns_db_t *db);
114
115
/***
116
 *** Basic DB Methods
117
 ***/
118
119
isc_result_t
120
dns_db_create(isc_mem_t *mctx, const char *db_type, const dns_name_t *origin,
121
        dns_dbtype_t type, dns_rdataclass_t rdclass, unsigned int argc,
122
17.6k
        char *argv[], dns_db_t **dbp) {
123
17.6k
  dns_dbimplementation_t *impinfo = NULL;
124
125
  /*
126
   * Create a new database using implementation 'db_type'.
127
   */
128
129
17.6k
  REQUIRE(dbp != NULL && *dbp == NULL);
130
17.6k
  REQUIRE(dns_name_isabsolute(origin));
131
132
17.6k
  RWLOCK(&implock, isc_rwlocktype_read);
133
17.6k
  impinfo = impfind(db_type);
134
17.6k
  if (impinfo != NULL) {
135
17.6k
    isc_result_t result;
136
17.6k
    result = ((impinfo->create)(mctx, origin, type, rdclass, argc,
137
17.6k
              argv, impinfo->driverarg, dbp));
138
17.6k
    RWUNLOCK(&implock, isc_rwlocktype_read);
139
140
#if DNS_DB_TRACE
141
    fprintf(stderr, "dns_db_create:%s:%s:%d:%p->references = 1\n",
142
      __func__, __FILE__, __LINE__ + 1, *dbp);
143
#endif
144
145
17.6k
    if (result == ISC_R_SUCCESS && type != dns_dbtype_cache &&
146
17.6k
        (*dbp)->methods->addglue == NULL)
147
0
    {
148
0
      isc_log_write(DNS_LOGCATEGORY_DATABASE,
149
0
              DNS_LOGMODULE_DB, ISC_LOG_ERROR,
150
0
              "database type '%s' does not implement "
151
0
              "required addglue method",
152
0
              db_type);
153
0
      dns_db_detach(dbp);
154
0
      result = ISC_R_NOTIMPLEMENTED;
155
0
    }
156
17.6k
    return result;
157
17.6k
  }
158
159
0
  RWUNLOCK(&implock, isc_rwlocktype_read);
160
161
0
  isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DB, ISC_LOG_ERROR,
162
0
          "unsupported database type '%s'", db_type);
163
164
0
  return ISC_R_NOTFOUND;
165
0
}
166
167
static void
168
17.6k
dns__db_destroy(dns_db_t *db) {
169
17.6k
  (db->methods->destroy)(db);
170
17.6k
}
171
172
#if DNS_DB_TRACE
173
ISC_REFCOUNT_TRACE_IMPL(dns_db, dns__db_destroy);
174
#else
175
35.8k
ISC_REFCOUNT_IMPL(dns_db, dns__db_destroy);
dns_db_ref
Line
Count
Source
175
ISC_REFCOUNT_IMPL(dns_db, dns__db_destroy);
dns_db_unref
Line
Count
Source
175
ISC_REFCOUNT_IMPL(dns_db, dns__db_destroy);
dns_db_detach
Line
Count
Source
175
ISC_REFCOUNT_IMPL(dns_db, dns__db_destroy);
176
35.8k
#endif
177
35.8k
178
35.8k
bool
179
35.8k
dns_db_iscache(dns_db_t *db) {
180
  /*
181
   * Does 'db' have cache semantics?
182
   */
183
184
194
  REQUIRE(DNS_DB_VALID(db));
185
186
194
  if ((db->attributes & DNS_DBATTR_CACHE) != 0) {
187
0
    return true;
188
0
  }
189
190
194
  return false;
191
194
}
192
193
bool
194
4
dns_db_iszone(dns_db_t *db) {
195
  /*
196
   * Does 'db' have zone semantics?
197
   */
198
199
4
  REQUIRE(DNS_DB_VALID(db));
200
201
4
  if ((db->attributes & (DNS_DBATTR_CACHE | DNS_DBATTR_STUB)) == 0) {
202
4
    return true;
203
4
  }
204
205
0
  return false;
206
4
}
207
208
bool
209
0
dns_db_isstub(dns_db_t *db) {
210
  /*
211
   * Does 'db' have stub semantics?
212
   */
213
214
0
  REQUIRE(DNS_DB_VALID(db));
215
216
0
  if ((db->attributes & DNS_DBATTR_STUB) != 0) {
217
0
    return true;
218
0
  }
219
220
0
  return false;
221
0
}
222
223
bool
224
2
dns_db_issecure(dns_db_t *db) {
225
  /*
226
   * Is 'db' secure?
227
   */
228
229
2
  REQUIRE(DNS_DB_VALID(db));
230
2
  REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
231
232
2
  if (db->methods->issecure != NULL) {
233
2
    return (db->methods->issecure)(db);
234
2
  }
235
0
  return false;
236
2
}
237
238
bool
239
4
dns_db_ispersistent(dns_db_t *db) {
240
  /*
241
   * Is 'db' persistent?
242
   */
243
244
4
  REQUIRE(DNS_DB_VALID(db));
245
246
4
  if (db->methods->beginload == NULL) {
247
    /* If the database can't be loaded, assume it's persistent */
248
0
    return true;
249
0
  }
250
251
4
  return false;
252
4
}
253
254
dns_name_t *
255
0
dns_db_origin(dns_db_t *db) {
256
  /*
257
   * The origin of the database.
258
   */
259
260
0
  REQUIRE(DNS_DB_VALID(db));
261
262
0
  return &db->origin;
263
0
}
264
265
dns_rdataclass_t
266
0
dns_db_class(dns_db_t *db) {
267
  /*
268
   * The class of the database.
269
   */
270
271
0
  REQUIRE(DNS_DB_VALID(db));
272
273
0
  return db->rdclass;
274
0
}
275
276
isc_result_t
277
17.6k
dns_db_beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
278
  /*
279
   * Begin loading 'db'.
280
   */
281
282
17.6k
  REQUIRE(DNS_DB_VALID(db));
283
17.6k
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
284
285
17.6k
  if (db->methods->beginload != NULL) {
286
17.6k
    return (db->methods->beginload)(db, callbacks);
287
17.6k
  }
288
0
  return ISC_R_NOTIMPLEMENTED;
289
17.6k
}
290
291
isc_result_t
292
17.6k
dns_db_endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
293
  /*
294
   * Finish loading 'db'.
295
   */
296
297
17.6k
  REQUIRE(DNS_DB_VALID(db));
298
17.6k
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
299
17.6k
  REQUIRE(callbacks->add_private != NULL);
300
301
  /*
302
   * When dns_db_endload() is called, we call the onupdate function
303
   * for all registered listeners, regardless of whether the underlying
304
   * database has an 'endload' implementation.
305
   */
306
17.6k
  call_updatenotify(db);
307
308
17.6k
  if (db->methods->endload != NULL) {
309
17.6k
    return (db->methods->endload)(db, callbacks);
310
17.6k
  }
311
312
0
  return ISC_R_NOTIMPLEMENTED;
313
17.6k
}
314
315
isc_result_t
316
dns_db_beginupdate(dns_db_t *db, dns_dbversion_t *ver,
317
0
       dns_rdatacallbacks_t *callbacks) {
318
  /*
319
   * Begin updating 'db'.
320
   */
321
322
0
  REQUIRE(DNS_DB_VALID(db));
323
0
  REQUIRE(dns_db_iszone(db));
324
0
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
325
326
0
  if (db->methods->beginupdate != NULL) {
327
0
    return (db->methods->beginupdate)(db, ver, callbacks);
328
0
  }
329
0
  return ISC_R_NOTIMPLEMENTED;
330
0
}
331
332
isc_result_t
333
0
dns_db_commitupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
334
  /*
335
   * Commit the update to 'db'.
336
   */
337
338
0
  REQUIRE(DNS_DB_VALID(db));
339
0
  REQUIRE(dns_db_iszone(db));
340
0
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
341
342
0
  if (db->methods->commitupdate != NULL) {
343
0
    return (db->methods->commitupdate)(db, callbacks);
344
0
  }
345
346
0
  return ISC_R_NOTIMPLEMENTED;
347
0
}
348
349
isc_result_t
350
0
dns_db_abortupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
351
  /*
352
   * Abort the update to 'db'.
353
   */
354
355
0
  REQUIRE(DNS_DB_VALID(db));
356
0
  REQUIRE(dns_db_iszone(db));
357
0
  REQUIRE(DNS_CALLBACK_VALID(callbacks));
358
359
0
  if (db->methods->abortupdate != NULL) {
360
0
    return (db->methods->abortupdate)(db, callbacks);
361
0
  }
362
363
0
  return ISC_R_NOTIMPLEMENTED;
364
0
}
365
366
isc_result_t
367
dns_db_load(dns_db_t *db, const char *filename, dns_masterformat_t format,
368
0
      unsigned int options) {
369
0
  isc_result_t result, eresult;
370
0
  dns_rdatacallbacks_t callbacks;
371
372
  /*
373
   * Load master file 'filename' into 'db'.
374
   */
375
376
0
  REQUIRE(DNS_DB_VALID(db));
377
378
0
  if ((db->attributes & DNS_DBATTR_CACHE) != 0) {
379
0
    options |= DNS_MASTER_AGETTL;
380
0
  }
381
382
0
  dns_rdatacallbacks_init(&callbacks);
383
0
  RETERR(dns_db_beginload(db, &callbacks));
384
0
  result = dns_master_loadfile(filename, &db->origin, &db->origin,
385
0
             db->rdclass, options, 0, &callbacks, NULL,
386
0
             NULL, db->mctx, format, 0);
387
0
  eresult = dns_db_endload(db, &callbacks);
388
  /*
389
   * We always call dns_db_endload(), but we only want to return its
390
   * result if dns_master_loadfile() succeeded.  If dns_master_loadfile()
391
   * failed, we want to return the result code it gave us.
392
   */
393
0
  if (eresult != ISC_R_SUCCESS &&
394
0
      (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE))
395
0
  {
396
0
    result = eresult;
397
0
  }
398
399
0
  return result;
400
0
}
401
402
/***
403
 *** Version Methods
404
 ***/
405
406
void
407
6
dns_db_currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
408
  /*
409
   * Open the current version for reading.
410
   */
411
412
6
  REQUIRE(DNS_DB_VALID(db));
413
6
  REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
414
6
  REQUIRE(versionp != NULL && *versionp == NULL);
415
416
6
  (db->methods->currentversion)(db, versionp);
417
6
}
418
419
isc_result_t
420
0
dns_db_newversion(dns_db_t *db, dns_dbversion_t **versionp) {
421
  /*
422
   * Open a new version for reading and writing.
423
   */
424
425
0
  REQUIRE(DNS_DB_VALID(db));
426
0
  REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
427
0
  REQUIRE(versionp != NULL && *versionp == NULL);
428
429
0
  if (db->methods->newversion != NULL) {
430
0
    return (db->methods->newversion)(db, versionp);
431
0
  }
432
0
  return ISC_R_NOTIMPLEMENTED;
433
0
}
434
435
void
436
dns_db_attachversion(dns_db_t *db, dns_dbversion_t *source,
437
0
         dns_dbversion_t **targetp) {
438
  /*
439
   * Attach '*targetp' to 'source'.
440
   */
441
442
0
  REQUIRE(DNS_DB_VALID(db));
443
0
  REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
444
0
  REQUIRE(source != NULL);
445
0
  REQUIRE(targetp != NULL && *targetp == NULL);
446
447
0
  (db->methods->attachversion)(db, source, targetp);
448
449
0
  ENSURE(*targetp != NULL);
450
0
}
451
452
void
453
dns__db_closeversion(dns_db_t *db, dns_dbversion_t **versionp,
454
6
         bool commit DNS__DB_FLARG) {
455
  /*
456
   * Close version '*versionp'.
457
   */
458
459
6
  REQUIRE(DNS_DB_VALID(db));
460
6
  REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
461
6
  REQUIRE(versionp != NULL && *versionp != NULL);
462
463
6
  (db->methods->closeversion)(db, versionp, commit DNS__DB_FLARG_PASS);
464
465
6
  if (commit) {
466
0
    call_updatenotify(db);
467
0
  }
468
469
6
  ENSURE(*versionp == NULL);
470
6
}
471
472
/***
473
 *** Node Methods
474
 ***/
475
476
isc_result_t
477
dns__db_findnode(dns_db_t *db, const dns_name_t *name, bool create,
478
     dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo,
479
6
     dns_dbnode_t **nodep DNS__DB_FLARG) {
480
  /*
481
   * Find the node with name 'name', passing 'arg' to the database
482
   * implementation.
483
   */
484
485
6
  REQUIRE(DNS_DB_VALID(db));
486
6
  REQUIRE(nodep != NULL && *nodep == NULL);
487
488
6
  if (db->methods->findnode != NULL) {
489
6
    return (db->methods->findnode)(db, name, create, methods,
490
6
                 clientinfo,
491
6
                 nodep DNS__DB_FLARG_PASS);
492
6
  }
493
0
  return ISC_R_NOTIMPLEMENTED;
494
6
}
495
496
isc_result_t
497
dns__db_findnsec3node(dns_db_t *db, const dns_name_t *name, bool create,
498
0
          dns_dbnode_t **nodep DNS__DB_FLARG) {
499
  /*
500
   * Find the node with name 'name'.
501
   */
502
503
0
  REQUIRE(DNS_DB_VALID(db));
504
0
  REQUIRE(nodep != NULL && *nodep == NULL);
505
506
0
  return (db->methods->findnsec3node)(db, name, create,
507
0
              nodep DNS__DB_FLARG_PASS);
508
0
}
509
510
isc_result_t
511
dns__db_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
512
       dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
513
       dns_name_t *foundname, dns_clientinfomethods_t *methods,
514
       dns_clientinfo_t *clientinfo, dns_rdataset_t *rdataset,
515
194
       dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
516
  /*
517
   * Find the best match for 'name' and 'type' in version 'version'
518
   * of 'db', passing in 'arg'.
519
   */
520
521
194
  REQUIRE(DNS_DB_VALID(db));
522
194
  REQUIRE(type != dns_rdatatype_rrsig);
523
194
  REQUIRE(dns_name_hasbuffer(foundname));
524
194
  REQUIRE(rdataset == NULL || (DNS_RDATASET_VALID(rdataset) &&
525
194
             !dns_rdataset_isassociated(rdataset)));
526
194
  REQUIRE(sigrdataset == NULL ||
527
194
    (DNS_RDATASET_VALID(sigrdataset) &&
528
194
     !dns_rdataset_isassociated(sigrdataset)));
529
530
194
  if (db->methods->find != NULL) {
531
194
    return (db->methods->find)(db, name, version, type, options,
532
194
             now, foundname, methods, clientinfo,
533
194
             rdataset,
534
194
             sigrdataset DNS__DB_FLARG_PASS);
535
194
  }
536
0
  return ISC_R_NOTIMPLEMENTED;
537
194
}
538
539
void
540
0
dns__db_attachnode(dns_dbnode_t *source, dns_dbnode_t **targetp DNS__DB_FLARG) {
541
  /*
542
   * Attach *targetp to source.
543
   */
544
545
0
  REQUIRE(source != NULL);
546
0
  REQUIRE(targetp != NULL && *targetp == NULL);
547
0
  REQUIRE(source->methods != NULL && source->methods->attachnode != NULL);
548
549
0
  (source->methods->attachnode)(source, targetp DNS__DB_FLARG_PASS);
550
0
}
551
552
void
553
8
dns__db_detachnode(dns_dbnode_t **nodep DNS__DB_FLARG) {
554
  /*
555
   * Detach *nodep from its node.
556
   */
557
558
8
  REQUIRE(nodep != NULL && *nodep != NULL);
559
8
  dns_dbnode_t *node = *nodep;
560
8
  REQUIRE(node->methods != NULL && node->methods->detachnode != NULL);
561
562
8
  (node->methods->detachnode)(nodep DNS__DB_FLARG_PASS);
563
564
8
  ENSURE(*nodep == NULL);
565
8
}
566
567
void
568
dns_db_transfernode(dns_db_t *db, dns_dbnode_t **sourcep,
569
0
        dns_dbnode_t **targetp) {
570
0
  REQUIRE(DNS_DB_VALID(db));
571
0
  REQUIRE(targetp != NULL && *targetp == NULL);
572
0
  REQUIRE(sourcep != NULL && *sourcep != NULL);
573
574
0
  *targetp = *sourcep;
575
0
  *sourcep = NULL;
576
0
}
577
578
/***
579
 *** DB Iterator Creation
580
 ***/
581
582
isc_result_t
583
dns_db_createiterator(dns_db_t *db, unsigned int flags,
584
0
          dns_dbiterator_t **iteratorp) {
585
  /*
586
   * Create an iterator for version 'version' of 'db'.
587
   */
588
589
0
  REQUIRE(DNS_DB_VALID(db));
590
0
  REQUIRE(iteratorp != NULL && *iteratorp == NULL);
591
0
  REQUIRE((flags & (DNS_DB_NSEC3ONLY | DNS_DB_NONSEC3)) !=
592
0
    (DNS_DB_NSEC3ONLY | DNS_DB_NONSEC3));
593
594
0
  if (db->methods->createiterator != NULL) {
595
0
    return db->methods->createiterator(db, flags, iteratorp);
596
0
  }
597
0
  return ISC_R_NOTIMPLEMENTED;
598
0
}
599
600
/***
601
 *** Rdataset Methods
602
 ***/
603
604
isc_result_t
605
dns__db_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
606
         dns_rdatatype_t type, dns_rdatatype_t covers,
607
         isc_stdtime_t now, dns_rdataset_t *rdataset,
608
17.6k
         dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
609
17.6k
  REQUIRE(DNS_DB_VALID(db));
610
17.6k
  REQUIRE(node != NULL);
611
17.6k
  REQUIRE(DNS_RDATASET_VALID(rdataset));
612
17.6k
  REQUIRE(!dns_rdataset_isassociated(rdataset));
613
17.6k
  REQUIRE(covers == dns_rdatatype_none || dns_rdatatype_issig(type));
614
17.6k
  REQUIRE(type != dns_rdatatype_any);
615
17.6k
  REQUIRE(sigrdataset == NULL ||
616
17.6k
    (DNS_RDATASET_VALID(sigrdataset) &&
617
17.6k
     !dns_rdataset_isassociated(sigrdataset)));
618
619
17.6k
  return (db->methods->findrdataset)(db, node, version, type, covers, now,
620
17.6k
             rdataset,
621
17.6k
             sigrdataset DNS__DB_FLARG_PASS);
622
17.6k
}
623
624
isc_result_t
625
dns__db_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
626
         unsigned int options, isc_stdtime_t now,
627
0
         dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) {
628
  /*
629
   * Make '*iteratorp' an rdataset iteratator for all rdatasets at
630
   * 'node' in version 'version' of 'db'.
631
   */
632
633
0
  REQUIRE(DNS_DB_VALID(db));
634
0
  REQUIRE(iteratorp != NULL && *iteratorp == NULL);
635
636
0
  return (db->methods->allrdatasets)(db, node, version, options, now,
637
0
             iteratorp DNS__DB_FLARG_PASS);
638
0
}
639
640
isc_result_t
641
dns__db_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
642
        isc_stdtime_t now, dns_rdataset_t *rdataset,
643
        unsigned int options,
644
0
        dns_rdataset_t *addedrdataset DNS__DB_FLARG) {
645
  /*
646
   * Add 'rdataset' to 'node' in version 'version' of 'db'.
647
   */
648
649
0
  REQUIRE(DNS_DB_VALID(db));
650
0
  REQUIRE(node != NULL);
651
0
  REQUIRE(((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL) ||
652
0
    ((db->attributes & DNS_DBATTR_CACHE) != 0 && version == NULL &&
653
0
     (options & DNS_DBADD_MERGE) == 0));
654
0
  REQUIRE((options & DNS_DBADD_EXACT) == 0 ||
655
0
    (options & DNS_DBADD_MERGE) != 0);
656
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
657
0
  REQUIRE(dns_rdataset_isassociated(rdataset));
658
0
  REQUIRE(rdataset->rdclass == db->rdclass);
659
0
  REQUIRE(addedrdataset == NULL ||
660
0
    (DNS_RDATASET_VALID(addedrdataset) &&
661
0
     !dns_rdataset_isassociated(addedrdataset)));
662
663
0
  if (db->methods->addrdataset != NULL) {
664
0
    return (db->methods->addrdataset)(
665
0
      db, node, version, now, rdataset, options,
666
0
      addedrdataset DNS__DB_FLARG_PASS);
667
0
  }
668
0
  return ISC_R_NOTIMPLEMENTED;
669
0
}
670
671
isc_result_t
672
dns__db_subtractrdataset(dns_db_t *db, dns_dbnode_t *node,
673
       dns_dbversion_t *version, dns_rdataset_t *rdataset,
674
       unsigned int options,
675
0
       dns_rdataset_t *newrdataset DNS__DB_FLARG) {
676
  /*
677
   * Remove any rdata in 'rdataset' from 'node' in version 'version' of
678
   * 'db'.
679
   */
680
681
0
  REQUIRE(DNS_DB_VALID(db));
682
0
  REQUIRE(node != NULL);
683
0
  REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL);
684
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
685
0
  REQUIRE(dns_rdataset_isassociated(rdataset));
686
0
  REQUIRE(rdataset->rdclass == db->rdclass);
687
0
  REQUIRE(newrdataset == NULL ||
688
0
    (DNS_RDATASET_VALID(newrdataset) &&
689
0
     !dns_rdataset_isassociated(newrdataset)));
690
691
0
  if (db->methods->subtractrdataset != NULL) {
692
0
    return (db->methods->subtractrdataset)(
693
0
      db, node, version, rdataset, options,
694
0
      newrdataset DNS__DB_FLARG_PASS);
695
0
  }
696
0
  return ISC_R_NOTIMPLEMENTED;
697
0
}
698
699
isc_result_t
700
dns__db_deleterdataset(dns_db_t *db, dns_dbnode_t *node,
701
           dns_dbversion_t *version, dns_rdatatype_t type,
702
0
           dns_rdatatype_t covers DNS__DB_FLARG) {
703
  /*
704
   * Make it so that no rdataset of type 'type' exists at 'node' in
705
   * version version 'version' of 'db'.
706
   */
707
708
0
  REQUIRE(DNS_DB_VALID(db));
709
0
  REQUIRE(node != NULL);
710
0
  REQUIRE(((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL) ||
711
0
    ((db->attributes & DNS_DBATTR_CACHE) != 0 && version == NULL));
712
713
0
  if (db->methods->deleterdataset != NULL) {
714
0
    return (db->methods->deleterdataset)(db, node, version, type,
715
0
                 covers DNS__DB_FLARG_PASS);
716
0
  }
717
0
  return ISC_R_NOTIMPLEMENTED;
718
0
}
719
720
isc_result_t
721
0
dns_db_getsoaserial(dns_db_t *db, dns_dbversion_t *ver, uint32_t *serialp) {
722
0
  isc_result_t result;
723
0
  dns_dbnode_t *node = NULL;
724
0
  dns_rdataset_t rdataset;
725
0
  dns_rdata_t rdata = DNS_RDATA_INIT;
726
0
  isc_buffer_t buffer;
727
728
0
  REQUIRE(dns_db_iszone(db) || dns_db_isstub(db));
729
730
0
  RETERR(dns_db_findnode(db, dns_db_origin(db), false, &node));
731
732
0
  dns_rdataset_init(&rdataset);
733
0
  result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa, 0,
734
0
             (isc_stdtime_t)0, &rdataset, NULL);
735
0
  if (result != ISC_R_SUCCESS) {
736
0
    goto freenode;
737
0
  }
738
739
0
  result = dns_rdataset_first(&rdataset);
740
0
  if (result != ISC_R_SUCCESS) {
741
0
    goto freerdataset;
742
0
  }
743
0
  dns_rdataset_current(&rdataset, &rdata);
744
0
  result = dns_rdataset_next(&rdataset);
745
0
  INSIST(result == ISC_R_NOMORE);
746
747
0
  INSIST(rdata.length > 20);
748
0
  isc_buffer_init(&buffer, rdata.data, rdata.length);
749
0
  isc_buffer_add(&buffer, rdata.length);
750
0
  isc_buffer_forward(&buffer, rdata.length - 20);
751
0
  *serialp = isc_buffer_getuint32(&buffer);
752
753
0
  result = ISC_R_SUCCESS;
754
755
0
freerdataset:
756
0
  dns_rdataset_disassociate(&rdataset);
757
758
0
freenode:
759
0
  dns_db_detachnode(&node);
760
0
  return result;
761
0
}
762
763
unsigned int
764
2
dns_db_nodecount(dns_db_t *db) {
765
2
  REQUIRE(DNS_DB_VALID(db));
766
767
2
  if (db->methods->nodecount != NULL) {
768
2
    return (db->methods->nodecount)(db);
769
2
  }
770
0
  return 0;
771
2
}
772
773
isc_result_t
774
dns_db_register(const char *name, dns_dbcreatefunc_t create, void *driverarg,
775
0
    isc_mem_t *mctx, dns_dbimplementation_t **dbimp) {
776
0
  dns_dbimplementation_t *imp;
777
778
0
  REQUIRE(name != NULL);
779
0
  REQUIRE(dbimp != NULL && *dbimp == NULL);
780
781
0
  RWLOCK(&implock, isc_rwlocktype_write);
782
0
  imp = impfind(name);
783
0
  if (imp != NULL) {
784
0
    RWUNLOCK(&implock, isc_rwlocktype_write);
785
0
    return ISC_R_EXISTS;
786
0
  }
787
788
0
  imp = isc_mem_get(mctx, sizeof(dns_dbimplementation_t));
789
0
  imp->name = name;
790
0
  imp->create = create;
791
0
  imp->mctx = NULL;
792
0
  imp->driverarg = driverarg;
793
0
  isc_mem_attach(mctx, &imp->mctx);
794
0
  ISC_LINK_INIT(imp, link);
795
0
  ISC_LIST_APPEND(implementations, imp, link);
796
0
  RWUNLOCK(&implock, isc_rwlocktype_write);
797
798
0
  *dbimp = imp;
799
800
0
  return ISC_R_SUCCESS;
801
0
}
802
803
void
804
0
dns_db_unregister(dns_dbimplementation_t **dbimp) {
805
0
  dns_dbimplementation_t *imp;
806
807
0
  REQUIRE(dbimp != NULL && *dbimp != NULL);
808
809
0
  imp = *dbimp;
810
0
  *dbimp = NULL;
811
0
  RWLOCK(&implock, isc_rwlocktype_write);
812
0
  ISC_LIST_UNLINK(implementations, imp, link);
813
0
  isc_mem_putanddetach(&imp->mctx, imp, sizeof(dns_dbimplementation_t));
814
0
  RWUNLOCK(&implock, isc_rwlocktype_write);
815
0
  ENSURE(*dbimp == NULL);
816
0
}
817
818
isc_result_t
819
2
dns__db_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) {
820
2
  REQUIRE(DNS_DB_VALID(db));
821
2
  REQUIRE(dns_db_iszone(db));
822
2
  REQUIRE(nodep != NULL && *nodep == NULL);
823
824
2
  if (db->methods->getoriginnode != NULL) {
825
2
    return (db->methods->getoriginnode)(db,
826
2
                nodep DNS__DB_FLARG_PASS);
827
2
  } else if (db->methods->findnode != NULL) {
828
0
    return (db->methods->findnode)(db, &db->origin, false, NULL,
829
0
                 NULL, nodep DNS__DB_FLARG_PASS);
830
0
  }
831
832
0
  return ISC_R_NOTFOUND;
833
2
}
834
835
dns_stats_t *
836
0
dns_db_getrrsetstats(dns_db_t *db) {
837
0
  REQUIRE(DNS_DB_VALID(db));
838
839
0
  if (db->methods->getrrsetstats != NULL) {
840
0
    return (db->methods->getrrsetstats)(db);
841
0
  }
842
843
0
  return NULL;
844
0
}
845
846
isc_result_t
847
0
dns_db_setcachestats(dns_db_t *db, isc_stats_t *stats) {
848
0
  REQUIRE(DNS_DB_VALID(db));
849
850
0
  if (db->methods->setcachestats != NULL) {
851
0
    return (db->methods->setcachestats)(db, stats);
852
0
  }
853
854
0
  return ISC_R_NOTIMPLEMENTED;
855
0
}
856
857
isc_result_t
858
dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
859
        dns_hash_t *hash, uint8_t *flags,
860
        uint16_t *iterations, unsigned char *salt,
861
0
        size_t *salt_length) {
862
0
  REQUIRE(DNS_DB_VALID(db));
863
0
  REQUIRE(dns_db_iszone(db));
864
865
0
  if (db->methods->getnsec3parameters != NULL) {
866
0
    return (db->methods->getnsec3parameters)(db, version, hash,
867
0
               flags, iterations,
868
0
               salt, salt_length);
869
0
  }
870
871
0
  return ISC_R_NOTFOUND;
872
0
}
873
874
isc_result_t
875
dns_db_getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records,
876
0
         uint64_t *bytes) {
877
0
  REQUIRE(DNS_DB_VALID(db));
878
0
  REQUIRE(dns_db_iszone(db));
879
880
0
  if (db->methods->getsize != NULL) {
881
0
    return (db->methods->getsize)(db, version, records, bytes);
882
0
  }
883
884
0
  return ISC_R_NOTFOUND;
885
0
}
886
887
isc_result_t
888
dns_db_setsigningtime(dns_db_t *db, dns_dbnode_t *node,
889
0
          dns_rdataset_t *rdataset, isc_stdtime_t resign) {
890
0
  if (db->methods->setsigningtime != NULL) {
891
0
    return (db->methods->setsigningtime)(db, node, rdataset,
892
0
                 resign);
893
0
  }
894
0
  return ISC_R_NOTIMPLEMENTED;
895
0
}
896
897
isc_result_t
898
dns_db_getsigningtime(dns_db_t *db, isc_stdtime_t *resign, dns_name_t *name,
899
0
          dns_typepair_t *typepair) {
900
0
  if (db->methods->getsigningtime != NULL) {
901
0
    return (db->methods->getsigningtime)(db, resign, name,
902
0
                 typepair);
903
0
  }
904
0
  return ISC_R_NOTFOUND;
905
0
}
906
907
static void
908
17.6k
call_updatenotify(dns_db_t *db) {
909
17.6k
  rcu_read_lock();
910
17.6k
  struct cds_lfht *update_listeners =
911
17.6k
    rcu_dereference(db->update_listeners);
912
17.6k
  if (update_listeners != NULL) {
913
17.6k
    struct cds_lfht_iter iter;
914
17.6k
    dns_dbonupdatelistener_t *listener;
915
17.6k
    cds_lfht_for_each_entry(update_listeners, &iter, listener,
916
17.6k
          ht_node) {
917
0
      if (!cds_lfht_is_node_deleted(&listener->ht_node)) {
918
0
        listener->onupdate(db, listener->onupdate_arg);
919
0
      }
920
0
    }
921
17.6k
  }
922
17.6k
  rcu_read_unlock();
923
17.6k
}
924
925
static void
926
0
updatenotify_free(struct rcu_head *rcu_head) {
927
0
  dns_dbonupdatelistener_t *listener =
928
0
    caa_container_of(rcu_head, dns_dbonupdatelistener_t, rcu_head);
929
0
  isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener));
930
0
}
931
932
static int
933
0
updatenotify_match(struct cds_lfht_node *ht_node, const void *_key) {
934
0
  const dns_dbonupdatelistener_t *listener =
935
0
    caa_container_of(ht_node, dns_dbonupdatelistener_t, ht_node);
936
0
  const dns_dbonupdatelistener_t *key = _key;
937
938
0
  return listener->onupdate == key->onupdate &&
939
0
         listener->onupdate_arg == key->onupdate_arg;
940
0
}
941
942
/*
943
 * Attach a notify-on-update function the database
944
 */
945
void
946
dns_db_updatenotify_register(dns_db_t *db, dns_dbupdate_callback_t fn,
947
0
           void *fn_arg) {
948
0
  REQUIRE(db != NULL);
949
0
  REQUIRE(fn != NULL);
950
951
0
  dns_dbonupdatelistener_t key = { .onupdate = fn,
952
0
           .onupdate_arg = fn_arg };
953
0
  uint32_t hash = isc_hash32(&key, sizeof(key), true);
954
0
  dns_dbonupdatelistener_t *listener = isc_mem_get(db->mctx,
955
0
               sizeof(*listener));
956
0
  *listener = key;
957
958
0
  isc_mem_attach(db->mctx, &listener->mctx);
959
960
0
  rcu_read_lock();
961
0
  struct cds_lfht *update_listeners =
962
0
    rcu_dereference(db->update_listeners);
963
0
  INSIST(update_listeners != NULL);
964
0
  struct cds_lfht_node *ht_node =
965
0
    cds_lfht_add_unique(update_listeners, hash, updatenotify_match,
966
0
            &key, &listener->ht_node);
967
0
  rcu_read_unlock();
968
969
0
  if (ht_node != &listener->ht_node) {
970
0
    updatenotify_free(&listener->rcu_head);
971
0
  }
972
0
}
973
974
void
975
dns_db_updatenotify_unregister(dns_db_t *db, dns_dbupdate_callback_t fn,
976
0
             void *fn_arg) {
977
0
  REQUIRE(db != NULL);
978
979
0
  dns_dbonupdatelistener_t key = { .onupdate = fn,
980
0
           .onupdate_arg = fn_arg };
981
0
  uint32_t hash = isc_hash32(&key, sizeof(key), true);
982
0
  struct cds_lfht_iter iter;
983
984
0
  rcu_read_lock();
985
0
  struct cds_lfht *update_listeners =
986
0
    rcu_dereference(db->update_listeners);
987
0
  INSIST(update_listeners != NULL);
988
0
  cds_lfht_lookup(update_listeners, hash, updatenotify_match, &key,
989
0
      &iter);
990
991
0
  struct cds_lfht_node *ht_node = cds_lfht_iter_get_node(&iter);
992
0
  if (ht_node != NULL && !cds_lfht_del(update_listeners, ht_node)) {
993
0
    dns_dbonupdatelistener_t *listener = caa_container_of(
994
0
      ht_node, dns_dbonupdatelistener_t, ht_node);
995
0
    call_rcu(&listener->rcu_head, updatenotify_free);
996
0
  }
997
0
  rcu_read_unlock();
998
0
}
999
1000
isc_result_t
1001
0
dns_db_setservestalettl(dns_db_t *db, dns_ttl_t ttl) {
1002
0
  REQUIRE(DNS_DB_VALID(db));
1003
0
  REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
1004
1005
0
  if (db->methods->setservestalettl != NULL) {
1006
0
    return (db->methods->setservestalettl)(db, ttl);
1007
0
  }
1008
0
  return ISC_R_NOTIMPLEMENTED;
1009
0
}
1010
1011
isc_result_t
1012
0
dns_db_getservestalettl(dns_db_t *db, dns_ttl_t *ttl) {
1013
0
  REQUIRE(DNS_DB_VALID(db));
1014
0
  REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
1015
1016
0
  if (db->methods->getservestalettl != NULL) {
1017
0
    return (db->methods->getservestalettl)(db, ttl);
1018
0
  }
1019
0
  return ISC_R_NOTIMPLEMENTED;
1020
0
}
1021
1022
isc_result_t
1023
0
dns_db_setservestalerefresh(dns_db_t *db, uint32_t interval) {
1024
0
  REQUIRE(DNS_DB_VALID(db));
1025
0
  REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
1026
1027
0
  if (db->methods->setservestalerefresh != NULL) {
1028
0
    return (db->methods->setservestalerefresh)(db, interval);
1029
0
  }
1030
0
  return ISC_R_NOTIMPLEMENTED;
1031
0
}
1032
1033
isc_result_t
1034
0
dns_db_getservestalerefresh(dns_db_t *db, uint32_t *interval) {
1035
0
  REQUIRE(DNS_DB_VALID(db));
1036
0
  REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
1037
1038
0
  if (db->methods->getservestalerefresh != NULL) {
1039
0
    return (db->methods->getservestalerefresh)(db, interval);
1040
0
  }
1041
0
  return ISC_R_NOTIMPLEMENTED;
1042
0
}
1043
1044
isc_result_t
1045
2
dns_db_setgluecachestats(dns_db_t *db, isc_stats_t *stats) {
1046
2
  REQUIRE(dns_db_iszone(db));
1047
2
  REQUIRE(stats != NULL);
1048
1049
2
  if (db->methods->setgluecachestats != NULL) {
1050
2
    return (db->methods->setgluecachestats)(db, stats);
1051
2
  }
1052
1053
0
  return ISC_R_NOTIMPLEMENTED;
1054
2
}
1055
1056
void
1057
dns_db_addglue(dns_db_t *db, dns_dbversion_t *version,
1058
         const dns_name_t *owner_name, dns_rdataset_t *rdataset,
1059
         dns_message_t *msg, dns_clientinfomethods_t *methods,
1060
0
         dns_clientinfo_t *clientinfo) {
1061
0
  REQUIRE(DNS_DB_VALID(db));
1062
0
  REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
1063
0
  REQUIRE(DNS_RDATASET_VALID(rdataset));
1064
0
  REQUIRE(rdataset->methods != NULL);
1065
0
  REQUIRE(rdataset->type == dns_rdatatype_ns);
1066
0
  REQUIRE(db->methods->addglue != NULL);
1067
1068
0
  (db->methods->addglue)(db, version, owner_name, rdataset, msg, methods,
1069
0
             clientinfo);
1070
0
}
1071
1072
void
1073
0
dns_db_expiredata(dns_dbnode_t *node, void *data) {
1074
0
  REQUIRE(node != NULL && node->methods != NULL);
1075
0
  if (node->methods->expiredata != NULL) {
1076
0
    (node->methods->expiredata)(node, data);
1077
0
  }
1078
0
}
1079
1080
void
1081
2
dns_db_setmaxrrperset(dns_db_t *db, uint32_t value) {
1082
2
  REQUIRE(DNS_DB_VALID(db));
1083
1084
2
  if (db->methods->setmaxrrperset != NULL) {
1085
2
    (db->methods->setmaxrrperset)(db, value);
1086
2
  }
1087
2
}
1088
1089
void
1090
2
dns_db_setmaxtypepername(dns_db_t *db, uint32_t value) {
1091
2
  REQUIRE(DNS_DB_VALID(db));
1092
1093
2
  if (db->methods->setmaxtypepername != NULL) {
1094
2
    (db->methods->setmaxtypepername)(db, value);
1095
2
  }
1096
2
}
1097
1098
void
1099
dns__db_logtoomanyrecords(dns_db_t *db, const dns_name_t *name,
1100
        dns_rdatatype_t type, const char *op,
1101
0
        uint32_t limit) {
1102
0
  char namebuf[DNS_NAME_FORMATSIZE];
1103
0
  char originbuf[DNS_NAME_FORMATSIZE];
1104
0
  char typebuf[DNS_RDATATYPE_FORMATSIZE];
1105
0
  char clsbuf[DNS_RDATACLASS_FORMATSIZE];
1106
1107
0
  dns_name_format(name, namebuf, sizeof(namebuf));
1108
0
  dns_name_format(&db->origin, originbuf, sizeof(originbuf));
1109
0
  dns_rdatatype_format(type, typebuf, sizeof(typebuf));
1110
0
  dns_rdataclass_format(db->rdclass, clsbuf, sizeof(clsbuf));
1111
1112
0
  isc_log_write(
1113
0
    DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DB, ISC_LOG_ERROR,
1114
0
    "error %s '%s/%s' in '%s/%s' (%s): %s (must not exceed %u)", op,
1115
0
    namebuf, typebuf, originbuf, clsbuf,
1116
0
    (db->attributes & DNS_DBATTR_CACHE) != 0 ? "cache" : "zone",
1117
0
    isc_result_totext(DNS_R_TOOMANYRECORDS), limit);
1118
0
}
1119
1120
isc_result_t
1121
0
dns_db_getzoneversion(dns_db_t *db, isc_buffer_t *b) {
1122
0
  REQUIRE(db != NULL);
1123
0
  REQUIRE(b != NULL);
1124
1125
0
  if (db->methods->getzoneversion != NULL) {
1126
0
    return (db->methods->getzoneversion)(db, b);
1127
0
  }
1128
0
  return ISC_R_NOTIMPLEMENTED;
1129
0
}