summaryrefslogtreecommitdiffstats
path: root/bin/tests/system/dyndb
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--bin/tests/system/dyndb/clean.sh25
-rw-r--r--bin/tests/system/dyndb/driver/AUTHORS33
-rw-r--r--bin/tests/system/dyndb/driver/Makefile.am25
-rw-r--r--bin/tests/system/dyndb/driver/Makefile.in811
-rw-r--r--bin/tests/system/dyndb/driver/README92
-rw-r--r--bin/tests/system/dyndb/driver/db.c776
-rw-r--r--bin/tests/system/dyndb/driver/db.h47
-rw-r--r--bin/tests/system/dyndb/driver/driver.c164
-rw-r--r--bin/tests/system/dyndb/driver/instance.c231
-rw-r--r--bin/tests/system/dyndb/driver/instance.h73
-rw-r--r--bin/tests/system/dyndb/driver/lock.c81
-rw-r--r--bin/tests/system/dyndb/driver/lock.h39
-rw-r--r--bin/tests/system/dyndb/driver/log.c44
-rw-r--r--bin/tests/system/dyndb/driver/log.h45
-rw-r--r--bin/tests/system/dyndb/driver/syncptr.c337
-rw-r--r--bin/tests/system/dyndb/driver/syncptr.h46
-rw-r--r--bin/tests/system/dyndb/driver/util.h82
-rw-r--r--bin/tests/system/dyndb/driver/zone.c265
-rw-r--r--bin/tests/system/dyndb/driver/zone.h45
-rw-r--r--bin/tests/system/dyndb/ns1/named.conf.in40
-rw-r--r--bin/tests/system/dyndb/prereq.sh21
-rw-r--r--bin/tests/system/dyndb/setup.sh16
-rw-r--r--bin/tests/system/dyndb/tests.sh166
-rw-r--r--bin/tests/system/dyndb/tests_sh_dyndb.py14
24 files changed, 3518 insertions, 0 deletions
diff --git a/bin/tests/system/dyndb/clean.sh b/bin/tests/system/dyndb/clean.sh
new file mode 100644
index 0000000..cb8ae94
--- /dev/null
+++ b/bin/tests/system/dyndb/clean.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+#
+# Clean up after dyndb tests.
+#
+rm -f */named.conf
+rm -f */named.run
+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.*
+rm -f ns*/managed-keys.bind*
diff --git a/bin/tests/system/dyndb/driver/AUTHORS b/bin/tests/system/dyndb/driver/AUTHORS
new file mode 100644
index 0000000..5b37853
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/AUTHORS
@@ -0,0 +1,33 @@
+Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+
+SPDX-License-Identifier: MPL-2.0 AND ISC
+
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, you can obtain one at https://mozilla.org/MPL/2.0/.
+
+See the COPYRIGHT file distributed with this work for additional
+information regarding copyright ownership.
+
+Copyright (C) 2009-2015 Red Hat
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+This sample driver is based on bind-dyndb-ldap project and small portions
+of code from ISC BIND 9.10.
+
+Authors listed in alphabetical order:
+Adam Tkac <atkac@redhat.com>
+Jiri Kuncar <jkuncar@redhat.com>
+Martin Nagy <mnagy@redhat.com>
+Petr Spacek <pspacek@redhat.com>
diff --git a/bin/tests/system/dyndb/driver/Makefile.am b/bin/tests/system/dyndb/driver/Makefile.am
new file mode 100644
index 0000000..86dddb6
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/Makefile.am
@@ -0,0 +1,25 @@
+include $(top_srcdir)/Makefile.top
+
+AM_CPPFLAGS += \
+ $(LIBISC_CFLAGS) \
+ $(LIBDNS_CFLAGS)
+
+noinst_LTLIBRARIES = sample.la
+
+sample_la_SOURCES = \
+ db.c \
+ driver.c \
+ instance.c \
+ lock.c \
+ log.c \
+ syncptr.c \
+ zone.c \
+ db.h \
+ instance.h \
+ lock.h \
+ log.h \
+ syncptr.h \
+ util.h \
+ zone.h
+
+sample_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -rpath $(abs_builddir)
diff --git a/bin/tests/system/dyndb/driver/Makefile.in b/bin/tests/system/dyndb/driver/Makefile.in
new file mode 100644
index 0000000..ccfad1d
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/Makefile.in
@@ -0,0 +1,811 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Hey Emacs, this is -*- makefile-automake -*- file!
+# vim: filetype=automake
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+@HOST_MACOS_TRUE@am__append_1 = \
+@HOST_MACOS_TRUE@ -Wl,-flat_namespace
+
+subdir = bin/tests/system/dyndb/driver
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/ax_check_openssl.m4 \
+ $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4/ax_jemalloc.m4 \
+ $(top_srcdir)/m4/ax_lib_lmdb.m4 \
+ $(top_srcdir)/m4/ax_perl_module.m4 \
+ $(top_srcdir)/m4/ax_posix_shell.m4 \
+ $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4/ax_pthread.m4 \
+ $(top_srcdir)/m4/ax_python_module.m4 \
+ $(top_srcdir)/m4/ax_restore_flags.m4 \
+ $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+sample_la_LIBADD =
+am_sample_la_OBJECTS = db.lo driver.lo instance.lo lock.lo log.lo \
+ syncptr.lo zone.lo
+sample_la_OBJECTS = $(am_sample_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+sample_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(sample_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/db.Plo ./$(DEPDIR)/driver.Plo \
+ ./$(DEPDIR)/instance.Plo ./$(DEPDIR)/lock.Plo \
+ ./$(DEPDIR)/log.Plo ./$(DEPDIR)/syncptr.Plo \
+ ./$(DEPDIR)/zone.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(sample_la_SOURCES)
+DIST_SOURCES = $(sample_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__extra_recursive_targets = test-recursive unit-recursive \
+ doc-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \
+ $(top_srcdir)/depcomp AUTHORS README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CURL = @CURL@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DEVELOPER_MODE = @DEVELOPER_MODE@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+FSTRM_CAPTURE = @FSTRM_CAPTURE@
+FUZZ_LDFLAGS = @FUZZ_LDFLAGS@
+FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@
+JEMALLOC_LIBS = @JEMALLOC_LIBS@
+JSON_C_CFLAGS = @JSON_C_CFLAGS@
+JSON_C_LIBS = @JSON_C_LIBS@
+KRB5_CFLAGS = @KRB5_CFLAGS@
+KRB5_CONFIG = @KRB5_CONFIG@
+KRB5_LIBS = @KRB5_LIBS@
+LATEXMK = @LATEXMK@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBCAP_LIBS = @LIBCAP_LIBS@
+LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@
+LIBIDN2_LIBS = @LIBIDN2_LIBS@
+LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@
+LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUV_CFLAGS = @LIBUV_CFLAGS@
+LIBUV_LIBS = @LIBUV_LIBS@
+LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
+LIBXML2_LIBS = @LIBXML2_LIBS@
+LIPO = @LIPO@
+LMDB_CFLAGS = @LMDB_CFLAGS@
+LMDB_LIBS = @LMDB_LIBS@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@
+MAXMINDDB_LIBS = @MAXMINDDB_LIBS@
+MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@
+MKDIR_P = @MKDIR_P@
+NC = @NC@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_CFLAGS = @OPENSSL_CFLAGS@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_CXX = @PTHREAD_CXX@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PYTEST = @PYTEST@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+READLINE_CFLAGS = @READLINE_CFLAGS@
+READLINE_LIBS = @READLINE_LIBS@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINX_BUILD = @SPHINX_BUILD@
+STD_CFLAGS = @STD_CFLAGS@
+STD_CPPFLAGS = @STD_CPPFLAGS@
+STD_LDFLAGS = @STD_LDFLAGS@
+STRIP = @STRIP@
+TEST_CFLAGS = @TEST_CFLAGS@
+VERSION = @VERSION@
+XELATEX = @XELATEX@
+XSLTPROC = @XSLTPROC@
+ZLIB_CFLAGS = @ZLIB_CFLAGS@
+ZLIB_LIBS = @ZLIB_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+ax_pthread_config = @ax_pthread_config@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4
+AM_CFLAGS = \
+ $(STD_CFLAGS)
+
+AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \
+ -I$(srcdir)/include $(LIBISC_CFLAGS) $(LIBDNS_CFLAGS)
+AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1)
+LDADD =
+LIBISC_CFLAGS = \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib/isc/include \
+ -I$(top_builddir)/lib/isc/include
+
+LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la
+LIBDNS_CFLAGS = \
+ -I$(top_srcdir)/lib/dns/include \
+ -I$(top_builddir)/lib/dns/include
+
+LIBDNS_LIBS = \
+ $(top_builddir)/lib/dns/libdns.la
+
+LIBNS_CFLAGS = \
+ -I$(top_srcdir)/lib/ns/include
+
+LIBNS_LIBS = \
+ $(top_builddir)/lib/ns/libns.la
+
+LIBIRS_CFLAGS = \
+ -I$(top_srcdir)/lib/irs/include
+
+LIBIRS_LIBS = \
+ $(top_builddir)/lib/irs/libirs.la
+
+LIBISCCFG_CFLAGS = \
+ -I$(top_srcdir)/lib/isccfg/include
+
+LIBISCCFG_LIBS = \
+ $(top_builddir)/lib/isccfg/libisccfg.la
+
+LIBISCCC_CFLAGS = \
+ -I$(top_srcdir)/lib/isccc/include/
+
+LIBISCCC_LIBS = \
+ $(top_builddir)/lib/isccc/libisccc.la
+
+LIBBIND9_CFLAGS = \
+ -I$(top_srcdir)/lib/bind9/include
+
+LIBBIND9_LIBS = \
+ $(top_builddir)/lib/bind9/libbind9.la
+
+noinst_LTLIBRARIES = sample.la
+sample_la_SOURCES = \
+ db.c \
+ driver.c \
+ instance.c \
+ lock.c \
+ log.c \
+ syncptr.c \
+ zone.c \
+ db.h \
+ instance.h \
+ lock.h \
+ log.h \
+ syncptr.h \
+ util.h \
+ zone.h
+
+sample_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -rpath $(abs_builddir)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/tests/system/dyndb/driver/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign bin/tests/system/dyndb/driver/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/Makefile.top $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+sample.la: $(sample_la_OBJECTS) $(sample_la_DEPENDENCIES) $(EXTRA_sample_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(sample_la_LINK) $(sample_la_OBJECTS) $(sample_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/driver.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/instance.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syncptr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+test-local:
+unit-local:
+doc-local:
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/db.Plo
+ -rm -f ./$(DEPDIR)/driver.Plo
+ -rm -f ./$(DEPDIR)/instance.Plo
+ -rm -f ./$(DEPDIR)/lock.Plo
+ -rm -f ./$(DEPDIR)/log.Plo
+ -rm -f ./$(DEPDIR)/syncptr.Plo
+ -rm -f ./$(DEPDIR)/zone.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+doc: doc-am
+
+doc-am: doc-local
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/db.Plo
+ -rm -f ./$(DEPDIR)/driver.Plo
+ -rm -f ./$(DEPDIR)/instance.Plo
+ -rm -f ./$(DEPDIR)/lock.Plo
+ -rm -f ./$(DEPDIR)/log.Plo
+ -rm -f ./$(DEPDIR)/syncptr.Plo
+ -rm -f ./$(DEPDIR)/zone.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+test: test-am
+
+test-am: test-local
+
+uninstall-am:
+
+unit: unit-am
+
+unit-am: unit-local
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir \
+ doc-am doc-local dvi dvi-am html html-am info info-am install \
+ install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-am test-am test-local uninstall \
+ uninstall-am unit-am unit-local
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/bin/tests/system/dyndb/driver/README b/bin/tests/system/dyndb/driver/README
new file mode 100644
index 0000000..db73396
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/README
@@ -0,0 +1,92 @@
+<!--
+Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+
+SPDX-License-Identifier: MPL-2.0 and ISC
+
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, you can obtain one at https://mozilla.org/MPL/2.0/.
+
+See the COPYRIGHT file distributed with this work for additional
+information regarding copyright ownership.
+
+Copyright (C) Red Hat
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+-->
+
+To use the Dynamic DB sample driver, run named and check the log.
+
+ $ cd testing
+ $ named -gc named.conf
+
+You should be able to see something like:
+
+zone test/IN: loaded serial 0
+zone arpa/IN: loaded serial 0
+
+This means that the sample driver created empty zones "test." and
+"arpa." as defined by "arg" parameters in named.conf.
+
+$ dig @localhost test.
+
+should work as usual and you should be able to see the dummy zone with
+NS record pointing to the zone apex and A record with 127.0.0.1:
+
+;; ANSWER SECTION:
+test. 86400 IN A 127.0.0.1
+test. 86400 IN NS test.
+test. 86400 IN SOA test. test. 0 28800 7200 604800 86400
+
+This driver creates two empty zones and allows query/transfer/update to
+all IP addresses for demonstration purposes.
+
+The driver wraps the RBT database implementation used natively by BIND,
+and modifies the addrdataset() and substractrdataset() functions to do
+additional work during dynamic updates.
+
+A dynamic update modifies the target zone as usual. After that, the
+driver detects whether the modified RR was of type A or AAAA, and if so,
+attempts to appropriately generate or delete a matching PTR record in
+one of the two zones managed by the driver.
+
+E.g.:
+
+$ nsupdate
+> update add a.test. 300 IN A 192.0.2.1
+> send
+
+will add the A record
+a.test. 300 IN A 192.0.2.1
+
+and also automatically generate the PTR record
+1.2.0.192.in-addr.arpa. 300 IN PTR a.test.
+
+AXFR and RR deletion via dynamic updates should work as usual. Deletion
+of a type A or AAAA record should delete the corresponding PTR record
+too.
+
+The zone is stored only in memory, and all changes will be lost on
+reload/reconfig.
+
+Hints for code readers:
+- Driver initialization starts in driver.c: dyndb_init() function.
+- New database implementation is registered by calling dns_db_register()
+ and passing a function pointer to it. This sample uses the function
+ create_db() to initialize the database.
+- Zones are created later in instance.c: load_sample_instance_zones().
+- Database entry points are in structure db.c: dns_dbmethods_t
+ sampledb_methods
+- sampledb_methods points to an implementation of the database interface.
+ See the db.c: addrdataset() implementation and look at how the RBT
+ database instance is wrapped into an additional layer of logic.
diff --git a/bin/tests/system/dyndb/driver/db.c b/bin/tests/system/dyndb/driver/db.c
new file mode 100644
index 0000000..334fd54
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/db.c
@@ -0,0 +1,776 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2009-2015 Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * 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.
+ */
+
+#include "db.h"
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/diff.h>
+#include <dns/enumclass.h>
+#include <dns/rbt.h>
+#include <dns/rdatalist.h>
+#include <dns/rdatastruct.h>
+#include <dns/soa.h>
+#include <dns/types.h>
+
+#include "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);
+ *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) {
+ REQUIRE(dbp != NULL && VALID_SAMPLEDB((sampledb_t *)(*dbp)));
+ sampledb_t *sampledb = (sampledb_t *)(*dbp);
+ *dbp = NULL;
+
+ if (isc_refcount_decrement(&sampledb->refs) == 1) {
+ free_sampledb(sampledb);
+ }
+}
+
+/*
+ * 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
+dump(dns_db_t *db, dns_dbversion_t *version, const char *filename,
+ dns_masterformat_t masterformat) {
+ UNUSED(db);
+ UNUSED(version);
+ UNUSED(filename);
+ UNUSED(masterformat);
+
+ FATAL_ERROR("current implementation should never call dump()");
+
+ /* Not reached */
+ return (ISC_R_SUCCESS);
+}
+
+static void
+currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_currentversion(sampledb->rbtdb, versionp);
+}
+
+static isc_result_t
+newversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_newversion(sampledb->rbtdb, versionp));
+}
+
+static void
+attachversion(dns_db_t *db, dns_dbversion_t *source,
+ dns_dbversion_t **targetp) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_attachversion(sampledb->rbtdb, source, targetp);
+}
+
+static void
+closeversion(dns_db_t *db, dns_dbversion_t **versionp, bool commit) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_closeversion(sampledb->rbtdb, versionp, commit);
+}
+
+static isc_result_t
+findnode(dns_db_t *db, const dns_name_t *name, bool create,
+ dns_dbnode_t **nodep) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_findnode(sampledb->rbtdb, name, create, nodep));
+}
+
+static isc_result_t
+find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
+ dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset) {
+ 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, const dns_name_t *name, unsigned int options,
+ isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_name_t *dcname, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_findzonecut(sampledb->rbtdb, name, options, now, nodep,
+ foundname, dcname, rdataset, sigrdataset));
+}
+
+static void
+attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_attachnode(sampledb->rbtdb, source, targetp);
+}
+
+static void
+detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_detachnode(sampledb->rbtdb, targetp);
+}
+
+static isc_result_t
+expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_expirenode(sampledb->rbtdb, node, now));
+}
+
+static void
+printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_printnode(sampledb->rbtdb, node, out);
+}
+
+static isc_result_t
+createiterator(dns_db_t *db, unsigned int options,
+ dns_dbiterator_t **iteratorp) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_createiterator(sampledb->rbtdb, options, iteratorp));
+}
+
+static isc_result_t
+findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers, isc_stdtime_t now,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_findrdataset(sampledb->rbtdb, node, version, type,
+ covers, now, rdataset, sigrdataset));
+}
+
+static isc_result_t
+allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ unsigned int options, 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, options,
+ now, iteratorp));
+}
+
+static isc_result_t
+addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options,
+ dns_rdataset_t *addedrdataset) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+ isc_result_t result;
+ dns_fixedname_t name;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_fixedname_init(&name);
+ CHECK(dns_db_addrdataset(sampledb->rbtdb, node, version, now, rdataset,
+ options, addedrdataset));
+ if (rdataset->type == dns_rdatatype_a ||
+ rdataset->type == dns_rdatatype_aaaa)
+ {
+ CHECK(sample_name_fromnode(node, dns_fixedname_name(&name)));
+ CHECK(syncptrs(sampledb->inst, dns_fixedname_name(&name),
+ rdataset, DNS_DIFFOP_ADD));
+ }
+
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdataset_t *rdataset, unsigned int options,
+ dns_rdataset_t *newrdataset) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+ isc_result_t result;
+ dns_fixedname_t name;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_fixedname_init(&name);
+ result = dns_db_subtractrdataset(sampledb->rbtdb, node, version,
+ rdataset, options, newrdataset);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NXRRSET) {
+ goto cleanup;
+ }
+
+ if (rdataset->type == dns_rdatatype_a ||
+ rdataset->type == dns_rdatatype_aaaa)
+ {
+ CHECK(sample_name_fromnode(node, dns_fixedname_name(&name)));
+ CHECK(syncptrs(sampledb->inst, dns_fixedname_name(&name),
+ rdataset, DNS_DIFFOP_DEL));
+ }
+
+cleanup:
+ return (result);
+}
+
+/*
+ * deleterdataset() function is not used during DNS update processing so syncptr
+ * implementation is left as an exercise to the reader.
+ */
+static isc_result_t
+deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_deleterdataset(sampledb->rbtdb, node, version, type,
+ covers));
+}
+
+static bool
+issecure(dns_db_t *db) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_issecure(sampledb->rbtdb));
+}
+
+static unsigned int
+nodecount(dns_db_t *db, dns_dbtree_t tree) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_nodecount(sampledb->rbtdb, tree));
+}
+
+/*
+ * The database does not need to be loaded from disk or written to disk.
+ * Always return true.
+ */
+static bool
+ispersistent(dns_db_t *db) {
+ UNUSED(db);
+
+ return (true);
+}
+
+static void
+overmem(dns_db_t *db, bool over) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_overmem(sampledb->rbtdb, over);
+}
+
+static void
+settask(dns_db_t *db, isc_task_t *task) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_settask(sampledb->rbtdb, task);
+}
+
+static isc_result_t
+getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_getoriginnode(sampledb->rbtdb, nodep));
+}
+
+static void
+transfernode(dns_db_t *db, dns_dbnode_t **sourcep, dns_dbnode_t **targetp) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_transfernode(sampledb->rbtdb, sourcep, targetp);
+}
+
+static isc_result_t
+getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash,
+ uint8_t *flags, uint16_t *iterations, unsigned char *salt,
+ size_t *salt_length) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_getnsec3parameters(sampledb->rbtdb, version, hash, flags,
+ iterations, salt, salt_length));
+}
+
+static isc_result_t
+findnsec3node(dns_db_t *db, const dns_name_t *name, bool create,
+ dns_dbnode_t **nodep) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_findnsec3node(sampledb->rbtdb, name, create, nodep));
+}
+
+static isc_result_t
+setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_setsigningtime(sampledb->rbtdb, rdataset, resign));
+}
+
+static isc_result_t
+getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *name) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_getsigningtime(sampledb->rbtdb, rdataset, name));
+}
+
+static void
+resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ dns_db_resigned(sampledb->rbtdb, rdataset, version);
+}
+
+static bool
+isdnssec(dns_db_t *db) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_isdnssec(sampledb->rbtdb));
+}
+
+static dns_stats_t *
+getrrsetstats(dns_db_t *db) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_getrrsetstats(sampledb->rbtdb));
+}
+
+static isc_result_t
+findnodeext(dns_db_t *db, const dns_name_t *name, bool create,
+ dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo,
+ dns_dbnode_t **nodep) {
+ 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, const dns_name_t *name, dns_dbversion_t *version,
+ dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_findext(sampledb->rbtdb, name, version, type, options,
+ now, nodep, foundname, methods, clientinfo,
+ rdataset, sigrdataset));
+}
+
+static isc_result_t
+setcachestats(dns_db_t *db, isc_stats_t *stats) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_setcachestats(sampledb->rbtdb, stats));
+}
+
+static size_t
+hashsize(dns_db_t *db) {
+ sampledb_t *sampledb = (sampledb_t *)db;
+
+ REQUIRE(VALID_SAMPLEDB(sampledb));
+
+ return (dns_db_hashsize(sampledb->rbtdb));
+}
+
+/*
+ * DB interface definition. Database driver uses this structure to
+ * determine which implementation of dns_db_*() function to call.
+ */
+static dns_dbmethods_t sampledb_methods = {
+ attach, detach, beginload,
+ endload, 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,
+ NULL, /* rpz_attach */
+ NULL, /* rpz_ready */
+ findnodeext, findext, setcachestats,
+ hashsize, NULL, /* nodefullname */
+ NULL, /* getsize */
+ NULL, /* setservestalettl */
+ NULL, /* getservestalettl */
+ NULL, /* setservestalerefresh */
+ NULL, /* getservestalerefresh */
+ NULL, /* setgluecachestats */
+};
+
+/* 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, const dns_name_t *name,
+ const dns_name_t *origin, const dns_name_t *contact) {
+ dns_dbnode_t *node = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ unsigned char buf[DNS_SOA_BUFFERSIZE];
+
+ dns_rdataset_init(&rdataset);
+ dns_rdatalist_init(&rdatalist);
+ CHECK(dns_soa_buildrdata(origin, contact, dns_db_class(db), 0, 28800,
+ 7200, 604800, 86400, buf, &rdata));
+ rdatalist.type = rdata.type;
+ rdatalist.covers = 0;
+ rdatalist.rdclass = rdata.rdclass;
+ rdatalist.ttl = 86400;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+ CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+ CHECK(dns_db_findnode(db, name, true, &node));
+ CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL));
+cleanup:
+ if (node != NULL) {
+ dns_db_detachnode(db, &node);
+ }
+ return (result);
+}
+
+static isc_result_t
+add_ns(dns_db_t *db, dns_dbversion_t *version, const dns_name_t *name,
+ const dns_name_t *nsname) {
+ dns_dbnode_t *node = NULL;
+ dns_rdata_ns_t ns;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ isc_buffer_t b;
+ unsigned char buf[DNS_NAME_MAXWIRE];
+
+ isc_buffer_init(&b, buf, sizeof(buf));
+
+ dns_rdataset_init(&rdataset);
+ dns_rdatalist_init(&rdatalist);
+ ns.common.rdtype = dns_rdatatype_ns;
+ ns.common.rdclass = dns_db_class(db);
+ ns.mctx = NULL;
+ dns_name_init(&ns.name, NULL);
+ dns_name_clone(nsname, &ns.name);
+ CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_ns,
+ &ns, &b));
+ rdatalist.type = rdata.type;
+ rdatalist.covers = 0;
+ rdatalist.rdclass = rdata.rdclass;
+ rdatalist.ttl = 86400;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+ CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+ CHECK(dns_db_findnode(db, name, true, &node));
+ CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL));
+cleanup:
+ if (node != NULL) {
+ dns_db_detachnode(db, &node);
+ }
+ return (result);
+}
+
+static isc_result_t
+add_a(dns_db_t *db, dns_dbversion_t *version, const dns_name_t *name,
+ struct in_addr addr) {
+ dns_dbnode_t *node = NULL;
+ dns_rdata_in_a_t a;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ isc_buffer_t b;
+ unsigned char buf[DNS_NAME_MAXWIRE];
+
+ isc_buffer_init(&b, buf, sizeof(buf));
+
+ dns_rdataset_init(&rdataset);
+ dns_rdatalist_init(&rdatalist);
+ a.common.rdtype = dns_rdatatype_a;
+ a.common.rdclass = dns_db_class(db);
+ a.in_addr = addr;
+ CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_a,
+ &a, &b));
+ rdatalist.type = rdata.type;
+ rdatalist.covers = 0;
+ rdatalist.rdclass = rdata.rdclass;
+ rdatalist.ttl = 86400;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+ CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+ CHECK(dns_db_findnode(db, name, true, &node));
+ CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL));
+cleanup:
+ if (node != NULL) {
+ dns_db_detachnode(db, &node);
+ }
+ return (result);
+}
+
+/*
+ * Driver-specific implementation of dns_db_create().
+ *
+ * @param[in] argv Database-specific parameters from dns_db_create().
+ * @param[in] driverarg Driver-specific parameter from dns_db_register().
+ */
+isc_result_t
+create_db(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type,
+ dns_rdataclass_t rdclass, unsigned int argc, char *argv[],
+ void *driverarg, dns_db_t **dbp) {
+ sampledb_t *sampledb = NULL;
+ isc_result_t result;
+ dns_dbversion_t *version = NULL;
+ struct in_addr a_addr;
+
+ REQUIRE(type == dns_dbtype_zone);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ REQUIRE(argc == 0);
+ REQUIRE(argv != NULL);
+ REQUIRE(driverarg != NULL); /* pointer to driver instance */
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ UNUSED(driverarg); /* no driver-specific configuration */
+
+ a_addr.s_addr = 0x0100007fU;
+
+ CHECKED_MEM_GET_PTR(mctx, sampledb);
+ ZERO_PTR(sampledb);
+
+ isc_mem_attach(mctx, &sampledb->common.mctx);
+ dns_name_init(&sampledb->common.origin, NULL);
+
+ 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));
+
+ isc_refcount_init(&sampledb->refs, 1);
+
+ /* Translate instance name to instance pointer. */
+ sampledb->inst = driverarg;
+
+ /* Create internal instance of RBT DB implementation from BIND. */
+ CHECK(dns_db_create(mctx, "rbt", origin, dns_dbtype_zone,
+ dns_rdataclass_in, 0, NULL, &sampledb->rbtdb));
+
+ /* Create fake SOA, NS, and A records to make database loadable. */
+ CHECK(dns_db_newversion(sampledb->rbtdb, &version));
+ CHECK(add_soa(sampledb->rbtdb, version, origin, origin, origin));
+ CHECK(add_ns(sampledb->rbtdb, version, origin, origin));
+ CHECK(add_a(sampledb->rbtdb, version, origin, a_addr));
+ dns_db_closeversion(sampledb->rbtdb, &version, true);
+
+ *dbp = (dns_db_t *)sampledb;
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ if (sampledb != NULL) {
+ if (dns_name_dynamic(&sampledb->common.origin)) {
+ dns_name_free(&sampledb->common.origin, mctx);
+ }
+
+ isc_mem_putanddetach(&sampledb->common.mctx, sampledb,
+ sizeof(*sampledb));
+ }
+
+ return (result);
+}
diff --git a/bin/tests/system/dyndb/driver/db.h b/bin/tests/system/dyndb/driver/db.h
new file mode 100644
index 0000000..c520c8b
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/db.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2009-2015 Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * Database API implementation.
+ */
+
+#pragma once
+
+#include <isc/mem.h>
+#include <isc/result.h>
+
+#include <dns/db.h>
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+
+isc_result_t
+create_db(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type,
+ dns_rdataclass_t rdclass, unsigned int argc, char *argv[],
+ void *driverarg, dns_db_t **dbp);
diff --git a/bin/tests/system/dyndb/driver/driver.c b/bin/tests/system/dyndb/driver/driver.c
new file mode 100644
index 0000000..7e8a249
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/driver.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * 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.
+ */
+
+#include <isc/commandline.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dyndb.h>
+#include <dns/types.h>
+
+#include "db.h"
+#include "instance.h"
+#include "log.h"
+#include "util.h"
+
+/* aliases for the exported symbols */
+
+dns_dyndb_destroy_t dyndb_destroy;
+dns_dyndb_register_t dyndb_init;
+dns_dyndb_version_t dyndb_version;
+
+/*
+ * Driver init is called for each dyndb section in named.conf
+ * once during startup and then again on every reload.
+ *
+ * @code
+ * dyndb example-name "sample.so" { param1 param2 };
+ * @endcode
+ *
+ * @param[in] name User-defined string from dyndb "name" {}; definition
+ * in named.conf.
+ * The example above will have name = "example-name".
+ * @param[in] parameters User-defined parameters from dyndb section as one
+ * string. The example above will have
+ * params = "param1 param2";
+ * @param[in] file The name of the file from which the parameters
+ * were read.
+ * @param[in] line The line number from which the parameters were read.
+ * @param[out] instp Pointer to instance-specific data
+ * (for one dyndb section).
+ */
+isc_result_t
+dyndb_init(isc_mem_t *mctx, const char *name, const char *parameters,
+ const char *file, unsigned long line, const dns_dyndbctx_t *dctx,
+ void **instp) {
+ isc_result_t result;
+ unsigned int argc;
+ char **argv = NULL;
+ char *s = NULL;
+ sample_instance_t *sample_inst = NULL;
+
+ REQUIRE(name != NULL);
+ REQUIRE(dctx != NULL);
+
+ s = isc_mem_strdup(mctx, parameters);
+
+ result = isc_commandline_strtoargv(mctx, s, &argc, &argv, 0);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "dyndb_init: isc_commandline_strtoargv -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ log_write(ISC_LOG_DEBUG(9), "loading params for dyndb '%s' from %s:%lu",
+ name, file, line);
+
+ /* Finally, create the instance. */
+ result = new_sample_instance(mctx, name, argc, argv, dctx,
+ &sample_inst);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "dyndb_init: new_sample_instance -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ /*
+ * This is an example so we create and load zones
+ * right now. This step can be arbitrarily postponed.
+ */
+ result = load_sample_instance_zones(sample_inst);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "dyndb_init: load_sample_instance_zones -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ *instp = sample_inst;
+
+cleanup:
+ 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 between the driver and the caller.
+ */
+int
+dyndb_version(unsigned int *flags) {
+ UNUSED(flags);
+
+ return (DNS_DYNDB_VERSION);
+}
diff --git a/bin/tests/system/dyndb/driver/instance.c b/bin/tests/system/dyndb/driver/instance.c
new file mode 100644
index 0000000..9e90a2c
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/instance.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2009-2015 Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * 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.
+ */
+
+#include "instance.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 "log.h"
+#include "util.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;
+ }
+ result = dns_name_fromstring2(z1, argv[0], dns_rootname, 0, mctx);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "parse_params: dns_name_fromstring2 -> %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ result = dns_name_fromstring2(z2, argv[1], dns_rootname, 0, mctx);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "parse_params: dns_name_fromstring2 -> %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ return (result);
+}
+
+/*
+ * Initialize new driver instance. It will not create zones until
+ * load_sample_instance_zones() is called.
+ */
+isc_result_t
+new_sample_instance(isc_mem_t *mctx, const char *db_name, int argc, char **argv,
+ const dns_dyndbctx_t *dctx,
+ sample_instance_t **sample_instp) {
+ isc_result_t result;
+ sample_instance_t *inst = NULL;
+
+ REQUIRE(sample_instp != NULL && *sample_instp == NULL);
+
+ CHECKED_MEM_GET_PTR(mctx, inst);
+ ZERO_PTR(inst);
+ isc_mem_attach(mctx, &inst->mctx);
+
+ inst->db_name = isc_mem_strdup(mctx, db_name);
+
+ inst->zone1_name = dns_fixedname_initname(&inst->zone1_fn);
+ inst->zone2_name = dns_fixedname_initname(&inst->zone2_fn);
+
+ result = parse_params(mctx, argc, argv, inst->zone1_name,
+ inst->zone2_name);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "new_sample_instance: parse_params -> %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ 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. */
+ result = dns_db_register(db_name, create_db, inst, mctx, &inst->db_imp);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "new_sample_instance: dns_db_register -> %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ *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;
+
+ result = create_zone(inst, inst->zone1_name, &inst->zone1);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "load_sample_instance_zones: create_zone -> %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ result = activate_zone(inst, inst->zone1);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "load_sample_instance_zones: activate_zone -> %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ result = create_zone(inst, inst->zone2_name, &inst->zone2);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "load_sample_instance_zones: create_zone -> %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ result = activate_zone(inst, inst->zone2);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "load_sample_instance_zones: activate_zone -> %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+cleanup:
+ return (result);
+}
+
+void
+destroy_sample_instance(sample_instance_t **instp) {
+ sample_instance_t *inst;
+ REQUIRE(instp != NULL);
+
+ inst = *instp;
+ *instp = NULL;
+ if (inst == NULL) {
+ return;
+ }
+
+ if (inst->db_name != NULL) {
+ isc_mem_free(inst->mctx, inst->db_name);
+ }
+ if (inst->zone1 != NULL) {
+ dns_zone_detach(&inst->zone1);
+ }
+ if (inst->zone2 != NULL) {
+ dns_zone_detach(&inst->zone2);
+ }
+ if (inst->db_imp != NULL) {
+ dns_db_unregister(&inst->db_imp);
+ }
+
+ dns_view_detach(&inst->view);
+ dns_zonemgr_detach(&inst->zmgr);
+ isc_task_detach(&inst->task);
+
+ MEM_PUT_AND_DETACH(inst);
+}
diff --git a/bin/tests/system/dyndb/driver/instance.h b/bin/tests/system/dyndb/driver/instance.h
new file mode 100644
index 0000000..67a219c
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/instance.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2009-2015 Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * Driver instance object.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/types.h>
+
+struct sample_instance {
+ isc_mem_t *mctx;
+ char *db_name;
+ dns_dbimplementation_t *db_imp;
+
+ /* These are needed for zone creation. */
+ dns_view_t *view;
+ dns_zonemgr_t *zmgr;
+ isc_task_t *task;
+ bool exiting;
+
+ dns_zone_t *zone1;
+ dns_fixedname_t zone1_fn;
+ dns_name_t *zone1_name;
+
+ dns_zone_t *zone2;
+ dns_fixedname_t zone2_fn;
+ dns_name_t *zone2_name;
+};
+
+typedef struct sample_instance sample_instance_t;
+
+isc_result_t
+new_sample_instance(isc_mem_t *mctx, const char *db_name, int argc, char **argv,
+ const dns_dyndbctx_t *dctx,
+ sample_instance_t **sample_instp);
+
+isc_result_t
+load_sample_instance_zones(sample_instance_t *inst);
+
+void
+destroy_sample_instance(sample_instance_t **sample_instp);
diff --git a/bin/tests/system/dyndb/driver/lock.c b/bin/tests/system/dyndb/driver/lock.c
new file mode 100644
index 0000000..5d73871
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/lock.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "lock.h"
+
+#include <isc/task.h>
+#include <isc/util.h>
+
+/*
+ * Lock BIND dispatcher and allow only single task to run.
+ *
+ * @warning
+ * All calls to isc_task_beginexclusive() have to operate on the same task
+ * otherwise it would not be possible to distinguish recursive locking
+ * from real conflict on the dispatcher lock.
+ * For this reason this wrapper function always works with inst->task.
+ * As a result, this function have to be be called only from inst->task.
+ *
+ * Recursive locking is allowed. Auxiliary variable pointed to by "statep"
+ * stores information if last run_exclusive_enter() operation really locked
+ * something or if the lock was called recursively and was no-op.
+ *
+ * The pair (inst, state) used for run_exclusive_enter() has to be
+ * used for run_exclusive_exit().
+ *
+ * @param[in] inst The instance with the only task which is allowed to
+ * run.
+ * @param[in,out] statep Lock state: ISC_R_SUCCESS or ISC_R_LOCKBUSY
+ */
+void
+run_exclusive_enter(sample_instance_t *inst, isc_result_t *statep) {
+ REQUIRE(statep != NULL);
+ REQUIRE(*statep == ISC_R_IGNORE);
+
+ *statep = isc_task_beginexclusive(inst->task);
+ RUNTIME_CHECK(*statep == ISC_R_SUCCESS || *statep == ISC_R_LOCKBUSY);
+}
+
+/*
+ * Exit task-exclusive mode.
+ *
+ * @param[in] inst The instance used for previous run_exclusive_enter() call.
+ * @param[in] state Lock state as returned by run_exclusive_enter().
+ */
+void
+run_exclusive_exit(sample_instance_t *inst, isc_result_t state) {
+ if (state == ISC_R_SUCCESS) {
+ isc_task_endexclusive(inst->task);
+ } else {
+ /* Unlocking recursive lock or the lock was never locked. */
+ INSIST(state == ISC_R_LOCKBUSY || state == ISC_R_IGNORE);
+ }
+
+ return;
+}
diff --git a/bin/tests/system/dyndb/driver/lock.h b/bin/tests/system/dyndb/driver/lock.h
new file mode 100644
index 0000000..9f2ed9e
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/lock.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+#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);
diff --git a/bin/tests/system/dyndb/driver/log.c b/bin/tests/system/dyndb/driver/log.c
new file mode 100644
index 0000000..ef8b1ee
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/log.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "log.h"
+
+#include <isc/util.h>
+
+#include <dns/log.h>
+
+void
+log_write(int level, const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
+ level, format, args);
+ va_end(args);
+}
diff --git a/bin/tests/system/dyndb/driver/log.h b/bin/tests/system/dyndb/driver/log.h
new file mode 100644
index 0000000..375db2b
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/log.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+#include <isc/error.h>
+#include <isc/result.h>
+
+#include <dns/log.h>
+
+#define log_error_r(fmt, ...) \
+ log_error(fmt ": %s", ##__VA_ARGS__, isc_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);
diff --git a/bin/tests/system/dyndb/driver/syncptr.c b/bin/tests/system/dyndb/driver/syncptr.c
new file mode 100644
index 0000000..5124df3
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/syncptr.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Automatic A/AAAA/PTR record synchronization.
+ */
+
+#include "syncptr.h"
+
+#include <isc/event.h>
+#include <isc/eventclass.h>
+#include <isc/netaddr.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/byaddr.h>
+#include <dns/db.h>
+#include <dns/name.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include "instance.h"
+#include "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);
+
+ log_write(ISC_LOG_INFO, "ENTER: syncptr_write");
+
+ result = dns_zone_getdb(pevent->zone, &db);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "syncptr_write: dns_zone_getdb -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ result = dns_db_newversion(db, &version);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "syncptr_write: dns_db_newversion -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ result = dns_diff_apply(&pevent->diff, db, version);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "syncptr_write: dns_diff_apply -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+cleanup:
+ if (db != NULL) {
+ if (version != NULL) {
+ dns_db_closeversion(db, &version, true);
+ }
+ dns_db_detach(&db);
+ }
+ dns_zone_detach(&pevent->zone);
+ dns_diff_clear(&pevent->diff);
+ isc_event_free(&event);
+}
+
+/*
+ * Find a reverse zone for given IP address.
+ *
+ * @param[in] rdata IP address as A/AAAA record
+ * @param[out] name Owner name for the PTR record
+ * @param[out] zone DNS zone for reverse record matching the IP address
+ *
+ * @retval ISC_R_SUCCESS DNS name derived from given IP address belongs to an
+ * reverse zone managed by this driver instance.
+ * PTR record synchronization can continue.
+ * @retval ISC_R_NOTFOUND Suitable reverse zone was not found because it
+ * does not exist or is not managed by this driver.
+ */
+static isc_result_t
+syncptr_find_zone(sample_instance_t *inst, dns_rdata_t *rdata, dns_name_t *name,
+ dns_zone_t **zone) {
+ isc_result_t result;
+ isc_netaddr_t isc_ip; /* internal net address representation */
+ dns_rdata_in_a_t ipv4;
+ dns_rdata_in_aaaa_t ipv6;
+
+ REQUIRE(inst != NULL);
+ REQUIRE(zone != NULL && *zone == NULL);
+
+ switch (rdata->type) {
+ case dns_rdatatype_a:
+ CHECK(dns_rdata_tostruct(rdata, &ipv4, inst->mctx));
+ isc_netaddr_fromin(&isc_ip, &ipv4.in_addr);
+ break;
+
+ case dns_rdatatype_aaaa:
+ CHECK(dns_rdata_tostruct(rdata, &ipv6, inst->mctx));
+ isc_netaddr_fromin6(&isc_ip, &ipv6.in6_addr);
+ break;
+
+ default:
+ FATAL_ERROR("unsupported address type 0x%x", rdata->type);
+ break;
+ }
+
+ /*
+ * Convert IP address to PTR owner name.
+ *
+ * @example
+ * 192.168.0.1 -> 1.0.168.192.in-addr.arpa
+ */
+ result = dns_byaddr_createptrname(&isc_ip, 0, name);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "syncptr_find_zone: dns_byaddr_createptrname -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ /* 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) {
+ log_write(ISC_LOG_ERROR,
+ "syncptr_find_zone: dns_zt_find -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ /* Make sure that the zone is managed by this driver. */
+ if (*zone != inst->zone1 && *zone != inst->zone2) {
+ dns_zone_detach(zone);
+ log_write(ISC_LOG_INFO, "syncptr_find_zone: zone not managed");
+ 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));
+ 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 synchronization 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);
+ dns_name_copy(name, dns_fixedname_name(&pevent->ptr_target_name));
+ dns_name_clone(dns_fixedname_name(&pevent->ptr_target_name),
+ &ptr_struct.ptr);
+ dns_diff_init(inst->mctx, &pevent->diff);
+ result = dns_rdata_fromstruct(&ptr_rdata, dns_rdataclass_in,
+ dns_rdatatype_ptr, &ptr_struct,
+ &pevent->b);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "syncptr: dns_rdata_fromstruct -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ /* Create diff */
+ result = dns_difftuple_create(mctx, op, dns_fixedname_name(&ptr_name),
+ ttl, &ptr_rdata, &tp);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "syncptr: dns_difftuple_create -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ dns_diff_append(&pevent->diff, &tp);
+
+ /*
+ * Send update event to the reverse zone.
+ * It will be processed asynchronously.
+ */
+ dns_zone_gettask(ptr_zone, &task);
+ isc_task_send(task, (isc_event_t **)&pevent);
+
+cleanup:
+ if (ptr_zone != NULL) {
+ dns_zone_detach(&ptr_zone);
+ }
+ if (tp != NULL) {
+ dns_difftuple_free(&tp);
+ }
+ if (task != NULL) {
+ isc_task_detach(&task);
+ }
+ if (pevent != NULL) {
+ isc_event_free((isc_event_t **)&pevent);
+ }
+
+ return (result);
+}
+
+/*
+ * Generate update event for every rdata in rdataset.
+ *
+ * @param[in] name Owner name for A/AAAA records in rdataset.
+ * @param[in] rdataset A/AAAA records.
+ * @param[in] op DNS_DIFFOP_ADD / DNS_DIFFOP_DEL for adding / deleting
+ * the rdata
+ */
+isc_result_t
+syncptrs(sample_instance_t *inst, dns_name_t *name, dns_rdataset_t *rdataset,
+ dns_diffop_t op) {
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset))
+ {
+ dns_rdataset_current(rdataset, &rdata);
+ result = syncptr(inst, name, &rdata, rdataset->ttl, op);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
+ goto cleanup;
+ }
+ }
+ if (result == ISC_R_NOMORE) {
+ result = ISC_R_SUCCESS;
+ }
+
+cleanup:
+ return (result);
+}
diff --git a/bin/tests/system/dyndb/driver/syncptr.h b/bin/tests/system/dyndb/driver/syncptr.h
new file mode 100644
index 0000000..91edee1
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/syncptr.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Sync PTR records
+ */
+
+#pragma once
+
+#include <isc/result.h>
+
+#include <dns/diff.h>
+#include <dns/name.h>
+#include <dns/rdataset.h>
+
+#include "instance.h"
+
+isc_result_t
+syncptrs(sample_instance_t *inst, dns_name_t *name, dns_rdataset_t *rdataset,
+ dns_diffop_t op);
diff --git a/bin/tests/system/dyndb/driver/util.h b/bin/tests/system/dyndb/driver/util.h
new file mode 100644
index 0000000..d59a7fd
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/util.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Memory allocation and error handling utilities.
+ */
+
+#pragma once
+
+#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)))
diff --git a/bin/tests/system/dyndb/driver/zone.c b/bin/tests/system/dyndb/driver/zone.c
new file mode 100644
index 0000000..59f87a6
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/zone.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) Red Hat
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND AUTHORS DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Zone management.
+ */
+
+#include "zone.h"
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/util.h>
+
+#include <dns/dyndb.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include "instance.h"
+#include "lock.h"
+#include "log.h"
+#include "util.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;
+
+ result = dns_zone_create(&raw, inst->mctx);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR, "create_zone: dns_zone_create -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ result = dns_zone_setorigin(raw, name);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "create_zone: dns_zone_setorigin -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ dns_zone_setclass(raw, dns_rdataclass_in);
+ dns_zone_settype(raw, dns_zone_primary);
+ dns_zone_setdbtype(raw, 1, zone_argv);
+
+ result = dns_zonemgr_managezone(inst->zmgr, raw);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "create_zone: dns_zonemgr_managezone -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ /* This is completely insecure - use some sensible values instead! */
+ result = dns_acl_any(inst->mctx, &acl_any);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR, "create_zone: dns_acl_any -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ dns_zone_setupdateacl(raw, acl_any);
+ dns_zone_setqueryacl(raw, acl_any);
+ dns_zone_setxfracl(raw, acl_any);
+ dns_acl_detach(&acl_any);
+
+ *rawp = raw;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ dns_name_format(name, zone_name, DNS_NAME_FORMATSIZE);
+ log_error_r("failed to create new zone '%s'", zone_name);
+
+ if (raw != NULL) {
+ if (dns_zone_getmgr(raw) != NULL) {
+ dns_zonemgr_releasezone(inst->zmgr, raw);
+ }
+ dns_zone_detach(&raw);
+ }
+ if (acl_any != NULL) {
+ dns_acl_detach(&acl_any);
+ }
+
+ return (result);
+}
+
+/*
+ * Add zone to the view defined in inst->view. This will make the zone visible
+ * to clients.
+ */
+static isc_result_t
+publish_zone(sample_instance_t *inst, dns_zone_t *zone) {
+ isc_result_t result;
+ bool freeze = false;
+ dns_zone_t *zone_in_view = NULL;
+ dns_view_t *view_in_zone = NULL;
+ isc_result_t lock_state = ISC_R_IGNORE;
+
+ REQUIRE(inst != NULL);
+ REQUIRE(zone != NULL);
+
+ /* Return success if the zone is already in the view as expected. */
+ result = dns_view_findzone(inst->view, dns_zone_getorigin(zone),
+ &zone_in_view);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
+ goto cleanup;
+ }
+
+ view_in_zone = dns_zone_getview(zone);
+ if (view_in_zone != NULL) {
+ /* Zone has a view set -> view should contain the same zone. */
+ if (zone_in_view == zone) {
+ /* Zone is already published in the right view. */
+ CLEANUP_WITH(ISC_R_SUCCESS);
+ } else if (view_in_zone != inst->view) {
+ /*
+ * Un-published inactive zone will have
+ * inst->view in zone but will not be present
+ * in the view itself.
+ */
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone->view doesn't "
+ "match data in the view");
+ CLEANUP_WITH(ISC_R_UNEXPECTED);
+ }
+ }
+
+ if (zone_in_view != NULL) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "cannot publish zone: view already "
+ "contains another zone with this name");
+ CLEANUP_WITH(ISC_R_UNEXPECTED);
+ }
+
+ run_exclusive_enter(inst, &lock_state);
+ if (inst->view->frozen) {
+ freeze = true;
+ dns_view_thaw(inst->view);
+ }
+
+ dns_zone_setview(zone, inst->view);
+ result = dns_view_addzone(inst->view, zone);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "publish_zone: dns_view_addzone -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+cleanup:
+ if (zone_in_view != NULL) {
+ dns_zone_detach(&zone_in_view);
+ }
+ if (freeze) {
+ dns_view_freeze(inst->view);
+ }
+ run_exclusive_exit(inst, lock_state);
+
+ return (result);
+}
+
+/*
+ * @warning Never call this on raw part of in-line secure zone, call it only
+ * on the secure zone!
+ */
+static isc_result_t
+load_zone(dns_zone_t *zone) {
+ isc_result_t result;
+ bool zone_dynamic;
+ uint32_t serial;
+
+ result = dns_zone_load(zone, false);
+ 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);
+
+ result = dns_zone_getserial(zone, &serial);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR,
+ "load_zone: dns_zone_getserial -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ 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",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ result = load_zone(raw);
+ if (result != ISC_R_SUCCESS) {
+ log_write(ISC_LOG_ERROR, "activate_zone: load_zone -> %s\n",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+cleanup:
+ return (result);
+}
diff --git a/bin/tests/system/dyndb/driver/zone.h b/bin/tests/system/dyndb/driver/zone.h
new file mode 100644
index 0000000..85575a0
--- /dev/null
+++ b/bin/tests/system/dyndb/driver/zone.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) Red Hat
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
+ * USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+#include <isc/result.h>
+
+#include <dns/name.h>
+
+#include "instance.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);
diff --git a/bin/tests/system/dyndb/ns1/named.conf.in b/bin/tests/system/dyndb/ns1/named.conf.in
new file mode 100644
index 0000000..d319a2a
--- /dev/null
+++ b/bin/tests/system/dyndb/ns1/named.conf.in
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+controls { };
+
+options {
+ query-source address 10.53.0.1;
+ notify-source 10.53.0.1;
+ transfer-source 10.53.0.1;
+ port @PORT@;
+ 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;
+ dnssec-validation no;
+};
+
+key rndc_key {
+ secret "1234abcd8765";
+ algorithm @DEFAULT_HMAC@;
+};
+
+controls {
+ inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+dyndb sample "../driver/.libs/sample.so" { ipv4.example.nil. in-addr.arpa. };
+dyndb sample2 "../driver/.libs/sample.so" { ipv6.example.nil. 8.b.d.0.1.0.0.2.ip6.arpa. };
diff --git a/bin/tests/system/dyndb/prereq.sh b/bin/tests/system/dyndb/prereq.sh
new file mode 100644
index 0000000..19e0467
--- /dev/null
+++ b/bin/tests/system/dyndb/prereq.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+. ../conf.sh
+
+$FEATURETEST --tsan && {
+ echo_i "TSAN - skipping dyndb test"
+ exit 255
+}
+
+exit 0
diff --git a/bin/tests/system/dyndb/setup.sh b/bin/tests/system/dyndb/setup.sh
new file mode 100644
index 0000000..82240a7
--- /dev/null
+++ b/bin/tests/system/dyndb/setup.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+. ../conf.sh
+
+copy_setports ns1/named.conf.in ns1/named.conf
diff --git a/bin/tests/system/dyndb/tests.sh b/bin/tests/system/dyndb/tests.sh
new file mode 100644
index 0000000..ef02dea
--- /dev/null
+++ b/bin/tests/system/dyndb/tests.sh
@@ -0,0 +1,166 @@
+#!/bin/sh
+
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+set -e
+
+. ../conf.sh
+
+status=0
+n=0
+
+DIGOPTS="@10.53.0.1 -p ${PORT}"
+RNDCCMD="$RNDC -c ../common/rndc.conf -p ${CONTROLPORT} -s"
+
+newtest() {
+ n=$((n + 1))
+ echo_i "${1} (${n})"
+ ret=0
+}
+
+test_add() {
+ host="$1"
+ type="$2"
+ ip="$3"
+
+ cat <<EOF > ns1/update.txt
+server 10.53.0.1 ${PORT}
+ttl 86400
+update add $host $type $ip
+send
+EOF
+
+ newtest "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
+ }
+
+ for i in 1 2 3 4 5 6 7 8 9 10
+ do
+ out=$($DIG $DIGOPTS +noall +answer -x $ip)
+ echo $out > added.ptr.out.$n
+ lines=$(echo "$out" | grep "$host" | wc -l)
+ [ $lines -eq 1 ] && break;
+ $PERL -e 'select(undef, undef, undef, 0.1);'
+ done
+ [ $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 ${PORT}
+update del $host $type
+send
+EOF
+
+ newtest "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
+ }
+
+ for i in 1 2 3 4 5 6 7 8 9 10
+ do
+ out=$($DIG $DIGOPTS +noall +answer -x $ip)
+ echo $out > deleted.ptr.out.$n
+ lines=$(echo "$out" | grep "$host" | wc -l)
+ [ $lines -eq 0 ] && break
+ $PERL -e 'select(undef, undef, undef, 0.1);'
+ done
+ [ $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=$((status + ret))
+
+test_add test2.ipv4.example.nil. A "10.53.0.11" || ret=1
+status=$((status + ret))
+
+test_add test3.ipv4.example.nil. A "10.53.0.12" || ret=1
+status=$((status + ret))
+
+test_add test4.ipv6.example.nil. AAAA "2001:db8::1" || ret=1
+status=$((status + ret))
+
+test_del test1.ipv4.example.nil. A || ret=1
+status=$((status + ret))
+
+test_del test2.ipv4.example.nil. A || ret=1
+status=$((status + ret))
+
+test_del test3.ipv4.example.nil. A || ret=1
+status=$((status + ret))
+
+test_del test4.ipv6.example.nil. AAAA || ret=1
+status=$((status + ret))
+
+newtest "checking parameter logging"
+grep "loading params for dyndb 'sample' from .*named.conf:" ns1/named.run > /dev/null || ret=1
+grep "loading params for dyndb 'sample2' from .*named.conf:" ns1/named.run > /dev/null || ret=1
+[ $ret -eq 1 ] && echo_i "failed"
+status=$((status + ret))
+
+echo_i "checking dyndb still works after reload"
+rndc_reload ns1 10.53.0.1
+
+test_add test5.ipv4.example.nil. A "10.53.0.10" || ret=1
+status=$((status + ret))
+
+test_add test6.ipv6.example.nil. AAAA "2001:db8::1" || ret=1
+status=$((status + ret))
+
+test_del test5.ipv4.example.nil. A || ret=1
+status=$((status + ret))
+
+test_del test6.ipv6.example.nil. AAAA || ret=1
+status=$((status + ret))
+
+echo_i "exit status: $status"
+[ $status -eq 0 ] || exit 1
diff --git a/bin/tests/system/dyndb/tests_sh_dyndb.py b/bin/tests/system/dyndb/tests_sh_dyndb.py
new file mode 100644
index 0000000..ca8f7a1
--- /dev/null
+++ b/bin/tests/system/dyndb/tests_sh_dyndb.py
@@ -0,0 +1,14 @@
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+
+def test_dyndb(run_tests_sh):
+ run_tests_sh()