diff --git a/CHANGES b/CHANGES index 82c58c19cc..dc30f6c176 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,17 @@ +4224. [func] Added support for "dyndb", a new interface for loading + zone data from an external database, developed by + Red Hat for the FreeIPA project. + + DynDB drivers fully implement the BIND database + API, and are capable of significantly better + performance and functionality than DLZ drivers, + while taking advantage of advanced database + features not available in BIND such as multi-master + replication. + + Thanks to Adam Tkac and Petr Spacek of Red Hat. + [RT #35271] + 4223. [func] Add support for setting max-cache-size to percentage of available physical memory, set default to 90%. [RT #38442] diff --git a/COPYRIGHT b/COPYRIGHT index 93dd09d2e2..2d3c6ac47b 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -536,3 +536,18 @@ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- + +Copyright (C) 2008-2011 Red Hat, Inc. + +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 Red Hat DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL Red Hat 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/named/Makefile.in b/bin/named/Makefile.in index efc58bcf3f..15c645d7b8 100644 --- a/bin/named/Makefile.in +++ b/bin/named/Makefile.in @@ -13,8 +13,6 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.116 2011/03/10 23:47:49 tbox Exp $ - srcdir = @srcdir@ VPATH = @srcdir@ top_srcdir = @top_srcdir@ @@ -136,6 +134,7 @@ config.@O@: config.c ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \ -DVERSION=\"${VERSION}\" \ -DSRCID=\"${SRCID}\" \ + -DDYNDB_LIBDIR=\"@libdir@/bind\" \ -DNS_LOCALSTATEDIR=\"${localstatedir}\" \ -DNS_SYSCONFDIR=\"${sysconfdir}\" \ -c ${srcdir}/config.c diff --git a/bin/named/main.c b/bin/named/main.c index 60ceb4721f..bdbbf92102 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -43,6 +43,7 @@ #include #include +#include #include #include #include diff --git a/bin/named/server.c b/bin/named/server.c index 9a11df915c..02665cd97b 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -1349,6 +1351,32 @@ configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) { return (result); } +static isc_result_t +configure_dyndb(const cfg_obj_t *dyndb, isc_mem_t *mctx, + const dns_dyndbctx_t *dctx) +{ + isc_result_t result = ISC_R_SUCCESS; + const cfg_obj_t *obj; + const char *name, *library; + + /* Get the name of the dyndb instance and the library path . */ + name = cfg_obj_asstring(cfg_tuple_get(dyndb, "name")); + library = cfg_obj_asstring(cfg_tuple_get(dyndb, "library")); + + obj = cfg_tuple_get(dyndb, "parameters"); + if (obj != NULL) + result = dns_dyndb_load(library, name, + cfg_obj_asstring(obj), mctx, dctx); + + if (result != ISC_R_SUCCESS) + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_SERVER, ISC_LOG_ERROR, + "dynamic database '%s' configuration failed: %s", + name, isc_result_totext(result)); + return (result); +} + + static isc_result_t disable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) { isc_result_t result; @@ -2397,6 +2425,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, const cfg_obj_t *dlz; unsigned int dlzargc; char **dlzargv; + const cfg_obj_t *dyndb_list; const cfg_obj_t *disabled; const cfg_obj_t *obj, *obj2; const cfg_listelt_t *element; @@ -2438,6 +2467,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, ns_cfgctx_t *nzctx; isc_boolean_t old_rpz_ok = ISC_FALSE; isc_dscp_t dscp4 = -1, dscp6 = -1; + dns_dyndbctx_t *dctx = NULL; REQUIRE(DNS_VIEW_VALID(view)); @@ -2636,7 +2666,8 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, goto cleanup; } - result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv); + result = isc_commandline_strtoargv(mctx, s, &dlzargc, + &dlzargv, 0); if (result != ISC_R_SUCCESS) { isc_mem_free(mctx, s); goto cleanup; @@ -3785,6 +3816,31 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, } else dns_view_setrootdelonly(view, ISC_FALSE); + /* + * Load DynDB modules. + */ + dyndb_list = NULL; + if (voptions != NULL) + (void)cfg_map_get(voptions, "dyndb", &dyndb_list); + else + (void)cfg_map_get(config, "dyndb", &dyndb_list); + + for (element = cfg_list_first(dyndb_list); + element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *dyndb = cfg_listelt_value(element); + + if (dctx == NULL) + CHECK(dns_dyndb_createctx(mctx, isc_hashctx, + ns_g_lctx, view, + ns_g_server->zonemgr, + ns_g_server->task, + ns_g_timermgr, &dctx)); + + CHECK(configure_dyndb(dyndb, mctx, dctx)); + } + /* * Setup automatic empty zones. If recursion is off then * they are disabled by default. @@ -3983,6 +4039,8 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, if (cache != NULL) dns_cache_detach(&cache); + if (dctx != NULL) + dns_dyndb_destroyctx(&dctx); return (result); } @@ -5634,6 +5692,11 @@ load_configuration(const char *filename, ns_server_t *server, cfg_aclconfctx_detach(&ns_g_aclconfctx); CHECK(cfg_aclconfctx_create(ns_g_mctx, &ns_g_aclconfctx)); + /* + * Shut down all dyndb instances. + */ + dns_dyndb_cleanup(ISC_FALSE); + /* * Parse the global default pseudo-config file. */ @@ -6922,6 +6985,8 @@ shutdown_server(isc_task_t *task, isc_event_t *event) { dns_view_detach(&view); } + dns_dyndb_cleanup(ISC_TRUE); + while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) { ISC_LIST_UNLINK(server->cachelist, nsc, link); dns_cache_detach(&nsc->cache); diff --git a/bin/tests/Makefile.in b/bin/tests/Makefile.in index 8b402f2349..788081c824 100644 --- a/bin/tests/Makefile.in +++ b/bin/tests/Makefile.in @@ -13,8 +13,6 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.145 2011/02/03 05:41:53 marka Exp $ - srcdir = @srcdir@ VPATH = @srcdir@ top_srcdir = @top_srcdir@ @@ -42,8 +40,9 @@ LWRESDEPLIBS = ../../lib/lwres/liblwres.@A@ LIBS = @LIBS@ -SUBDIRS = atomic db dst master mem hashes names net rbt resolver \ - sockaddr tasks timers system @PKCS11_TOOLS@ +SUBDIRS = atomic db dst master mem hashes names \ + net rbt resolver sockaddr tasks timers system \ + @PKCS11_TOOLS@ # Test programs that are built by default: # cfg_test is needed for regenerating doc/misc/options diff --git a/bin/tests/system/Makefile.in b/bin/tests/system/Makefile.in index a9d65e3c6b..63dc27fe13 100644 --- a/bin/tests/system/Makefile.in +++ b/bin/tests/system/Makefile.in @@ -13,15 +13,13 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.38 2011/11/01 18:35:53 each Exp $ - srcdir = @srcdir@ VPATH = @srcdir@ top_srcdir = @top_srcdir@ @BIND9_MAKE_INCLUDES@ -SUBDIRS = builtin dlzexternal filter-aaaa geoip lwresd pipelined \ +SUBDIRS = builtin dlzexternal dyndb filter-aaaa geoip lwresd pipelined \ resolver rndc rpz rsabigexponent statistics tkey tsiggss TARGETS = diff --git a/bin/tests/system/dlzexternal/tests.sh b/bin/tests/system/dlzexternal/tests.sh index e7f09c9630..1b8313cdd8 100644 --- a/bin/tests/system/dlzexternal/tests.sh +++ b/bin/tests/system/dlzexternal/tests.sh @@ -14,8 +14,6 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# tests for TSIG-GSS updates - SYSTEMTESTTOP=.. . $SYSTEMTESTTOP/conf.sh diff --git a/bin/tests/system/dyndb/Makefile.in b/bin/tests/system/dyndb/Makefile.in new file mode 100644 index 0000000000..c7792f2133 --- /dev/null +++ b/bin/tests/system/dyndb/Makefile.in @@ -0,0 +1,26 @@ +# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +# +# 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 ISC 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. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +@BIND9_VERSION@ + +@BIND9_MAKE_INCLUDES@ + +SUBDIRS = driver +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/bin/tests/system/dyndb/clean.sh b/bin/tests/system/dyndb/clean.sh new file mode 100644 index 0000000000..2273396485 --- /dev/null +++ b/bin/tests/system/dyndb/clean.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +# +# 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 ISC 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. + +# +# Clean up after dyndb tests. +# +rm -f ns1/named.memstats +rm -f ns1/update.txt +rm -f added.a.out.* +rm -f added.ptr.out.* +rm -f deleted.a.out.* +rm -f deleted.ptr.out.* diff --git a/bin/tests/system/dyndb/driver/AUTHORS b/bin/tests/system/dyndb/driver/AUTHORS new file mode 100644 index 0000000000..acc109cc26 --- /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 +Jiri Kuncar +Martin Nagy +Petr Spacek diff --git a/bin/tests/system/dyndb/driver/COPYING b/bin/tests/system/dyndb/driver/COPYING new file mode 100644 index 0000000000..08d4d77c8a --- /dev/null +++ b/bin/tests/system/dyndb/driver/COPYING @@ -0,0 +1,15 @@ +Copyright (C) 2009-2015 Red Hat +Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") +Copyright (C) 1999-2003 Internet Software Consortium. + +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 0000000000..7c6923ad42 --- /dev/null +++ b/bin/tests/system/dyndb/driver/Makefile.in @@ -0,0 +1,55 @@ +# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +# +# 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 ISC 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. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +@BIND9_MAKE_INCLUDES@ + +CINCLUDES = ${DNS_INCLUDES} ${ISC_INCLUDES} + +CDEFINES = +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@ + +TARGETS = sample.@SO@ + +@BIND9_MAKE_RULES@ + +CFLAGS = @CFLAGS@ @SO_CFLAGS@ +SO_LDFLAGS = @LDFLAGS@ @SO_LDFLAGS@ + +sample.@SO@: ${OBJS} + ${LIBTOOL_MODE_LINK} @SO_LD@ ${SO_LDFLAGS} -o $@ ${OBJS} \ + ${DNSLIBS} ${ISCLIBS} + +clean distclean:: + rm -f ${OBJS} sample.so diff --git a/bin/tests/system/dyndb/driver/README b/bin/tests/system/dyndb/driver/README new file mode 100644 index 0000000000..9aac0a621b --- /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 0000000000..ba247f6142 --- /dev/null +++ b/bin/tests/system/dyndb/driver/db.c @@ -0,0 +1,814 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#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, isc_boolean_t 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, isc_boolean_t 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)); + + return (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 isc_boolean_t +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 ISC_TRUE. + */ +static isc_boolean_t +ispersistent(dns_db_t *db) { + UNUSED(db); + + return (ISC_TRUE); +} + +static void +overmem(dns_db_t *db, isc_boolean_t overmem) { + sampledb_t *sampledb = (sampledb_t *) db; + + REQUIRE(VALID_SAMPLEDB(sampledb)); + + dns_db_overmem(sampledb->rbtdb, overmem); +} + +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, isc_uint8_t *flags, + isc_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, isc_boolean_t 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 isc_boolean_t +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, + isc_boolean_t 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 unsigned int +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 +}; + +/* 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, ISC_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, ISC_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, ISC_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 = { 0x0100007f }; + + 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 */ + + 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, ISC_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 0000000000..80693a7d90 --- /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 0000000000..f9c9213a3c --- /dev/null +++ b/bin/tests/system/dyndb/driver/driver.c @@ -0,0 +1,138 @@ +/* + * 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 {} 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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] argc Number of arg parameters + * definition. The example above will have + * argc = 2; + * @param[in] argv User-defined strings from arg parameters in dyndb + * definition. The example above will have + * argv[0] = "param1"; + * argv[1] = "param2"; + * @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 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); + } + + if (isc_hashctx != NULL && isc_hashctx != dctx->hctx) + isc_hash_ctxdetach(&isc_hashctx); + isc_hashctx = dctx->hctx; + + 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; + + /* 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 0000000000..ad7349aefc --- /dev/null +++ b/bin/tests/system/dyndb/driver/instance.c @@ -0,0 +1,154 @@ +/* + * 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 + +#include +#include + +#include +#include +#include +#include +#include +#include + +#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); + inst->db_name = db_name; /* const during lifetime of inst */ + isc_mem_attach(mctx, &inst->mctx); + + dns_fixedname_init(&inst->zone1_fn); + inst->zone1_name = dns_fixedname_name(&inst->zone1_fn); + + dns_fixedname_init(&inst->zone2_fn); + inst->zone2_name = dns_fixedname_name(&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->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 0000000000..2e5b4c8637 --- /dev/null +++ b/bin/tests/system/dyndb/driver/instance.h @@ -0,0 +1,47 @@ +/** + * Driver instance object. + * + * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license + */ + +#ifndef _LD_INSTANCE_H_ +#define _LD_INSTANCE_H_ + +#include +#include +#include + +struct sample_instance { + isc_mem_t *mctx; + const 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; + isc_boolean_t 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 0000000000..c97c490451 --- /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 + +#include +#include + +#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 0000000000..35c9c84e81 --- /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 0000000000..2238c7e087 --- /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 + +#include + +#include + +#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 0000000000..27b38c87b8 --- /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 +#include +#include + +#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 0000000000..2191bae087 --- /dev/null +++ b/bin/tests/system/dyndb/driver/syncptr.c @@ -0,0 +1,265 @@ +/* + * Automatic A/AAAA/PTR record synchronization. + * + * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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, ISC_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 0000000000..2f9b3a6eca --- /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 +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 0000000000..2a00fe37a0 --- /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 +#include + +#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 0000000000..8d957e5970 --- /dev/null +++ b/bin/tests/system/dyndb/driver/zone.c @@ -0,0 +1,191 @@ +/* + * Zone management. + * + * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license + */ + +#include + +#include + +#include +#include +#include + +#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; + isc_boolean_t freeze = ISC_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 = ISC_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; + isc_boolean_t zone_dynamic; + isc_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 0000000000..a862691941 --- /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_ */ diff --git a/bin/tests/system/dyndb/ns1/named.conf b/bin/tests/system/dyndb/ns1/named.conf new file mode 100644 index 0000000000..78d8d5d5dd --- /dev/null +++ b/bin/tests/system/dyndb/ns1/named.conf @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") + * + * 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 ISC 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. + */ + +controls { }; + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port 5300; + pid-file "named.pid"; + session-keyfile "session.key"; + listen-on { 10.53.0.1; 127.0.0.1; }; + listen-on-v6 { none; }; + recursion no; + notify yes; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.1 port 9953 allow { any; } keys { rndc_key; }; +}; + +dyndb sample "../driver/sample.so" { ipv4.example.nil. in-addr.arpa. }; +dyndb sample2 "../driver/sample.so" { ipv6.example.nil. 8.b.d.0.1.0.0.2.ip6.arpa. }; diff --git a/bin/tests/system/dyndb/prereq.sh b/bin/tests/system/dyndb/prereq.sh new file mode 100644 index 0000000000..fe7ef71e21 --- /dev/null +++ b/bin/tests/system/dyndb/prereq.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# +# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +# +# 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 ISC 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. + +../dlzexternal/dlopen || { + echo "I:dlopen() not supported - skipping dyndb test" + exit 255 +} +exit 0 diff --git a/bin/tests/system/dyndb/tests.sh b/bin/tests/system/dyndb/tests.sh new file mode 100644 index 0000000000..133f060210 --- /dev/null +++ b/bin/tests/system/dyndb/tests.sh @@ -0,0 +1,150 @@ +#!/bin/sh +# +# Copyright (C) 2010-2014 Internet Systems Consortium, Inc. ("ISC") +# +# 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 ISC 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. + +SYSTEMTESTTOP=.. +. $SYSTEMTESTTOP/conf.sh + +status=0 +n=0 + +DIGOPTS="@10.53.0.1 -p 5300" + +newtest() { + n=`expr $n + 1` + echo "${1} (${n})" + ret=0 +} + +test_add() { + host="$1" + type="$2" + ip="$3" + + cat < ns1/update.txt +server 10.53.0.1 5300 +ttl 86400 +update add $host $type $ip +send +EOF + + newtest "I:adding $host $type $ip" + $NSUPDATE ns1/update.txt > /dev/null 2>&1 || { + [ "$should_fail" ] || \ + echo "I:update failed for $host $type $ip" + return 1 + } + + out=`$DIG $DIGOPTS +noall +answer -t $type -q $host` + echo $out > added.a.out.$n + lines=`echo "$out" | grep "$ip" | wc -l` + [ $lines -eq 1 ] || { + [ "$should_fail" ] || \ + echo "I:dig output incorrect for $host $type $cmd: $out" + return 1 + } + + out=`$DIG $DIGOPTS +noall +answer -x $ip` + echo $out > added.ptr.out.$n + lines=`echo "$out" | grep "$host" | wc -l` + [ $lines -eq 1 ] || { + [ "$should_fail" ] || \ + echo "I:dig reverse output incorrect for $host $type $cmd: $out" + return 1 + } + + return 0 +} + +test_del() { + host="$1" + type="$2" + + ip=`$DIG $DIGOPTS +short $host $type` + + cat < ns1/update.txt +server 10.53.0.1 5300 +update del $host $type +send +EOF + + newtest "I:deleting $host $type (was $ip)" + $NSUPDATE ns1/update.txt > /dev/null 2>&1 || { + [ "$should_fail" ] || \ + echo "I:update failed deleting $host $type" + return 1 + } + + out=`$DIG $DIGOPTS +noall +answer -t $type -q $host` + echo $out > deleted.a.out.$n + lines=`echo "$out" | grep "$ip" | wc -l` + [ $lines -eq 0 ] || { + [ "$should_fail" ] || \ + echo "I:dig output incorrect for $host $type $cmd: $out" + return 1 + } + + out=`$DIG $DIGOPTS +noall +answer -x $ip` + echo $out > deleted.ptr.out.$n + lines=`echo "$out" | grep "$host" | wc -l` + [ $lines -eq 0 ] || { + [ "$should_fail" ] || \ + echo "I:dig reverse output incorrect for $host $type $cmd: $out" + return 1 + } + + return 0 +} + +test_add test1.ipv4.example.nil. A "10.53.0.10" || ret=1 +status=`expr $status + $ret` + +test_add test2.ipv4.example.nil. A "10.53.0.11" || ret=1 +status=`expr $status + $ret` + +test_add test3.ipv4.example.nil. A "10.53.0.12" || ret=1 +status=`expr $status + $ret` + +test_add test4.ipv6.example.nil. AAAA "2001:db8::1" || ret=1 +status=`expr $status + $ret` + +test_del test1.ipv4.example.nil. A || ret=1 +status=`expr $status + $ret` + +test_del test2.ipv4.example.nil. A || ret=1 +status=`expr $status + $ret` + +test_del test3.ipv4.example.nil. A || ret=1 +status=`expr $status + $ret` + +test_del test4.ipv6.example.nil. AAAA || ret=1 +status=`expr $status + $ret` + +echo "I:checking dyndb still works after reload" +$RNDC -c ../common/rndc.conf -s 10.53.0.1 -p 9953 reload 2>&1 | sed 's/^/I:ns1 /' + +test_add test5.ipv4.example.nil. A "10.53.0.10" || ret=1 +status=`expr $status + $ret` + +test_add test6.ipv6.example.nil. AAAA "2001:db8::1" || ret=1 +status=`expr $status + $ret` + +test_del test5.ipv4.example.nil. A || ret=1 +status=`expr $status + $ret` + +test_del test6.ipv6.example.nil. AAAA || ret=1 +status=`expr $status + $ret` + +exit $status diff --git a/configure b/configure index b90f9d9c74..32b2b48d50 100755 --- a/configure +++ b/configure @@ -21028,6 +21028,7 @@ $as_echo "#define ISC_DLZ_DLOPEN 1" >>confdefs.h fi fi +CFLAGS="$CFLAGS $SO_CFLAGS" @@ -22019,7 +22020,7 @@ ac_config_commands="$ac_config_commands chmod" # elsewhere if there's a good reason for doing so. # -ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/delv/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/named/Makefile bin/named/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/rndc/Makefile bin/tests/Makefile bin/tests/atomic/Makefile bin/tests/db/Makefile bin/tests/dst/Makefile bin/tests/dst/Kdh.+002+18602.key bin/tests/dst/Kdh.+002+18602.private bin/tests/dst/Kdh.+002+48957.key bin/tests/dst/Kdh.+002+48957.private bin/tests/dst/Ktest.+001+00002.key bin/tests/dst/Ktest.+001+54622.key bin/tests/dst/Ktest.+001+54622.private bin/tests/dst/Ktest.+003+23616.key bin/tests/dst/Ktest.+003+23616.private bin/tests/dst/Ktest.+003+49667.key bin/tests/dst/dst_2_data bin/tests/dst/t2_data_1 bin/tests/dst/t2_data_2 bin/tests/dst/t2_dsasig bin/tests/dst/t2_rsasig bin/tests/hashes/Makefile bin/tests/headerdep_test.sh bin/tests/master/Makefile bin/tests/mem/Makefile bin/tests/names/Makefile bin/tests/net/Makefile bin/tests/pkcs11/Makefile bin/tests/pkcs11/benchmarks/Makefile bin/tests/rbt/Makefile bin/tests/resolver/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/builtin/Makefile bin/tests/system/conf.sh bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/named.conf bin/tests/system/filter-aaaa/Makefile bin/tests/system/geoip/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/lwresd/Makefile bin/tests/system/pipelined/Makefile bin/tests/system/resolver/Makefile bin/tests/system/rndc/Makefile bin/tests/system/rpz/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/statistics/Makefile bin/tests/system/tkey/Makefile bin/tests/system/tsiggss/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/scripts/check-secure-delegation.pl contrib/scripts/zone-edit.sh doc/Makefile doc/arm/Makefile doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-html.xsl doc/xsl/isc-notes-latex.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/include/pk11/Makefile lib/isc/include/pkcs11/Makefile lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/unix/include/pkcs11/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/tests/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile lib/samples/Makefile lib/samples/Makefile-postinstall unit/Makefile unit/unittest.sh" +ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/delv/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/named/Makefile bin/named/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/rndc/Makefile bin/tests/Makefile bin/tests/atomic/Makefile bin/tests/db/Makefile bin/tests/dst/Makefile bin/tests/dst/Kdh.+002+18602.key bin/tests/dst/Kdh.+002+18602.private bin/tests/dst/Kdh.+002+48957.key bin/tests/dst/Kdh.+002+48957.private bin/tests/dst/Ktest.+001+00002.key bin/tests/dst/Ktest.+001+54622.key bin/tests/dst/Ktest.+001+54622.private bin/tests/dst/Ktest.+003+23616.key bin/tests/dst/Ktest.+003+23616.private bin/tests/dst/Ktest.+003+49667.key bin/tests/dst/dst_2_data bin/tests/dst/t2_data_1 bin/tests/dst/t2_data_2 bin/tests/dst/t2_dsasig bin/tests/dst/t2_rsasig bin/tests/hashes/Makefile bin/tests/headerdep_test.sh bin/tests/master/Makefile bin/tests/mem/Makefile bin/tests/names/Makefile bin/tests/net/Makefile bin/tests/pkcs11/Makefile bin/tests/pkcs11/benchmarks/Makefile bin/tests/rbt/Makefile bin/tests/resolver/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/builtin/Makefile bin/tests/system/conf.sh bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/named.conf bin/tests/system/dyndb/Makefile bin/tests/system/dyndb/driver/Makefile bin/tests/system/filter-aaaa/Makefile bin/tests/system/geoip/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/lwresd/Makefile bin/tests/system/pipelined/Makefile bin/tests/system/resolver/Makefile bin/tests/system/rndc/Makefile bin/tests/system/rpz/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/statistics/Makefile bin/tests/system/tkey/Makefile bin/tests/system/tsiggss/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/scripts/check-secure-delegation.pl contrib/scripts/zone-edit.sh doc/Makefile doc/arm/Makefile doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-html.xsl doc/xsl/isc-notes-latex.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/include/pk11/Makefile lib/isc/include/pkcs11/Makefile lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/unix/include/pkcs11/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/tests/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile lib/samples/Makefile lib/samples/Makefile-postinstall unit/Makefile unit/unittest.sh" # @@ -23067,6 +23068,8 @@ do "bin/tests/system/dlz/prereq.sh") CONFIG_FILES="$CONFIG_FILES bin/tests/system/dlz/prereq.sh" ;; "bin/tests/system/dlzexternal/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/dlzexternal/Makefile" ;; "bin/tests/system/dlzexternal/ns1/named.conf") CONFIG_FILES="$CONFIG_FILES bin/tests/system/dlzexternal/ns1/named.conf" ;; + "bin/tests/system/dyndb/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/dyndb/Makefile" ;; + "bin/tests/system/dyndb/driver/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/dyndb/driver/Makefile" ;; "bin/tests/system/filter-aaaa/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/filter-aaaa/Makefile" ;; "bin/tests/system/geoip/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/geoip/Makefile" ;; "bin/tests/system/inline/checkdsa.sh") CONFIG_FILES="$CONFIG_FILES bin/tests/system/inline/checkdsa.sh" ;; diff --git a/configure.in b/configure.in index 4145dc7455..74e1723e00 100644 --- a/configure.in +++ b/configure.in @@ -4414,6 +4414,7 @@ if test "$dlopen" = "yes"; then [Define to allow building of objects for dlopen().]) fi fi +CFLAGS="$CFLAGS $SO_CFLAGS" AC_SUBST(SO) AC_SUBST(SO_CFLAGS) @@ -4651,6 +4652,8 @@ AC_CONFIG_FILES([ bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/named.conf + bin/tests/system/dyndb/Makefile + bin/tests/system/dyndb/driver/Makefile bin/tests/system/filter-aaaa/Makefile bin/tests/system/geoip/Makefile bin/tests/system/inline/checkdsa.sh diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 52bef29cd2..500b068bc3 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -2401,6 +2401,8 @@ options { + + IPv6 Support in <acronym>BIND</acronym> 9 diff --git a/doc/arm/dyndb.xml b/doc/arm/dyndb.xml new file mode 100644 index 0000000000..4d92b22e7b --- /dev/null +++ b/doc/arm/dyndb.xml @@ -0,0 +1,105 @@ + + + + + DynDB (Dynamic Database) + + DynDB is an extension to BIND 9 which, like DLZ + (see ), allows zone data to be + retrieved from an external database. Unlike DLZ, a DynDB module + provides a full-featured BIND zone database interface. Where + DLZ translates DNS queries into real-time database lookups, + resulting in relatively poor query performance, and is unable + to handle DNSSEC-signed data due to its limited API, a DynDB + module can pre-load an in-memory database from the external + data source, providing the same performance and functionality + as zones served natively by BIND. + + + A DynDB module supporting LDAP has been created by Red Hat + and is available from + https://fedorahosted.org/bind-dyndb-ldap/. + + + A sample DynDB module for testing and developer guidance + is included with the BIND source code, in the directory + bin/tests/system/dyndb/driver. + + + + Configuring DynDB + + A DynDB database is configured with a dyndb + statement in named.conf: + + + dyndb example "driver.so" { + parameters + }; + + + The file driver.so is a DynDB module which + implements the full DNS database API. Multiple + dyndb statements can be specified, to load + different drivers or multiple instances of the same driver. + Zones provided by a DynDB module are added to the view's zone + table, and are treated as normal authoritative zones when BIND + is responding to queries. Zone configuration is handled internally + by the DynDB module. + + + The parameters are passed as an opaque + string to the DynDB module's initialization routine. Configuration + syntax will differ depending on the driver. + + + + Sample DynDB Module + + For guidance in implementation of DynDB modules, the directory + bin/tests/system/dyndb/driver. + contains a basic DynDB module. + The example sets up two zones, whose names are passed + to the module as arguments in the dyndb + statement: + + + dyndb sample "sample.so" { example.nil. arpa. }; + + + In the above example, the module is configured to create a zone + "example.nil", which can answer queries and AXFR requests, and + accept DDNS updates. At runtime, prior to any updates, the zone + contains an SOA, NS, and a single A record at the apex: + + + example.nil. 86400 IN SOA example.nil. example.nil. ( + 0 28800 7200 604800 86400 + ) + example.nil. 86400 IN NS example.nil. + example.nil. 86400 IN A 127.0.0.1 + + + When the zone is updated dynamically, the DynDB module will determine + whether the updated RR is an address (i.e., type A or AAAA) and if + so, it will automatically update the corresponding PTR record in a + reverse zone. (Updates are not stored permanently; all updates are + lost when the server is restarted.) + + + diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index c9c2cb98c1..4906f30a83 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -138,6 +138,29 @@ New Features + + + Added support for DynDB, a new interface for loading zone data + from an external database, developed by Red Hat for the FreeIPA + project. (Thanks in particular to Adam Tkac and Petr + Špaček of Red Hat for the contribution.) + + + Unlike the existing DLZ and SDB interfaces, which provide a + limited subset of database functionality within BIND — + translating DNS queries into real-time database lookups with + relatively poor performance and with no ability to handle + DNSSEC-signed data — DynDB is able to fully implement + and extend the database API used natively by BIND. + + + A DynDB module could pre-load data from an external data + source, then serve it with the same performance and + functionality as conventional BIND zones, and with the + ability to take advantage of database features not + available in BIND, such as multi-master replication. + + New quotas have been added to limit the queries that are diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in index 33463bb6ff..c00550c00d 100644 --- a/lib/dns/Makefile.in +++ b/lib/dns/Makefile.in @@ -65,7 +65,7 @@ GEOIPLINKOBJS = geoip.@O@ DNSOBJS = acache.@O@ acl.@O@ adb.@O@ badcache.@O@ byaddr.@O@ \ cache.@O@ callbacks.@O@ clientinfo.@O@ compress.@O@ \ db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \ - dlz.@O@ dns64.@O@ dnssec.@O@ ds.@O@ forward.@O@ \ + dlz.@O@ dns64.@O@ dnssec.@O@ ds.@O@ dyndb.@O@ forward.@O@ \ iptable.@O@ journal.@O@ keydata.@O@ keytable.@O@ \ lib.@O@ log.@O@ lookup.@O@ \ master.@O@ masterdump.@O@ message.@O@ \ @@ -103,7 +103,7 @@ GEOIPLINKSRCS = geoip.c DNSSRCS = acache.c acl.c adb.c badcache. byaddr.c \ cache.c callbacks.c clientinfo.c compress.c \ db.c dbiterator.c dbtable.c diff.c dispatch.c \ - dlz.c dns64.c dnssec.c ds.c forward.c geoip.c \ + dlz.c dns64.c dnssec.c ds.c dyndb.c forward.c geoip.c \ iptable.c journal.c keydata.c keytable.c lib.c log.c \ lookup.c master.c masterdump.c message.c \ name.c ncache.c nsec.c nsec3.c nta.c \ diff --git a/lib/dns/dlz.c b/lib/dns/dlz.c index 536462579c..c0e0b1dadb 100644 --- a/lib/dns/dlz.c +++ b/lib/dns/dlz.c @@ -70,6 +70,7 @@ #include +#include #include #include #include @@ -357,67 +358,6 @@ dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods, return (ISC_R_SUCCESS); } -/*% - * Helper function for dns_dlzstrtoargv(). - * Pardon the gratuitous recursion. - */ -static isc_result_t -dns_dlzstrtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp, - char ***argvp, unsigned int n) -{ - isc_result_t result; - - restart: - /* Discard leading whitespace. */ - while (*s == ' ' || *s == '\t') - s++; - - if (*s == '\0') { - /* We have reached the end of the string. */ - *argcp = n; - *argvp = isc_mem_get(mctx, n * sizeof(char *)); - if (*argvp == NULL) - return (ISC_R_NOMEMORY); - } else { - char *p = s; - while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') { - if (*p == '\n') { - *p = ' '; - goto restart; - } - p++; - } - - /* do "grouping", items between { and } are one arg */ - if (*p == '{') { - char *t = p; - /* - * shift all characters to left by 1 to get rid of '{' - */ - while (*t != '\0') { - t++; - *(t-1) = *t; - } - while (*p != '\0' && *p != '}') { - p++; - } - /* get rid of '}' character */ - if (*p == '}') { - *p = '\0'; - p++; - } - /* normal case, no "grouping" */ - } else if (*p != '\0') - *p++ = '\0'; - - result = dns_dlzstrtoargvsub(mctx, p, argcp, argvp, n + 1); - if (result != ISC_R_SUCCESS) - return (result); - (*argvp)[n] = s; - } - return (ISC_R_SUCCESS); -} - /*% * Tokenize the string "s" into whitespace-separated words, * return the number of words in '*argcp' and an array @@ -429,7 +369,7 @@ isc_result_t dns_dlzstrtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) { - return(dns_dlzstrtoargvsub(mctx, s, argcp, argvp, 0)); + return(isc_commandline_strtoargv(mctx, s, argcp, argvp, 0)); } /*% diff --git a/lib/dns/dyndb.c b/lib/dns/dyndb.c new file mode 100644 index 0000000000..ecea86ff06 --- /dev/null +++ b/lib/dns/dyndb.c @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2008-2011 Red Hat, Inc. + * + * 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 Red Hat DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL Red Hat 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. + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#if HAVE_DLFCN_H +#include +#endif + +#define CHECK(op) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) goto cleanup; \ + } while (0) + + +typedef struct dyndb_implementation dyndb_implementation_t; +struct dyndb_implementation { + isc_mem_t *mctx; + void *handle; + dns_dyndb_register_t *register_func; + dns_dyndb_destroy_t *destroy_func; + char *name; + void *inst; + LINK(dyndb_implementation_t) link; +}; + +/* + * List of dyndb implementations. Locked by dyndb_lock. + * + * These are stored here so they can be cleaned up on shutdown. + * (The order in which they are stored is not important.) + */ +static LIST(dyndb_implementation_t) dyndb_implementations; + +/* Locks dyndb_implementations. */ +static isc_mutex_t dyndb_lock; +static isc_once_t once = ISC_ONCE_INIT; + +static void +dyndb_initialize(void) { + RUNTIME_CHECK(isc_mutex_init(&dyndb_lock) == ISC_R_SUCCESS); + INIT_LIST(dyndb_implementations); +} + +static dyndb_implementation_t * +impfind(const char *name) { + dyndb_implementation_t *imp; + + for (imp = ISC_LIST_HEAD(dyndb_implementations); + imp != NULL; + imp = ISC_LIST_NEXT(imp, link)) + if (strcasecmp(name, imp->name) == 0) + return (imp); + return (NULL); +} + +#if HAVE_DLFCN_H +static isc_result_t +load_symbol(void *handle, const char *filename, + const char *symbol_name, void **symbolp) +{ + const char *errmsg; + void *symbol; + + REQUIRE(handle != NULL); + REQUIRE(symbolp != NULL && *symbolp == NULL); + + symbol = dlsym(handle, symbol_name); + if (symbol == NULL) { + errmsg = dlerror(); + if (errmsg == NULL) + errmsg = "returned function pointer is NULL"; + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR, + "failed to lookup symbol %s in " + "dyndb module '%s': %s", + symbol_name, filename, errmsg); + return (ISC_R_FAILURE); + } + dlerror(); + + *symbolp = symbol; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +load_library(isc_mem_t *mctx, const char *filename, const char *instname, + dyndb_implementation_t **impp) +{ + isc_result_t result; + void *handle = NULL; + dyndb_implementation_t *imp = NULL; + dns_dyndb_register_t *register_func = NULL; + dns_dyndb_destroy_t *destroy_func = NULL; + dns_dyndb_version_t *version_func = NULL; + int version, flags; + + REQUIRE(impp != NULL && *impp == NULL); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DYNDB, ISC_LOG_INFO, + "loading DynDB instance '%s' driver '%s'", + instname, filename); + + flags = RTLD_NOW|RTLD_LOCAL; +#ifdef RTLD_DEEPBIND + flags |= RTLD_DEEPBIND; +#endif + + handle = dlopen(filename, flags); + if (handle == NULL) + CHECK(ISC_R_FAILURE); + + /* Clear dlerror */ + dlerror(); + + CHECK(load_symbol(handle, filename, "dyndb_version", + (void **)&version_func)); + + version = version_func(NULL); + if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) || + version > DNS_DYNDB_VERSION) + { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR, + "driver API version mismatch: %d/%d", + version, DNS_DYNDB_VERSION); + CHECK(ISC_R_FAILURE); + } + + CHECK(load_symbol(handle, filename, "dyndb_init", + (void **)®ister_func)); + CHECK(load_symbol(handle, filename, "dyndb_destroy", + (void **)&destroy_func)); + + imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t)); + if (imp == NULL) + CHECK(ISC_R_NOMEMORY); + + imp->mctx = NULL; + isc_mem_attach(mctx, &imp->mctx); + imp->handle = handle; + imp->register_func = register_func; + imp->destroy_func = destroy_func; + imp->name = isc_mem_strdup(mctx, instname); + if (imp->name == NULL) + CHECK(ISC_R_NOMEMORY); + + imp->inst = NULL; + INIT_LINK(imp, link); + + *impp = imp; + imp = NULL; + +cleanup: + if (result != ISC_R_SUCCESS) + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR, + "failed to dynamically load instance '%s' " + "driver '%s': %s (%s)", instname, filename, + dlerror(), isc_result_totext(result)); + if (imp != NULL) + isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t)); + if (result != ISC_R_SUCCESS && handle != NULL) + dlclose(handle); + + return (result); +} + +static void +unload_library(dyndb_implementation_t **impp) { + dyndb_implementation_t *imp; + + REQUIRE(impp != NULL && *impp != NULL); + + imp = *impp; + + isc_mem_free(imp->mctx, imp->name); + isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t)); + + *impp = NULL; +} +#else /* HAVE_DLFCN_H */ +static isc_result_t +load_library(isc_mem_t *mctx, const char *filename, const char *instname, + dyndb_implementation_t **impp) +{ + UNUSED(mctx); + UNUSED(filename); + UNUSED(instname); + UNUSED(impp); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB, + ISC_LOG_ERROR, + "dynamic database support is not implemented") + + return (ISC_R_NOTIMPLEMENTED); +} + +static void +unload_library(dyndb_implementation_t **impp) +{ + dyndb_implementation_t *imp; + + REQUIRE(impp != NULL && *impp != NULL); + + imp = *impp; + + if (imp->handle != NULL) + dlclose(imp->handle); + + isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t)); + + *impp = NULL; +} +#endif /* HAVE_DLFCN_H */ + +isc_result_t +dns_dyndb_load(const char *libname, const char *name, const char *parameters, + isc_mem_t *mctx, const dns_dyndbctx_t *dctx) +{ + isc_result_t result; + dyndb_implementation_t *implementation = NULL; + + REQUIRE(DNS_DYNDBCTX_VALID(dctx)); + REQUIRE(name != NULL); + + RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS); + + LOCK(&dyndb_lock); + + /* duplicate instance names are not allowed */ + if (impfind(name) != NULL) + CHECK(ISC_R_EXISTS); + + CHECK(load_library(mctx, libname, name, &implementation)); + CHECK(implementation->register_func(mctx, name, parameters, dctx, + &implementation->inst)); + + APPEND(dyndb_implementations, implementation, link); + result = ISC_R_SUCCESS; + +cleanup: + if (result != ISC_R_SUCCESS) + if (implementation != NULL) + unload_library(&implementation); + + UNLOCK(&dyndb_lock); + return (result); +} + +void +dns_dyndb_cleanup(isc_boolean_t exiting) { + dyndb_implementation_t *elem; + dyndb_implementation_t *prev; + + RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS); + + LOCK(&dyndb_lock); + elem = TAIL(dyndb_implementations); + while (elem != NULL) { + prev = PREV(elem, link); + UNLINK(dyndb_implementations, elem, link); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DYNDB, ISC_LOG_INFO, + "unloading DynDB instance '%s'", elem->name); + elem->destroy_func(&elem->inst); + ENSURE(elem->inst == NULL); + unload_library(&elem); + elem = prev; + } + UNLOCK(&dyndb_lock); + + if (exiting == ISC_TRUE) + isc_mutex_destroy(&dyndb_lock); +} + +isc_result_t +dns_dyndb_createctx(isc_mem_t *mctx, isc_hash_t *hctx, isc_log_t *lctx, + dns_view_t *view, dns_zonemgr_t *zmgr, + isc_task_t *task, isc_timermgr_t *tmgr, + dns_dyndbctx_t **dctxp) { + dns_dyndbctx_t *dctx; + + REQUIRE(dctxp != NULL && *dctxp == NULL); + + dctx = isc_mem_get(mctx, sizeof(*dctx)); + if (dctx == NULL) + return (ISC_R_NOMEMORY); + + memset(dctx, 0, sizeof(*dctx)); + if (view != NULL) + dns_view_attach(view, &dctx->view); + if (zmgr != NULL) + dns_zonemgr_attach(zmgr, &dctx->zmgr); + if (task != NULL) + isc_task_attach(task, &dctx->task); + dctx->timermgr = tmgr; + dctx->hctx = hctx; + dctx->lctx = lctx; + dctx->refvar = &isc_bind9; + + isc_mem_attach(mctx, &dctx->mctx); + dctx->magic = DNS_DYNDBCTX_MAGIC; + + *dctxp = dctx; + + return (ISC_R_SUCCESS); +} + +void +dns_dyndb_destroyctx(dns_dyndbctx_t **dctxp) { + dns_dyndbctx_t *dctx; + + REQUIRE(dctxp != NULL && DNS_DYNDBCTX_VALID(*dctxp)); + + dctx = *dctxp; + if (dctxp == NULL) + return; + + dctx->magic = 0; + + if (dctx->view != NULL) + dns_view_detach(&dctx->view); + if (dctx->zmgr != NULL) + dns_zonemgr_detach(&dctx->zmgr); + if (dctx->task != NULL) + isc_task_detach(&dctx->task); + dctx->timermgr = NULL; + dctx->lctx = NULL; + + isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx)); + + *dctxp = NULL; +} diff --git a/lib/dns/include/dns/Makefile.in b/lib/dns/include/dns/Makefile.in index ce134c28d5..d667348e4f 100644 --- a/lib/dns/include/dns/Makefile.in +++ b/lib/dns/include/dns/Makefile.in @@ -22,8 +22,8 @@ top_srcdir = @top_srcdir@ HEADERS = acache.h acl.h adb.h badcache.h bit.h byaddr.h \ cache.h callbacks.h cert.h \ client.h clientinfo.h compress.h \ - db.h dbiterator.h dbtable.h diff.h dispatch.h \ - dlz.h dlz_dlopen.h dns64.h dnssec.h ds.h dsdigest.h \ + db.h dbiterator.h dbtable.h diff.h dispatch.h dlz.h \ + dlz_dlopen.h dns64.h dnssec.h ds.h dsdigest.h dynamic_db.h \ edns.h ecdb.h events.h fixedname.h forward.h geoip.h iptable.h \ journal.h keydata.h keyflags.h keytable.h keyvalues.h \ lib.h lookup.h log.h master.h masterdump.h message.h \ diff --git a/lib/dns/include/dns/dyndb.h b/lib/dns/include/dns/dyndb.h new file mode 100644 index 0000000000..4a903dbb5f --- /dev/null +++ b/lib/dns/include/dns/dyndb.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2008-2011 Red Hat, Inc. + * + * 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 DISCLAIM ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL AUTHORS 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. + */ + +#ifndef DNS_DYNDB_H +#define DNS_DYNDB_H + +#include + +#include + +ISC_LANG_BEGINDECLS + +/*! + * \brief + * Context for intializing a dyndb module. + * + * This structure passes pointers to globals to which a dyndb + * module will need access -- the server memory context, hash + * context, log context, etc. The structure doesn't persist + * beyond configuring the dyndb module. The module's register function + * should attach to all reference-counted variables and its destroy + * function should detach from them. + */ +struct dns_dyndbctx { + unsigned int magic; + isc_mem_t *mctx; + isc_hash_t *hctx; + isc_log_t *lctx; + dns_view_t *view; + dns_zonemgr_t *zmgr; + isc_task_t *task; + isc_timermgr_t *timermgr; + isc_boolean_t *refvar; +}; + +#define DNS_DYNDBCTX_MAGIC ISC_MAGIC('D', 'd', 'b', 'c') +#define DNS_DYNDBCTX_VALID(d) ISC_MAGIC_VALID(d, DNS_DYNDBCTX_MAGIC) + +/* + * API version + * + * When the API changes, increment DNS_DYNDB_VERSION. If the + * change is backward-compatible (e.g., adding a new function call + * but not changing or removing an old one), increment DNS_DYNDB_AGE; + * if not, set DNS_DYNDB_AGE to 0. + */ +#ifndef DNS_DYNDB_VERSION +#define DNS_DYNDB_VERSION 1 +#define DNS_DYNDB_AGE 0 +#endif + +typedef isc_result_t dns_dyndb_register_t(isc_mem_t *mctx, + const char *name, + const char *parameters, + const dns_dyndbctx_t *dctx, + void **instp); +/*% + * Called when registering a new driver instance. 'name' must be unique. + * 'parameters' contains the driver configuration text. 'dctx' is the + * initialization context set up in dns_dyndb_createctx(). + * + * '*instp' must be set to the driver instance handle if the functino + * is successful. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li Other errors are possible + */ + +typedef void dns_dyndb_destroy_t(void **instp); +/*% + * Destroy a driver instance. Dereference any reference-counted + * variables passed in 'dctx' and 'inst' in the register function. + * + * \c *instp must be set to \c NULL by the function before it returns. + */ + +typedef int dns_dyndb_version_t(unsigned int *flags); +/*% + * Return the API version number a dyndb module was compiled with. + * + * If the returned version number is no greater than than + * DNS_DYNDB_VERSION, and no less than DNS_DYNDB_VERSION - DNS_DYNDB_AGE, + * then the module is API-compatible with named. + * + * 'flags' is currently unused and may be NULL, but could be used in + * the future to pass back driver capabilities or other information. + */ + +isc_result_t +dns_dyndb_load(const char *libname, const char *name, const char *parameters, + isc_mem_t *mctx, const dns_dyndbctx_t *dctx); +/*% + * Load a dyndb module. + * + * This loads a dyndb module using dlopen(), calls its register + * function (see dns_dyndb_register_t above), and if successful, adds + * the instance handle to a list of dyndb instances so it can be cleaned + * up later. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li Other errors are possible + */ + +void +dns_dyndb_cleanup(isc_boolean_t exiting); +/*% + * Shut down and destroy all running dyndb modules. + * + * 'exiting' indicates whether the server is shutting down, + * as opposed to merely being reconfigured. + */ + +isc_result_t +dns_dyndb_createctx(isc_mem_t *mctx, isc_hash_t *hctx, isc_log_t *lctx, + dns_view_t *view, dns_zonemgr_t *zmgr, + isc_task_t *task, isc_timermgr_t *tmgr, + dns_dyndbctx_t **dctxp); +/*% + * Create a dyndb initialization context structure, with + * pointers to structures in the server that the dyndb module will + * need to access (view, zone manager, memory context, hash context, + * etc). This structure is expected to last only until all dyndb + * modules have been loaded and initialized; after that it will be + * destroyed with dns_dyndb_destroyctx(). + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li Other errors are possible + */ + +void +dns_dyndb_destroyctx(dns_dyndbctx_t **dctxp); +/*% + * Destroys a dyndb initialization context structure; all + * reference-counted members are detached and the structure is freed. + */ + +ISC_LANG_ENDDECLS + +#endif /* DNS_DYNDB_H */ diff --git a/lib/dns/include/dns/log.h b/lib/dns/include/dns/log.h index 533c42679d..91ebbe0964 100644 --- a/lib/dns/include/dns/log.h +++ b/lib/dns/include/dns/log.h @@ -79,6 +79,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[]; #define DNS_LOGMODULE_CRYPTO (&dns_modules[28]) #define DNS_LOGMODULE_PACKETS (&dns_modules[29]) #define DNS_LOGMODULE_NTA (&dns_modules[30]) +#define DNS_LOGMODULE_DYNDB (&dns_modules[31]) ISC_LANG_BEGINDECLS diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 412b45d27e..dc02cd2b02 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -62,6 +62,7 @@ typedef void dns_dbversion_t; typedef struct dns_dlzimplementation dns_dlzimplementation_t; typedef struct dns_dlzdb dns_dlzdb_t; typedef ISC_LIST(dns_dlzdb_t) dns_dlzdblist_t; +typedef struct dns_dyndbctx dns_dyndbctx_t; typedef struct dns_sdlzimplementation dns_sdlzimplementation_t; typedef struct dns_decompress dns_decompress_t; typedef struct dns_dispatch dns_dispatch_t; diff --git a/lib/dns/lib.c b/lib/dns/lib.c index 329d04d8f0..11b42f82ed 100644 --- a/lib/dns/lib.c +++ b/lib/dns/lib.c @@ -156,7 +156,9 @@ dns_lib_shutdown(void) { return; dst_lib_destroy(); - isc_hash_destroy(); + + if (isc_hashctx != NULL) + isc_hash_destroy(); if (dbimp != NULL) dns_ecdb_unregister(&dbimp); if (dns_g_mctx != NULL) diff --git a/lib/dns/log.c b/lib/dns/log.c index 101c26639d..cff764bf54 100644 --- a/lib/dns/log.c +++ b/lib/dns/log.c @@ -85,6 +85,7 @@ LIBDNS_EXTERNAL_DATA isc_logmodule_t dns_modules[] = { { "dns/crypto", 0 }, { "dns/packets", 0 }, { "dns/nta", 0 }, + { "dns/dyndb", 0 }, { NULL, 0 } }; diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index 06b1911527..605e56daef 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -321,6 +321,10 @@ dns_dumpctx_cancel dns_dumpctx_db dns_dumpctx_detach dns_dumpctx_version +dns_dyndb_load +dns_dyndb_cleanup +dns_dyndb_createctx +dns_dyndb_destroyctx dns_ecdb_register dns_ecdb_unregister dns_fwdtable_add diff --git a/lib/isc/commandline.c b/lib/isc/commandline.c index 416fba1927..c4e18df496 100644 --- a/lib/isc/commandline.c +++ b/lib/isc/commandline.c @@ -64,6 +64,7 @@ #include #include +#include #include #include #include @@ -220,3 +221,62 @@ isc_commandline_parse(int argc, char * const *argv, const char *options) { return (isc_commandline_option); } + +isc_result_t +isc_commandline_strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, + char ***argvp, unsigned int n) +{ + isc_result_t result; + + restart: + /* Discard leading whitespace. */ + while (*s == ' ' || *s == '\t') + s++; + + if (*s == '\0') { + /* We have reached the end of the string. */ + *argcp = n; + *argvp = isc_mem_get(mctx, n * sizeof(char *)); + if (*argvp == NULL) + return (ISC_R_NOMEMORY); + } else { + char *p = s; + while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') { + if (*p == '\n') { + *p = ' '; + goto restart; + } + p++; + } + + /* do "grouping", items between { and } are one arg */ + if (*p == '{') { + char *t = p; + /* + * shift all characters to left by 1 to get rid of '{' + */ + while (*t != '\0') { + t++; + *(t-1) = *t; + } + while (*p != '\0' && *p != '}') { + p++; + } + /* get rid of '}' character */ + if (*p == '}') { + *p = '\0'; + p++; + } + /* normal case, no "grouping" */ + } else if (*p != '\0') + *p++ = '\0'; + + result = isc_commandline_strtoargv(mctx, p, + argcp, argvp, n + 1); + if (result != ISC_R_SUCCESS) + return (result); + (*argvp)[n] = s; + } + + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/hash.c b/lib/isc/hash.c index 4005e35a22..00d869261d 100644 --- a/lib/isc/hash.c +++ b/lib/isc/hash.c @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: hash.c,v 1.16 2009/09/01 00:22:28 jinmei Exp $ */ - /*! \file * Some portion of this code was derived from universal hash function * libraries of Rice University. @@ -101,7 +99,8 @@ struct isc_hash { static isc_mutex_t createlock; static isc_once_t once = ISC_ONCE_INIT; -static isc_hash_t *hash = NULL; + +LIBISC_EXTERNAL_DATA isc_hash_t *isc_hashctx = NULL; static unsigned char maptolower[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -220,14 +219,15 @@ isc_hash_create(isc_mem_t *mctx, isc_entropy_t *entropy, size_t limit) { isc_result_t result = ISC_R_SUCCESS; REQUIRE(mctx != NULL); - INSIST(hash == NULL); + INSIST(isc_hashctx == NULL); RUNTIME_CHECK(isc_once_do(&once, initialize_lock) == ISC_R_SUCCESS); LOCK(&createlock); - if (hash == NULL) - result = isc_hash_ctxcreate(mctx, entropy, limit, &hash); + if (isc_hashctx == NULL) + result = isc_hash_ctxcreate(mctx, entropy, limit, + &isc_hashctx); UNLOCK(&createlock); @@ -276,9 +276,9 @@ isc_hash_ctxinit(isc_hash_t *hctx) { void isc_hash_init(void) { - INSIST(hash != NULL && VALID_HASH(hash)); + INSIST(isc_hashctx != NULL && VALID_HASH(isc_hashctx)); - isc_hash_ctxinit(hash); + isc_hash_ctxinit(isc_hashctx); } void @@ -337,12 +337,12 @@ void isc_hash_destroy(void) { unsigned int refs; - INSIST(hash != NULL && VALID_HASH(hash)); + INSIST(isc_hashctx != NULL && VALID_HASH(isc_hashctx)); - isc_refcount_decrement(&hash->refcnt, &refs); + isc_refcount_decrement(&isc_hashctx->refcnt, &refs); INSIST(refs == 0); - destroy(&hash); + destroy(&isc_hashctx); } static inline unsigned int @@ -384,10 +384,10 @@ unsigned int isc_hash_calc(const unsigned char *key, unsigned int keylen, isc_boolean_t case_sensitive) { - INSIST(hash != NULL && VALID_HASH(hash)); - REQUIRE(keylen <= hash->limit); + INSIST(isc_hashctx != NULL && VALID_HASH(isc_hashctx)); + REQUIRE(keylen <= isc_hashctx->limit); - return (hash_calc(hash, key, keylen, case_sensitive)); + return (hash_calc(isc_hashctx, key, keylen, case_sensitive)); } void @@ -395,10 +395,10 @@ isc__hash_setvec(const isc_uint16_t *vec) { int i; hash_random_t *p; - if (hash == NULL) + if (isc_hashctx == NULL) return; - p = hash->rndvector; + p = isc_hashctx->rndvector; for (i = 0; i < 256; i++) { p[i] = vec[i]; } diff --git a/lib/isc/include/isc/commandline.h b/lib/isc/include/isc/commandline.h index 384640a45b..d35ccbf1ae 100644 --- a/lib/isc/include/isc/commandline.h +++ b/lib/isc/include/isc/commandline.h @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: commandline.h,v 1.16 2007/06/19 23:47:18 tbox Exp $ */ - #ifndef ISC_COMMANDLINE_H #define ISC_COMMANDLINE_H 1 @@ -25,6 +23,7 @@ #include #include #include +#include /*% Index into parent argv vector. */ LIBISC_EXTERNAL_DATA extern int isc_commandline_index; @@ -41,9 +40,22 @@ LIBISC_EXTERNAL_DATA extern isc_boolean_t isc_commandline_reset; ISC_LANG_BEGINDECLS -/*% parse command line */ int isc_commandline_parse(int argc, char * const *argv, const char *options); +/*%< + * Parse a command line (similar to getopt()) + */ + +isc_result_t +isc_commandline_strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, + char ***argvp, unsigned int n); +/*%< + * Tokenize the string "s" into whitespace-separated words, + * returning the number of words in '*argcp' and an array + * of pointers to the words in '*argvp'. The caller + * must free the array using isc_mem_free(). The string + * is modified in-place. + */ ISC_LANG_ENDDECLS diff --git a/lib/isc/include/isc/hash.h b/lib/isc/include/isc/hash.h index 1eeba87dd7..33c4339f17 100644 --- a/lib/isc/include/isc/hash.h +++ b/lib/isc/include/isc/hash.h @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: hash.h,v 1.12 2009/01/17 23:47:43 tbox Exp $ */ - #ifndef ISC_HASH_H #define ISC_HASH_H 1 @@ -83,6 +81,8 @@ ***/ ISC_LANG_BEGINDECLS +LIBDNS_EXTERNAL_DATA extern isc_hash_t *isc_hashctx; + isc_result_t isc_hash_ctxcreate(isc_mem_t *mctx, isc_entropy_t *entropy, size_t limit, isc_hash_t **hctx); diff --git a/lib/isc/include/isc/lex.h b/lib/isc/include/isc/lex.h index 8612150991..9312dd8f3c 100644 --- a/lib/isc/include/isc/lex.h +++ b/lib/isc/include/isc/lex.h @@ -90,6 +90,7 @@ ISC_LANG_BEGINDECLS #define ISC_LEXOPT_ESCAPE 0x100 /*%< Recognize escapes. */ #define ISC_LEXOPT_QSTRINGMULTILINE 0x200 /*%< Allow multiline "" strings */ #define ISC_LEXOPT_OCTAL 0x400 /*%< Expect a octal number. */ +#define ISC_LEXOPT_BTEXT 0x800 /*%< Bracketed text. */ /*@}*/ /*@{*/ /*! @@ -122,7 +123,8 @@ typedef enum { isc_tokentype_eof = 5, isc_tokentype_initialws = 6, isc_tokentype_special = 7, - isc_tokentype_nomore = 8 + isc_tokentype_nomore = 8, + isc_tokentype_btext = 8 } isc_tokentype_t; typedef union { diff --git a/lib/isc/lex.c b/lib/isc/lex.c index ce1c559941..4a0debc463 100644 --- a/lib/isc/lex.c +++ b/lib/isc/lex.c @@ -63,6 +63,7 @@ struct isc_lex { unsigned int comments; isc_boolean_t comment_ok; isc_boolean_t last_was_eol; + unsigned int brace_count; unsigned int paren_count; unsigned int saved_paren_count; isc_lexspecials_t specials; @@ -111,6 +112,7 @@ isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) { lex->comments = 0; lex->comment_ok = ISC_TRUE; lex->last_was_eol = ISC_TRUE; + lex->brace_count = 0; lex->paren_count = 0; lex->saved_paren_count = 0; memset(lex->specials, 0, 256); @@ -312,7 +314,8 @@ typedef enum { lexstate_ccomment, lexstate_ccommentend, lexstate_eatline, - lexstate_qstring + lexstate_qstring, + lexstate_btext } lexstate; #define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL) @@ -395,10 +398,17 @@ isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { source->at_eof) { if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && - lex->paren_count != 0) { + lex->paren_count != 0) + { lex->paren_count = 0; return (ISC_R_UNBALANCED); } + if ((options & ISC_LEXOPT_BTEXT) != 0 && + lex->brace_count != 0) + { + lex->brace_count = 0; + return (ISC_R_UNBALANCED); + } if ((options & ISC_LEXOPT_EOF) != 0) { tokenp->type = isc_tokentype_eof; return (ISC_R_SUCCESS); @@ -513,6 +523,12 @@ isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { result = ISC_R_UNBALANCED; goto done; } + if ((options & ISC_LEXOPT_BTEXT) != 0 && + lex->brace_count != 0) { + lex->brace_count = 0; + result = ISC_R_UNBALANCED; + goto done; + } if ((options & ISC_LEXOPT_EOF) == 0) { result = ISC_R_EOF; goto done; @@ -545,22 +561,35 @@ isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { } else if (lex->specials[c]) { lex->last_was_eol = ISC_FALSE; if ((c == '(' || c == ')') && - (options & ISC_LEXOPT_DNSMULTILINE) != 0) { + (options & ISC_LEXOPT_DNSMULTILINE) != 0) + { if (c == '(') { if (lex->paren_count == 0) options &= ~IWSEOL; lex->paren_count++; } else { if (lex->paren_count == 0) { - result = ISC_R_UNBALANCED; - goto done; + result = + ISC_R_UNBALANCED; + goto done; } lex->paren_count--; if (lex->paren_count == 0) - options = - saved_options; + options = saved_options; } continue; + } else if (c == '{' && + (options & ISC_LEXOPT_BTEXT) != 0) + { + if (lex->brace_count != 0) { + result = ISC_R_UNBALANCED; + goto done; + } + lex->brace_count++; + options &= ~IWSEOL; + state = lexstate_btext; + no_comments = ISC_TRUE; + continue; } tokenp->type = isc_tokentype_special; tokenp->value.as_char = c; @@ -778,6 +807,57 @@ isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { remaining--; } break; + case lexstate_btext: + if (c == EOF) { + result = ISC_R_UNEXPECTEDEND; + goto done; + } + if (c == '{') { + if (escaped) { + escaped = ISC_FALSE; + INSIST(prev != NULL); + *prev = '{'; + } else { + lex->brace_count++; + } + } else if (c == '}') { + if (escaped) { + escaped = ISC_FALSE; + INSIST(prev != NULL); + *prev = '}'; + break; + } + + INSIST(lex->brace_count > 0); + lex->brace_count--; + if (lex->brace_count > 0) + break; + + tokenp->type = isc_tokentype_btext; + tokenp->value.as_textregion.base = lex->data; + tokenp->value.as_textregion.length = + (unsigned int) (lex->max_token - + remaining); + no_comments = ISC_FALSE; + done = ISC_TRUE; + } else { + if (c == '\\' && !escaped) + escaped = ISC_TRUE; + else + escaped = ISC_FALSE; + if (remaining == 0U) { + result = grow_data(lex, &remaining, + &curr, &prev); + if (result != ISC_R_SUCCESS) + goto done; + } + INSIST(remaining > 0U); + prev = curr; + *curr++ = c; + *curr = '\0'; + remaining--; + } + break; default: FATAL_ERROR(__FILE__, __LINE__, isc_msgcat_get(isc_msgcat, ISC_MSGSET_LEX, diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index 56b0892811..b08795612b 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -168,6 +168,7 @@ isc_buffer_reserve isc_bufferlist_availablecount isc_bufferlist_usedcount isc_commandline_parse +isc_commandline_strtoargv isc_condition_broadcast isc_condition_destroy isc_condition_init @@ -261,6 +262,7 @@ isc_hash_ctxdetach isc_hash_ctxinit isc_hash_destroy isc_hash_init +isc_hashctx isc_heap_create isc_heap_decreased isc_heap_delete diff --git a/lib/isccfg/include/isccfg/grammar.h b/lib/isccfg/include/isccfg/grammar.h index 2546a6ca03..f20d91a7c9 100644 --- a/lib/isccfg/include/isccfg/grammar.h +++ b/lib/isccfg/include/isccfg/grammar.h @@ -281,6 +281,7 @@ LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_qstring; LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_astring; LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_ustring; LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_sstring; +LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_bracketed_text; LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_sockaddr; LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_sockaddrdscp; LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_netaddr; diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 7569bada16..cb8a8cbf5c 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -144,6 +144,7 @@ static cfg_type_t cfg_type_zone; static cfg_type_t cfg_type_zoneopts; static cfg_type_t cfg_type_filter_aaaa; static cfg_type_t cfg_type_dlz; +static cfg_type_t cfg_type_dyndb; /*% tkey-dhkey */ @@ -979,6 +980,7 @@ namedconf_or_view_clauses[] = { { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI }, { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI }, { "dlz", &cfg_type_dlz, CFG_CLAUSEFLAG_MULTI }, + { "dyndb", &cfg_type_dyndb, CFG_CLAUSEFLAG_MULTI }, { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI }, { "trusted-keys", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI }, { "managed-keys", &cfg_type_managedkeys, CFG_CLAUSEFLAG_MULTI }, @@ -1853,6 +1855,22 @@ static cfg_type_t cfg_type_dlz = { &cfg_rep_map, dlz_clausesets }; +/*% + * The "dyndb" statement syntax. + */ + +static cfg_tuplefielddef_t dyndb_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "library", &cfg_type_qstring, 0 }, + { "parameters", &cfg_type_bracketed_text, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_dyndb = { + "dyndb", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, dyndb_fields +}; + /*% * Clauses that can be found within the 'key' statement. */ diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c index 4e1eb477ce..f486acbf59 100644 --- a/lib/isccfg/parser.c +++ b/lib/isccfg/parser.c @@ -978,6 +978,42 @@ cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, return (result); } +static isc_result_t +parse_btext(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) +{ + isc_result_t result; + UNUSED(type); + + CHECK(cfg_gettoken(pctx, ISC_LEXOPT_BTEXT)); + if (pctx->token.type != isc_tokentype_btext) { + cfg_parser_error(pctx, CFG_LOG_NEAR, + "expected bracketed text"); + return (ISC_R_UNEXPECTEDTOKEN); + } + return (create_string(pctx, + TOKEN_STRING(pctx), + &cfg_type_bracketed_text, + ret)); + cleanup: + return (result); +} + +static void +print_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) { + cfg_print_cstr(pctx, "{"); + cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length); + print_close(pctx); +} + +static void +doc_btext(cfg_printer_t *pctx, const cfg_type_t *type) { + UNUSED(type); + + cfg_print_cstr(pctx, "{ }"); +} + + isc_boolean_t cfg_is_enum(const char *s, const char *const *enums) { const char * const *p; @@ -1091,6 +1127,16 @@ cfg_type_t cfg_type_sstring = { &cfg_rep_string, NULL }; +/* + * Text enclosed in brackets. Used to pass a block of configuration + * text to dynamic library or external application. Checked for + * bracket balance, but not otherwise parsed. + */ +cfg_type_t cfg_type_bracketed_text = { + "bracketed_text", parse_btext, print_btext, doc_btext, + &cfg_rep_string, NULL +}; + /* * Booleans */