+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]
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.
# 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@
${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \
-DVERSION=\"${VERSION}\" \
-DSRCID=\"${SRCID}\" \
+ -DDYNDB_LIBDIR=\"@libdir@/bind\" \
-DNS_LOCALSTATEDIR=\"${localstatedir}\" \
-DNS_SYSCONFDIR=\"${sysconfdir}\" \
-c ${srcdir}/config.c
#include <isccc/result.h>
#include <dns/dispatch.h>
+#include <dns/dyndb.h>
#include <dns/name.h>
#include <dns/result.h>
#include <dns/view.h>
#include <isc/aes.h>
#include <isc/app.h>
#include <isc/base64.h>
+#include <isc/commandline.h>
#include <isc/dir.h>
#include <isc/entropy.h>
#include <isc/file.h>
#include <dns/dispatch.h>
#include <dns/dlz.h>
#include <dns/dns64.h>
+#include <dns/dyndb.h>
#include <dns/forward.h>
#include <dns/journal.h>
#include <dns/keytable.h>
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;
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;
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));
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;
} 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.
if (cache != NULL)
dns_cache_detach(&cache);
+ if (dctx != NULL)
+ dns_dyndb_destroyctx(&dctx);
return (result);
}
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.
*/
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);
# 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@
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
# 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 =
# 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
--- /dev/null
+# 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@
--- /dev/null
+#!/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.*
--- /dev/null
+This sample driver is based on bind-dyndb-ldap project and small portions
+of code from ISC BIND 9.10.
+
+Authors listed in alphabetical order:
+Adam Tkac <atkac@redhat.com>
+Jiri Kuncar <jkuncar@redhat.com>
+Martin Nagy <mnagy@redhat.com>
+Petr Spacek <pspacek@redhat.com>
--- /dev/null
+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.
--- /dev/null
+# 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
--- /dev/null
+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.
--- /dev/null
+/*
+ * Database API implementation. The interface is defined in lib/dns/db.h.
+ *
+ * dns_db_*() calls on database instances backed by this driver use
+ * struct sampledb_methods to find appropriate function implementation.
+ *
+ * This example re-uses RBT DB implementation from original BIND and blindly
+ * proxies most of dns_db_*() calls to this underlying RBT DB.
+ * See struct sampledb below.
+ *
+ * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license
+ */
+#include <config.h>
+
+#include <dns/db.h>
+#include <dns/diff.h>
+#include <dns/enumclass.h>
+#include <dns/rbt.h>
+#include <dns/rdatalist.h>
+#include <dns/rdatastruct.h>
+#include <dns/soa.h>
+#include <dns/types.h>
+
+#include "db.h"
+#include "instance.h"
+#include "syncptr.h"
+#include "util.h"
+
+#define SAMPLEDB_MAGIC ISC_MAGIC('S', 'M', 'D', 'B')
+#define VALID_SAMPLEDB(sampledb) \
+ ((sampledb) != NULL && (sampledb)->common.impmagic == SAMPLEDB_MAGIC)
+
+struct sampledb {
+ dns_db_t common;
+ isc_refcount_t refs;
+ sample_instance_t *inst;
+
+ /*
+ * Internal RBT database implementation provided by BIND.
+ * Most dns_db_* calls (find(), createiterator(), etc.)
+ * are blindly forwarded to this RBT DB.
+ */
+ dns_db_t *rbtdb;
+};
+
+typedef struct sampledb sampledb_t;
+
+/*
+ * Get full DNS name from the node.
+ *
+ * @warning
+ * The code silently expects that "node" came from RBTDB and thus
+ * assumption dns_dbnode_t (from RBTDB) == dns_rbtnode_t is correct.
+ *
+ * This should work as long as we use only RBTDB and nothing else.
+ */
+static isc_result_t
+sample_name_fromnode(dns_dbnode_t *node, dns_name_t *name) {
+ dns_rbtnode_t *rbtnode = (dns_rbtnode_t *) node;
+ return (dns_rbt_fullnamefromnode(rbtnode, name));
+}
+
+static void
+attach(dns_db_t *source, dns_db_t **targetp) {
+ sampledb_t *sampledb = (sampledb_t *)source;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ isc_refcount_increment(&sampledb->refs, NULL);
+ *targetp = source;
+}
+
+static void
+free_sampledb(sampledb_t *sampledb) {
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_detach(&sampledb->rbtdb);
+ dns_name_free(&sampledb->common.origin, sampledb->common.mctx);
+ isc_mem_putanddetach(&sampledb->common.mctx, sampledb, sizeof(*sampledb));
+}
+
+static void
+detach(dns_db_t **dbp) {
+ sampledb_t *sampledb = (sampledb_t *)(*dbp);
+ unsigned int refs;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ isc_refcount_decrement(&sampledb->refs, &refs);
+ if (refs == 0)
+ free_sampledb(sampledb);
+ *dbp = NULL;
+}
+
+/*
+ * This method should never be called, because DB is "persistent".
+ * See ispersistent() function. It means that database do not need to be
+ * loaded in the usual sense.
+ */
+static isc_result_t
+beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+ UNUSED(db);
+ UNUSED(callbacks);
+
+ fatal_error("current implementation should never call beginload()");
+
+ /* Not reached */
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * This method should never be called, because DB is "persistent".
+ * See ispersistent() function. It means that database do not need to be
+ * loaded in the usual sense.
+ */
+static isc_result_t
+endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+ UNUSED(db);
+ UNUSED(callbacks);
+
+ fatal_error("current implementation should never call endload()");
+
+ /* Not reached */
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+serialize(dns_db_t *db, dns_dbversion_t *version, FILE *file) {
+ sampledb_t *sampledb = (sampledb_t *) db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_serialize(sampledb->rbtdb, version, file));
+}
+
+static isc_result_t
+dump(dns_db_t *db, dns_dbversion_t *version, const char *filename,
+ dns_masterformat_t masterformat)
+{
+
+ UNUSED(db);
+ UNUSED(version);
+ UNUSED(filename);
+ UNUSED(masterformat);
+
+ fatal_error("current implementation should never call dump()");
+
+ /* Not reached */
+ return (ISC_R_SUCCESS);
+}
+
+static void
+currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_currentversion(sampledb->rbtdb, versionp);
+}
+
+static isc_result_t
+newversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_newversion(sampledb->rbtdb, versionp));
+}
+
+static void
+attachversion(dns_db_t *db, dns_dbversion_t *source,
+ dns_dbversion_t **targetp)
+{
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_attachversion(sampledb->rbtdb, source, targetp);
+}
+
+static void
+closeversion(dns_db_t *db, dns_dbversion_t **versionp, 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);
+}
--- /dev/null
+/**
+ * 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_ */
--- /dev/null
+/*
+ * Driver API implementation and main entry point for BIND.
+ *
+ * BIND calls dyndb_version() before loading, dyndb_init() during startup
+ * and dyndb_destroy() during shutdown.
+ *
+ * It is completely up to implementation what to do.
+ *
+ * dyndb <name> <driver> {} sections in named.conf are independent so
+ * driver init() and destroy() functions are called independently for
+ * each section even if they reference the same driver/library. It is
+ * up to driver implementation to detect and catch this situation if
+ * it is undesirable.
+ *
+ * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license
+ */
+
+#include <config.h>
+
+#include <isc/commandline.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/lib.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dyndb.h>
+#include <dns/lib.h>
+#include <dns/types.h>
+
+#include "db.h"
+#include "log.h"
+#include "instance.h"
+#include "util.h"
+
+dns_dyndb_destroy_t dyndb_destroy;
+dns_dyndb_register_t dyndb_init;
+dns_dyndb_version_t dyndb_version;
+
+/*
+ * Driver init is called for each dyndb section in named.conf
+ * once during startup and then again on every reload.
+ *
+ * @code
+ * dyndb example-name "sample.so" { param1 param2 };
+ * @endcode
+ *
+ * @param[in] name User-defined string from dyndb "name" {}; definition
+ * in named.conf.
+ * The example above will have name = "example-name".
+ * @param[in] 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);
+}
--- /dev/null
+/*
+ * Driver instance object.
+ *
+ * One instance is equivalent to dynamic-db section in named.conf.
+ * This module parses arguments and provide high-level operations
+ * instance init/zone load/instance destroy.
+ *
+ * Copyright (C) 2008-2015 Red Hat ; see COPYRIGHT for license
+ */
+
+#include <config.h>
+
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dyndb.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include "db.h"
+#include "util.h"
+#include "instance.h"
+#include "log.h"
+#include "zone.h"
+
+/*
+ * Parse parameters and convert them to zone names. Caller has to deallocate
+ * resulting DNS names.
+ *
+ * @param[in] argv NULL-terminated string array of length 2 (excluding NULL)
+ * Each string has to be a valid DNS name.
+ * @param[out] z1 Zone name from argv[0]
+ * @param[out] z2 Zone name from argv[1]
+ */
+static isc_result_t
+parse_params(isc_mem_t *mctx, int argc, char **argv,
+ dns_name_t *z1, dns_name_t *z2)
+{
+ isc_result_t result;
+ int i;
+
+ REQUIRE(argv != NULL);
+ REQUIRE(z1 != NULL);
+ REQUIRE(z2 != NULL);
+
+ for (i = 0; i < argc; i++) {
+ log_info("param: '%s'", argv[i]);
+ }
+ log_info("number of params: %d", i);
+
+ if (argc != 2) {
+ log_error("exactly two parameters "
+ "(absolute zone names) are required");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ CHECK(dns_name_fromstring2(z1, argv[0], dns_rootname, 0, mctx));
+ CHECK(dns_name_fromstring2(z2, argv[1], dns_rootname, 0, mctx));
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ return (result);
+}
+
+/*
+ * Initialize new driver instance. It will not create zones until
+ * load_sample_instance_zones() is called.
+ */
+isc_result_t
+new_sample_instance(isc_mem_t *mctx, const char *db_name,
+ int argc, char **argv, const dns_dyndbctx_t *dctx,
+ sample_instance_t **sample_instp)
+{
+ isc_result_t result;
+ sample_instance_t *inst = NULL;
+
+ REQUIRE(sample_instp != NULL && *sample_instp == NULL);
+
+ CHECKED_MEM_GET_PTR(mctx, inst);
+ ZERO_PTR(inst);
+ 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;
+}
--- /dev/null
+/**
+ * Driver instance object.
+ *
+ * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license
+ */
+
+#ifndef _LD_INSTANCE_H_
+#define _LD_INSTANCE_H_
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/types.h>
+
+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_ */
--- /dev/null
+/*
+ * Copyright (C) 2014-2015 Red Hat ; see COPYRIGHT for license
+ */
+
+#include <config.h>
+
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include "lock.h"
+
+/*
+ * Lock BIND dispatcher and allow only single task to run.
+ *
+ * @warning
+ * All calls to isc_task_beginexclusive() have to operate on the same task
+ * otherwise it would not be possible to distinguish recursive locking
+ * from real conflict on the dispatcher lock.
+ * For this reason this wrapper function always works with inst->task.
+ * As a result, this function have to be be called only from inst->task.
+ *
+ * Recursive locking is allowed. Auxiliary variable pointed to by "statep"
+ * stores information if last run_exclusive_enter() operation really locked
+ * something or if the lock was called recursively and was no-op.
+ *
+ * The pair (inst, state) used for run_exclusive_enter() has to be
+ * used for run_exclusive_exit().
+ *
+ * @param[in] inst The instance with the only task which is allowed to run.
+ * @param[in,out] statep Lock state: ISC_R_SUCCESS or ISC_R_LOCKBUSY
+ */
+void
+run_exclusive_enter(sample_instance_t *inst, isc_result_t *statep) {
+ REQUIRE(statep != NULL);
+ REQUIRE(*statep == ISC_R_IGNORE);
+
+ *statep = isc_task_beginexclusive(inst->task);
+ RUNTIME_CHECK(*statep == ISC_R_SUCCESS || *statep == ISC_R_LOCKBUSY);
+}
+
+/*
+ * Exit task-exclusive mode.
+ *
+ * @param[in] inst The instance used for previous run_exclusive_enter() call.
+ * @param[in] state Lock state as returned by run_exclusive_enter().
+ */
+void
+run_exclusive_exit(sample_instance_t *inst, isc_result_t state) {
+ if (state == ISC_R_SUCCESS)
+ isc_task_endexclusive(inst->task);
+ else
+ /* Unlocking recursive lock or the lock was never locked. */
+ INSIST(state == ISC_R_LOCKBUSY || state == ISC_R_IGNORE);
+
+ return;
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license
+ */
+
+#include <config.h>
+
+#include <isc/util.h>
+
+#include <dns/log.h>
+
+#include "log.h"
+
+void
+log_write(int level, const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
+ level, format, args);
+ va_end(args);
+}
--- /dev/null
+/*
+ * Copyright (C) 2009--2015 Red Hat ; see COPYRIGHT for license
+ */
+
+#ifndef _LD_LOG_H_
+#define _LD_LOG_H_
+
+#include <isc/error.h>
+#include <dns/log.h>
+#include <dns/result.h>
+
+#define fatal_error(...) \
+ isc_error_fatal(__FILE__, __LINE__, __VA_ARGS__)
+
+#define log_error_r(fmt, ...) \
+ log_error(fmt ": %s", ##__VA_ARGS__, dns_result_totext(result))
+
+#define log_error(format, ...) \
+ log_write(ISC_LOG_ERROR, format, ##__VA_ARGS__)
+
+#define log_info(format, ...) \
+ log_write(ISC_LOG_INFO, format, ##__VA_ARGS__)
+
+void
+log_write(int level, const char *format, ...) ISC_FORMAT_PRINTF(2, 3);
+
+#endif /* !_LD_LOG_H_ */
--- /dev/null
+/*
+ * Automatic A/AAAA/PTR record synchronization.
+ *
+ * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license
+ */
+
+#include <config.h>
+
+#include <isc/event.h>
+#include <isc/eventclass.h>
+#include <isc/netaddr.h>
+#include <isc/task.h>
+
+#include <dns/byaddr.h>
+#include <dns/db.h>
+#include <dns/name.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include "instance.h"
+#include "syncptr.h"
+#include "util.h"
+
+/* Almost random value. See eventclass.h */
+#define SYNCPTR_WRITE_EVENT (ISC_EVENTCLASS(1025) + 1)
+
+/*
+ * Event used for making changes to reverse zones.
+ */
+typedef struct syncptrevent syncptrevent_t;
+struct syncptrevent {
+ ISC_EVENT_COMMON(syncptrevent_t);
+ isc_mem_t *mctx;
+ dns_zone_t *zone;
+ dns_diff_t diff;
+ dns_fixedname_t ptr_target_name; /* referenced by owner name in tuple */
+ isc_buffer_t b; /* referenced by target name in tuple */
+ unsigned char buf[DNS_NAME_MAXWIRE];
+};
+
+/*
+ * Write diff generated in syncptr() to reverse zone.
+ *
+ * This function will be called asynchronously and syncptr() will not get
+ * any result from it.
+ *
+ */
+static void
+syncptr_write(isc_task_t *task, isc_event_t *event) {
+ syncptrevent_t *pevent = (syncptrevent_t *)event;
+ dns_dbversion_t *version = NULL;
+ dns_db_t *db = NULL;
+ isc_result_t result;
+
+ REQUIRE(event->ev_type == SYNCPTR_WRITE_EVENT);
+
+ UNUSED(task);
+
+ CHECK(dns_zone_getdb(pevent->zone, &db));
+ CHECK(dns_db_newversion(db, &version));
+ CHECK(dns_diff_apply(&pevent->diff, db, version));
+
+cleanup:
+ if (db != NULL) {
+ if (version != NULL)
+ dns_db_closeversion(db, &version, 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);
+}
--- /dev/null
+/*
+ * Sync PTR records
+ *
+ * Copyright (C) 2014-2015 Red Hat ; see COPYRIGHT for license
+ */
+
+#ifndef SYNCPTR_H_
+#define SYNCPTR_H_
+
+#include <dns/diff.h>
+isc_result_t
+syncptrs(sample_instance_t *inst, dns_name_t *name, dns_rdataset_t *rdataset,
+ dns_diffop_t op);
+
+#endif /* SYNCPTR_H_ */
--- /dev/null
+/*
+ * Memory allocation and error handling utilities.
+ *
+ * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license
+ */
+
+#ifndef _LD_UTIL_H_
+#define _LD_UTIL_H_
+
+#include <isc/mem.h>
+#include <dns/types.h>
+
+#include "log.h"
+
+#define CLEANUP_WITH(result_code) \
+ do { \
+ result = (result_code); \
+ goto cleanup; \
+ } while(0)
+
+#define CHECK(op) \
+ do { \
+ result = (op); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+#define CHECKED_MEM_GET(m, target_ptr, s) \
+ do { \
+ (target_ptr) = isc_mem_get((m), (s)); \
+ if ((target_ptr) == NULL) { \
+ result = ISC_R_NOMEMORY; \
+ log_error("Memory allocation failed"); \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define CHECKED_MEM_GET_PTR(m, target_ptr) \
+ CHECKED_MEM_GET(m, target_ptr, sizeof(*(target_ptr)))
+
+#define CHECKED_MEM_STRDUP(m, source, target) \
+ do { \
+ (target) = isc_mem_strdup((m), (source)); \
+ if ((target) == NULL) { \
+ result = ISC_R_NOMEMORY; \
+ log_error("Memory allocation failed"); \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define ZERO_PTR(ptr) memset((ptr), 0, sizeof(*(ptr)))
+
+#define MEM_PUT_AND_DETACH(target_ptr) \
+ isc_mem_putanddetach(&(target_ptr)->mctx, target_ptr, \
+ sizeof(*(target_ptr)))
+
+#endif /* !_LD_UTIL_H_ */
--- /dev/null
+/*
+ * Zone management.
+ *
+ * Copyright (C) 2009-2015 Red Hat ; see COPYRIGHT for license
+ */
+
+#include <config.h>
+
+#include <isc/util.h>
+
+#include <dns/dyndb.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include "util.h"
+#include "instance.h"
+#include "lock.h"
+#include "log.h"
+#include "zone.h"
+
+extern const char *impname;
+
+/*
+ * Create a new zone with origin 'name'. The zone stay invisible to clients
+ * until it is explicitly added to a view.
+ */
+isc_result_t
+create_zone(sample_instance_t * const inst, dns_name_t * const name,
+ dns_zone_t ** const rawp)
+{
+ isc_result_t result;
+ dns_zone_t *raw = NULL;
+ const char *zone_argv[1];
+ char zone_name[DNS_NAME_FORMATSIZE];
+ dns_acl_t *acl_any = NULL;
+
+ REQUIRE(inst != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(rawp != NULL && *rawp == NULL);
+
+ zone_argv[0] = inst->db_name;
+
+ CHECK(dns_zone_create(&raw, inst->mctx));
+ CHECK(dns_zone_setorigin(raw, name));
+ dns_zone_setclass(raw, dns_rdataclass_in);
+ dns_zone_settype(raw, dns_zone_master);
+ CHECK(dns_zone_setdbtype(raw, 1, zone_argv));
+ CHECK(dns_zonemgr_managezone(inst->zmgr, raw));
+
+ /* This is completely insecure - use some sensible values instead! */
+ CHECK(dns_acl_any(inst->mctx, &acl_any));
+ dns_zone_setupdateacl(raw, acl_any);
+ dns_zone_setqueryacl(raw, acl_any);
+ dns_zone_setxfracl(raw, acl_any);
+ dns_acl_detach(&acl_any);
+
+ *rawp = raw;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ dns_name_format(name, zone_name, DNS_NAME_FORMATSIZE);
+ log_error_r("failed to create new zone '%s'", zone_name);
+
+ if (raw != NULL) {
+ if (dns_zone_getmgr(raw) != NULL)
+ dns_zonemgr_releasezone(inst->zmgr, raw);
+ dns_zone_detach(&raw);
+ }
+ if (acl_any != NULL)
+ dns_acl_detach(&acl_any);
+
+ return (result);
+}
+
+/*
+ * Add zone to the view defined in inst->view. This will make the zone visible
+ * to clients.
+ */
+static isc_result_t
+publish_zone(sample_instance_t *inst, dns_zone_t *zone) {
+ isc_result_t result;
+ 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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. };
--- /dev/null
+#!/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
--- /dev/null
+#!/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 <<EOF > 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 <<EOF > 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
fi
fi
+CFLAGS="$CFLAGS $SO_CFLAGS"
# 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"
#
"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" ;;
[Define to allow building of objects for dlopen().])
fi
fi
+CFLAGS="$CFLAGS $SO_CFLAGS"
AC_SUBST(SO)
AC_SUBST(SO_CFLAGS)
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
<xi:include href="dlz.xml"/>
+ <xi:include href="dyndb.xml"/>
+
<sect1>
<title>IPv6 Support in <acronym>BIND</acronym> 9</title>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ - 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.
+-->
+
+<sect1 id="dyndb-info">
+ <title>DynDB (Dynamic Database)</title>
+ <para>
+ DynDB is an extension to BIND 9 which, like DLZ
+ (see <xref linkend="dlz-info"/>), 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.
+ </para>
+ <para>
+ A DynDB module supporting LDAP has been created by Red Hat
+ and is available from
+ <ulink url="https://fedorahosted.org/bind-dyndb-ldap/"
+ >https://fedorahosted.org/bind-dyndb-ldap/</ulink>.
+ </para>
+ <para>
+ A sample DynDB module for testing and developer guidance
+ is included with the BIND source code, in the directory
+ <filename>bin/tests/system/dyndb/driver</filename>.
+ </para>
+
+ <sect2>
+ <title>Configuring DynDB</title>
+ <para>
+ A DynDB database is configured with a <command>dyndb</command>
+ statement in <filename>named.conf</filename>:
+ </para>
+ <screen>
+ dyndb example "driver.so" {
+ <replaceable>parameters</replaceable>
+ };
+ </screen>
+ <para>
+ The file <filename>driver.so</filename> is a DynDB module which
+ implements the full DNS database API. Multiple
+ <command>dyndb</command> 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.
+ </para>
+ <para>
+ The <replaceable>parameters</replaceable> are passed as an opaque
+ string to the DynDB module's initialization routine. Configuration
+ syntax will differ depending on the driver.
+ </para>
+ </sect2>
+ <sect2>
+ <title>Sample DynDB Module</title>
+ <para>
+ For guidance in implementation of DynDB modules, the directory
+ <filename>bin/tests/system/dyndb/driver</filename>.
+ contains a basic DynDB module.
+ The example sets up two zones, whose names are passed
+ to the module as arguments in the <command>dyndb</command>
+ statement:
+ </para>
+ <screen>
+ dyndb sample "sample.so" { example.nil. arpa. };
+ </screen>
+ <para>
+ 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:
+ </para>
+ <screen>
+ 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
+ </screen>
+ <para>
+ 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.)
+ </para>
+ </sect2>
+</sect1>
<sect2 id="relnotes_features">
<title>New Features</title>
<itemizedlist>
+ <listitem>
+ <para>
+ 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.)
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ 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.
+ </para>
+ </listitem>
<listitem>
<para>
New quotas have been added to limit the queries that are
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@ \
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 \
#include <isc/buffer.h>
+#include <isc/commandline.h>
#include <isc/magic.h>
#include <isc/mem.h>
#include <isc/once.h>
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
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));
}
/*%
--- /dev/null
+/*
+ * 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 <config.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/once.h>
+#include <isc/result.h>
+#include <isc/region.h>
+#include <isc/task.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/dyndb.h>
+#include <dns/log.h>
+#include <dns/types.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include <string.h>
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#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;
+}
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 \
--- /dev/null
+/*
+ * 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 <isc/types.h>
+
+#include <dns/types.h>
+
+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 */
#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
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;
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)
{ "dns/crypto", 0 },
{ "dns/packets", 0 },
{ "dns/nta", 0 },
+ { "dns/dyndb", 0 },
{ NULL, 0 }
};
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
#include <stdio.h>
#include <isc/commandline.h>
+#include <isc/mem.h>
#include <isc/msgs.h>
#include <isc/print.h>
#include <isc/string.h>
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);
+}
* 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.
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,
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);
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
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
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
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];
}
* 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
#include <isc/boolean.h>
#include <isc/lang.h>
#include <isc/platform.h>
+#include <isc/result.h>
/*% Index into parent argv vector. */
LIBISC_EXTERNAL_DATA extern int isc_commandline_index;
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
* 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
***/
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);
#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. */
/*@}*/
/*@{*/
/*!
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 {
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;
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);
lexstate_ccomment,
lexstate_ccommentend,
lexstate_eatline,
- lexstate_qstring
+ lexstate_qstring,
+ lexstate_btext
} lexstate;
#define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL)
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);
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;
} 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;
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,
isc_bufferlist_availablecount
isc_bufferlist_usedcount
isc_commandline_parse
+isc_commandline_strtoargv
isc_condition_broadcast
isc_condition_destroy
isc_condition_init
isc_hash_ctxinit
isc_hash_destroy
isc_hash_init
+isc_hashctx
isc_heap_create
isc_heap_decreased
isc_heap_delete
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;
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 */
{ "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 },
&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.
*/
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, "{ <unspecified text> }");
+}
+
+
isc_boolean_t
cfg_is_enum(const char *s, const char *const *enums) {
const char * const *p;
&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
*/