mirror of
https://github.com/isc-projects/bind9.git
synced 2026-04-27 00:58:39 -04:00
- the DNS_DB_NSEC3ONLY and DNS_DB_NONSEC3 flags are mutually
exclusive; it never made sense to set both at the same time.
to enforce this, it is now a fatal error to do so. the
dbiterator implementation has been cleaned up to remove
code that treated the two as independent: if nonsec3 is
true, we can be certain nsec3only is false, and vice versa.
- previously, iterating a database backwards omitted
NSEC3 records even if DNS_DB_NONSEC3 had not been set. this
has been corrected.
- when an iterator reaches the origin node of the NSEC3 tree, we
need to skip over it and go to the next node in the sequence.
the NSEC3 origin node is there for housekeeping purposes and
never contains data.
- the dbiterator_test unit test has been expanded, several
incorrect expectations have been fixed. (for example, the
expected number of iterations has been reduced by one; we were
previously counting the NSEC3 origin node and we should not
have been doing so.)
(cherry picked from commit e40fd4ed06)
1123 lines
27 KiB
C
1123 lines
27 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
/*! \file */
|
|
|
|
/***
|
|
*** Imports
|
|
***/
|
|
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
|
|
#include <isc/buffer.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/once.h>
|
|
#include <isc/result.h>
|
|
#include <isc/rwlock.h>
|
|
#include <isc/string.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/callbacks.h>
|
|
#include <dns/clientinfo.h>
|
|
#include <dns/db.h>
|
|
#include <dns/dbiterator.h>
|
|
#include <dns/log.h>
|
|
#include <dns/master.h>
|
|
#include <dns/rdata.h>
|
|
#include <dns/rdataset.h>
|
|
#include <dns/rdatasetiter.h>
|
|
|
|
/***
|
|
*** Private Types
|
|
***/
|
|
|
|
struct dns_dbimplementation {
|
|
const char *name;
|
|
dns_dbcreatefunc_t create;
|
|
isc_mem_t *mctx;
|
|
void *driverarg;
|
|
ISC_LINK(dns_dbimplementation_t) link;
|
|
};
|
|
|
|
/***
|
|
*** Supported DB Implementations Registry
|
|
***/
|
|
|
|
/*
|
|
* Built in database implementations are registered here.
|
|
*/
|
|
|
|
#include "rbtdb.h"
|
|
|
|
unsigned int dns_pps = 0U;
|
|
|
|
static ISC_LIST(dns_dbimplementation_t) implementations;
|
|
static isc_rwlock_t implock;
|
|
static isc_once_t once = ISC_ONCE_INIT;
|
|
|
|
static dns_dbimplementation_t rbtimp;
|
|
|
|
static void
|
|
initialize(void) {
|
|
isc_rwlock_init(&implock, 0, 0);
|
|
|
|
rbtimp.name = "rbt";
|
|
rbtimp.create = dns_rbtdb_create;
|
|
rbtimp.mctx = NULL;
|
|
rbtimp.driverarg = NULL;
|
|
ISC_LINK_INIT(&rbtimp, link);
|
|
|
|
ISC_LIST_INIT(implementations);
|
|
ISC_LIST_APPEND(implementations, &rbtimp, link);
|
|
}
|
|
|
|
static dns_dbimplementation_t *
|
|
impfind(const char *name) {
|
|
dns_dbimplementation_t *imp;
|
|
|
|
for (imp = ISC_LIST_HEAD(implementations); imp != NULL;
|
|
imp = ISC_LIST_NEXT(imp, link))
|
|
{
|
|
if (strcasecmp(name, imp->name) == 0) {
|
|
return (imp);
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
/***
|
|
*** Basic DB Methods
|
|
***/
|
|
|
|
isc_result_t
|
|
dns_db_create(isc_mem_t *mctx, const char *db_type, const dns_name_t *origin,
|
|
dns_dbtype_t type, dns_rdataclass_t rdclass, unsigned int argc,
|
|
char *argv[], dns_db_t **dbp) {
|
|
dns_dbimplementation_t *impinfo;
|
|
|
|
RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
|
|
|
|
/*
|
|
* Create a new database using implementation 'db_type'.
|
|
*/
|
|
|
|
REQUIRE(dbp != NULL && *dbp == NULL);
|
|
REQUIRE(dns_name_isabsolute(origin));
|
|
|
|
RWLOCK(&implock, isc_rwlocktype_read);
|
|
impinfo = impfind(db_type);
|
|
if (impinfo != NULL) {
|
|
isc_result_t result;
|
|
result = ((impinfo->create)(mctx, origin, type, rdclass, argc,
|
|
argv, impinfo->driverarg, dbp));
|
|
RWUNLOCK(&implock, isc_rwlocktype_read);
|
|
return (result);
|
|
}
|
|
|
|
RWUNLOCK(&implock, isc_rwlocktype_read);
|
|
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DB,
|
|
ISC_LOG_ERROR, "unsupported database type '%s'", db_type);
|
|
|
|
return (ISC_R_NOTFOUND);
|
|
}
|
|
|
|
void
|
|
dns_db_attach(dns_db_t *source, dns_db_t **targetp) {
|
|
/*
|
|
* Attach *targetp to source.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(source));
|
|
REQUIRE(targetp != NULL && *targetp == NULL);
|
|
|
|
(source->methods->attach)(source, targetp);
|
|
|
|
ENSURE(*targetp == source);
|
|
}
|
|
|
|
void
|
|
dns_db_detach(dns_db_t **dbp) {
|
|
/*
|
|
* Detach *dbp from its database.
|
|
*/
|
|
|
|
REQUIRE(dbp != NULL);
|
|
REQUIRE(DNS_DB_VALID(*dbp));
|
|
|
|
((*dbp)->methods->detach)(dbp);
|
|
|
|
ENSURE(*dbp == NULL);
|
|
}
|
|
|
|
bool
|
|
dns_db_iscache(dns_db_t *db) {
|
|
/*
|
|
* Does 'db' have cache semantics?
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
if ((db->attributes & DNS_DBATTR_CACHE) != 0) {
|
|
return (true);
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
bool
|
|
dns_db_iszone(dns_db_t *db) {
|
|
/*
|
|
* Does 'db' have zone semantics?
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
if ((db->attributes & (DNS_DBATTR_CACHE | DNS_DBATTR_STUB)) == 0) {
|
|
return (true);
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
bool
|
|
dns_db_isstub(dns_db_t *db) {
|
|
/*
|
|
* Does 'db' have stub semantics?
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
if ((db->attributes & DNS_DBATTR_STUB) != 0) {
|
|
return (true);
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
bool
|
|
dns_db_isdnssec(dns_db_t *db) {
|
|
/*
|
|
* Is 'db' secure or partially secure?
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
|
|
|
|
if (db->methods->isdnssec != NULL) {
|
|
return ((db->methods->isdnssec)(db));
|
|
}
|
|
return ((db->methods->issecure)(db));
|
|
}
|
|
|
|
bool
|
|
dns_db_issecure(dns_db_t *db) {
|
|
/*
|
|
* Is 'db' secure?
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
|
|
|
|
return ((db->methods->issecure)(db));
|
|
}
|
|
|
|
bool
|
|
dns_db_ispersistent(dns_db_t *db) {
|
|
/*
|
|
* Is 'db' persistent?
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
return ((db->methods->ispersistent)(db));
|
|
}
|
|
|
|
dns_name_t *
|
|
dns_db_origin(dns_db_t *db) {
|
|
/*
|
|
* The origin of the database.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
return (&db->origin);
|
|
}
|
|
|
|
dns_rdataclass_t
|
|
dns_db_class(dns_db_t *db) {
|
|
/*
|
|
* The class of the database.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
return (db->rdclass);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
|
|
/*
|
|
* Begin loading 'db'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(DNS_CALLBACK_VALID(callbacks));
|
|
|
|
return ((db->methods->beginload)(db, callbacks));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
|
|
dns_dbonupdatelistener_t *listener;
|
|
|
|
/*
|
|
* Finish loading 'db'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(DNS_CALLBACK_VALID(callbacks));
|
|
REQUIRE(callbacks->add_private != NULL);
|
|
|
|
for (listener = ISC_LIST_HEAD(db->update_listeners); listener != NULL;
|
|
listener = ISC_LIST_NEXT(listener, link))
|
|
{
|
|
listener->onupdate(db, listener->onupdate_arg);
|
|
}
|
|
|
|
return ((db->methods->endload)(db, callbacks));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_load(dns_db_t *db, const char *filename, dns_masterformat_t format,
|
|
unsigned int options) {
|
|
isc_result_t result, eresult;
|
|
dns_rdatacallbacks_t callbacks;
|
|
|
|
/*
|
|
* Load master file 'filename' into 'db'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
if ((db->attributes & DNS_DBATTR_CACHE) != 0) {
|
|
options |= DNS_MASTER_AGETTL;
|
|
}
|
|
|
|
dns_rdatacallbacks_init(&callbacks);
|
|
result = dns_db_beginload(db, &callbacks);
|
|
if (result != ISC_R_SUCCESS) {
|
|
return (result);
|
|
}
|
|
result = dns_master_loadfile(filename, &db->origin, &db->origin,
|
|
db->rdclass, options, 0, &callbacks, NULL,
|
|
NULL, db->mctx, format, 0);
|
|
eresult = dns_db_endload(db, &callbacks);
|
|
/*
|
|
* We always call dns_db_endload(), but we only want to return its
|
|
* result if dns_master_loadfile() succeeded. If dns_master_loadfile()
|
|
* failed, we want to return the result code it gave us.
|
|
*/
|
|
if (eresult != ISC_R_SUCCESS &&
|
|
(result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE))
|
|
{
|
|
result = eresult;
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_dump(dns_db_t *db, dns_dbversion_t *version, const char *filename) {
|
|
return ((db->methods->dump)(db, version, filename,
|
|
dns_masterformat_text));
|
|
}
|
|
|
|
/***
|
|
*** Version Methods
|
|
***/
|
|
|
|
void
|
|
dns_db_currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
|
|
/*
|
|
* Open the current version for reading.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
|
|
REQUIRE(versionp != NULL && *versionp == NULL);
|
|
|
|
(db->methods->currentversion)(db, versionp);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_newversion(dns_db_t *db, dns_dbversion_t **versionp) {
|
|
/*
|
|
* Open a new version for reading and writing.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
|
|
REQUIRE(versionp != NULL && *versionp == NULL);
|
|
|
|
return ((db->methods->newversion)(db, versionp));
|
|
}
|
|
|
|
void
|
|
dns_db_attachversion(dns_db_t *db, dns_dbversion_t *source,
|
|
dns_dbversion_t **targetp) {
|
|
/*
|
|
* Attach '*targetp' to 'source'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
|
|
REQUIRE(source != NULL);
|
|
REQUIRE(targetp != NULL && *targetp == NULL);
|
|
|
|
(db->methods->attachversion)(db, source, targetp);
|
|
|
|
ENSURE(*targetp != NULL);
|
|
}
|
|
|
|
void
|
|
dns_db_closeversion(dns_db_t *db, dns_dbversion_t **versionp, bool commit) {
|
|
dns_dbonupdatelistener_t *listener;
|
|
|
|
/*
|
|
* Close version '*versionp'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
|
|
REQUIRE(versionp != NULL && *versionp != NULL);
|
|
|
|
(db->methods->closeversion)(db, versionp, commit);
|
|
|
|
if (commit) {
|
|
for (listener = ISC_LIST_HEAD(db->update_listeners);
|
|
listener != NULL; listener = ISC_LIST_NEXT(listener, link))
|
|
{
|
|
listener->onupdate(db, listener->onupdate_arg);
|
|
}
|
|
}
|
|
|
|
ENSURE(*versionp == NULL);
|
|
}
|
|
|
|
/***
|
|
*** Node Methods
|
|
***/
|
|
|
|
isc_result_t
|
|
dns_db_findnode(dns_db_t *db, const dns_name_t *name, bool create,
|
|
dns_dbnode_t **nodep) {
|
|
/*
|
|
* Find the node with name 'name'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(nodep != NULL && *nodep == NULL);
|
|
|
|
if (db->methods->findnode != NULL) {
|
|
return ((db->methods->findnode)(db, name, create, nodep));
|
|
} else {
|
|
return ((db->methods->findnodeext)(db, name, create, NULL, NULL,
|
|
nodep));
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_findnodeext(dns_db_t *db, const dns_name_t *name, bool create,
|
|
dns_clientinfomethods_t *methods,
|
|
dns_clientinfo_t *clientinfo, dns_dbnode_t **nodep) {
|
|
/*
|
|
* Find the node with name 'name', passing 'arg' to the database
|
|
* implementation.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(nodep != NULL && *nodep == NULL);
|
|
|
|
if (db->methods->findnodeext != NULL) {
|
|
return ((db->methods->findnodeext)(db, name, create, methods,
|
|
clientinfo, nodep));
|
|
} else {
|
|
return ((db->methods->findnode)(db, name, create, nodep));
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_findnsec3node(dns_db_t *db, const dns_name_t *name, bool create,
|
|
dns_dbnode_t **nodep) {
|
|
/*
|
|
* Find the node with name 'name'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(nodep != NULL && *nodep == NULL);
|
|
|
|
return ((db->methods->findnsec3node)(db, name, create, nodep));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
|
|
dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
|
|
dns_dbnode_t **nodep, dns_name_t *foundname,
|
|
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) {
|
|
/*
|
|
* Find the best match for 'name' and 'type' in version 'version'
|
|
* of 'db'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(type != dns_rdatatype_rrsig);
|
|
REQUIRE(nodep == NULL || *nodep == NULL);
|
|
REQUIRE(dns_name_hasbuffer(foundname));
|
|
REQUIRE(rdataset == NULL || (DNS_RDATASET_VALID(rdataset) &&
|
|
!dns_rdataset_isassociated(rdataset)));
|
|
REQUIRE(sigrdataset == NULL ||
|
|
(DNS_RDATASET_VALID(sigrdataset) &&
|
|
!dns_rdataset_isassociated(sigrdataset)));
|
|
|
|
if (db->methods->find != NULL) {
|
|
return ((db->methods->find)(db, name, version, type, options,
|
|
now, nodep, foundname, rdataset,
|
|
sigrdataset));
|
|
} else {
|
|
return ((db->methods->findext)(db, name, version, type, options,
|
|
now, nodep, foundname, NULL,
|
|
NULL, rdataset, sigrdataset));
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_findext(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
|
|
dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
|
|
dns_dbnode_t **nodep, dns_name_t *foundname,
|
|
dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo,
|
|
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) {
|
|
/*
|
|
* Find the best match for 'name' and 'type' in version 'version'
|
|
* of 'db', passing in 'arg'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(type != dns_rdatatype_rrsig);
|
|
REQUIRE(nodep == NULL || *nodep == NULL);
|
|
REQUIRE(dns_name_hasbuffer(foundname));
|
|
REQUIRE(rdataset == NULL || (DNS_RDATASET_VALID(rdataset) &&
|
|
!dns_rdataset_isassociated(rdataset)));
|
|
REQUIRE(sigrdataset == NULL ||
|
|
(DNS_RDATASET_VALID(sigrdataset) &&
|
|
!dns_rdataset_isassociated(sigrdataset)));
|
|
|
|
if (db->methods->findext != NULL) {
|
|
return ((db->methods->findext)(
|
|
db, name, version, type, options, now, nodep, foundname,
|
|
methods, clientinfo, rdataset, sigrdataset));
|
|
} else {
|
|
return ((db->methods->find)(db, name, version, type, options,
|
|
now, nodep, foundname, rdataset,
|
|
sigrdataset));
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
|
|
isc_stdtime_t now, dns_dbnode_t **nodep,
|
|
dns_name_t *foundname, dns_name_t *dcname,
|
|
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) {
|
|
/*
|
|
* Find the deepest known zonecut which encloses 'name' in 'db'.
|
|
* foundname is the zonecut, dcname is the deepest name we have
|
|
* in database that is part of queried name.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
|
|
REQUIRE(nodep == NULL || *nodep == NULL);
|
|
REQUIRE(dns_name_hasbuffer(foundname));
|
|
REQUIRE(sigrdataset == NULL ||
|
|
(DNS_RDATASET_VALID(sigrdataset) &&
|
|
!dns_rdataset_isassociated(sigrdataset)));
|
|
|
|
return ((db->methods->findzonecut)(db, name, options, now, nodep,
|
|
foundname, dcname, rdataset,
|
|
sigrdataset));
|
|
}
|
|
|
|
void
|
|
dns_db_attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
|
|
/*
|
|
* Attach *targetp to source.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(source != NULL);
|
|
REQUIRE(targetp != NULL && *targetp == NULL);
|
|
|
|
(db->methods->attachnode)(db, source, targetp);
|
|
}
|
|
|
|
void
|
|
dns_db_detachnode(dns_db_t *db, dns_dbnode_t **nodep) {
|
|
/*
|
|
* Detach *nodep from its node.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(nodep != NULL && *nodep != NULL);
|
|
|
|
(db->methods->detachnode)(db, nodep);
|
|
|
|
ENSURE(*nodep == NULL);
|
|
}
|
|
|
|
void
|
|
dns_db_transfernode(dns_db_t *db, dns_dbnode_t **sourcep,
|
|
dns_dbnode_t **targetp) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(targetp != NULL && *targetp == NULL);
|
|
/*
|
|
* This doesn't check the implementation magic. If we find that
|
|
* we need such checks in future then this will be done in the
|
|
* method.
|
|
*/
|
|
REQUIRE(sourcep != NULL && *sourcep != NULL);
|
|
|
|
UNUSED(db);
|
|
|
|
if (db->methods->transfernode == NULL) {
|
|
*targetp = *sourcep;
|
|
*sourcep = NULL;
|
|
} else {
|
|
(db->methods->transfernode)(db, sourcep, targetp);
|
|
}
|
|
|
|
ENSURE(*sourcep == NULL);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
|
|
/*
|
|
* Mark as stale all records at 'node' which expire at or before 'now'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
|
|
REQUIRE(node != NULL);
|
|
|
|
return ((db->methods->expirenode)(db, node, now));
|
|
}
|
|
|
|
void
|
|
dns_db_printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
|
|
/*
|
|
* Print a textual representation of the contents of the node to
|
|
* 'out'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(node != NULL);
|
|
|
|
(db->methods->printnode)(db, node, out);
|
|
}
|
|
|
|
/***
|
|
*** DB Iterator Creation
|
|
***/
|
|
|
|
isc_result_t
|
|
dns_db_createiterator(dns_db_t *db, unsigned int flags,
|
|
dns_dbiterator_t **iteratorp) {
|
|
/*
|
|
* Create an iterator for version 'version' of 'db'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(iteratorp != NULL && *iteratorp == NULL);
|
|
REQUIRE((flags & (DNS_DB_NSEC3ONLY | DNS_DB_NONSEC3)) !=
|
|
(DNS_DB_NSEC3ONLY | DNS_DB_NONSEC3));
|
|
|
|
return (db->methods->createiterator(db, flags, iteratorp));
|
|
}
|
|
|
|
/***
|
|
*** Rdataset Methods
|
|
***/
|
|
|
|
isc_result_t
|
|
dns_db_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
|
|
dns_rdatatype_t type, dns_rdatatype_t covers,
|
|
isc_stdtime_t now, dns_rdataset_t *rdataset,
|
|
dns_rdataset_t *sigrdataset) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(node != NULL);
|
|
REQUIRE(DNS_RDATASET_VALID(rdataset));
|
|
REQUIRE(!dns_rdataset_isassociated(rdataset));
|
|
REQUIRE(covers == 0 || type == dns_rdatatype_rrsig);
|
|
REQUIRE(type != dns_rdatatype_any);
|
|
REQUIRE(sigrdataset == NULL ||
|
|
(DNS_RDATASET_VALID(sigrdataset) &&
|
|
!dns_rdataset_isassociated(sigrdataset)));
|
|
|
|
return ((db->methods->findrdataset)(db, node, version, type, covers,
|
|
now, rdataset, sigrdataset));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
|
|
unsigned int options, isc_stdtime_t now,
|
|
dns_rdatasetiter_t **iteratorp) {
|
|
/*
|
|
* Make '*iteratorp' an rdataset iteratator for all rdatasets at
|
|
* 'node' in version 'version' of 'db'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(iteratorp != NULL && *iteratorp == NULL);
|
|
|
|
return ((db->methods->allrdatasets)(db, node, version, options, now,
|
|
iteratorp));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
|
|
isc_stdtime_t now, dns_rdataset_t *rdataset,
|
|
unsigned int options, dns_rdataset_t *addedrdataset) {
|
|
/*
|
|
* Add 'rdataset' to 'node' in version 'version' of 'db'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(node != NULL);
|
|
REQUIRE(((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL) ||
|
|
((db->attributes & DNS_DBATTR_CACHE) != 0 && version == NULL &&
|
|
(options & DNS_DBADD_MERGE) == 0));
|
|
REQUIRE((options & DNS_DBADD_EXACT) == 0 ||
|
|
(options & DNS_DBADD_MERGE) != 0);
|
|
REQUIRE(DNS_RDATASET_VALID(rdataset));
|
|
REQUIRE(dns_rdataset_isassociated(rdataset));
|
|
REQUIRE(rdataset->rdclass == db->rdclass);
|
|
REQUIRE(addedrdataset == NULL ||
|
|
(DNS_RDATASET_VALID(addedrdataset) &&
|
|
!dns_rdataset_isassociated(addedrdataset)));
|
|
|
|
return ((db->methods->addrdataset)(db, node, version, now, rdataset,
|
|
options, addedrdataset));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_subtractrdataset(dns_db_t *db, dns_dbnode_t *node,
|
|
dns_dbversion_t *version, dns_rdataset_t *rdataset,
|
|
unsigned int options, dns_rdataset_t *newrdataset) {
|
|
/*
|
|
* Remove any rdata in 'rdataset' from 'node' in version 'version' of
|
|
* 'db'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(node != NULL);
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL);
|
|
REQUIRE(DNS_RDATASET_VALID(rdataset));
|
|
REQUIRE(dns_rdataset_isassociated(rdataset));
|
|
REQUIRE(rdataset->rdclass == db->rdclass);
|
|
REQUIRE(newrdataset == NULL ||
|
|
(DNS_RDATASET_VALID(newrdataset) &&
|
|
!dns_rdataset_isassociated(newrdataset)));
|
|
|
|
return ((db->methods->subtractrdataset)(db, node, version, rdataset,
|
|
options, newrdataset));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_deleterdataset(dns_db_t *db, dns_dbnode_t *node,
|
|
dns_dbversion_t *version, dns_rdatatype_t type,
|
|
dns_rdatatype_t covers) {
|
|
/*
|
|
* Make it so that no rdataset of type 'type' exists at 'node' in
|
|
* version version 'version' of 'db'.
|
|
*/
|
|
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(node != NULL);
|
|
REQUIRE(((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL) ||
|
|
((db->attributes & DNS_DBATTR_CACHE) != 0 && version == NULL));
|
|
|
|
return ((db->methods->deleterdataset)(db, node, version, type, covers));
|
|
}
|
|
|
|
void
|
|
dns_db_overmem(dns_db_t *db, bool overmem) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
(db->methods->overmem)(db, overmem);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_getsoaserial(dns_db_t *db, dns_dbversion_t *ver, uint32_t *serialp) {
|
|
isc_result_t result;
|
|
dns_dbnode_t *node = NULL;
|
|
dns_rdataset_t rdataset;
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
isc_buffer_t buffer;
|
|
|
|
REQUIRE(dns_db_iszone(db) || dns_db_isstub(db));
|
|
|
|
result = dns_db_findnode(db, dns_db_origin(db), false, &node);
|
|
if (result != ISC_R_SUCCESS) {
|
|
return (result);
|
|
}
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa, 0,
|
|
(isc_stdtime_t)0, &rdataset, NULL);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto freenode;
|
|
}
|
|
|
|
result = dns_rdataset_first(&rdataset);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto freerdataset;
|
|
}
|
|
dns_rdataset_current(&rdataset, &rdata);
|
|
result = dns_rdataset_next(&rdataset);
|
|
INSIST(result == ISC_R_NOMORE);
|
|
|
|
INSIST(rdata.length > 20);
|
|
isc_buffer_init(&buffer, rdata.data, rdata.length);
|
|
isc_buffer_add(&buffer, rdata.length);
|
|
isc_buffer_forward(&buffer, rdata.length - 20);
|
|
*serialp = isc_buffer_getuint32(&buffer);
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
freerdataset:
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
freenode:
|
|
dns_db_detachnode(db, &node);
|
|
return (result);
|
|
}
|
|
|
|
unsigned int
|
|
dns_db_nodecount(dns_db_t *db, dns_dbtree_t tree) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
return ((db->methods->nodecount)(db, tree));
|
|
}
|
|
|
|
size_t
|
|
dns_db_hashsize(dns_db_t *db) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
if (db->methods->hashsize == NULL) {
|
|
return (0);
|
|
}
|
|
|
|
return ((db->methods->hashsize)(db));
|
|
}
|
|
|
|
void
|
|
dns_db_settask(dns_db_t *db, isc_task_t *task) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
(db->methods->settask)(db, task);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_register(const char *name, dns_dbcreatefunc_t create, void *driverarg,
|
|
isc_mem_t *mctx, dns_dbimplementation_t **dbimp) {
|
|
dns_dbimplementation_t *imp;
|
|
|
|
REQUIRE(name != NULL);
|
|
REQUIRE(dbimp != NULL && *dbimp == NULL);
|
|
|
|
RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
|
|
|
|
RWLOCK(&implock, isc_rwlocktype_write);
|
|
imp = impfind(name);
|
|
if (imp != NULL) {
|
|
RWUNLOCK(&implock, isc_rwlocktype_write);
|
|
return (ISC_R_EXISTS);
|
|
}
|
|
|
|
imp = isc_mem_get(mctx, sizeof(dns_dbimplementation_t));
|
|
imp->name = name;
|
|
imp->create = create;
|
|
imp->mctx = NULL;
|
|
imp->driverarg = driverarg;
|
|
isc_mem_attach(mctx, &imp->mctx);
|
|
ISC_LINK_INIT(imp, link);
|
|
ISC_LIST_APPEND(implementations, imp, link);
|
|
RWUNLOCK(&implock, isc_rwlocktype_write);
|
|
|
|
*dbimp = imp;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
void
|
|
dns_db_unregister(dns_dbimplementation_t **dbimp) {
|
|
dns_dbimplementation_t *imp;
|
|
|
|
REQUIRE(dbimp != NULL && *dbimp != NULL);
|
|
|
|
RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
|
|
|
|
imp = *dbimp;
|
|
*dbimp = NULL;
|
|
RWLOCK(&implock, isc_rwlocktype_write);
|
|
ISC_LIST_UNLINK(implementations, imp, link);
|
|
isc_mem_putanddetach(&imp->mctx, imp, sizeof(dns_dbimplementation_t));
|
|
RWUNLOCK(&implock, isc_rwlocktype_write);
|
|
ENSURE(*dbimp == NULL);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(dns_db_iszone(db));
|
|
REQUIRE(nodep != NULL && *nodep == NULL);
|
|
|
|
if (db->methods->getoriginnode != NULL) {
|
|
return ((db->methods->getoriginnode)(db, nodep));
|
|
}
|
|
|
|
return (ISC_R_NOTFOUND);
|
|
}
|
|
|
|
dns_stats_t *
|
|
dns_db_getrrsetstats(dns_db_t *db) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
if (db->methods->getrrsetstats != NULL) {
|
|
return ((db->methods->getrrsetstats)(db));
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_setcachestats(dns_db_t *db, isc_stats_t *stats) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
|
|
if (db->methods->setcachestats != NULL) {
|
|
return ((db->methods->setcachestats)(db, stats));
|
|
}
|
|
|
|
return (ISC_R_NOTIMPLEMENTED);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
|
|
dns_hash_t *hash, uint8_t *flags,
|
|
uint16_t *iterations, unsigned char *salt,
|
|
size_t *salt_length) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(dns_db_iszone(db));
|
|
|
|
if (db->methods->getnsec3parameters != NULL) {
|
|
return ((db->methods->getnsec3parameters)(db, version, hash,
|
|
flags, iterations,
|
|
salt, salt_length));
|
|
}
|
|
|
|
return (ISC_R_NOTFOUND);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records,
|
|
uint64_t *bytes) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE(dns_db_iszone(db));
|
|
|
|
if (db->methods->getsize != NULL) {
|
|
return ((db->methods->getsize)(db, version, records, bytes));
|
|
}
|
|
|
|
return (ISC_R_NOTFOUND);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
|
|
isc_stdtime_t resign) {
|
|
if (db->methods->setsigningtime != NULL) {
|
|
return ((db->methods->setsigningtime)(db, rdataset, resign));
|
|
}
|
|
return (ISC_R_NOTIMPLEMENTED);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
|
|
dns_name_t *name) {
|
|
if (db->methods->getsigningtime != NULL) {
|
|
return ((db->methods->getsigningtime)(db, rdataset, name));
|
|
}
|
|
return (ISC_R_NOTFOUND);
|
|
}
|
|
|
|
void
|
|
dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset,
|
|
dns_dbversion_t *version) {
|
|
if (db->methods->resigned != NULL) {
|
|
(db->methods->resigned)(db, rdataset, version);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Attach a database to policy zone databases.
|
|
* This should only happen when the caller has already ensured that
|
|
* it is dealing with a database that understands response policy zones.
|
|
*/
|
|
void
|
|
dns_db_rpz_attach(dns_db_t *db, void *rpzs, uint8_t rpz_num) {
|
|
REQUIRE(db->methods->rpz_attach != NULL);
|
|
(db->methods->rpz_attach)(db, rpzs, rpz_num);
|
|
}
|
|
|
|
/*
|
|
* Finish loading a response policy zone.
|
|
*/
|
|
isc_result_t
|
|
dns_db_rpz_ready(dns_db_t *db) {
|
|
if (db->methods->rpz_ready == NULL) {
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
return ((db->methods->rpz_ready)(db));
|
|
}
|
|
|
|
/*
|
|
* Attach a notify-on-update function the database
|
|
*/
|
|
isc_result_t
|
|
dns_db_updatenotify_register(dns_db_t *db, dns_dbupdate_callback_t fn,
|
|
void *fn_arg) {
|
|
dns_dbonupdatelistener_t *listener;
|
|
|
|
REQUIRE(db != NULL);
|
|
REQUIRE(fn != NULL);
|
|
|
|
for (listener = ISC_LIST_HEAD(db->update_listeners); listener != NULL;
|
|
listener = ISC_LIST_NEXT(listener, link))
|
|
{
|
|
if ((listener->onupdate == fn) &&
|
|
(listener->onupdate_arg == fn_arg))
|
|
{
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
}
|
|
|
|
listener = isc_mem_get(db->mctx, sizeof(dns_dbonupdatelistener_t));
|
|
|
|
listener->onupdate = fn;
|
|
listener->onupdate_arg = fn_arg;
|
|
|
|
ISC_LINK_INIT(listener, link);
|
|
ISC_LIST_APPEND(db->update_listeners, listener, link);
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_updatenotify_unregister(dns_db_t *db, dns_dbupdate_callback_t fn,
|
|
void *fn_arg) {
|
|
dns_dbonupdatelistener_t *listener;
|
|
|
|
REQUIRE(db != NULL);
|
|
|
|
for (listener = ISC_LIST_HEAD(db->update_listeners); listener != NULL;
|
|
listener = ISC_LIST_NEXT(listener, link))
|
|
{
|
|
if ((listener->onupdate == fn) &&
|
|
(listener->onupdate_arg == fn_arg))
|
|
{
|
|
ISC_LIST_UNLINK(db->update_listeners, listener, link);
|
|
isc_mem_put(db->mctx, listener,
|
|
sizeof(dns_dbonupdatelistener_t));
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
}
|
|
|
|
return (ISC_R_NOTFOUND);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name) {
|
|
REQUIRE(db != NULL);
|
|
REQUIRE(node != NULL);
|
|
REQUIRE(name != NULL);
|
|
|
|
if (db->methods->nodefullname == NULL) {
|
|
return (ISC_R_NOTIMPLEMENTED);
|
|
}
|
|
return ((db->methods->nodefullname)(db, node, name));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_setservestalettl(dns_db_t *db, dns_ttl_t ttl) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
|
|
|
|
if (db->methods->setservestalettl != NULL) {
|
|
return ((db->methods->setservestalettl)(db, ttl));
|
|
}
|
|
return (ISC_R_NOTIMPLEMENTED);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_getservestalettl(dns_db_t *db, dns_ttl_t *ttl) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
|
|
|
|
if (db->methods->getservestalettl != NULL) {
|
|
return ((db->methods->getservestalettl)(db, ttl));
|
|
}
|
|
return (ISC_R_NOTIMPLEMENTED);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_setservestalerefresh(dns_db_t *db, uint32_t interval) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
|
|
|
|
if (db->methods->setservestalerefresh != NULL) {
|
|
return ((db->methods->setservestalerefresh)(db, interval));
|
|
}
|
|
return (ISC_R_NOTIMPLEMENTED);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_getservestalerefresh(dns_db_t *db, uint32_t *interval) {
|
|
REQUIRE(DNS_DB_VALID(db));
|
|
REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
|
|
|
|
if (db->methods->getservestalerefresh != NULL) {
|
|
return ((db->methods->getservestalerefresh)(db, interval));
|
|
}
|
|
return (ISC_R_NOTIMPLEMENTED);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_db_setgluecachestats(dns_db_t *db, isc_stats_t *stats) {
|
|
REQUIRE(dns_db_iszone(db));
|
|
REQUIRE(stats != NULL);
|
|
|
|
if (db->methods->setgluecachestats != NULL) {
|
|
return ((db->methods->setgluecachestats)(db, stats));
|
|
}
|
|
|
|
return (ISC_R_NOTIMPLEMENTED);
|
|
}
|