diff options
Diffstat (limited to 'bin/tests/system/dyndb/driver')
-rw-r--r-- | bin/tests/system/dyndb/driver/AUTHORS | 8 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/COPYING | 19 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/Makefile.in | 57 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/README | 65 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/db.c | 822 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/db.h | 15 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/driver.c | 141 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/instance.c | 158 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/instance.h | 49 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/lock.c | 56 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/lock.h | 17 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/log.c | 21 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/log.h | 27 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/syncptr.c | 266 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/syncptr.h | 15 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/util.h | 57 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/zone.c | 194 | ||||
-rw-r--r-- | bin/tests/system/dyndb/driver/zone.h | 15 |
18 files changed, 2002 insertions, 0 deletions
diff --git a/bin/tests/system/dyndb/driver/AUTHORS b/bin/tests/system/dyndb/driver/AUTHORS new file mode 100644 index 0000000..acc109c --- /dev/null +++ b/bin/tests/system/dyndb/driver/AUTHORS @@ -0,0 +1,8 @@ +This sample driver is based on bind-dyndb-ldap project and small portions +of code from ISC BIND 9.10. + +Authors listed in alphabetical order: +Adam Tkac <atkac@redhat.com> +Jiri Kuncar <jkuncar@redhat.com> +Martin Nagy <mnagy@redhat.com> +Petr Spacek <pspacek@redhat.com> diff --git a/bin/tests/system/dyndb/driver/COPYING b/bin/tests/system/dyndb/driver/COPYING new file mode 100644 index 0000000..e383928 --- /dev/null +++ b/bin/tests/system/dyndb/driver/COPYING @@ -0,0 +1,19 @@ +Copyright (C) 1999-2014 Internet Systems Consortium, Inc. ("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 http://mozilla.org/MPL/2.0/. + +Copyright (C) 2009-2015 Red Hat + +Permission to use, copy, modify, and/or 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 AUTHORS DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL ISC 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. diff --git a/bin/tests/system/dyndb/driver/Makefile.in b/bin/tests/system/dyndb/driver/Makefile.in new file mode 100644 index 0000000..e62b247 --- /dev/null +++ b/bin/tests/system/dyndb/driver/Makefile.in @@ -0,0 +1,57 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("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 http://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +@BIND9_MAKE_INCLUDES@ + +CINCLUDES = ${DNS_INCLUDES} ${ISC_INCLUDES} @DST_OPENSSL_INC@ + +CDEFINES = @CRYPTO@ +CWARNINGS = + +DNSLIBS = ../../../../../lib/dns/libdns.@A@ +ISCLIBS = ../../../../../lib/isc/libisc.@A@ + +DNSDEPLIBS = ../../../../../lib/dns/libdns.@A@ +ISCDEPLIBS = ../../../../../lib/isc/libisc.@A@ + +DEPLIBS = ${DNSDEPLIBS} ${ISCDEPLIBS} + +LIBS = ${DNSLIBS} ${ISCLIBS} @LIBS@ + + +SRCS = db.c driver.c instance.c \ + lock.c log.c syncptr.c zone.c + +OBJS = db.@O@ driver.@O@ instance.@O@ \ + lock.@O@ log.@O@ syncptr.@O@ zone.@O@ + +SO_TARGETS = lib/sample.@SO@ +TARGETS = @SO_TARGETS@ +SO_STRIP = @SO_STRIP@ + +@BIND9_MAKE_RULES@ + +CFLAGS = @CFLAGS@ @SO_CFLAGS@ +SO_LDFLAGS = @LDFLAGS@ @SO_LDFLAGS@ + +lib/sample.@SO@: sample.@SO@ + $(SHELL) ${top_srcdir}/mkinstalldirs `pwd`/lib + ${LIBTOOL_MODE_INSTALL} ${INSTALL} sample.@SO@ `pwd`/lib + +sample.@SO@: ${OBJS} ${DNSDEPLIBS} ${ISCDEPLIBS} + CLEANED=`echo "${DNSLIBS} ${ISCLIBS} @DNS_CRYPTO_LIBS@ ${LIBS}" | ${SO_STRIP}`; \ + ${LIBTOOL_MODE_LINK} @SO_LD@ ${SO_LDFLAGS} -o $@ ${OBJS} \ + $${CLEANED} + +clean distclean:: + rm -f ${OBJS} sample.so lib/sample.so diff --git a/bin/tests/system/dyndb/driver/README b/bin/tests/system/dyndb/driver/README new file mode 100644 index 0000000..9aac0a6 --- /dev/null +++ b/bin/tests/system/dyndb/driver/README @@ -0,0 +1,65 @@ +To use the Dynamic DB sample driver, run named and check the log. + + $ cd testing + $ named -gc named.conf + +You should be able to see something like: + +zone test/IN: loaded serial 0 +zone arpa/IN: loaded serial 0 + +This means that the sample driver created empty zones "test." and +"arpa." as defined by "arg" parameters in named.conf. + +$ dig @localhost test. + +should work as usual and you should be able to see the dummy zone with +NS record pointing to the zone apex and A record with 127.0.0.1: + +;; ANSWER SECTION: +test. 86400 IN A 127.0.0.1 +test. 86400 IN NS test. +test. 86400 IN SOA test. test. 0 28800 7200 604800 86400 + +This driver creates two empty zones and allows query/transfer/update to +all IP addresses for demonstration purposes. + +The driver wraps the RBT database implementation used natively by BIND, +and modifies the addrdataset() and substractrdataset() functions to do +additional work during dynamic updates. + +A dynamic update modifies the target zone as usual. After that, the +driver detects whether the modified RR was of type A or AAAA, and if so, +attempts to appropriately generate or delete a matching PTR record in +one of the two zones managed by the driver. + +E.g.: + +$ nsupdate +> update add a.test. 300 IN A 192.0.2.1 +> send + +will add the A record +a.test. 300 IN A 192.0.2.1 + +and also automatically generate the PTR record +1.2.0.192.in-addr.arpa. 300 IN PTR a.test. + +AXFR and RR deletion via dynamic updates should work as usual. Deletion +of a type A or AAAA record should delete the corresponding PTR record +too. + +The zone is stored only in memory, and all changes will be lost on +reload/reconfig. + +Hints for code readers: +- Driver initialization starts in driver.c: dyndb_init() function. +- New database implementation is registered by calling dns_db_register() + and passing a function pointer to it. This sample uses the function + create_db() to initialize the database. +- Zones are created later in instance.c: load_sample_instance_zones(). +- Database entry points are in structure db.c: dns_dbmethods_t + sampledb_methods +- sampledb_methods points to an implementation of the database interface. + See the db.c: addrdataset() implementation and look at how the RBT + database instance is wrapped into an additional layer of logic. diff --git a/bin/tests/system/dyndb/driver/db.c b/bin/tests/system/dyndb/driver/db.c new file mode 100644 index 0000000..02aa6ab --- /dev/null +++ b/bin/tests/system/dyndb/driver/db.c @@ -0,0 +1,822 @@ +/* + * Database API implementation. The interface is defined in lib/dns/db.h. + * + * dns_db_*() calls on database instances backed by this driver use + * struct sampledb_methods to find appropriate function implementation. + * + * This example re-uses RBT DB implementation from original BIND and blindly + * proxies most of dns_db_*() calls to this underlying RBT DB. + * See struct sampledb below. + * + * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license + */ +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/string.h> +#include <isc/util.h> + +#include <dns/db.h> +#include <dns/diff.h> +#include <dns/enumclass.h> +#include <dns/rbt.h> +#include <dns/rdatalist.h> +#include <dns/rdatastruct.h> +#include <dns/soa.h> +#include <dns/types.h> + +#include "db.h" +#include "instance.h" +#include "syncptr.h" +#include "util.h" + +#define SAMPLEDB_MAGIC ISC_MAGIC('S', 'M', 'D', 'B') +#define VALID_SAMPLEDB(sampledb) \ + ((sampledb) != NULL && (sampledb)->common.impmagic == SAMPLEDB_MAGIC) + +struct sampledb { + dns_db_t common; + isc_refcount_t refs; + sample_instance_t *inst; + + /* + * Internal RBT database implementation provided by BIND. + * Most dns_db_* calls (find(), createiterator(), etc.) + * are blindly forwarded to this RBT DB. + */ + dns_db_t *rbtdb; +}; + +typedef struct sampledb sampledb_t; + +/* + * Get full DNS name from the node. + * + * @warning + * The code silently expects that "node" came from RBTDB and thus + * assumption dns_dbnode_t (from RBTDB) == dns_rbtnode_t is correct. + * + * This should work as long as we use only RBTDB and nothing else. + */ +static isc_result_t +sample_name_fromnode(dns_dbnode_t *node, dns_name_t *name) { + dns_rbtnode_t *rbtnode = (dns_rbtnode_t *) node; + return (dns_rbt_fullnamefromnode(rbtnode, name)); +} + +static void +attach(dns_db_t *source, dns_db_t **targetp) { + sampledb_t *sampledb = (sampledb_t *)source; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + isc_refcount_increment(&sampledb->refs, NULL); + *targetp = source; +} + +static void +free_sampledb(sampledb_t *sampledb) { + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_detach(&sampledb->rbtdb); + dns_name_free(&sampledb->common.origin, sampledb->common.mctx); + isc_mem_putanddetach(&sampledb->common.mctx, sampledb, sizeof(*sampledb)); +} + +static void +detach(dns_db_t **dbp) { + sampledb_t *sampledb = (sampledb_t *)(*dbp); + unsigned int refs; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + isc_refcount_decrement(&sampledb->refs, &refs); + if (refs == 0) + free_sampledb(sampledb); + *dbp = NULL; +} + +/* + * This method should never be called, because DB is "persistent". + * See ispersistent() function. It means that database do not need to be + * loaded in the usual sense. + */ +static isc_result_t +beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) { + UNUSED(db); + UNUSED(callbacks); + + fatal_error("current implementation should never call beginload()"); + + /* Not reached */ + return (ISC_R_SUCCESS); +} + +/* + * This method should never be called, because DB is "persistent". + * See ispersistent() function. It means that database do not need to be + * loaded in the usual sense. + */ +static isc_result_t +endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) { + UNUSED(db); + UNUSED(callbacks); + + fatal_error("current implementation should never call endload()"); + + /* Not reached */ + return (ISC_R_SUCCESS); +} + +static isc_result_t +serialize(dns_db_t *db, dns_dbversion_t *version, FILE *file) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_serialize(sampledb->rbtdb, version, file)); +} + +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); + + fatal_error("current implementation should never call dump()"); + + /* Not reached */ + return (ISC_R_SUCCESS); +} + +static void +currentversion(dns_db_t *db, dns_dbversion_t **versionp) { + sampledb_t *sampledb = (sampledb_t *)db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_currentversion(sampledb->rbtdb, versionp); +} + +static isc_result_t +newversion(dns_db_t *db, dns_dbversion_t **versionp) { + sampledb_t *sampledb = (sampledb_t *)db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_newversion(sampledb->rbtdb, versionp)); +} + +static void +attachversion(dns_db_t *db, dns_dbversion_t *source, + dns_dbversion_t **targetp) +{ + sampledb_t *sampledb = (sampledb_t *)db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_attachversion(sampledb->rbtdb, source, targetp); +} + +static void +closeversion(dns_db_t *db, dns_dbversion_t **versionp, bool commit) { + sampledb_t *sampledb = (sampledb_t *)db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_closeversion(sampledb->rbtdb, versionp, commit); +} + +static isc_result_t +findnode(dns_db_t *db, dns_name_t *name, bool create, + dns_dbnode_t **nodep) +{ + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_findnode(sampledb->rbtdb, name, create, nodep)); +} + +static isc_result_t +find(dns_db_t *db, 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) +{ + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_find(sampledb->rbtdb, name, version, type, + options, now, nodep, foundname, + rdataset, sigrdataset)); +} + +static isc_result_t +findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options, + isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +{ + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_findzonecut(sampledb->rbtdb, name, options, + now, nodep, foundname, rdataset, + sigrdataset)); +} + +static void +attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_attachnode(sampledb->rbtdb, source, targetp); + +} + +static void +detachnode(dns_db_t *db, dns_dbnode_t **targetp) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_detachnode(sampledb->rbtdb, targetp); +} + +static isc_result_t +expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_expirenode(sampledb->rbtdb, node, now)); +} + +static void +printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_printnode(sampledb->rbtdb, node, out); +} + +static isc_result_t +createiterator(dns_db_t *db, unsigned int options, + dns_dbiterator_t **iteratorp) +{ + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_createiterator(sampledb->rbtdb, options, iteratorp)); +} + +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) +{ + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_findrdataset(sampledb->rbtdb, node, version, type, + covers, now, rdataset, sigrdataset)); +} + +static isc_result_t +allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + isc_stdtime_t now, dns_rdatasetiter_t **iteratorp) +{ + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_allrdatasets(sampledb->rbtdb, node, version, + now, iteratorp)); +} + +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) +{ + sampledb_t *sampledb = (sampledb_t *) db; + isc_result_t result; + dns_fixedname_t name; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_fixedname_init(&name); + CHECK(dns_db_addrdataset(sampledb->rbtdb, node, version, now, + rdataset, options, addedrdataset)); + if (rdataset->type == dns_rdatatype_a || + rdataset->type == dns_rdatatype_aaaa) { + CHECK(sample_name_fromnode(node, dns_fixedname_name(&name))); + CHECK(syncptrs(sampledb->inst, dns_fixedname_name(&name), + rdataset, DNS_DIFFOP_ADD)); + } + +cleanup: + 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) +{ + sampledb_t *sampledb = (sampledb_t *) db; + isc_result_t result; + dns_fixedname_t name; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_fixedname_init(&name); + result = dns_db_subtractrdataset(sampledb->rbtdb, node, version, + rdataset, options, newrdataset); + if (result != ISC_R_SUCCESS && result != DNS_R_NXRRSET) + goto cleanup; + + if (rdataset->type == dns_rdatatype_a || + rdataset->type == dns_rdatatype_aaaa) { + CHECK(sample_name_fromnode(node, dns_fixedname_name(&name))); + CHECK(syncptrs(sampledb->inst, dns_fixedname_name(&name), + rdataset, DNS_DIFFOP_DEL)); + } + +cleanup: + return (result); +} + +/* + * deleterdataset() function is not used during DNS update processing so syncptr + * implementation is left as an exercise to the reader. + */ +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) +{ + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_deleterdataset(sampledb->rbtdb, node, version, + type, covers)); +} + +static bool +issecure(dns_db_t *db) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_issecure(sampledb->rbtdb)); +} + +static unsigned int +nodecount(dns_db_t *db) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_nodecount(sampledb->rbtdb)); +} + +/* + * The database does not need to be loaded from disk or written to disk. + * Always return true. + */ +static bool +ispersistent(dns_db_t *db) { + UNUSED(db); + + return (true); +} + +static void +overmem(dns_db_t *db, bool over) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_overmem(sampledb->rbtdb, over); +} + +static void +settask(dns_db_t *db, isc_task_t *task) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_settask(sampledb->rbtdb, task); +} + +static isc_result_t +getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_getoriginnode(sampledb->rbtdb, nodep)); +} + +static void +transfernode(dns_db_t *db, dns_dbnode_t **sourcep, dns_dbnode_t **targetp) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_transfernode(sampledb->rbtdb, sourcep, targetp); + +} + +static isc_result_t +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) +{ + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_getnsec3parameters(sampledb->rbtdb, version, + hash, flags, iterations, + salt, salt_length)); + +} + +static isc_result_t +findnsec3node(dns_db_t *db, dns_name_t *name, bool create, + dns_dbnode_t **nodep) +{ + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_findnsec3node(sampledb->rbtdb, name, create, nodep)); +} + +static isc_result_t +setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_setsigningtime(sampledb->rbtdb, rdataset, resign)); +} + +static isc_result_t +getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *name) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_getsigningtime(sampledb->rbtdb, rdataset, name)); +} + +static void +resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_resigned(sampledb->rbtdb, rdataset, version); +} + +static bool +isdnssec(dns_db_t *db) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_isdnssec(sampledb->rbtdb)); +} + +static dns_stats_t * +getrrsetstats(dns_db_t *db) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_getrrsetstats(sampledb->rbtdb)); + +} + +static void +rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_rpz_attach(sampledb->rbtdb, rpzs, rpz_num); +} + +static isc_result_t +rpz_ready(dns_db_t *db) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_rpz_ready(sampledb->rbtdb)); +} + +static isc_result_t +findnodeext(dns_db_t *db, dns_name_t *name, + bool create, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo, dns_dbnode_t **nodep) +{ + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_findnodeext(sampledb->rbtdb, name, create, + methods, clientinfo, nodep)); +} + +static isc_result_t +findext(dns_db_t *db, 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) +{ + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_findext(sampledb->rbtdb, name, version, type, + options, now, nodep, foundname, methods, + clientinfo, rdataset, sigrdataset)); +} + +static isc_result_t +setcachestats(dns_db_t *db, isc_stats_t *stats) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_setcachestats(sampledb->rbtdb, stats)); +} + +static size_t +hashsize(dns_db_t *db) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + return (dns_db_hashsize(sampledb->rbtdb)); +} + +/* + * DB interface definition. Database driver uses this structure to + * determine which implementation of dns_db_*() function to call. + */ +static dns_dbmethods_t sampledb_methods = { + attach, + detach, + beginload, + endload, + 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, + transfernode, + getnsec3parameters, + findnsec3node, + setsigningtime, + getsigningtime, + resigned, + isdnssec, + getrrsetstats, + rpz_attach, + rpz_ready, + findnodeext, + findext, + setcachestats, + hashsize, + NULL, + NULL, +}; + +/* Auxiliary driver functions. */ + +/* + * Auxiliary functions add_*() create minimal database which can be loaded. + * This is necessary because this driver create empty 'fake' zone which + * is not loaded from disk so there is no way for user to supply SOA, NS and A + * records. + * + * Following functions were copied from BIND 9.10.2rc1 named/server.c, + * credit goes to ISC. + */ +static isc_result_t +add_soa(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, + dns_name_t *origin, dns_name_t *contact) +{ + dns_dbnode_t *node = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdatalist_t rdatalist; + dns_rdataset_t rdataset; + isc_result_t result; + unsigned char buf[DNS_SOA_BUFFERSIZE]; + + dns_rdataset_init(&rdataset); + dns_rdatalist_init(&rdatalist); + CHECK(dns_soa_buildrdata(origin, contact, dns_db_class(db), + 0, 28800, 7200, 604800, 86400, buf, &rdata)); + rdatalist.type = rdata.type; + rdatalist.covers = 0; + rdatalist.rdclass = rdata.rdclass; + rdatalist.ttl = 86400; + ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); + CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset)); + CHECK(dns_db_findnode(db, name, true, &node)); + CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL)); + cleanup: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + + +static isc_result_t +add_ns(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, + dns_name_t *nsname) +{ + dns_dbnode_t *node = NULL; + dns_rdata_ns_t ns; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdatalist_t rdatalist; + dns_rdataset_t rdataset; + isc_result_t result; + isc_buffer_t b; + unsigned char buf[DNS_NAME_MAXWIRE]; + + isc_buffer_init(&b, buf, sizeof(buf)); + + dns_rdataset_init(&rdataset); + dns_rdatalist_init(&rdatalist); + ns.common.rdtype = dns_rdatatype_ns; + ns.common.rdclass = dns_db_class(db); + ns.mctx = NULL; + dns_name_init(&ns.name, NULL); + dns_name_clone(nsname, &ns.name); + CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_ns, + &ns, &b)); + rdatalist.type = rdata.type; + rdatalist.covers = 0; + rdatalist.rdclass = rdata.rdclass; + rdatalist.ttl = 86400; + ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); + CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset)); + CHECK(dns_db_findnode(db, name, true, &node)); + CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL)); + cleanup: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +static isc_result_t +add_a(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, + struct in_addr addr) +{ + dns_dbnode_t *node = NULL; + dns_rdata_in_a_t a; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdatalist_t rdatalist; + dns_rdataset_t rdataset; + isc_result_t result; + isc_buffer_t b; + unsigned char buf[DNS_NAME_MAXWIRE]; + + isc_buffer_init(&b, buf, sizeof(buf)); + + dns_rdataset_init(&rdataset); + dns_rdatalist_init(&rdatalist); + a.common.rdtype = dns_rdatatype_a; + a.common.rdclass = dns_db_class(db); + a.in_addr = addr; + CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_a, + &a, &b)); + rdatalist.type = rdata.type; + rdatalist.covers = 0; + rdatalist.rdclass = rdata.rdclass; + rdatalist.ttl = 86400; + ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); + CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset)); + CHECK(dns_db_findnode(db, name, true, &node)); + CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL)); + cleanup: + if (node != NULL) + dns_db_detachnode(db, &node); + return (result); +} + +/* + * Driver-specific implementation of dns_db_create(). + * + * @param[in] argv Database-specific parameters from dns_db_create(). + * @param[in] driverarg Driver-specific parameter from dns_db_register(). + */ +isc_result_t +create_db(isc_mem_t *mctx, dns_name_t *origin, dns_dbtype_t type, + dns_rdataclass_t rdclass, unsigned int argc, char *argv[], + void *driverarg, dns_db_t **dbp) +{ + sampledb_t *sampledb = NULL; + isc_result_t result; + dns_dbversion_t *version = NULL; + struct in_addr a_addr; + + REQUIRE(type == dns_dbtype_zone); + REQUIRE(rdclass == dns_rdataclass_in); + REQUIRE(argc == 0); + REQUIRE(argv != NULL); + REQUIRE(driverarg != NULL); /* pointer to driver instance */ + REQUIRE(dbp != NULL && *dbp == NULL); + + UNUSED(driverarg); /* no driver-specific configuration */ + + a_addr.s_addr = 0x0100007fU; + + CHECKED_MEM_GET_PTR(mctx, sampledb); + ZERO_PTR(sampledb); + + isc_mem_attach(mctx, &sampledb->common.mctx); + dns_name_init(&sampledb->common.origin, NULL); + isc_ondestroy_init(&sampledb->common.ondest); + + sampledb->common.magic = DNS_DB_MAGIC; + sampledb->common.impmagic = SAMPLEDB_MAGIC; + + sampledb->common.methods = &sampledb_methods; + sampledb->common.attributes = 0; + sampledb->common.rdclass = rdclass; + + CHECK(dns_name_dupwithoffsets(origin, mctx, &sampledb->common.origin)); + + CHECK(isc_refcount_init(&sampledb->refs, 1)); + + /* Translate instance name to instance pointer. */ + sampledb->inst = driverarg; + + /* Create internal instance of RBT DB implementation from BIND. */ + CHECK(dns_db_create(mctx, "rbt", origin, dns_dbtype_zone, + dns_rdataclass_in, 0, NULL, &sampledb->rbtdb)); + + /* Create fake SOA, NS, and A records to make database loadable. */ + CHECK(dns_db_newversion(sampledb->rbtdb, &version)); + CHECK(add_soa(sampledb->rbtdb, version, origin, origin, origin)); + CHECK(add_ns(sampledb->rbtdb, version, origin, origin)); + CHECK(add_a(sampledb->rbtdb, version, origin, a_addr)); + dns_db_closeversion(sampledb->rbtdb, &version, true); + + *dbp = (dns_db_t *)sampledb; + + return (ISC_R_SUCCESS); + +cleanup: + if (sampledb != NULL) { + if (dns_name_dynamic(&sampledb->common.origin)) + dns_name_free(&sampledb->common.origin, mctx); + + isc_mem_putanddetach(&sampledb->common.mctx, sampledb, + sizeof(*sampledb)); + } + + return (result); +} diff --git a/bin/tests/system/dyndb/driver/db.h b/bin/tests/system/dyndb/driver/db.h new file mode 100644 index 0000000..80693a7 --- /dev/null +++ b/bin/tests/system/dyndb/driver/db.h @@ -0,0 +1,15 @@ +/** + * Database API implementation. + * + * Copyright (C) 2015 Red Hat ; see COPYRIGHT for license + */ + +#ifndef DB_H_ +#define DB_H_ + +isc_result_t +create_db(isc_mem_t *mctx, dns_name_t *origin, dns_dbtype_t type, + dns_rdataclass_t rdclass, unsigned int argc, char *argv[], + void *driverarg, dns_db_t **dbp); + +#endif /* DB_H_ */ diff --git a/bin/tests/system/dyndb/driver/driver.c b/bin/tests/system/dyndb/driver/driver.c new file mode 100644 index 0000000..bef6acb --- /dev/null +++ b/bin/tests/system/dyndb/driver/driver.c @@ -0,0 +1,141 @@ +/* + * Driver API implementation and main entry point for BIND. + * + * BIND calls dyndb_version() before loading, dyndb_init() during startup + * and dyndb_destroy() during shutdown. + * + * It is completely up to implementation what to do. + * + * dyndb <name> <driver> {} sections in named.conf are independent so + * driver init() and destroy() functions are called independently for + * each section even if they reference the same driver/library. It is + * up to driver implementation to detect and catch this situation if + * it is undesirable. + * + * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license + */ + +#include <config.h> + +#include <isc/commandline.h> +#include <isc/hash.h> +#include <isc/mem.h> +#include <isc/lib.h> +#include <isc/util.h> + +#include <dns/db.h> +#include <dns/dyndb.h> +#include <dns/lib.h> +#include <dns/types.h> + +#include "db.h" +#include "log.h" +#include "instance.h" +#include "util.h" + +dns_dyndb_destroy_t dyndb_destroy; +dns_dyndb_register_t dyndb_init; +dns_dyndb_version_t dyndb_version; + +/* + * Driver init is called for each dyndb section in named.conf + * once during startup and then again on every reload. + * + * @code + * dyndb example-name "sample.so" { param1 param2 }; + * @endcode + * + * @param[in] name User-defined string from dyndb "name" {}; definition + * in named.conf. + * The example above will have name = "example-name". + * @param[in] parameters User-defined parameters from dyndb section as one + * string. The example above will have + * params = "param1 param2"; + * @param[in] file The name of the file from which the parameters + * were read. + * @param[in] line The line number from which the parameters were read. + * @param[out] instp Pointer to instance-specific data + * (for one dyndb section). + */ +isc_result_t +dyndb_init(isc_mem_t *mctx, const char *name, const char *parameters, + const char *file, unsigned long line, + const dns_dyndbctx_t *dctx, void **instp) +{ + isc_result_t result; + unsigned int argc; + char **argv = NULL; + char *s = NULL; + sample_instance_t *sample_inst = NULL; + + REQUIRE(name != NULL); + REQUIRE(dctx != NULL); + + /* + * Depending on how dlopen() was called, we may not have + * access to named's global namespace, in which case we need + * to initialize libisc/libdns + */ + if (dctx->refvar != &isc_bind9) { + isc_lib_register(); + isc_log_setcontext(dctx->lctx); + dns_log_setcontext(dctx->lctx); + } + + isc_hash_set_initializer(dctx->hashinit); + + s = isc_mem_strdup(mctx, parameters); + if (s == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + result = isc_commandline_strtoargv(mctx, s, &argc, &argv, 0); + if (result != ISC_R_SUCCESS) + goto cleanup; + + log_write(ISC_LOG_DEBUG(9), + "loading params for dyndb '%s' from %s:%lu", + name, file, line); + + /* Finally, create the instance. */ + CHECK(new_sample_instance(mctx, name, argc, argv, dctx, &sample_inst)); + + /* + * This is an example so we create and load zones + * right now. This step can be arbitrarily postponed. + */ + CHECK(load_sample_instance_zones(sample_inst)); + + *instp = sample_inst; + + cleanup: + if (s != NULL) + isc_mem_free(mctx, s); + if (argv != NULL) + isc_mem_put(mctx, argv, argc * sizeof(*argv)); + + return (result); +} + +/* + * Driver destroy is called for every instance on every reload and then once + * during shutdown. + * + * @param[out] instp Pointer to instance-specific data (for one dyndb section). + */ +void +dyndb_destroy(void **instp) { + destroy_sample_instance((sample_instance_t **)instp); +} + +/* + * Driver version is called when loading the driver to ensure there + * is no API mismatch betwen the driver and the caller. + */ +int +dyndb_version(unsigned int *flags) { + UNUSED(flags); + + return (DNS_DYNDB_VERSION); +} diff --git a/bin/tests/system/dyndb/driver/instance.c b/bin/tests/system/dyndb/driver/instance.c new file mode 100644 index 0000000..f1bd84e --- /dev/null +++ b/bin/tests/system/dyndb/driver/instance.c @@ -0,0 +1,158 @@ +/* + * Driver instance object. + * + * One instance is equivalent to dynamic-db section in named.conf. + * This module parses arguments and provide high-level operations + * instance init/zone load/instance destroy. + * + * Copyright (C) 2008-2015 Red Hat ; see COPYRIGHT for license + */ + +#include <config.h> + +#include <isc/task.h> +#include <isc/util.h> + +#include <dns/db.h> +#include <dns/dyndb.h> +#include <dns/fixedname.h> +#include <dns/name.h> +#include <dns/view.h> +#include <dns/zone.h> + +#include "db.h" +#include "util.h" +#include "instance.h" +#include "log.h" +#include "zone.h" + +/* + * Parse parameters and convert them to zone names. Caller has to deallocate + * resulting DNS names. + * + * @param[in] argv NULL-terminated string array of length 2 (excluding NULL) + * Each string has to be a valid DNS name. + * @param[out] z1 Zone name from argv[0] + * @param[out] z2 Zone name from argv[1] + */ +static isc_result_t +parse_params(isc_mem_t *mctx, int argc, char **argv, + dns_name_t *z1, dns_name_t *z2) +{ + isc_result_t result; + int i; + + REQUIRE(argv != NULL); + REQUIRE(z1 != NULL); + REQUIRE(z2 != NULL); + + for (i = 0; i < argc; i++) { + log_info("param: '%s'", argv[i]); + } + log_info("number of params: %d", i); + + if (argc != 2) { + log_error("exactly two parameters " + "(absolute zone names) are required"); + result = ISC_R_FAILURE; + goto cleanup; + } + CHECK(dns_name_fromstring2(z1, argv[0], dns_rootname, 0, mctx)); + CHECK(dns_name_fromstring2(z2, argv[1], dns_rootname, 0, mctx)); + + result = ISC_R_SUCCESS; + +cleanup: + return (result); +} + +/* + * Initialize new driver instance. It will not create zones until + * load_sample_instance_zones() is called. + */ +isc_result_t +new_sample_instance(isc_mem_t *mctx, const char *db_name, + int argc, char **argv, const dns_dyndbctx_t *dctx, + sample_instance_t **sample_instp) +{ + isc_result_t result; + sample_instance_t *inst = NULL; + + REQUIRE(sample_instp != NULL && *sample_instp == NULL); + + CHECKED_MEM_GET_PTR(mctx, inst); + ZERO_PTR(inst); + isc_mem_attach(mctx, &inst->mctx); + + inst->db_name = isc_mem_strdup(mctx, db_name); + if (inst->db_name == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + inst->zone1_name = dns_fixedname_initname(&inst->zone1_fn); + inst->zone2_name = dns_fixedname_initname(&inst->zone2_fn); + + CHECK(parse_params(mctx, argc, argv, + inst->zone1_name, inst->zone2_name)); + + dns_view_attach(dctx->view, &inst->view); + dns_zonemgr_attach(dctx->zmgr, &inst->zmgr); + isc_task_attach(dctx->task, &inst->task); + + /* Register new DNS DB implementation. */ + CHECK(dns_db_register(db_name, create_db, inst, mctx, &inst->db_imp)); + + *sample_instp = inst; + result = ISC_R_SUCCESS; + +cleanup: + if (result != ISC_R_SUCCESS) + destroy_sample_instance(&inst); + return (result); +} + +/* + * Create empty zones, add fake SOA, NS, and A records, load fake zones + * and add them to inst->view. + */ +isc_result_t +load_sample_instance_zones(sample_instance_t *inst) { + isc_result_t result; + + CHECK(create_zone(inst, inst->zone1_name, &inst->zone1)); + CHECK(activate_zone(inst, inst->zone1)); + + CHECK(create_zone(inst, inst->zone2_name, &inst->zone2)); + CHECK(activate_zone(inst, inst->zone2)); + +cleanup: + return (result); +} + +void +destroy_sample_instance(sample_instance_t **instp) { + sample_instance_t *inst; + REQUIRE(instp != NULL); + + inst = *instp; + if (inst == NULL) + return; + + if (inst->db_name != NULL) + isc_mem_free(inst->mctx, inst->db_name); + if (inst->zone1 != NULL) + dns_zone_detach(&inst->zone1); + if (inst->zone2 != NULL) + dns_zone_detach(&inst->zone2); + if (inst->db_imp != NULL) + dns_db_unregister(&inst->db_imp); + + dns_view_detach(&inst->view); + dns_zonemgr_detach(&inst->zmgr); + isc_task_detach(&inst->task); + + MEM_PUT_AND_DETACH(inst); + + *instp = NULL; +} diff --git a/bin/tests/system/dyndb/driver/instance.h b/bin/tests/system/dyndb/driver/instance.h new file mode 100644 index 0000000..6658cbb --- /dev/null +++ b/bin/tests/system/dyndb/driver/instance.h @@ -0,0 +1,49 @@ +/** + * Driver instance object. + * + * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license + */ + +#ifndef _LD_INSTANCE_H_ +#define _LD_INSTANCE_H_ + +#include <stdbool.h> + +#include <dns/fixedname.h> +#include <dns/name.h> +#include <dns/types.h> + +struct sample_instance { + isc_mem_t *mctx; + char *db_name; + dns_dbimplementation_t *db_imp; + + /* These are needed for zone creation. */ + dns_view_t *view; + dns_zonemgr_t *zmgr; + isc_task_t *task; + bool exiting; + + dns_zone_t *zone1; + dns_fixedname_t zone1_fn; + dns_name_t *zone1_name; + + dns_zone_t *zone2; + dns_fixedname_t zone2_fn; + dns_name_t *zone2_name; +}; + +typedef struct sample_instance sample_instance_t; + +isc_result_t +new_sample_instance(isc_mem_t *mctx, const char *db_name, + int argc, char **argv, const dns_dyndbctx_t *dctx, + sample_instance_t **sample_instp); + +isc_result_t +load_sample_instance_zones(sample_instance_t *inst); + +void +destroy_sample_instance(sample_instance_t **sample_instp); + +#endif /* !_LD_INSTANCE_H_ */ diff --git a/bin/tests/system/dyndb/driver/lock.c b/bin/tests/system/dyndb/driver/lock.c new file mode 100644 index 0000000..c97c490 --- /dev/null +++ b/bin/tests/system/dyndb/driver/lock.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014-2015 Red Hat ; see COPYRIGHT for license + */ + +#include <config.h> + +#include <isc/task.h> +#include <isc/util.h> + +#include "lock.h" + +/* + * Lock BIND dispatcher and allow only single task to run. + * + * @warning + * All calls to isc_task_beginexclusive() have to operate on the same task + * otherwise it would not be possible to distinguish recursive locking + * from real conflict on the dispatcher lock. + * For this reason this wrapper function always works with inst->task. + * As a result, this function have to be be called only from inst->task. + * + * Recursive locking is allowed. Auxiliary variable pointed to by "statep" + * stores information if last run_exclusive_enter() operation really locked + * something or if the lock was called recursively and was no-op. + * + * The pair (inst, state) used for run_exclusive_enter() has to be + * used for run_exclusive_exit(). + * + * @param[in] inst The instance with the only task which is allowed to run. + * @param[in,out] statep Lock state: ISC_R_SUCCESS or ISC_R_LOCKBUSY + */ +void +run_exclusive_enter(sample_instance_t *inst, isc_result_t *statep) { + REQUIRE(statep != NULL); + REQUIRE(*statep == ISC_R_IGNORE); + + *statep = isc_task_beginexclusive(inst->task); + RUNTIME_CHECK(*statep == ISC_R_SUCCESS || *statep == ISC_R_LOCKBUSY); +} + +/* + * Exit task-exclusive mode. + * + * @param[in] inst The instance used for previous run_exclusive_enter() call. + * @param[in] state Lock state as returned by run_exclusive_enter(). + */ +void +run_exclusive_exit(sample_instance_t *inst, isc_result_t state) { + if (state == ISC_R_SUCCESS) + isc_task_endexclusive(inst->task); + else + /* Unlocking recursive lock or the lock was never locked. */ + INSIST(state == ISC_R_LOCKBUSY || state == ISC_R_IGNORE); + + return; +} diff --git a/bin/tests/system/dyndb/driver/lock.h b/bin/tests/system/dyndb/driver/lock.h new file mode 100644 index 0000000..35c9c84 --- /dev/null +++ b/bin/tests/system/dyndb/driver/lock.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2014-2015 Red Hat ; see COPYRIGHT for license + */ + +#ifndef LOCK_H_ +#define LOCK_H_ + +#include "instance.h" +#include "util.h" + +void +run_exclusive_enter(sample_instance_t *inst, isc_result_t *statep); + +void +run_exclusive_exit(sample_instance_t *inst, isc_result_t state); + +#endif /* LOCK_H_ */ diff --git a/bin/tests/system/dyndb/driver/log.c b/bin/tests/system/dyndb/driver/log.c new file mode 100644 index 0000000..1098042 --- /dev/null +++ b/bin/tests/system/dyndb/driver/log.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license + */ + +#include <config.h> + +#include <isc/util.h> + +#include <dns/log.h> + +#include "log.h" + +void +log_write(int level, const char *format, ...) { + va_list args; + + va_start(args, format); + isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB, + level, format, args); + va_end(args); +} diff --git a/bin/tests/system/dyndb/driver/log.h b/bin/tests/system/dyndb/driver/log.h new file mode 100644 index 0000000..27b38c8 --- /dev/null +++ b/bin/tests/system/dyndb/driver/log.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2009--2015 Red Hat ; see COPYRIGHT for license + */ + +#ifndef _LD_LOG_H_ +#define _LD_LOG_H_ + +#include <isc/error.h> +#include <dns/log.h> +#include <dns/result.h> + +#define fatal_error(...) \ + isc_error_fatal(__FILE__, __LINE__, __VA_ARGS__) + +#define log_error_r(fmt, ...) \ + log_error(fmt ": %s", ##__VA_ARGS__, dns_result_totext(result)) + +#define log_error(format, ...) \ + log_write(ISC_LOG_ERROR, format, ##__VA_ARGS__) + +#define log_info(format, ...) \ + log_write(ISC_LOG_INFO, format, ##__VA_ARGS__) + +void +log_write(int level, const char *format, ...) ISC_FORMAT_PRINTF(2, 3); + +#endif /* !_LD_LOG_H_ */ diff --git a/bin/tests/system/dyndb/driver/syncptr.c b/bin/tests/system/dyndb/driver/syncptr.c new file mode 100644 index 0000000..e184fd2 --- /dev/null +++ b/bin/tests/system/dyndb/driver/syncptr.c @@ -0,0 +1,266 @@ +/* + * Automatic A/AAAA/PTR record synchronization. + * + * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license + */ + +#include <config.h> + +#include <isc/event.h> +#include <isc/eventclass.h> +#include <isc/netaddr.h> +#include <isc/task.h> +#include <isc/util.h> + +#include <dns/byaddr.h> +#include <dns/db.h> +#include <dns/name.h> +#include <dns/view.h> +#include <dns/zone.h> + +#include "instance.h" +#include "syncptr.h" +#include "util.h" + +/* Almost random value. See eventclass.h */ +#define SYNCPTR_WRITE_EVENT (ISC_EVENTCLASS(1025) + 1) + +/* + * Event used for making changes to reverse zones. + */ +typedef struct syncptrevent syncptrevent_t; +struct syncptrevent { + ISC_EVENT_COMMON(syncptrevent_t); + isc_mem_t *mctx; + dns_zone_t *zone; + dns_diff_t diff; + dns_fixedname_t ptr_target_name; /* referenced by owner name in tuple */ + isc_buffer_t b; /* referenced by target name in tuple */ + unsigned char buf[DNS_NAME_MAXWIRE]; +}; + +/* + * Write diff generated in syncptr() to reverse zone. + * + * This function will be called asynchronously and syncptr() will not get + * any result from it. + * + */ +static void +syncptr_write(isc_task_t *task, isc_event_t *event) { + syncptrevent_t *pevent = (syncptrevent_t *)event; + dns_dbversion_t *version = NULL; + dns_db_t *db = NULL; + isc_result_t result; + + REQUIRE(event->ev_type == SYNCPTR_WRITE_EVENT); + + UNUSED(task); + + CHECK(dns_zone_getdb(pevent->zone, &db)); + CHECK(dns_db_newversion(db, &version)); + CHECK(dns_diff_apply(&pevent->diff, db, version)); + +cleanup: + if (db != NULL) { + if (version != NULL) + dns_db_closeversion(db, &version, true); + dns_db_detach(&db); + } + dns_zone_detach(&pevent->zone); + dns_diff_clear(&pevent->diff); + isc_event_free(&event); +} + +/* + * Find a reverse zone for given IP address. + * + * @param[in] rdata IP address as A/AAAA record + * @param[out] name Owner name for the PTR record + * @param[out] zone DNS zone for reverse record matching the IP address + * + * @retval ISC_R_SUCCESS DNS name derived from given IP address belongs to an + * reverse zone managed by this driver instance. + * PTR record synchronization can continue. + * @retval ISC_R_NOTFOUND Suitable reverse zone was not found because it + * does not exist or is not managed by this driver. + */ +static isc_result_t +syncptr_find_zone(sample_instance_t *inst, dns_rdata_t *rdata, + dns_name_t *name, dns_zone_t **zone) +{ + isc_result_t result; + isc_netaddr_t isc_ip; /* internal net address representation */ + dns_rdata_in_a_t ipv4; + dns_rdata_in_aaaa_t ipv6; + + REQUIRE(inst != NULL); + REQUIRE(zone != NULL && *zone == NULL); + + switch (rdata->type) { + case dns_rdatatype_a: + CHECK(dns_rdata_tostruct(rdata, &ipv4, inst->mctx)); + isc_netaddr_fromin(&isc_ip, &ipv4.in_addr); + break; + + case dns_rdatatype_aaaa: + CHECK(dns_rdata_tostruct(rdata, &ipv6, inst->mctx)); + isc_netaddr_fromin6(&isc_ip, &ipv6.in6_addr); + break; + + default: + fatal_error("unsupported address type 0x%x", rdata->type); + break; + } + + /* + * Convert IP address to PTR owner name. + * + * @example + * 192.168.0.1 -> 1.0.168.192.in-addr.arpa + */ + CHECK(dns_byaddr_createptrname2(&isc_ip, 0, name)); + + /* Find a zone containing owner name of the PTR record. */ + result = dns_zt_find(inst->view->zonetable, name, 0, NULL, zone); + if (result == DNS_R_PARTIALMATCH) + result = ISC_R_SUCCESS; + else if (result != ISC_R_SUCCESS) + goto cleanup; + + /* Make sure that the zone is managed by this driver. */ + if (*zone != inst->zone1 && *zone != inst->zone2) { + dns_zone_detach(zone); + result = ISC_R_NOTFOUND; + } + +cleanup: + if (rdata->type == dns_rdatatype_a) + dns_rdata_freestruct(&ipv4); + else + dns_rdata_freestruct(&ipv6); + + return (result); +} + +/* + * Generate update event for PTR record to reflect change in A/AAAA record. + * + * @pre Reverse zone is managed by this driver. + * + * @param[in] a_name DNS domain of modified A/AAAA record + * @param[in] af Address family + * @param[in] ip_str IP address as a string (IPv4 or IPv6) + * @param[in] mod_op LDAP_MOD_DELETE if A/AAAA record is being deleted + * or LDAP_MOD_ADD if A/AAAA record is being added. + * + * @retval ISC_R_SUCCESS Event for PTR record update was generated and send. + * Change to reverse zone will be done asynchronously. + * @retval other Synchronization failed - reverse doesn't exist, + * is not managed by this driver instance, + * memory allocation error, etc. + */ +static isc_result_t +syncptr(sample_instance_t *inst, dns_name_t *name, + dns_rdata_t *addr_rdata, dns_ttl_t ttl, dns_diffop_t op) +{ + isc_result_t result; + isc_mem_t *mctx = inst->mctx; + dns_fixedname_t ptr_name; + dns_zone_t *ptr_zone = NULL; + dns_rdata_ptr_t ptr_struct; + dns_rdata_t ptr_rdata = DNS_RDATA_INIT; + dns_difftuple_t *tp = NULL; + isc_task_t *task = NULL; + syncptrevent_t *pevent = NULL; + + dns_fixedname_init(&ptr_name); + DNS_RDATACOMMON_INIT(&ptr_struct, dns_rdatatype_ptr, dns_rdataclass_in); + dns_name_init(&ptr_struct.ptr, NULL); + + pevent = (syncptrevent_t *)isc_event_allocate(inst->mctx, inst, + SYNCPTR_WRITE_EVENT, + syncptr_write, NULL, + sizeof(syncptrevent_t)); + if (pevent == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + isc_buffer_init(&pevent->b, pevent->buf, sizeof(pevent->buf)); + dns_fixedname_init(&pevent->ptr_target_name); + + /* Check if reverse zone is managed by this driver */ + result = syncptr_find_zone(inst, addr_rdata, + dns_fixedname_name(&ptr_name), &ptr_zone); + if (result != ISC_R_SUCCESS) { + log_error_r("PTR record synchonization skipped: reverse zone " + "is not managed by driver instance '%s'", + inst->db_name); + goto cleanup; + } + + /* Reverse zone is managed by this driver, prepare PTR record */ + pevent->zone = NULL; + dns_zone_attach(ptr_zone, &pevent->zone); + CHECK(dns_name_copy(name, dns_fixedname_name(&pevent->ptr_target_name), + NULL)); + dns_name_clone(dns_fixedname_name(&pevent->ptr_target_name), + &ptr_struct.ptr); + dns_diff_init(inst->mctx, &pevent->diff); + CHECK(dns_rdata_fromstruct(&ptr_rdata, dns_rdataclass_in, + dns_rdatatype_ptr, &ptr_struct, &pevent->b)); + + /* Create diff */ + CHECK(dns_difftuple_create(mctx, op, dns_fixedname_name(&ptr_name), + ttl, &ptr_rdata, &tp)); + dns_diff_append(&pevent->diff, &tp); + + /* + * Send update event to the reverse zone. + * It will be processed asynchronously. + */ + dns_zone_gettask(ptr_zone, &task); + isc_task_send(task, (isc_event_t **)&pevent); + +cleanup: + if (ptr_zone != NULL) + dns_zone_detach(&ptr_zone); + if (tp != NULL) + dns_difftuple_free(&tp); + if (task != NULL) + isc_task_detach(&task); + if (pevent != NULL) + isc_event_free((isc_event_t **)&pevent); + + return (result); +} + +/* + * Generate update event for every rdata in rdataset. + * + * @param[in] name Owner name for A/AAAA records in rdataset. + * @param[in] rdataset A/AAAA records. + * @param[in] op DNS_DIFFOP_ADD / DNS_DIFFOP_DEL for adding / deleting + * the rdata + */ +isc_result_t +syncptrs(sample_instance_t *inst, dns_name_t *name, + dns_rdataset_t *rdataset, dns_diffop_t op) +{ + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) { + dns_rdataset_current(rdataset, &rdata); + result = syncptr(inst, name, &rdata, rdataset->ttl, op); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto cleanup; + } + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + +cleanup: + return (result); +} diff --git a/bin/tests/system/dyndb/driver/syncptr.h b/bin/tests/system/dyndb/driver/syncptr.h new file mode 100644 index 0000000..2f9b3a6 --- /dev/null +++ b/bin/tests/system/dyndb/driver/syncptr.h @@ -0,0 +1,15 @@ +/* + * Sync PTR records + * + * Copyright (C) 2014-2015 Red Hat ; see COPYRIGHT for license + */ + +#ifndef SYNCPTR_H_ +#define SYNCPTR_H_ + +#include <dns/diff.h> +isc_result_t +syncptrs(sample_instance_t *inst, dns_name_t *name, dns_rdataset_t *rdataset, + dns_diffop_t op); + +#endif /* SYNCPTR_H_ */ diff --git a/bin/tests/system/dyndb/driver/util.h b/bin/tests/system/dyndb/driver/util.h new file mode 100644 index 0000000..2a00fe3 --- /dev/null +++ b/bin/tests/system/dyndb/driver/util.h @@ -0,0 +1,57 @@ +/* + * Memory allocation and error handling utilities. + * + * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license + */ + +#ifndef _LD_UTIL_H_ +#define _LD_UTIL_H_ + +#include <isc/mem.h> +#include <dns/types.h> + +#include "log.h" + +#define CLEANUP_WITH(result_code) \ + do { \ + result = (result_code); \ + goto cleanup; \ + } while(0) + +#define CHECK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + +#define CHECKED_MEM_GET(m, target_ptr, s) \ + do { \ + (target_ptr) = isc_mem_get((m), (s)); \ + if ((target_ptr) == NULL) { \ + result = ISC_R_NOMEMORY; \ + log_error("Memory allocation failed"); \ + goto cleanup; \ + } \ + } while (0) + +#define CHECKED_MEM_GET_PTR(m, target_ptr) \ + CHECKED_MEM_GET(m, target_ptr, sizeof(*(target_ptr))) + +#define CHECKED_MEM_STRDUP(m, source, target) \ + do { \ + (target) = isc_mem_strdup((m), (source)); \ + if ((target) == NULL) { \ + result = ISC_R_NOMEMORY; \ + log_error("Memory allocation failed"); \ + goto cleanup; \ + } \ + } while (0) + +#define ZERO_PTR(ptr) memset((ptr), 0, sizeof(*(ptr))) + +#define MEM_PUT_AND_DETACH(target_ptr) \ + isc_mem_putanddetach(&(target_ptr)->mctx, target_ptr, \ + sizeof(*(target_ptr))) + +#endif /* !_LD_UTIL_H_ */ diff --git a/bin/tests/system/dyndb/driver/zone.c b/bin/tests/system/dyndb/driver/zone.c new file mode 100644 index 0000000..47e4c15 --- /dev/null +++ b/bin/tests/system/dyndb/driver/zone.c @@ -0,0 +1,194 @@ +/* + * Zone management. + * + * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license + */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/util.h> + +#include <dns/dyndb.h> +#include <dns/view.h> +#include <dns/zone.h> + +#include "util.h" +#include "instance.h" +#include "lock.h" +#include "log.h" +#include "zone.h" + +extern const char *impname; + +/* + * Create a new zone with origin 'name'. The zone stay invisible to clients + * until it is explicitly added to a view. + */ +isc_result_t +create_zone(sample_instance_t * const inst, dns_name_t * const name, + dns_zone_t ** const rawp) +{ + isc_result_t result; + dns_zone_t *raw = NULL; + const char *zone_argv[1]; + char zone_name[DNS_NAME_FORMATSIZE]; + dns_acl_t *acl_any = NULL; + + REQUIRE(inst != NULL); + REQUIRE(name != NULL); + REQUIRE(rawp != NULL && *rawp == NULL); + + zone_argv[0] = inst->db_name; + + CHECK(dns_zone_create(&raw, inst->mctx)); + CHECK(dns_zone_setorigin(raw, name)); + dns_zone_setclass(raw, dns_rdataclass_in); + dns_zone_settype(raw, dns_zone_master); + CHECK(dns_zone_setdbtype(raw, 1, zone_argv)); + CHECK(dns_zonemgr_managezone(inst->zmgr, raw)); + + /* This is completely insecure - use some sensible values instead! */ + CHECK(dns_acl_any(inst->mctx, &acl_any)); + dns_zone_setupdateacl(raw, acl_any); + dns_zone_setqueryacl(raw, acl_any); + dns_zone_setxfracl(raw, acl_any); + dns_acl_detach(&acl_any); + + *rawp = raw; + return (ISC_R_SUCCESS); + +cleanup: + dns_name_format(name, zone_name, DNS_NAME_FORMATSIZE); + log_error_r("failed to create new zone '%s'", zone_name); + + if (raw != NULL) { + if (dns_zone_getmgr(raw) != NULL) + dns_zonemgr_releasezone(inst->zmgr, raw); + dns_zone_detach(&raw); + } + if (acl_any != NULL) + dns_acl_detach(&acl_any); + + return (result); +} + +/* + * Add zone to the view defined in inst->view. This will make the zone visible + * to clients. + */ +static isc_result_t +publish_zone(sample_instance_t *inst, dns_zone_t *zone) { + isc_result_t result; + bool freeze = false; + dns_zone_t *zone_in_view = NULL; + dns_view_t *view_in_zone = NULL; + isc_result_t lock_state = ISC_R_IGNORE; + + REQUIRE(inst != NULL); + REQUIRE(zone != NULL); + + /* Return success if the zone is already in the view as expected. */ + result = dns_view_findzone(inst->view, dns_zone_getorigin(zone), + &zone_in_view); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto cleanup; + + view_in_zone = dns_zone_getview(zone); + if (view_in_zone != NULL) { + /* Zone has a view set -> view should contain the same zone. */ + if (zone_in_view == zone) { + /* Zone is already published in the right view. */ + CLEANUP_WITH(ISC_R_SUCCESS); + } else if (view_in_zone != inst->view) { + /* + * Un-published inactive zone will have + * inst->view in zone but will not be present + * in the view itself. + */ + dns_zone_log(zone, ISC_LOG_ERROR, + "zone->view doesn't " + "match data in the view"); + CLEANUP_WITH(ISC_R_UNEXPECTED); + } + } + + if (zone_in_view != NULL) { + dns_zone_log(zone, ISC_LOG_ERROR, + "cannot publish zone: view already " + "contains another zone with this name"); + CLEANUP_WITH(ISC_R_UNEXPECTED); + } + + run_exclusive_enter(inst, &lock_state); + if (inst->view->frozen) { + freeze = true; + dns_view_thaw(inst->view); + } + + dns_zone_setview(zone, inst->view); + CHECK(dns_view_addzone(inst->view, zone)); + +cleanup: + if (zone_in_view != NULL) + dns_zone_detach(&zone_in_view); + if (freeze) + dns_view_freeze(inst->view); + run_exclusive_exit(inst, lock_state); + + return (result); +} + +/* + * @warning Never call this on raw part of in-line secure zone, call it only + * on the secure zone! + */ +static isc_result_t +load_zone(dns_zone_t *zone) { + isc_result_t result; + bool zone_dynamic; + uint32_t serial; + + result = dns_zone_load(zone); + if (result != ISC_R_SUCCESS && result != DNS_R_UPTODATE + && result != DNS_R_DYNAMIC && result != DNS_R_CONTINUE) + goto cleanup; + zone_dynamic = (result == DNS_R_DYNAMIC); + + CHECK(dns_zone_getserial2(zone, &serial)); + dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u", serial); + + if (zone_dynamic) + dns_zone_notify(zone); + +cleanup: + return (result); +} + +/* + * Add zone to view and call dns_zone_load(). + */ +isc_result_t +activate_zone(sample_instance_t *inst, dns_zone_t *raw) { + isc_result_t result; + + /* + * Zone has to be published *before* zone load + * otherwise it will race with zone->view != NULL check + * in zone_maintenance() in zone.c. + */ + result = publish_zone(inst, raw); + if (result != ISC_R_SUCCESS) { + dns_zone_log(raw, ISC_LOG_ERROR, + "cannot add zone to view: %s", + dns_result_totext(result)); + goto cleanup; + } + + CHECK(load_zone(raw)); + +cleanup: + return (result); +} diff --git a/bin/tests/system/dyndb/driver/zone.h b/bin/tests/system/dyndb/driver/zone.h new file mode 100644 index 0000000..a862691 --- /dev/null +++ b/bin/tests/system/dyndb/driver/zone.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2014-2015 Red Hat ; see COPYRIGHT for license + */ + +#ifndef ZONE_H_ +#define ZONE_H_ + +isc_result_t +create_zone(sample_instance_t * const inst, dns_name_t * const name, + dns_zone_t ** const rawp); + +isc_result_t +activate_zone(sample_instance_t *inst, dns_zone_t *raw); + +#endif /* ZONE_H_ */ |