diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
commit | 45d6379135504814ab723b57f0eb8be23393a51d (patch) | |
tree | d4f2ec4acca824a8446387a758b0ce4238a4dffa /lib/dns/sdlz.c | |
parent | Initial commit. (diff) | |
download | bind9-45d6379135504814ab723b57f0eb8be23393a51d.tar.xz bind9-45d6379135504814ab723b57f0eb8be23393a51d.zip |
Adding upstream version 1:9.16.44.upstream/1%9.16.44upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/dns/sdlz.c')
-rw-r--r-- | lib/dns/sdlz.c | 2108 |
1 files changed, 2108 insertions, 0 deletions
diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c new file mode 100644 index 0000000..8ba7aae --- /dev/null +++ b/lib/dns/sdlz.c @@ -0,0 +1,2108 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 AND ISC + * + * 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. + */ + +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE + * USE OR PERFORMANCE OF THIS SOFTWARE. + * + * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was + * conceived and contributed by Rob Butler. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE + * USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*! \file */ + +#include <inttypes.h> +#include <stdbool.h> +#include <string.h> + +#include <isc/buffer.h> +#include <isc/lex.h> +#include <isc/log.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/once.h> +#include <isc/print.h> +#include <isc/region.h> +#include <isc/rwlock.h> +#include <isc/string.h> +#include <isc/util.h> + +#include <dns/callbacks.h> +#include <dns/db.h> +#include <dns/dbiterator.h> +#include <dns/dlz.h> +#include <dns/fixedname.h> +#include <dns/log.h> +#include <dns/master.h> +#include <dns/rdata.h> +#include <dns/rdatalist.h> +#include <dns/rdataset.h> +#include <dns/rdatasetiter.h> +#include <dns/rdatatype.h> +#include <dns/result.h> +#include <dns/sdlz.h> +#include <dns/types.h> + +#include "rdatalist_p.h" + +/* + * Private Types + */ + +struct dns_sdlzimplementation { + const dns_sdlzmethods_t *methods; + isc_mem_t *mctx; + void *driverarg; + unsigned int flags; + isc_mutex_t driverlock; + dns_dlzimplementation_t *dlz_imp; +}; + +struct dns_sdlz_db { + /* Unlocked */ + dns_db_t common; + void *dbdata; + dns_sdlzimplementation_t *dlzimp; + + /* Atomic */ + isc_refcount_t references; + + /* Locked */ + dns_dbversion_t *future_version; + int dummy_version; +}; + +struct dns_sdlzlookup { + /* Unlocked */ + unsigned int magic; + dns_sdlz_db_t *sdlz; + ISC_LIST(dns_rdatalist_t) lists; + ISC_LIST(isc_buffer_t) buffers; + dns_name_t *name; + ISC_LINK(dns_sdlzlookup_t) link; + dns_rdatacallbacks_t callbacks; + + /* Atomic */ + isc_refcount_t references; +}; + +typedef struct dns_sdlzlookup dns_sdlznode_t; + +struct dns_sdlzallnodes { + dns_dbiterator_t common; + ISC_LIST(dns_sdlznode_t) nodelist; + dns_sdlznode_t *current; + dns_sdlznode_t *origin; +}; + +typedef dns_sdlzallnodes_t sdlz_dbiterator_t; + +typedef struct sdlz_rdatasetiter { + dns_rdatasetiter_t common; + dns_rdatalist_t *current; +} sdlz_rdatasetiter_t; + +#define SDLZDB_MAGIC ISC_MAGIC('D', 'L', 'Z', 'S') + +/* + * Note that "impmagic" is not the first four bytes of the struct, so + * ISC_MAGIC_VALID cannot be used. + */ + +#define VALID_SDLZDB(sdlzdb) \ + ((sdlzdb) != NULL && (sdlzdb)->common.impmagic == SDLZDB_MAGIC) + +#define SDLZLOOKUP_MAGIC ISC_MAGIC('D', 'L', 'Z', 'L') +#define VALID_SDLZLOOKUP(sdlzl) ISC_MAGIC_VALID(sdlzl, SDLZLOOKUP_MAGIC) +#define VALID_SDLZNODE(sdlzn) VALID_SDLZLOOKUP(sdlzn) + +/* These values are taken from RFC 1537 */ +#define SDLZ_DEFAULT_REFRESH 28800U /* 8 hours */ +#define SDLZ_DEFAULT_RETRY 7200U /* 2 hours */ +#define SDLZ_DEFAULT_EXPIRE 604800U /* 7 days */ +#define SDLZ_DEFAULT_MINIMUM 86400U /* 1 day */ + +/* This is a reasonable value */ +#define SDLZ_DEFAULT_TTL (60 * 60 * 24) + +#ifdef __COVERITY__ +#define MAYBE_LOCK(imp) LOCK(&imp->driverlock) +#define MAYBE_UNLOCK(imp) UNLOCK(&imp->driverlock) +#else /* ifdef __COVERITY__ */ +#define MAYBE_LOCK(imp) \ + do { \ + unsigned int flags = imp->flags; \ + if ((flags & DNS_SDLZFLAG_THREADSAFE) == 0) \ + LOCK(&imp->driverlock); \ + } while (0) + +#define MAYBE_UNLOCK(imp) \ + do { \ + unsigned int flags = imp->flags; \ + if ((flags & DNS_SDLZFLAG_THREADSAFE) == 0) \ + UNLOCK(&imp->driverlock); \ + } while (0) +#endif /* ifdef __COVERITY__ */ + +/* + * Forward references. + */ +static isc_result_t +getnodedata(dns_db_t *db, const dns_name_t *name, bool create, + unsigned int options, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo, dns_dbnode_t **nodep); + +static void +list_tordataset(dns_rdatalist_t *rdatalist, dns_db_t *db, dns_dbnode_t *node, + dns_rdataset_t *rdataset); + +static void +detachnode(dns_db_t *db, dns_dbnode_t **targetp); + +static void +dbiterator_destroy(dns_dbiterator_t **iteratorp); +static isc_result_t +dbiterator_first(dns_dbiterator_t *iterator); +static isc_result_t +dbiterator_last(dns_dbiterator_t *iterator); +static isc_result_t +dbiterator_seek(dns_dbiterator_t *iterator, const dns_name_t *name); +static isc_result_t +dbiterator_prev(dns_dbiterator_t *iterator); +static isc_result_t +dbiterator_next(dns_dbiterator_t *iterator); +static isc_result_t +dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, + dns_name_t *name); +static isc_result_t +dbiterator_pause(dns_dbiterator_t *iterator); +static isc_result_t +dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name); + +static dns_dbiteratormethods_t dbiterator_methods = { + dbiterator_destroy, dbiterator_first, dbiterator_last, + dbiterator_seek, dbiterator_prev, dbiterator_next, + dbiterator_current, dbiterator_pause, dbiterator_origin +}; + +/* + * Utility functions + */ + +/* + * Log a message at the given level + */ +static void +sdlz_log(int level, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, + ISC_LOG_DEBUG(level), fmt, ap); + va_end(ap); +} + +/*% Converts the input string to lowercase, in place. */ +static void +dns_sdlz_tolower(char *str) { + unsigned int len = strlen(str); + unsigned int i; + + for (i = 0; i < len; i++) { + if (str[i] >= 'A' && str[i] <= 'Z') { + str[i] += 32; + } + } +} + +static unsigned int +initial_size(const char *data) { + unsigned int len = (strlen(data) / 64) + 1; + return (len * 64 + 64); +} + +/* + * Rdataset Iterator Methods. These methods were "borrowed" from the SDB + * driver interface. See the SDB driver interface documentation for more info. + */ + +static void +rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) { + sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)(*iteratorp); + + detachnode(sdlziterator->common.db, &sdlziterator->common.node); + isc_mem_put(sdlziterator->common.db->mctx, sdlziterator, + sizeof(sdlz_rdatasetiter_t)); + *iteratorp = NULL; +} + +static isc_result_t +rdatasetiter_first(dns_rdatasetiter_t *iterator) { + sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)iterator->node; + + if (ISC_LIST_EMPTY(sdlznode->lists)) { + return (ISC_R_NOMORE); + } + sdlziterator->current = ISC_LIST_HEAD(sdlznode->lists); + return (ISC_R_SUCCESS); +} + +static isc_result_t +rdatasetiter_next(dns_rdatasetiter_t *iterator) { + sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator; + + sdlziterator->current = ISC_LIST_NEXT(sdlziterator->current, link); + if (sdlziterator->current == NULL) { + return (ISC_R_NOMORE); + } else { + return (ISC_R_SUCCESS); + } +} + +static void +rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) { + sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator; + + list_tordataset(sdlziterator->current, iterator->db, iterator->node, + rdataset); +} + +static dns_rdatasetitermethods_t rdatasetiter_methods = { + rdatasetiter_destroy, rdatasetiter_first, rdatasetiter_next, + rdatasetiter_current +}; + +/* + * DB routines. These methods were "borrowed" from the SDB driver interface. + * See the SDB driver interface documentation for more info. + */ + +static void +attach(dns_db_t *source, dns_db_t **targetp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)source; + + REQUIRE(VALID_SDLZDB(sdlz)); + + isc_refcount_increment(&sdlz->references); + + *targetp = source; +} + +static void +destroy(dns_sdlz_db_t *sdlz) { + sdlz->common.magic = 0; + sdlz->common.impmagic = 0; + + dns_name_free(&sdlz->common.origin, sdlz->common.mctx); + + isc_refcount_destroy(&sdlz->references); + isc_mem_putanddetach(&sdlz->common.mctx, sdlz, sizeof(dns_sdlz_db_t)); +} + +static void +detach(dns_db_t **dbp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)(*dbp); + + REQUIRE(VALID_SDLZDB(sdlz)); + + *dbp = NULL; + + if (isc_refcount_decrement(&sdlz->references) == 1) { + destroy(sdlz); + } +} + +static isc_result_t +beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) { + UNUSED(db); + UNUSED(callbacks); + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) { + UNUSED(db); + UNUSED(callbacks); + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +dump(dns_db_t *db, dns_dbversion_t *version, const char *filename, + dns_masterformat_t masterformat) { + UNUSED(db); + UNUSED(version); + UNUSED(filename); + UNUSED(masterformat); + return (ISC_R_NOTIMPLEMENTED); +} + +static void +currentversion(dns_db_t *db, dns_dbversion_t **versionp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(versionp != NULL && *versionp == NULL); + + *versionp = (void *)&sdlz->dummy_version; + return; +} + +static isc_result_t +newversion(dns_db_t *db, dns_dbversion_t **versionp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + char origin[DNS_NAME_MAXTEXT + 1]; + isc_result_t result; + + REQUIRE(VALID_SDLZDB(sdlz)); + + if (sdlz->dlzimp->methods->newversion == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + dns_name_format(&sdlz->common.origin, origin, sizeof(origin)); + + result = sdlz->dlzimp->methods->newversion( + origin, sdlz->dlzimp->driverarg, sdlz->dbdata, versionp); + if (result != ISC_R_SUCCESS) { + sdlz_log(ISC_LOG_ERROR, + "sdlz newversion on origin %s failed : %s", origin, + isc_result_totext(result)); + return (result); + } + + sdlz->future_version = *versionp; + return (ISC_R_SUCCESS); +} + +static void +attachversion(dns_db_t *db, dns_dbversion_t *source, + dns_dbversion_t **targetp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(source != NULL && source == (void *)&sdlz->dummy_version); + + *targetp = source; +} + +static void +closeversion(dns_db_t *db, dns_dbversion_t **versionp, bool commit) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + char origin[DNS_NAME_MAXTEXT + 1]; + + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(versionp != NULL); + + if (*versionp == (void *)&sdlz->dummy_version) { + *versionp = NULL; + return; + } + + REQUIRE(*versionp == sdlz->future_version); + REQUIRE(sdlz->dlzimp->methods->closeversion != NULL); + + dns_name_format(&sdlz->common.origin, origin, sizeof(origin)); + + sdlz->dlzimp->methods->closeversion(origin, commit, + sdlz->dlzimp->driverarg, + sdlz->dbdata, versionp); + if (*versionp != NULL) { + sdlz_log(ISC_LOG_ERROR, "sdlz closeversion on origin %s failed", + origin); + } + + sdlz->future_version = NULL; +} + +static isc_result_t +createnode(dns_sdlz_db_t *sdlz, dns_sdlznode_t **nodep) { + dns_sdlznode_t *node; + + node = isc_mem_get(sdlz->common.mctx, sizeof(dns_sdlznode_t)); + + node->sdlz = NULL; + attach((dns_db_t *)sdlz, (dns_db_t **)&node->sdlz); + ISC_LIST_INIT(node->lists); + ISC_LIST_INIT(node->buffers); + ISC_LINK_INIT(node, link); + node->name = NULL; + dns_rdatacallbacks_init(&node->callbacks); + + isc_refcount_init(&node->references, 1); + node->magic = SDLZLOOKUP_MAGIC; + + *nodep = node; + return (ISC_R_SUCCESS); +} + +static void +destroynode(dns_sdlznode_t *node) { + dns_rdatalist_t *list; + dns_rdata_t *rdata; + isc_buffer_t *b; + dns_sdlz_db_t *sdlz; + dns_db_t *db; + isc_mem_t *mctx; + + isc_refcount_destroy(&node->references); + + sdlz = node->sdlz; + mctx = sdlz->common.mctx; + + while (!ISC_LIST_EMPTY(node->lists)) { + list = ISC_LIST_HEAD(node->lists); + while (!ISC_LIST_EMPTY(list->rdata)) { + rdata = ISC_LIST_HEAD(list->rdata); + ISC_LIST_UNLINK(list->rdata, rdata, link); + isc_mem_put(mctx, rdata, sizeof(dns_rdata_t)); + } + ISC_LIST_UNLINK(node->lists, list, link); + isc_mem_put(mctx, list, sizeof(dns_rdatalist_t)); + } + + while (!ISC_LIST_EMPTY(node->buffers)) { + b = ISC_LIST_HEAD(node->buffers); + ISC_LIST_UNLINK(node->buffers, b, link); + isc_buffer_free(&b); + } + + if (node->name != NULL) { + dns_name_free(node->name, mctx); + isc_mem_put(mctx, node->name, sizeof(dns_name_t)); + } + + node->magic = 0; + isc_mem_put(mctx, node, sizeof(dns_sdlznode_t)); + db = &sdlz->common; + detach(&db); +} + +static isc_result_t +getnodedata(dns_db_t *db, const dns_name_t *name, bool create, + unsigned int options, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo, dns_dbnode_t **nodep) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_sdlznode_t *node = NULL; + isc_result_t result; + isc_buffer_t b; + char namestr[DNS_NAME_MAXTEXT + 1]; + isc_buffer_t b2; + char zonestr[DNS_NAME_MAXTEXT + 1]; + bool isorigin; + dns_sdlzauthorityfunc_t authority; + + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(nodep != NULL && *nodep == NULL); + + if (sdlz->dlzimp->methods->newversion == NULL) { + REQUIRE(!create); + } + + isc_buffer_init(&b, namestr, sizeof(namestr)); + if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVEOWNER) != 0) { + dns_name_t relname; + unsigned int labels; + + labels = dns_name_countlabels(name) - + dns_name_countlabels(&sdlz->common.origin); + dns_name_init(&relname, NULL); + dns_name_getlabelsequence(name, 0, labels, &relname); + result = dns_name_totext(&relname, true, &b); + if (result != ISC_R_SUCCESS) { + return (result); + } + } else { + result = dns_name_totext(name, true, &b); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + isc_buffer_putuint8(&b, 0); + + isc_buffer_init(&b2, zonestr, sizeof(zonestr)); + result = dns_name_totext(&sdlz->common.origin, true, &b2); + if (result != ISC_R_SUCCESS) { + return (result); + } + isc_buffer_putuint8(&b2, 0); + + result = createnode(sdlz, &node); + if (result != ISC_R_SUCCESS) { + return (result); + } + + isorigin = dns_name_equal(name, &sdlz->common.origin); + + /* make sure strings are always lowercase */ + dns_sdlz_tolower(zonestr); + dns_sdlz_tolower(namestr); + + MAYBE_LOCK(sdlz->dlzimp); + + /* try to lookup the host (namestr) */ + result = sdlz->dlzimp->methods->lookup( + zonestr, namestr, sdlz->dlzimp->driverarg, sdlz->dbdata, node, + methods, clientinfo); + + /* + * If the name was not found and DNS_DBFIND_NOWILD is not + * set, then we try to find a wildcard entry. + * + * If DNS_DBFIND_NOZONECUT is set and there are multiple + * levels between the host and the zone origin, we also look + * for wildcards at each level. + */ + if (result == ISC_R_NOTFOUND && !create && + (options & DNS_DBFIND_NOWILD) == 0) + { + unsigned int i, dlabels, nlabels; + + nlabels = dns_name_countlabels(name); + dlabels = nlabels - dns_name_countlabels(&sdlz->common.origin); + for (i = 0; i < dlabels; i++) { + char wildstr[DNS_NAME_MAXTEXT + 1]; + dns_fixedname_t fixed; + const dns_name_t *wild; + + dns_fixedname_init(&fixed); + if (i == dlabels - 1) { + wild = dns_wildcardname; + } else { + dns_name_t *fname; + fname = dns_fixedname_name(&fixed); + dns_name_getlabelsequence( + name, i + 1, dlabels - i - 1, fname); + result = dns_name_concatenate( + dns_wildcardname, fname, fname, NULL); + if (result != ISC_R_SUCCESS) { + MAYBE_UNLOCK(sdlz->dlzimp); + return (result); + } + wild = fname; + } + + isc_buffer_init(&b, wildstr, sizeof(wildstr)); + result = dns_name_totext(wild, true, &b); + if (result != ISC_R_SUCCESS) { + MAYBE_UNLOCK(sdlz->dlzimp); + return (result); + } + isc_buffer_putuint8(&b, 0); + + result = sdlz->dlzimp->methods->lookup( + zonestr, wildstr, sdlz->dlzimp->driverarg, + sdlz->dbdata, node, methods, clientinfo); + if (result == ISC_R_SUCCESS) { + break; + } + } + } + + MAYBE_UNLOCK(sdlz->dlzimp); + + if (result == ISC_R_NOTFOUND && (isorigin || create)) { + result = ISC_R_SUCCESS; + } + + if (result != ISC_R_SUCCESS) { + isc_refcount_decrementz(&node->references); + destroynode(node); + return (result); + } + + if (isorigin && sdlz->dlzimp->methods->authority != NULL) { + MAYBE_LOCK(sdlz->dlzimp); + authority = sdlz->dlzimp->methods->authority; + result = (*authority)(zonestr, sdlz->dlzimp->driverarg, + sdlz->dbdata, node); + MAYBE_UNLOCK(sdlz->dlzimp); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) { + isc_refcount_decrementz(&node->references); + destroynode(node); + return (result); + } + } + + if (node->name == NULL) { + node->name = isc_mem_get(sdlz->common.mctx, sizeof(dns_name_t)); + dns_name_init(node->name, NULL); + dns_name_dup(name, sdlz->common.mctx, node->name); + } + + *nodep = node; + return (ISC_R_SUCCESS); +} + +static isc_result_t +findnodeext(dns_db_t *db, const dns_name_t *name, bool create, + dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo, + dns_dbnode_t **nodep) { + return (getnodedata(db, name, create, 0, methods, clientinfo, nodep)); +} + +static isc_result_t +findnode(dns_db_t *db, const dns_name_t *name, bool create, + dns_dbnode_t **nodep) { + return (getnodedata(db, name, create, 0, NULL, NULL, nodep)); +} + +static isc_result_t +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) { + UNUSED(db); + UNUSED(name); + UNUSED(options); + UNUSED(now); + UNUSED(nodep); + UNUSED(foundname); + UNUSED(dcname); + UNUSED(rdataset); + UNUSED(sigrdataset); + + return (ISC_R_NOTIMPLEMENTED); +} + +static void +attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_sdlznode_t *node = (dns_sdlznode_t *)source; + + REQUIRE(VALID_SDLZDB(sdlz)); + + UNUSED(sdlz); + + isc_refcount_increment(&node->references); + + *targetp = source; +} + +static void +detachnode(dns_db_t *db, dns_dbnode_t **targetp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_sdlznode_t *node; + + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(targetp != NULL && *targetp != NULL); + + UNUSED(sdlz); + + node = (dns_sdlznode_t *)(*targetp); + *targetp = NULL; + + if (isc_refcount_decrement(&node->references) == 1) { + destroynode(node); + } +} + +static isc_result_t +expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) { + UNUSED(db); + UNUSED(node); + UNUSED(now); + UNREACHABLE(); +} + +static void +printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) { + UNUSED(db); + UNUSED(node); + UNUSED(out); + return; +} + +static isc_result_t +createiterator(dns_db_t *db, unsigned int options, + dns_dbiterator_t **iteratorp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + sdlz_dbiterator_t *sdlziter; + isc_result_t result; + isc_buffer_t b; + char zonestr[DNS_NAME_MAXTEXT + 1]; + + REQUIRE(VALID_SDLZDB(sdlz)); + + if (sdlz->dlzimp->methods->allnodes == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + if ((options & DNS_DB_NSEC3ONLY) != 0 || + (options & DNS_DB_NONSEC3) != 0) + { + return (ISC_R_NOTIMPLEMENTED); + } + + isc_buffer_init(&b, zonestr, sizeof(zonestr)); + result = dns_name_totext(&sdlz->common.origin, true, &b); + if (result != ISC_R_SUCCESS) { + return (result); + } + isc_buffer_putuint8(&b, 0); + + sdlziter = isc_mem_get(sdlz->common.mctx, sizeof(sdlz_dbiterator_t)); + + sdlziter->common.methods = &dbiterator_methods; + sdlziter->common.db = NULL; + dns_db_attach(db, &sdlziter->common.db); + sdlziter->common.relative_names = ((options & DNS_DB_RELATIVENAMES) != + 0); + sdlziter->common.magic = DNS_DBITERATOR_MAGIC; + ISC_LIST_INIT(sdlziter->nodelist); + sdlziter->current = NULL; + sdlziter->origin = NULL; + + /* make sure strings are always lowercase */ + dns_sdlz_tolower(zonestr); + + MAYBE_LOCK(sdlz->dlzimp); + result = sdlz->dlzimp->methods->allnodes( + zonestr, sdlz->dlzimp->driverarg, sdlz->dbdata, sdlziter); + MAYBE_UNLOCK(sdlz->dlzimp); + if (result != ISC_R_SUCCESS) { + dns_dbiterator_t *iter = &sdlziter->common; + dbiterator_destroy(&iter); + return (result); + } + + if (sdlziter->origin != NULL) { + ISC_LIST_UNLINK(sdlziter->nodelist, sdlziter->origin, link); + ISC_LIST_PREPEND(sdlziter->nodelist, sdlziter->origin, link); + } + + *iteratorp = (dns_dbiterator_t *)sdlziter; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +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(VALID_SDLZNODE(node)); + dns_rdatalist_t *list; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)node; + + UNUSED(db); + UNUSED(version); + UNUSED(covers); + UNUSED(now); + UNUSED(sigrdataset); + + if (type == dns_rdatatype_sig || type == dns_rdatatype_rrsig) { + return (ISC_R_NOTIMPLEMENTED); + } + + list = ISC_LIST_HEAD(sdlznode->lists); + while (list != NULL) { + if (list->type == type) { + break; + } + list = ISC_LIST_NEXT(list, link); + } + if (list == NULL) { + return (ISC_R_NOTFOUND); + } + + list_tordataset(list, db, node, rdataset); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +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) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_dbnode_t *node = NULL; + dns_fixedname_t fname; + dns_rdataset_t xrdataset; + dns_name_t *xname; + unsigned int nlabels, olabels; + isc_result_t result; + unsigned int i; + + REQUIRE(VALID_SDLZDB(sdlz)); + REQUIRE(nodep == NULL || *nodep == NULL); + REQUIRE(version == NULL || version == (void *)&sdlz->dummy_version || + version == sdlz->future_version); + + UNUSED(sdlz); + + if (!dns_name_issubdomain(name, &db->origin)) { + return (DNS_R_NXDOMAIN); + } + + olabels = dns_name_countlabels(&db->origin); + nlabels = dns_name_countlabels(name); + + xname = dns_fixedname_initname(&fname); + + if (rdataset == NULL) { + dns_rdataset_init(&xrdataset); + rdataset = &xrdataset; + } + + result = DNS_R_NXDOMAIN; + + /* + * If we're not walking down searching for zone + * cuts, we can cut straight to the chase + */ + if ((options & DNS_DBFIND_NOZONECUT) != 0) { + i = nlabels; + goto search; + } + + for (i = olabels; i <= nlabels; i++) { + search: + /* + * Look up the next label. + */ + dns_name_getlabelsequence(name, nlabels - i, i, xname); + result = getnodedata(db, xname, false, options, methods, + clientinfo, &node); + if (result == ISC_R_NOTFOUND) { + result = DNS_R_NXDOMAIN; + continue; + } else if (result != ISC_R_SUCCESS) { + break; + } + + /* + * Look for a DNAME at the current label, unless this is + * the qname. + */ + if (i < nlabels) { + result = findrdataset(db, node, version, + dns_rdatatype_dname, 0, now, + rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) { + result = DNS_R_DNAME; + break; + } + } + + /* + * Look for an NS at the current label, unless this is the + * origin, glue is ok, or there are known to be no zone cuts. + */ + if (i != olabels && (options & DNS_DBFIND_GLUEOK) == 0 && + (options & DNS_DBFIND_NOZONECUT) == 0) + { + result = findrdataset(db, node, version, + dns_rdatatype_ns, 0, now, + rdataset, sigrdataset); + + if (result == ISC_R_SUCCESS && i == nlabels && + type == dns_rdatatype_any) + { + result = DNS_R_ZONECUT; + dns_rdataset_disassociate(rdataset); + if (sigrdataset != NULL && + dns_rdataset_isassociated(sigrdataset)) + { + dns_rdataset_disassociate(sigrdataset); + } + break; + } else if (result == ISC_R_SUCCESS) { + result = DNS_R_DELEGATION; + break; + } + } + + /* + * If the current name is not the qname, add another label + * and try again. + */ + if (i < nlabels) { + detachnode(db, &node); + node = NULL; + continue; + } + + /* + * If we're looking for ANY, we're done. + */ + if (type == dns_rdatatype_any) { + result = ISC_R_SUCCESS; + break; + } + + /* + * Look for the qtype. + */ + result = findrdataset(db, node, version, type, 0, now, rdataset, + sigrdataset); + if (result == ISC_R_SUCCESS) { + break; + } + + /* + * Look for a CNAME + */ + if (type != dns_rdatatype_cname) { + result = findrdataset(db, node, version, + dns_rdatatype_cname, 0, now, + rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) { + result = DNS_R_CNAME; + break; + } + } + + result = DNS_R_NXRRSET; + break; + } + + if (rdataset == &xrdataset && dns_rdataset_isassociated(rdataset)) { + dns_rdataset_disassociate(rdataset); + } + + if (foundname != NULL) { + dns_name_copynf(xname, foundname); + } + + if (nodep != NULL) { + *nodep = node; + } else if (node != NULL) { + detachnode(db, &node); + } + + return (result); +} + +static isc_result_t +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) { + return (findext(db, name, version, type, options, now, nodep, foundname, + NULL, NULL, rdataset, sigrdataset)); +} + +static isc_result_t +allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + unsigned int options, isc_stdtime_t now, + dns_rdatasetiter_t **iteratorp) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + sdlz_rdatasetiter_t *iterator; + + REQUIRE(VALID_SDLZDB(sdlz)); + + REQUIRE(version == NULL || version == (void *)&sdlz->dummy_version || + version == sdlz->future_version); + + UNUSED(version); + UNUSED(now); + + iterator = isc_mem_get(db->mctx, sizeof(sdlz_rdatasetiter_t)); + + iterator->common.magic = DNS_RDATASETITER_MAGIC; + iterator->common.methods = &rdatasetiter_methods; + iterator->common.db = db; + iterator->common.node = NULL; + attachnode(db, node, &iterator->common.node); + iterator->common.version = version; + iterator->common.options = options; + iterator->common.now = now; + + *iteratorp = (dns_rdatasetiter_t *)iterator; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +modrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdataset_t *rdataset, unsigned int options, + dns_sdlzmodrdataset_t mod_function) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + dns_master_style_t *style = NULL; + isc_result_t result; + isc_buffer_t *buffer = NULL; + isc_mem_t *mctx; + dns_sdlznode_t *sdlznode; + char *rdatastr = NULL; + char name[DNS_NAME_MAXTEXT + 1]; + + REQUIRE(VALID_SDLZDB(sdlz)); + + if (mod_function == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + sdlznode = (dns_sdlznode_t *)node; + + UNUSED(options); + + dns_name_format(sdlznode->name, name, sizeof(name)); + + mctx = sdlz->common.mctx; + + isc_buffer_allocate(mctx, &buffer, 1024); + + result = dns_master_stylecreate(&style, 0, 0, 0, 0, 0, 0, 1, 0xffffffff, + mctx); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = dns_master_rdatasettotext(sdlznode->name, rdataset, style, + NULL, buffer); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + if (isc_buffer_usedlength(buffer) < 1) { + result = ISC_R_BADADDRESSFORM; + goto cleanup; + } + + rdatastr = isc_buffer_base(buffer); + if (rdatastr == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + rdatastr[isc_buffer_usedlength(buffer) - 1] = 0; + + MAYBE_LOCK(sdlz->dlzimp); + result = mod_function(name, rdatastr, sdlz->dlzimp->driverarg, + sdlz->dbdata, version); + MAYBE_UNLOCK(sdlz->dlzimp); + +cleanup: + isc_buffer_free(&buffer); + if (style != NULL) { + dns_master_styledestroy(&style, mctx); + } + + return (result); +} + +static isc_result_t +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) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + isc_result_t result; + + UNUSED(now); + UNUSED(addedrdataset); + REQUIRE(VALID_SDLZDB(sdlz)); + + if (sdlz->dlzimp->methods->addrdataset == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + result = modrdataset(db, node, version, rdataset, options, + sdlz->dlzimp->methods->addrdataset); + return (result); +} + +static isc_result_t +subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdataset_t *rdataset, unsigned int options, + dns_rdataset_t *newrdataset) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + isc_result_t result; + + UNUSED(newrdataset); + REQUIRE(VALID_SDLZDB(sdlz)); + + if (sdlz->dlzimp->methods->subtractrdataset == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + result = modrdataset(db, node, version, rdataset, options, + sdlz->dlzimp->methods->subtractrdataset); + return (result); +} + +static isc_result_t +deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdatatype_t type, dns_rdatatype_t covers) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + char name[DNS_NAME_MAXTEXT + 1]; + char b_type[DNS_RDATATYPE_FORMATSIZE]; + dns_sdlznode_t *sdlznode; + isc_result_t result; + + UNUSED(covers); + + REQUIRE(VALID_SDLZDB(sdlz)); + + if (sdlz->dlzimp->methods->delrdataset == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + sdlznode = (dns_sdlznode_t *)node; + dns_name_format(sdlznode->name, name, sizeof(name)); + dns_rdatatype_format(type, b_type, sizeof(b_type)); + + MAYBE_LOCK(sdlz->dlzimp); + result = sdlz->dlzimp->methods->delrdataset( + name, b_type, sdlz->dlzimp->driverarg, sdlz->dbdata, version); + MAYBE_UNLOCK(sdlz->dlzimp); + + return (result); +} + +static bool +issecure(dns_db_t *db) { + UNUSED(db); + + return (false); +} + +static unsigned int +nodecount(dns_db_t *db) { + UNUSED(db); + + return (0); +} + +static bool +ispersistent(dns_db_t *db) { + UNUSED(db); + return (true); +} + +static void +overmem(dns_db_t *db, bool over) { + UNUSED(db); + UNUSED(over); +} + +static void +settask(dns_db_t *db, isc_task_t *task) { + UNUSED(db); + UNUSED(task); +} + +/* + * getoriginnode() is used by the update code to find the + * dns_rdatatype_dnskey record for a zone + */ +static isc_result_t +getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) { + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db; + isc_result_t result; + + REQUIRE(VALID_SDLZDB(sdlz)); + if (sdlz->dlzimp->methods->newversion == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + result = getnodedata(db, &sdlz->common.origin, false, 0, NULL, NULL, + nodep); + if (result != ISC_R_SUCCESS) { + sdlz_log(ISC_LOG_ERROR, "sdlz getoriginnode failed: %s", + isc_result_totext(result)); + } + return (result); +} + +static dns_dbmethods_t sdlzdb_methods = { + attach, + detach, + beginload, + endload, + NULL, /* serialize */ + dump, + currentversion, + newversion, + attachversion, + closeversion, + findnode, + find, + findzonecut, + attachnode, + detachnode, + expirenode, + printnode, + createiterator, + findrdataset, + allrdatasets, + addrdataset, + subtractrdataset, + deleterdataset, + issecure, + nodecount, + ispersistent, + overmem, + settask, + getoriginnode, + NULL, /* transfernode */ + NULL, /* getnsec3parameters */ + NULL, /* findnsec3node */ + NULL, /* setsigningtime */ + NULL, /* getsigningtime */ + NULL, /* resigned */ + NULL, /* isdnssec */ + NULL, /* getrrsetstats */ + NULL, /* rpz_attach */ + NULL, /* rpz_ready */ + findnodeext, + findext, + NULL, /* setcachestats */ + NULL, /* hashsize */ + NULL, /* nodefullname */ + NULL, /* getsize */ + NULL, /* setservestalettl */ + NULL, /* getservestalettl */ + NULL, /* setservestalerefresh */ + NULL, /* getservestalerefresh */ + NULL, /* setgluecachestats */ + NULL /* adjusthashsize */ +}; + +/* + * Database Iterator Methods. These methods were "borrowed" from the SDB + * driver interface. See the SDB driver interface documentation for more info. + */ + +static void +dbiterator_destroy(dns_dbiterator_t **iteratorp) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)(*iteratorp); + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)sdlziter->common.db; + + while (!ISC_LIST_EMPTY(sdlziter->nodelist)) { + dns_sdlznode_t *node; + node = ISC_LIST_HEAD(sdlziter->nodelist); + ISC_LIST_UNLINK(sdlziter->nodelist, node, link); + isc_refcount_decrementz(&node->references); + destroynode(node); + } + + dns_db_detach(&sdlziter->common.db); + isc_mem_put(sdlz->common.mctx, sdlziter, sizeof(sdlz_dbiterator_t)); + + *iteratorp = NULL; +} + +static isc_result_t +dbiterator_first(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_HEAD(sdlziter->nodelist); + if (sdlziter->current == NULL) { + return (ISC_R_NOMORE); + } else { + return (ISC_R_SUCCESS); + } +} + +static isc_result_t +dbiterator_last(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_TAIL(sdlziter->nodelist); + if (sdlziter->current == NULL) { + return (ISC_R_NOMORE); + } else { + return (ISC_R_SUCCESS); + } +} + +static isc_result_t +dbiterator_seek(dns_dbiterator_t *iterator, const dns_name_t *name) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_HEAD(sdlziter->nodelist); + while (sdlziter->current != NULL) { + if (dns_name_equal(sdlziter->current->name, name)) { + return (ISC_R_SUCCESS); + } + sdlziter->current = ISC_LIST_NEXT(sdlziter->current, link); + } + return (ISC_R_NOTFOUND); +} + +static isc_result_t +dbiterator_prev(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_PREV(sdlziter->current, link); + if (sdlziter->current == NULL) { + return (ISC_R_NOMORE); + } else { + return (ISC_R_SUCCESS); + } +} + +static isc_result_t +dbiterator_next(dns_dbiterator_t *iterator) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + sdlziter->current = ISC_LIST_NEXT(sdlziter->current, link); + if (sdlziter->current == NULL) { + return (ISC_R_NOMORE); + } else { + return (ISC_R_SUCCESS); + } +} + +static isc_result_t +dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, + dns_name_t *name) { + sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; + + attachnode(iterator->db, sdlziter->current, nodep); + if (name != NULL) { + dns_name_copynf(sdlziter->current->name, name); + return (ISC_R_SUCCESS); + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_pause(dns_dbiterator_t *iterator) { + UNUSED(iterator); + return (ISC_R_SUCCESS); +} + +static isc_result_t +dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) { + UNUSED(iterator); + dns_name_copynf(dns_rootname, name); + return (ISC_R_SUCCESS); +} + +/* + * Rdataset Methods. These methods were "borrowed" from the SDB driver + * interface. See the SDB driver interface documentation for more info. + */ + +static void +disassociate(dns_rdataset_t *rdataset) { + dns_dbnode_t *node = rdataset->private5; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)node; + dns_db_t *db = (dns_db_t *)sdlznode->sdlz; + + detachnode(db, &node); + isc__rdatalist_disassociate(rdataset); +} + +static void +rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) { + dns_dbnode_t *node = source->private5; + dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)node; + dns_db_t *db = (dns_db_t *)sdlznode->sdlz; + dns_dbnode_t *tempdb = NULL; + + isc__rdatalist_clone(source, target); + attachnode(db, node, &tempdb); + source->private5 = tempdb; +} + +static dns_rdatasetmethods_t rdataset_methods = { + disassociate, + isc__rdatalist_first, + isc__rdatalist_next, + isc__rdatalist_current, + rdataset_clone, + isc__rdatalist_count, + isc__rdatalist_addnoqname, + isc__rdatalist_getnoqname, + NULL, /* addclosest */ + NULL, /* getclosest */ + NULL, /* settrust */ + NULL, /* expire */ + NULL, /* clearprefetch */ + NULL, /* setownercase */ + NULL, /* getownercase */ + NULL /* addglue */ +}; + +static void +list_tordataset(dns_rdatalist_t *rdatalist, dns_db_t *db, dns_dbnode_t *node, + dns_rdataset_t *rdataset) { + /* + * The sdlz rdataset is an rdatalist with some additions. + * - private1 & private2 are used by the rdatalist. + * - private3 & private 4 are unused. + * - private5 is the node. + */ + + /* This should never fail. */ + RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == + ISC_R_SUCCESS); + + rdataset->methods = &rdataset_methods; + dns_db_attachnode(db, node, &rdataset->private5); +} + +/* + * SDLZ core methods. This is the core of the new DLZ functionality. + */ + +/*% + * Build a 'bind' database driver structure to be returned by + * either the find zone or the allow zone transfer method. + * This method is only available in this source file, it is + * not made available anywhere else. + */ + +static isc_result_t +dns_sdlzcreateDBP(isc_mem_t *mctx, void *driverarg, void *dbdata, + const dns_name_t *name, dns_rdataclass_t rdclass, + dns_db_t **dbp) { + isc_result_t result; + dns_sdlz_db_t *sdlzdb; + dns_sdlzimplementation_t *imp; + + /* check that things are as we expect */ + REQUIRE(dbp != NULL && *dbp == NULL); + REQUIRE(name != NULL); + + imp = (dns_sdlzimplementation_t *)driverarg; + + /* allocate and zero memory for driver structure */ + sdlzdb = isc_mem_get(mctx, sizeof(dns_sdlz_db_t)); + memset(sdlzdb, 0, sizeof(dns_sdlz_db_t)); + + /* initialize and set origin */ + dns_name_init(&sdlzdb->common.origin, NULL); + result = dns_name_dupwithoffsets(name, mctx, &sdlzdb->common.origin); + if (result != ISC_R_SUCCESS) { + goto mem_cleanup; + } + + /* set the rest of the database structure attributes */ + sdlzdb->dlzimp = imp; + sdlzdb->common.methods = &sdlzdb_methods; + sdlzdb->common.attributes = 0; + sdlzdb->common.rdclass = rdclass; + sdlzdb->common.mctx = NULL; + sdlzdb->dbdata = dbdata; + isc_refcount_init(&sdlzdb->references, 1); + + /* attach to the memory context */ + isc_mem_attach(mctx, &sdlzdb->common.mctx); + + /* mark structure as valid */ + sdlzdb->common.magic = DNS_DB_MAGIC; + sdlzdb->common.impmagic = SDLZDB_MAGIC; + *dbp = (dns_db_t *)sdlzdb; + + return (result); +mem_cleanup: + isc_mem_put(mctx, sdlzdb, sizeof(dns_sdlz_db_t)); + return (result); +} + +static isc_result_t +dns_sdlzallowzonexfr(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, const dns_name_t *name, + const isc_sockaddr_t *clientaddr, dns_db_t **dbp) { + isc_buffer_t b; + isc_buffer_t b2; + char namestr[DNS_NAME_MAXTEXT + 1]; + char clientstr[(sizeof "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255." + "255") + + 1]; + isc_netaddr_t netaddr; + isc_result_t result; + dns_sdlzimplementation_t *imp; + + /* + * Perform checks to make sure data is as we expect it to be. + */ + REQUIRE(driverarg != NULL); + REQUIRE(name != NULL); + REQUIRE(clientaddr != NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + imp = (dns_sdlzimplementation_t *)driverarg; + + /* Convert DNS name to ascii text */ + isc_buffer_init(&b, namestr, sizeof(namestr)); + result = dns_name_totext(name, true, &b); + if (result != ISC_R_SUCCESS) { + return (result); + } + isc_buffer_putuint8(&b, 0); + + /* convert client address to ascii text */ + isc_buffer_init(&b2, clientstr, sizeof(clientstr)); + isc_netaddr_fromsockaddr(&netaddr, clientaddr); + result = isc_netaddr_totext(&netaddr, &b2); + if (result != ISC_R_SUCCESS) { + return (result); + } + isc_buffer_putuint8(&b2, 0); + + /* make sure strings are always lowercase */ + dns_sdlz_tolower(namestr); + dns_sdlz_tolower(clientstr); + + /* Call SDLZ driver's find zone method */ + if (imp->methods->allowzonexfr != NULL) { + isc_result_t rresult = ISC_R_SUCCESS; + + MAYBE_LOCK(imp); + result = imp->methods->allowzonexfr(imp->driverarg, dbdata, + namestr, clientstr); + MAYBE_UNLOCK(imp); + /* + * if zone is supported and transfers are (or might be) + * allowed, build a 'bind' database driver + */ + if (result == ISC_R_SUCCESS || result == ISC_R_DEFAULT) { + rresult = dns_sdlzcreateDBP(mctx, driverarg, dbdata, + name, rdclass, dbp); + } + if (rresult != ISC_R_SUCCESS) { + result = rresult; + } + return (result); + } + + return (ISC_R_NOTIMPLEMENTED); +} + +static isc_result_t +dns_sdlzcreate(isc_mem_t *mctx, const char *dlzname, unsigned int argc, + char *argv[], void *driverarg, void **dbdata) { + dns_sdlzimplementation_t *imp; + isc_result_t result = ISC_R_NOTFOUND; + + /* Write debugging message to log */ + sdlz_log(ISC_LOG_DEBUG(2), "Loading SDLZ driver."); + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(driverarg != NULL); + REQUIRE(dlzname != NULL); + REQUIRE(dbdata != NULL); + UNUSED(mctx); + + imp = driverarg; + + /* If the create method exists, call it. */ + if (imp->methods->create != NULL) { + MAYBE_LOCK(imp); + result = imp->methods->create(dlzname, argc, argv, + imp->driverarg, dbdata); + MAYBE_UNLOCK(imp); + } + + /* Write debugging message to log */ + if (result == ISC_R_SUCCESS) { + sdlz_log(ISC_LOG_DEBUG(2), "SDLZ driver loaded successfully."); + } else { + sdlz_log(ISC_LOG_ERROR, "SDLZ driver failed to load."); + } + + return (result); +} + +static void +dns_sdlzdestroy(void *driverdata, void **dbdata) { + dns_sdlzimplementation_t *imp; + + /* Write debugging message to log */ + sdlz_log(ISC_LOG_DEBUG(2), "Unloading SDLZ driver."); + + imp = driverdata; + + /* If the destroy method exists, call it. */ + if (imp->methods->destroy != NULL) { + MAYBE_LOCK(imp); + imp->methods->destroy(imp->driverarg, dbdata); + MAYBE_UNLOCK(imp); + } +} + +static isc_result_t +dns_sdlzfindzone(void *driverarg, void *dbdata, isc_mem_t *mctx, + dns_rdataclass_t rdclass, const dns_name_t *name, + dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo, + dns_db_t **dbp) { + isc_buffer_t b; + char namestr[DNS_NAME_MAXTEXT + 1]; + isc_result_t result; + dns_sdlzimplementation_t *imp; + + /* + * Perform checks to make sure data is as we expect it to be. + */ + REQUIRE(driverarg != NULL); + REQUIRE(name != NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + imp = (dns_sdlzimplementation_t *)driverarg; + + /* Convert DNS name to ascii text */ + isc_buffer_init(&b, namestr, sizeof(namestr)); + result = dns_name_totext(name, true, &b); + if (result != ISC_R_SUCCESS) { + return (result); + } + isc_buffer_putuint8(&b, 0); + + /* make sure strings are always lowercase */ + dns_sdlz_tolower(namestr); + + /* Call SDLZ driver's find zone method */ + MAYBE_LOCK(imp); + result = imp->methods->findzone(imp->driverarg, dbdata, namestr, + methods, clientinfo); + MAYBE_UNLOCK(imp); + + /* + * if zone is supported build a 'bind' database driver + * structure to return + */ + if (result == ISC_R_SUCCESS) { + result = dns_sdlzcreateDBP(mctx, driverarg, dbdata, name, + rdclass, dbp); + } + + return (result); +} + +static isc_result_t +dns_sdlzconfigure(void *driverarg, void *dbdata, dns_view_t *view, + dns_dlzdb_t *dlzdb) { + isc_result_t result; + dns_sdlzimplementation_t *imp; + + REQUIRE(driverarg != NULL); + + imp = (dns_sdlzimplementation_t *)driverarg; + + /* Call SDLZ driver's configure method */ + if (imp->methods->configure != NULL) { + MAYBE_LOCK(imp); + result = imp->methods->configure(view, dlzdb, imp->driverarg, + dbdata); + MAYBE_UNLOCK(imp); + } else { + result = ISC_R_SUCCESS; + } + + return (result); +} + +static bool +dns_sdlzssumatch(const dns_name_t *signer, const dns_name_t *name, + const isc_netaddr_t *tcpaddr, dns_rdatatype_t type, + const dst_key_t *key, void *driverarg, void *dbdata) { + dns_sdlzimplementation_t *imp; + char b_signer[DNS_NAME_FORMATSIZE]; + char b_name[DNS_NAME_FORMATSIZE]; + char b_addr[ISC_NETADDR_FORMATSIZE]; + char b_type[DNS_RDATATYPE_FORMATSIZE]; + char b_key[DST_KEY_FORMATSIZE]; + isc_buffer_t *tkey_token = NULL; + isc_region_t token_region = { NULL, 0 }; + uint32_t token_len = 0; + bool ret; + + REQUIRE(driverarg != NULL); + + imp = (dns_sdlzimplementation_t *)driverarg; + if (imp->methods->ssumatch == NULL) { + return (false); + } + + /* + * Format the request elements. sdlz operates on strings, not + * structures + */ + if (signer != NULL) { + dns_name_format(signer, b_signer, sizeof(b_signer)); + } else { + b_signer[0] = 0; + } + + dns_name_format(name, b_name, sizeof(b_name)); + + if (tcpaddr != NULL) { + isc_netaddr_format(tcpaddr, b_addr, sizeof(b_addr)); + } else { + b_addr[0] = 0; + } + + dns_rdatatype_format(type, b_type, sizeof(b_type)); + + if (key != NULL) { + dst_key_format(key, b_key, sizeof(b_key)); + tkey_token = dst_key_tkeytoken(key); + } else { + b_key[0] = 0; + } + + if (tkey_token != NULL) { + isc_buffer_region(tkey_token, &token_region); + token_len = token_region.length; + } + + MAYBE_LOCK(imp); + ret = imp->methods->ssumatch(b_signer, b_name, b_addr, b_type, b_key, + token_len, + token_len != 0 ? token_region.base : NULL, + imp->driverarg, dbdata); + MAYBE_UNLOCK(imp); + return (ret); +} + +static dns_dlzmethods_t sdlzmethods = { dns_sdlzcreate, dns_sdlzdestroy, + dns_sdlzfindzone, dns_sdlzallowzonexfr, + dns_sdlzconfigure, dns_sdlzssumatch }; + +/* + * Public functions. + */ + +isc_result_t +dns_sdlz_putrr(dns_sdlzlookup_t *lookup, const char *type, dns_ttl_t ttl, + const char *data) { + dns_rdatalist_t *rdatalist; + dns_rdata_t *rdata; + dns_rdatatype_t typeval; + isc_consttextregion_t r; + isc_buffer_t b; + isc_buffer_t *rdatabuf = NULL; + isc_lex_t *lex; + isc_result_t result; + unsigned int size; + isc_mem_t *mctx; + const dns_name_t *origin; + + REQUIRE(VALID_SDLZLOOKUP(lookup)); + REQUIRE(type != NULL); + REQUIRE(data != NULL); + + mctx = lookup->sdlz->common.mctx; + + r.base = type; + r.length = strlen(type); + result = dns_rdatatype_fromtext(&typeval, (void *)&r); + if (result != ISC_R_SUCCESS) { + return (result); + } + + rdatalist = ISC_LIST_HEAD(lookup->lists); + while (rdatalist != NULL) { + if (rdatalist->type == typeval) { + break; + } + rdatalist = ISC_LIST_NEXT(rdatalist, link); + } + + if (rdatalist == NULL) { + rdatalist = isc_mem_get(mctx, sizeof(dns_rdatalist_t)); + dns_rdatalist_init(rdatalist); + rdatalist->rdclass = lookup->sdlz->common.rdclass; + rdatalist->type = typeval; + rdatalist->ttl = ttl; + ISC_LIST_APPEND(lookup->lists, rdatalist, link); + } else if (rdatalist->ttl > ttl) { + /* + * BIND9 doesn't enforce all RRs in an RRset + * having the same TTL, as per RFC 2136, + * section 7.12. If a DLZ backend has + * different TTLs, then the best + * we can do is return the lowest. + */ + rdatalist->ttl = ttl; + } + + rdata = isc_mem_get(mctx, sizeof(dns_rdata_t)); + dns_rdata_init(rdata); + + if ((lookup->sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVERDATA) != 0) { + origin = &lookup->sdlz->common.origin; + } else { + origin = dns_rootname; + } + + lex = NULL; + result = isc_lex_create(mctx, 64, &lex); + if (result != ISC_R_SUCCESS) { + goto failure; + } + + size = initial_size(data); + do { + isc_buffer_constinit(&b, data, strlen(data)); + isc_buffer_add(&b, strlen(data)); + + result = isc_lex_openbuffer(lex, &b); + if (result != ISC_R_SUCCESS) { + goto failure; + } + + rdatabuf = NULL; + isc_buffer_allocate(mctx, &rdatabuf, size); + + result = dns_rdata_fromtext(rdata, rdatalist->rdclass, + rdatalist->type, lex, origin, false, + mctx, rdatabuf, &lookup->callbacks); + if (result != ISC_R_SUCCESS) { + isc_buffer_free(&rdatabuf); + } + if (size >= 65535) { + break; + } + size *= 2; + if (size >= 65535) { + size = 65535; + } + } while (result == ISC_R_NOSPACE); + + if (result != ISC_R_SUCCESS) { + result = DNS_R_SERVFAIL; + goto failure; + } + + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + ISC_LIST_APPEND(lookup->buffers, rdatabuf, link); + + if (lex != NULL) { + isc_lex_destroy(&lex); + } + + return (ISC_R_SUCCESS); + +failure: + if (rdatabuf != NULL) { + isc_buffer_free(&rdatabuf); + } + if (lex != NULL) { + isc_lex_destroy(&lex); + } + isc_mem_put(mctx, rdata, sizeof(dns_rdata_t)); + + return (result); +} + +isc_result_t +dns_sdlz_putnamedrr(dns_sdlzallnodes_t *allnodes, const char *name, + const char *type, dns_ttl_t ttl, const char *data) { + dns_name_t *newname; + const dns_name_t *origin; + dns_fixedname_t fnewname; + dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)allnodes->common.db; + dns_sdlznode_t *sdlznode; + isc_mem_t *mctx = sdlz->common.mctx; + isc_buffer_t b; + isc_result_t result; + + newname = dns_fixedname_initname(&fnewname); + + if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVERDATA) != 0) { + origin = &sdlz->common.origin; + } else { + origin = dns_rootname; + } + isc_buffer_constinit(&b, name, strlen(name)); + isc_buffer_add(&b, strlen(name)); + + result = dns_name_fromtext(newname, &b, origin, 0, NULL); + if (result != ISC_R_SUCCESS) { + return (result); + } + + if (allnodes->common.relative_names) { + /* All names are relative to the root */ + unsigned int nlabels = dns_name_countlabels(newname); + dns_name_getlabelsequence(newname, 0, nlabels - 1, newname); + } + + sdlznode = ISC_LIST_HEAD(allnodes->nodelist); + if (sdlznode == NULL || !dns_name_equal(sdlznode->name, newname)) { + sdlznode = NULL; + result = createnode(sdlz, &sdlznode); + if (result != ISC_R_SUCCESS) { + return (result); + } + sdlznode->name = isc_mem_get(mctx, sizeof(dns_name_t)); + dns_name_init(sdlznode->name, NULL); + dns_name_dup(newname, mctx, sdlznode->name); + ISC_LIST_PREPEND(allnodes->nodelist, sdlznode, link); + if (allnodes->origin == NULL && + dns_name_equal(newname, &sdlz->common.origin)) + { + allnodes->origin = sdlznode; + } + } + return (dns_sdlz_putrr(sdlznode, type, ttl, data)); +} + +isc_result_t +dns_sdlz_putsoa(dns_sdlzlookup_t *lookup, const char *mname, const char *rname, + uint32_t serial) { + char str[2 * DNS_NAME_MAXTEXT + 5 * (sizeof("2147483647")) + 7]; + int n; + + REQUIRE(mname != NULL); + REQUIRE(rname != NULL); + + n = snprintf(str, sizeof str, "%s %s %u %u %u %u %u", mname, rname, + serial, SDLZ_DEFAULT_REFRESH, SDLZ_DEFAULT_RETRY, + SDLZ_DEFAULT_EXPIRE, SDLZ_DEFAULT_MINIMUM); + if (n >= (int)sizeof(str) || n < 0) { + return (ISC_R_NOSPACE); + } + return (dns_sdlz_putrr(lookup, "SOA", SDLZ_DEFAULT_TTL, str)); +} + +isc_result_t +dns_sdlzregister(const char *drivername, const dns_sdlzmethods_t *methods, + void *driverarg, unsigned int flags, isc_mem_t *mctx, + dns_sdlzimplementation_t **sdlzimp) { + dns_sdlzimplementation_t *imp; + isc_result_t result; + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(drivername != NULL); + REQUIRE(methods != NULL); + REQUIRE(methods->findzone != NULL); + REQUIRE(methods->lookup != NULL); + REQUIRE(mctx != NULL); + REQUIRE(sdlzimp != NULL && *sdlzimp == NULL); + REQUIRE((flags & + ~(DNS_SDLZFLAG_RELATIVEOWNER | DNS_SDLZFLAG_RELATIVERDATA | + DNS_SDLZFLAG_THREADSAFE)) == 0); + + /* Write debugging message to log */ + sdlz_log(ISC_LOG_DEBUG(2), "Registering SDLZ driver '%s'", drivername); + + /* + * Allocate memory for a sdlz_implementation object. Error if + * we cannot. + */ + imp = isc_mem_get(mctx, sizeof(dns_sdlzimplementation_t)); + + /* Make sure memory region is set to all 0's */ + memset(imp, 0, sizeof(dns_sdlzimplementation_t)); + + /* Store the data passed into this method */ + imp->methods = methods; + imp->driverarg = driverarg; + imp->flags = flags; + imp->mctx = NULL; + + /* attach the new sdlz_implementation object to a memory context */ + isc_mem_attach(mctx, &imp->mctx); + + /* + * initialize the driver lock, error if we cannot + * (used if a driver does not support multiple threads) + */ + isc_mutex_init(&imp->driverlock); + + imp->dlz_imp = NULL; + + /* + * register the DLZ driver. Pass in our "extra" sdlz information as + * a driverarg. (that's why we stored the passed in driver arg in our + * sdlz_implementation structure) Also, store the dlz_implementation + * structure in our sdlz_implementation. + */ + result = dns_dlzregister(drivername, &sdlzmethods, imp, mctx, + &imp->dlz_imp); + + /* if registration fails, cleanup and get outta here. */ + if (result != ISC_R_SUCCESS) { + goto cleanup_mutex; + } + + *sdlzimp = imp; + + return (ISC_R_SUCCESS); + +cleanup_mutex: + /* destroy the driver lock, we don't need it anymore */ + isc_mutex_destroy(&imp->driverlock); + + /* + * return the memory back to the available memory pool and + * remove it from the memory context. + */ + isc_mem_putanddetach(&imp->mctx, imp, sizeof(dns_sdlzimplementation_t)); + return (result); +} + +void +dns_sdlzunregister(dns_sdlzimplementation_t **sdlzimp) { + dns_sdlzimplementation_t *imp; + + /* Write debugging message to log */ + sdlz_log(ISC_LOG_DEBUG(2), "Unregistering SDLZ driver."); + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(sdlzimp != NULL && *sdlzimp != NULL); + + imp = *sdlzimp; + *sdlzimp = NULL; + + /* Unregister the DLZ driver implementation */ + dns_dlzunregister(&imp->dlz_imp); + + /* destroy the driver lock, we don't need it anymore */ + isc_mutex_destroy(&imp->driverlock); + + /* + * return the memory back to the available memory pool and + * remove it from the memory context. + */ + isc_mem_putanddetach(&imp->mctx, imp, sizeof(dns_sdlzimplementation_t)); +} + +isc_result_t +dns_sdlz_setdb(dns_dlzdb_t *dlzdatabase, dns_rdataclass_t rdclass, + const dns_name_t *name, dns_db_t **dbp) { + isc_result_t result; + + result = dns_sdlzcreateDBP(dlzdatabase->mctx, + dlzdatabase->implementation->driverarg, + dlzdatabase->dbdata, name, rdclass, dbp); + return (result); +} |