From a00f9e2f50675bd43cc6a9fe2669709162a2ccb4 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Mon, 28 Sep 2015 23:12:35 -0700 Subject: [PATCH] [master] merge dyndb 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] --- CHANGES | 14 + COPYRIGHT | 15 + bin/named/Makefile.in | 3 +- bin/named/main.c | 1 + bin/named/server.c | 67 +- bin/tests/Makefile.in | 7 +- bin/tests/system/Makefile.in | 4 +- bin/tests/system/dlzexternal/tests.sh | 2 - bin/tests/system/dyndb/Makefile.in | 26 + bin/tests/system/dyndb/clean.sh | 25 + bin/tests/system/dyndb/driver/AUTHORS | 8 + bin/tests/system/dyndb/driver/COPYING | 15 + bin/tests/system/dyndb/driver/Makefile.in | 55 ++ bin/tests/system/dyndb/driver/README | 65 ++ bin/tests/system/dyndb/driver/db.c | 814 ++++++++++++++++++++++ bin/tests/system/dyndb/driver/db.h | 15 + bin/tests/system/dyndb/driver/driver.c | 138 ++++ bin/tests/system/dyndb/driver/instance.c | 154 ++++ bin/tests/system/dyndb/driver/instance.h | 47 ++ bin/tests/system/dyndb/driver/lock.c | 56 ++ bin/tests/system/dyndb/driver/lock.h | 17 + bin/tests/system/dyndb/driver/log.c | 21 + bin/tests/system/dyndb/driver/log.h | 27 + bin/tests/system/dyndb/driver/syncptr.c | 265 +++++++ bin/tests/system/dyndb/driver/syncptr.h | 15 + bin/tests/system/dyndb/driver/util.h | 57 ++ bin/tests/system/dyndb/driver/zone.c | 191 +++++ bin/tests/system/dyndb/driver/zone.h | 15 + bin/tests/system/dyndb/ns1/named.conf | 42 ++ bin/tests/system/dyndb/prereq.sh | 21 + bin/tests/system/dyndb/tests.sh | 150 ++++ configure | 5 +- configure.in | 3 + doc/arm/Bv9ARM-book.xml | 2 + doc/arm/dyndb.xml | 105 +++ doc/arm/notes.xml | 23 + lib/dns/Makefile.in | 4 +- lib/dns/dlz.c | 64 +- lib/dns/dyndb.c | 368 ++++++++++ lib/dns/include/dns/Makefile.in | 4 +- lib/dns/include/dns/dyndb.h | 159 +++++ lib/dns/include/dns/log.h | 1 + lib/dns/include/dns/types.h | 1 + lib/dns/lib.c | 4 +- lib/dns/log.c | 1 + lib/dns/win32/libdns.def.in | 4 + lib/isc/commandline.c | 60 ++ lib/isc/hash.c | 32 +- lib/isc/include/isc/commandline.h | 18 +- lib/isc/include/isc/hash.h | 4 +- lib/isc/include/isc/lex.h | 4 +- lib/isc/lex.c | 94 ++- lib/isc/win32/libisc.def.in | 2 + lib/isccfg/include/isccfg/grammar.h | 1 + lib/isccfg/namedconf.c | 18 + lib/isccfg/parser.c | 46 ++ 56 files changed, 3270 insertions(+), 109 deletions(-) create mode 100644 bin/tests/system/dyndb/Makefile.in create mode 100644 bin/tests/system/dyndb/clean.sh create mode 100644 bin/tests/system/dyndb/driver/AUTHORS create mode 100644 bin/tests/system/dyndb/driver/COPYING create mode 100644 bin/tests/system/dyndb/driver/Makefile.in create mode 100644 bin/tests/system/dyndb/driver/README create mode 100644 bin/tests/system/dyndb/driver/db.c create mode 100644 bin/tests/system/dyndb/driver/db.h create mode 100644 bin/tests/system/dyndb/driver/driver.c create mode 100644 bin/tests/system/dyndb/driver/instance.c create mode 100644 bin/tests/system/dyndb/driver/instance.h create mode 100644 bin/tests/system/dyndb/driver/lock.c create mode 100644 bin/tests/system/dyndb/driver/lock.h create mode 100644 bin/tests/system/dyndb/driver/log.c create mode 100644 bin/tests/system/dyndb/driver/log.h create mode 100644 bin/tests/system/dyndb/driver/syncptr.c create mode 100644 bin/tests/system/dyndb/driver/syncptr.h create mode 100644 bin/tests/system/dyndb/driver/util.h create mode 100644 bin/tests/system/dyndb/driver/zone.c create mode 100644 bin/tests/system/dyndb/driver/zone.h create mode 100644 bin/tests/system/dyndb/ns1/named.conf create mode 100644 bin/tests/system/dyndb/prereq.sh create mode 100644 bin/tests/system/dyndb/tests.sh create mode 100644 doc/arm/dyndb.xml create mode 100644 lib/dns/dyndb.c create mode 100644 lib/dns/include/dns/dyndb.h 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 */