summaryrefslogtreecommitdiffstats
path: root/src/lib/d2srv/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/d2srv/tests')
-rw-r--r--src/lib/d2srv/tests/Makefile.am53
-rw-r--r--src/lib/d2srv/tests/Makefile.in1088
-rw-r--r--src/lib/d2srv/tests/d2_tsig_key_unittest.cc98
-rw-r--r--src/lib/d2srv/tests/d2_update_message_unittests.cc696
-rw-r--r--src/lib/d2srv/tests/d2_zone_unittests.cc83
-rw-r--r--src/lib/d2srv/tests/dns_client_unittests.cc678
-rw-r--r--src/lib/d2srv/tests/nc_trans_unittests.cc1279
-rw-r--r--src/lib/d2srv/tests/run_unittests.cc20
8 files changed, 3995 insertions, 0 deletions
diff --git a/src/lib/d2srv/tests/Makefile.am b/src/lib/d2srv/tests/Makefile.am
new file mode 100644
index 0000000..436cb21
--- /dev/null
+++ b/src/lib/d2srv/tests/Makefile.am
@@ -0,0 +1,53 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += libd2srv_unittests
+
+libd2srv_unittests_SOURCES = run_unittests.cc
+libd2srv_unittests_SOURCES += d2_tsig_key_unittest.cc
+libd2srv_unittests_SOURCES += d2_update_message_unittests.cc
+libd2srv_unittests_SOURCES += d2_zone_unittests.cc
+libd2srv_unittests_SOURCES += dns_client_unittests.cc
+libd2srv_unittests_SOURCES += nc_trans_unittests.cc
+
+libd2srv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+libd2srv_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+libd2srv_unittests_LDADD = $(top_builddir)/src/lib/d2srv/libkea-d2srv.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/d2srv/testutils/libd2srvtest.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/process/libkea-process.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libkea-asiodns.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/database/libkea-database.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+libd2srv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libd2srv_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(GTEST_LDADD)
+
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/d2srv/tests/Makefile.in b/src/lib/d2srv/tests/Makefile.in
new file mode 100644
index 0000000..3dc72db
--- /dev/null
+++ b/src/lib/d2srv/tests/Makefile.in
@@ -0,0 +1,1088 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 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@
+
+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@
+TESTS = $(am__EXEEXT_1)
+@HAVE_GTEST_TRUE@am__append_1 = libd2srv_unittests
+noinst_PROGRAMS = $(am__EXEEXT_2)
+subdir = src/lib/d2srv/tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp11.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_sysrepo.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/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 =
+@HAVE_GTEST_TRUE@am__EXEEXT_1 = libd2srv_unittests$(EXEEXT)
+am__EXEEXT_2 = $(am__EXEEXT_1)
+PROGRAMS = $(noinst_PROGRAMS)
+am__libd2srv_unittests_SOURCES_DIST = run_unittests.cc \
+ d2_tsig_key_unittest.cc d2_update_message_unittests.cc \
+ d2_zone_unittests.cc dns_client_unittests.cc \
+ nc_trans_unittests.cc
+@HAVE_GTEST_TRUE@am_libd2srv_unittests_OBJECTS = \
+@HAVE_GTEST_TRUE@ libd2srv_unittests-run_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ libd2srv_unittests-d2_tsig_key_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ libd2srv_unittests-d2_update_message_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ libd2srv_unittests-d2_zone_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ libd2srv_unittests-dns_client_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ libd2srv_unittests-nc_trans_unittests.$(OBJEXT)
+libd2srv_unittests_OBJECTS = $(am_libd2srv_unittests_OBJECTS)
+am__DEPENDENCIES_1 =
+@HAVE_GTEST_TRUE@libd2srv_unittests_DEPENDENCIES = $(top_builddir)/src/lib/d2srv/libkea-d2srv.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/d2srv/testutils/libd2srvtest.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiodns/libkea-asiodns.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/database/libkea-database.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1)
+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 =
+libd2srv_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(AM_CXXFLAGS) $(CXXFLAGS) $(libd2srv_unittests_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)/libd2srv_unittests-d2_tsig_key_unittest.Po \
+ ./$(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po \
+ ./$(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po \
+ ./$(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po \
+ ./$(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po \
+ ./$(DEPDIR)/libd2srv_unittests-run_unittests.Po
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+SOURCES = $(libd2srv_unittests_SOURCES)
+DIST_SOURCES = $(am__libd2srv_unittests_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+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)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_SYSREPO = @HAVE_SYSREPO@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+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@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+YACC = @YACC@
+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_CXX = @ac_ct_CXX@
+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@
+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_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = .
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \
+ $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static
+CLEANFILES = *.gcno *.gcda
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+@HAVE_GTEST_TRUE@libd2srv_unittests_SOURCES = run_unittests.cc \
+@HAVE_GTEST_TRUE@ d2_tsig_key_unittest.cc \
+@HAVE_GTEST_TRUE@ d2_update_message_unittests.cc \
+@HAVE_GTEST_TRUE@ d2_zone_unittests.cc dns_client_unittests.cc \
+@HAVE_GTEST_TRUE@ nc_trans_unittests.cc
+@HAVE_GTEST_TRUE@libd2srv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+@HAVE_GTEST_TRUE@libd2srv_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+@HAVE_GTEST_TRUE@libd2srv_unittests_LDADD = $(top_builddir)/src/lib/d2srv/libkea-d2srv.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/d2srv/testutils/libd2srvtest.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiodns/libkea-asiodns.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/database/libkea-database.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+@HAVE_GTEST_TRUE@ $(LOG4CPLUS_LIBS) $(BOOST_LIBS) \
+@HAVE_GTEST_TRUE@ $(GTEST_LDADD)
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(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 src/lib/d2srv/tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib/d2srv/tests/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_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+libd2srv_unittests$(EXEEXT): $(libd2srv_unittests_OBJECTS) $(libd2srv_unittests_DEPENDENCIES) $(EXTRA_libd2srv_unittests_DEPENDENCIES)
+ @rm -f libd2srv_unittests$(EXEEXT)
+ $(AM_V_CXXLD)$(libd2srv_unittests_LINK) $(libd2srv_unittests_OBJECTS) $(libd2srv_unittests_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-run_unittests.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+libd2srv_unittests-run_unittests.o: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-run_unittests.Tpo -c -o libd2srv_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-run_unittests.Tpo $(DEPDIR)/libd2srv_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='libd2srv_unittests-run_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+
+libd2srv_unittests-run_unittests.obj: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-run_unittests.Tpo -c -o libd2srv_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-run_unittests.Tpo $(DEPDIR)/libd2srv_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='libd2srv_unittests-run_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+
+libd2srv_unittests-d2_tsig_key_unittest.o: d2_tsig_key_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_tsig_key_unittest.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Tpo -c -o libd2srv_unittests-d2_tsig_key_unittest.o `test -f 'd2_tsig_key_unittest.cc' || echo '$(srcdir)/'`d2_tsig_key_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Tpo $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_tsig_key_unittest.cc' object='libd2srv_unittests-d2_tsig_key_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_tsig_key_unittest.o `test -f 'd2_tsig_key_unittest.cc' || echo '$(srcdir)/'`d2_tsig_key_unittest.cc
+
+libd2srv_unittests-d2_tsig_key_unittest.obj: d2_tsig_key_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_tsig_key_unittest.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Tpo -c -o libd2srv_unittests-d2_tsig_key_unittest.obj `if test -f 'd2_tsig_key_unittest.cc'; then $(CYGPATH_W) 'd2_tsig_key_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/d2_tsig_key_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Tpo $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_tsig_key_unittest.cc' object='libd2srv_unittests-d2_tsig_key_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_tsig_key_unittest.obj `if test -f 'd2_tsig_key_unittest.cc'; then $(CYGPATH_W) 'd2_tsig_key_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/d2_tsig_key_unittest.cc'; fi`
+
+libd2srv_unittests-d2_update_message_unittests.o: d2_update_message_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_update_message_unittests.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Tpo -c -o libd2srv_unittests-d2_update_message_unittests.o `test -f 'd2_update_message_unittests.cc' || echo '$(srcdir)/'`d2_update_message_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Tpo $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_update_message_unittests.cc' object='libd2srv_unittests-d2_update_message_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_update_message_unittests.o `test -f 'd2_update_message_unittests.cc' || echo '$(srcdir)/'`d2_update_message_unittests.cc
+
+libd2srv_unittests-d2_update_message_unittests.obj: d2_update_message_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_update_message_unittests.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Tpo -c -o libd2srv_unittests-d2_update_message_unittests.obj `if test -f 'd2_update_message_unittests.cc'; then $(CYGPATH_W) 'd2_update_message_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_update_message_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Tpo $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_update_message_unittests.cc' object='libd2srv_unittests-d2_update_message_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_update_message_unittests.obj `if test -f 'd2_update_message_unittests.cc'; then $(CYGPATH_W) 'd2_update_message_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_update_message_unittests.cc'; fi`
+
+libd2srv_unittests-d2_zone_unittests.o: d2_zone_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_zone_unittests.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Tpo -c -o libd2srv_unittests-d2_zone_unittests.o `test -f 'd2_zone_unittests.cc' || echo '$(srcdir)/'`d2_zone_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Tpo $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_zone_unittests.cc' object='libd2srv_unittests-d2_zone_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_zone_unittests.o `test -f 'd2_zone_unittests.cc' || echo '$(srcdir)/'`d2_zone_unittests.cc
+
+libd2srv_unittests-d2_zone_unittests.obj: d2_zone_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_zone_unittests.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Tpo -c -o libd2srv_unittests-d2_zone_unittests.obj `if test -f 'd2_zone_unittests.cc'; then $(CYGPATH_W) 'd2_zone_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_zone_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Tpo $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_zone_unittests.cc' object='libd2srv_unittests-d2_zone_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_zone_unittests.obj `if test -f 'd2_zone_unittests.cc'; then $(CYGPATH_W) 'd2_zone_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_zone_unittests.cc'; fi`
+
+libd2srv_unittests-dns_client_unittests.o: dns_client_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-dns_client_unittests.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Tpo -c -o libd2srv_unittests-dns_client_unittests.o `test -f 'dns_client_unittests.cc' || echo '$(srcdir)/'`dns_client_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Tpo $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dns_client_unittests.cc' object='libd2srv_unittests-dns_client_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-dns_client_unittests.o `test -f 'dns_client_unittests.cc' || echo '$(srcdir)/'`dns_client_unittests.cc
+
+libd2srv_unittests-dns_client_unittests.obj: dns_client_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-dns_client_unittests.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Tpo -c -o libd2srv_unittests-dns_client_unittests.obj `if test -f 'dns_client_unittests.cc'; then $(CYGPATH_W) 'dns_client_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/dns_client_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Tpo $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dns_client_unittests.cc' object='libd2srv_unittests-dns_client_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-dns_client_unittests.obj `if test -f 'dns_client_unittests.cc'; then $(CYGPATH_W) 'dns_client_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/dns_client_unittests.cc'; fi`
+
+libd2srv_unittests-nc_trans_unittests.o: nc_trans_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-nc_trans_unittests.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Tpo -c -o libd2srv_unittests-nc_trans_unittests.o `test -f 'nc_trans_unittests.cc' || echo '$(srcdir)/'`nc_trans_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Tpo $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nc_trans_unittests.cc' object='libd2srv_unittests-nc_trans_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-nc_trans_unittests.o `test -f 'nc_trans_unittests.cc' || echo '$(srcdir)/'`nc_trans_unittests.cc
+
+libd2srv_unittests-nc_trans_unittests.obj: nc_trans_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-nc_trans_unittests.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Tpo -c -o libd2srv_unittests-nc_trans_unittests.obj `if test -f 'nc_trans_unittests.cc'; then $(CYGPATH_W) 'nc_trans_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/nc_trans_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Tpo $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nc_trans_unittests.cc' object='libd2srv_unittests-nc_trans_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-nc_trans_unittests.obj `if test -f 'nc_trans_unittests.cc'; then $(CYGPATH_W) 'nc_trans_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/nc_trans_unittests.cc'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(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-recursive
+
+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-recursive
+
+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
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ fi; \
+ echo "$${col}$$dashes$${std}"; \
+ echo "$${col}$$banner$${std}"; \
+ test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+ test -z "$$report" || echo "$${col}$$report$${std}"; \
+ echo "$${col}$$dashes$${std}"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+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
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+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:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+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-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Po
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-run_unittests.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Po
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po
+ -rm -f ./$(DEPDIR)/libd2srv_unittests-run_unittests.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) check-am install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-TESTS check-am clean clean-generic \
+ clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir 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 installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.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/src/lib/d2srv/tests/d2_tsig_key_unittest.cc b/src/lib/d2srv/tests/d2_tsig_key_unittest.cc
new file mode 100644
index 0000000..2bc6171
--- /dev/null
+++ b/src/lib/d2srv/tests/d2_tsig_key_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <cc/data.h>
+#include <d2srv/d2_stats.h>
+#include <d2srv/d2_tsig_key.h>
+#include <stats/stats_mgr.h>
+
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <sstream>
+
+using namespace isc::d2;
+using namespace isc::data;
+using namespace isc::dns;
+using namespace isc::stats;
+using namespace std;
+
+namespace {
+
+/// @brief Check statistics names.
+TEST(D2StatsTest, names) {
+ ASSERT_EQ(3, D2Stats::ncr.size());
+ ASSERT_EQ(6, D2Stats::update.size());
+ ASSERT_EQ(4, D2Stats::key.size());
+}
+
+/// @brief Fixture class for TSIG key / DNS update statistics.
+class D2TsigKeyTest : public ::testing::Test {
+public:
+ /// @brief Constructor.
+ D2TsigKeyTest() {
+ StatsMgr::instance();
+ StatsMgr::instance().removeAll();
+ StatsMgr::instance().setMaxSampleCountDefault(0);
+ }
+
+ /// @brief Destructor.
+ ~D2TsigKeyTest() {
+ StatsMgr::instance().removeAll();
+ StatsMgr::instance().setMaxSampleCountDefault(0);
+ }
+};
+
+/// @brief Check TSIG key life.
+TEST_F(D2TsigKeyTest, key) {
+ // Get the statistics manager.
+ StatsMgr& stat_mgr = StatsMgr::instance();
+ ASSERT_EQ(0, stat_mgr.count());
+
+ // Create a key.
+ const string& key_spec = "foo.bar.::test";
+ D2TsigKeyPtr key(new D2TsigKey(key_spec));
+ EXPECT_EQ(4, stat_mgr.count());
+
+ // Create a context.
+ TSIGContextPtr ctx;
+ ASSERT_NO_THROW(ctx = key->createContext());
+ ASSERT_TRUE(ctx);
+ EXPECT_EQ(TSIGContext::INIT, ctx->getState());
+
+ // Get the 'sent' statistics.
+ const string& stat_name = "key[foo.bar.].update-sent";
+ EXPECT_EQ(1, stat_mgr.getSize(stat_name));
+ ObservationPtr stat = stat_mgr.getObservation(stat_name);
+ ASSERT_TRUE(stat);
+ IntegerSample sample;
+ ASSERT_NO_THROW(sample = stat->getInteger());
+ EXPECT_EQ(0, sample.first);
+
+ // Increment the 'sent' statistics.
+ stat_mgr.addValue(stat_name, static_cast<int64_t>(1));
+ stat = stat_mgr.getObservation(stat_name);
+ ASSERT_TRUE(stat);
+ ASSERT_NO_THROW(sample = stat->getInteger());
+ EXPECT_EQ(1, sample.first);
+
+ // Reset the key statistics.
+ ASSERT_NO_THROW(key->resetStats());
+ stat = stat_mgr.getObservation(stat_name);
+ ASSERT_TRUE(stat);
+ ASSERT_NO_THROW(sample = stat->getInteger());
+ EXPECT_EQ(0, sample.first);
+
+ // Destroy the key: its stats are removed.
+ key.reset();
+ EXPECT_EQ(0, stat_mgr.count());
+ stat = stat_mgr.getObservation(stat_name);
+ EXPECT_FALSE(stat);
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/d2srv/tests/d2_update_message_unittests.cc b/src/lib/d2srv/tests/d2_update_message_unittests.cc
new file mode 100644
index 0000000..6f0cfca
--- /dev/null
+++ b/src/lib/d2srv/tests/d2_update_message_unittests.cc
@@ -0,0 +1,696 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <d2srv/d2_config.h>
+#include <d2srv/d2_update_message.h>
+#include <d2srv/d2_zone.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrttl.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::util;
+
+namespace {
+ /// @brief Test fixture class for testing D2UpdateMessage object
+class D2UpdateMessageTest : public ::testing::Test {
+public:
+ /// @brief Constructor
+ //
+ // Does nothing.
+ D2UpdateMessageTest() { }
+
+ /// @brief Destructor
+ //
+ // Does nothing.
+ ~D2UpdateMessageTest() { };
+
+ /// @brief Returns string representation of the name encoded in wire format.
+ //
+ // This function reads the number of bytes specified in the second
+ // argument from the buffer. It doesn't check if buffer has sufficient
+ // length for reading given number of bytes. Caller should verify it
+ // prior to calling this function.
+ //
+ // @param buf input buffer, its internal pointer will be moved to
+ // the position after a name being read from it.
+ // @param name_length length of the name stored in the buffer
+ // @param no_zero_byte if true it indicates that the given buffer does not
+ // comprise the zero byte, which signals end of the name. This is
+ // the case, when dealing with compressed messages which don't have
+ // this byte.
+ //
+ // @return string representation of the name.
+ std::string readNameFromWire(InputBuffer& buf, size_t name_length,
+ const bool no_zero_byte = false) {
+ std::vector<uint8_t> name_data;
+ // Create another InputBuffer which holds only the name in the wire
+ // format.
+ buf.readVector(name_data, name_length);
+ if (no_zero_byte) {
+ ++name_length;
+ name_data.push_back(0);
+ }
+ InputBuffer name_buf(&name_data[0], name_length);
+ // Parse the name and return its textual representation.
+ Name name(name_buf);
+ return (name.toText());
+ }
+};
+
+// This test verifies that DNS Update message ID can be set using
+// setId function.
+TEST_F(D2UpdateMessageTest, setId) {
+ // Message ID is initialized to 0.
+ D2UpdateMessage msg;
+ EXPECT_EQ(0, msg.getId());
+ // Override the default value and verify that it has been set.
+ msg.setId(0x1234);
+ EXPECT_EQ(0x1234, msg.getId());
+}
+
+// This test verifies that the DNS Update message RCODE can be set
+// using setRcode function.
+TEST_F(D2UpdateMessageTest, setRcode) {
+ D2UpdateMessage msg;
+ // Rcode must be explicitly set before it is accessed.
+ msg.setRcode(Rcode::NOERROR());
+ EXPECT_EQ(Rcode::NOERROR().getCode(), msg.getRcode().getCode());
+ // Let's override current value to make sure that getter does
+ // not return fixed value.
+ msg.setRcode(Rcode::NOTIMP());
+ EXPECT_EQ(Rcode::NOTIMP().getCode(), msg.getRcode().getCode());
+}
+
+// This test verifies that the Zone section in the DNS Update message
+// can be set.
+TEST_F(D2UpdateMessageTest, setZone) {
+ D2UpdateMessage msg;
+ // The zone pointer is initialized to NULL.
+ D2ZonePtr zone = msg.getZone();
+ EXPECT_FALSE(zone);
+ // Let's create a new Zone and check that it is returned
+ // via getter.
+ msg.setZone(Name("example.com"), RRClass::ANY());
+ zone = msg.getZone();
+ EXPECT_TRUE(zone);
+ EXPECT_EQ("example.com.", zone->getName().toText());
+ EXPECT_EQ(RRClass::ANY().getCode(), zone->getClass().getCode());
+
+ // Now, let's check that the existing Zone object can be
+ // overridden with a new one.
+ msg.setZone(Name("foo.example.com"), RRClass::NONE());
+ zone = msg.getZone();
+ EXPECT_TRUE(zone);
+ EXPECT_EQ("foo.example.com.", zone->getName().toText());
+ EXPECT_EQ(RRClass::NONE().getCode(), zone->getClass().getCode());
+}
+
+// This test verifies that the DNS message is properly decoded from the
+// wire format.
+TEST_F(D2UpdateMessageTest, fromWire) {
+ // The following table holds the DNS response in on-wire format.
+ // This message comprises the following sections:
+ // - HEADER
+ // - PREREQUISITE section with one RR
+ // - UPDATE section with 1 RR.
+ // Such a response may be generated by the DNS server as a result
+ // of copying the contents of the REQUEST message sent by DDNS client.
+ const uint8_t bin_msg[] = {
+ // HEADER section starts here (see RFC 2136, section 2).
+ 0x05, 0xAF, // ID=0x05AF
+ 0xA8, 0x6, // QR=1, Opcode=6, RCODE=YXDOMAIN
+ 0x0, 0x1, // ZOCOUNT=1
+ 0x0, 0x2, // PRCOUNT=2
+ 0x0, 0x1, // UPCOUNT=1
+ 0x0, 0x0, // ADCOUNT=0
+
+ // Zone section starts here. The The first field comprises
+ // the Zone name encoded as a set of labels, each preceded
+ // by a length of the following label. The whole Zone name is
+ // terminated with a NULL char.
+ // For Zone section format see (RFC 2136, section 2.3).
+ 0x7, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example (7 is length)
+ 0x3, 0x63, 0x6F, 0x6D, //.com. (0x3 is a length)
+ 0x0, // NULL character terminates the Zone name.
+ 0x0, 0x6, // ZTYPE='SOA'
+ 0x0, 0x1, // ZCLASS='IN'
+
+ // Prerequisite section starts here. This section comprises two
+ // prerequisites:
+ // - 'Name is not in use'
+ // - 'Name is in use'
+ // See RFC 2136, section 2.4 for the format of Prerequisite section.
+ // Each prerequisite RR starts with its name. It is expressed in the
+ // compressed format as described in RFC 1035, section 4.1.4. The first
+ // label is expressed as in case of non-compressed name. It is preceded
+ // by the length value. The following two bytes are the pointer to the
+ // offset in the message where 'example.com' was used. That is, in the
+ // Zone name at offset 12. Pointer starts with two bits set - they
+ // mark start of the pointer.
+
+ // First prerequisite. NONE class indicates that the update requires
+ // that the name 'foo.example.com' is not use/
+ 0x03, 0x66, 0x6F, 0x6F, // foo.
+ 0xC0, 0x0C, // pointer to example.com.
+ 0x0, 0x1C, // TYPE=AAAA
+ 0x0, 0xFE, // CLASS=NONE
+ 0x0, 0x0, 0x0, 0x0, // TTL=0
+ 0x0, 0x0, // RDLENGTH=0
+
+ // Second prerequisite. ANY class indicates tha the update requires
+ // that the name 'bar.example.com' exists.
+ 0x03, 0x62, 0x61, 0x72, // bar.
+ 0xC0, 0x0C, // pointer to example.com.
+ 0x0, 0x1C, // TYPE=AAAA
+ 0x0, 0xFF, // CLASS=ANY
+ 0x0, 0x0, 0x0, 0x0, // TTL=0
+ 0x0, 0x0, // RDLENGTH=0
+
+ // Update section starts here. The format of this section conforms to
+ // RFC 2136, section 2.5. The name of the RR is again expressed in
+ // compressed format. The two pointer bytes point to the offset in the
+ // message where 'foo.example.com' was used already - 29.
+ 0xC0, 0x1D, // pointer to foo.example.com.
+ 0x0, 0x1C, // TYPE=AAAA
+ 0x0, 0x1, // CLASS=IN
+ 0xAA, 0xBB, 0xCC, 0xDD, // TTL=0xAABBCCDD
+ 0x0, 0x10, // RDLENGTH=16
+ // The following 16 bytes of RDATA hold IPv6 address: 2001:db8:1::1.
+ 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+ };
+
+ // Create an object to be used to decode the message from the wire format.
+ D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+
+ // Decode the message.
+ ASSERT_NO_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)));
+
+ // Check that the message header is valid.
+ EXPECT_EQ(0x05AF, msg.getId());
+ EXPECT_EQ(D2UpdateMessage::RESPONSE, msg.getQRFlag());
+ EXPECT_EQ(Rcode::YXDOMAIN_CODE, msg.getRcode().getCode());
+
+ // The ZOCOUNT must contain exactly one zone. If it does, we should get
+ // the name, class and type of the zone and verify they are valid.
+ ASSERT_EQ(1, msg.getRRCount(D2UpdateMessage::SECTION_ZONE));
+ D2ZonePtr zone = msg.getZone();
+ ASSERT_TRUE(zone);
+ EXPECT_EQ("example.com.", zone->getName().toText());
+ EXPECT_EQ(RRClass::IN().getCode(), zone->getClass().getCode());
+
+ // Check the Prerequisite section. It should contain two records.
+ ASSERT_EQ(2, msg.getRRCount(D2UpdateMessage::SECTION_PREREQUISITE));
+
+ // Proceed to the first prerequisite.
+ RRsetIterator rrset_it =
+ msg.beginSection(D2UpdateMessage::SECTION_PREREQUISITE);
+ RRsetPtr prereq1 = *rrset_it;
+ ASSERT_TRUE(prereq1);
+ // Check record fields.
+ EXPECT_EQ("foo.example.com.", prereq1->getName().toText()); // NAME
+ EXPECT_EQ(RRType::AAAA().getCode(), prereq1->getType().getCode()); // TYPE
+ EXPECT_EQ(RRClass::NONE().getCode(),
+ prereq1->getClass().getCode()); // CLASS
+ EXPECT_EQ(0, prereq1->getTTL().getValue()); // TTL
+ EXPECT_EQ(0, prereq1->getRdataCount()); // RDLENGTH
+
+ // Move to next prerequisite section.
+ ++rrset_it;
+ RRsetPtr prereq2 = *rrset_it;
+ ASSERT_TRUE(prereq2);
+ // Check record fields.
+ EXPECT_EQ("bar.example.com.", prereq2->getName().toText()); // NAME
+ EXPECT_EQ(RRType::AAAA().getCode(), prereq2->getType().getCode()); // TYPE
+ EXPECT_EQ(RRClass::ANY().getCode(), prereq2->getClass().getCode()); // CLASS
+ EXPECT_EQ(0, prereq2->getTTL().getValue()); // TTL
+ EXPECT_EQ(0, prereq2->getRdataCount()); // RDLENGTH
+
+ // Check the Update section. There is only one record, so beginSection()
+ // should return the pointer to this sole record.
+ ASSERT_EQ(1, msg.getRRCount(D2UpdateMessage::SECTION_UPDATE));
+ rrset_it = msg.beginSection(D2UpdateMessage::SECTION_UPDATE);
+ RRsetPtr update = *rrset_it;
+ ASSERT_TRUE(update);
+ // Check the record fields.
+ EXPECT_EQ("foo.example.com.", update->getName().toText()); // NAME
+ EXPECT_EQ(RRType::AAAA().getCode(), update->getType().getCode()); // TYPE
+ EXPECT_EQ(RRClass::IN().getCode(), update->getClass().getCode()); // CLASS
+ EXPECT_EQ(0xAABBCCDD, update->getTTL().getValue()); // TTL
+ // There should be exactly one record holding the IPv6 address.
+ // This record can be accessed using RdataIterator. This record
+ // can be compared with the reference record, holding expected IPv6
+ // address using compare function.
+ ASSERT_EQ(1, update->getRdataCount());
+ RdataIteratorPtr rdata_it = update->getRdataIterator();
+ ASSERT_TRUE(rdata_it);
+ in::AAAA rdata_ref("2001:db8:1::1");
+ EXPECT_EQ(0, rdata_ref.compare(rdata_it->getCurrent()));
+
+ // @todo: at this point we don't test Additional Data records. We may
+ // consider implementing tests for it in the future.
+}
+
+// This test verifies that the fromWire function throws appropriate exception
+// if the message being parsed comprises invalid Opcode (is not a DNS Update).
+TEST_F(D2UpdateMessageTest, fromWireInvalidOpcode) {
+ // This is a binary representation of the DNS message.
+ // It comprises invalid Opcode=3, expected value is 6
+ // (Update).
+ const uint8_t bin_msg[] = {
+ 0x05, 0xAF, // ID=0x05AF
+ 0x98, 0x6, // QR=1, Opcode=3, RCODE=YXDOMAIN
+ 0x0, 0x0, // ZOCOUNT=0
+ 0x0, 0x0, // PRCOUNT=0
+ 0x0, 0x0, // UPCOUNT=0
+ 0x0, 0x0 // ADCOUNT=0
+ };
+ // The 'true' argument passed to the constructor turns the
+ // message into the parse mode in which the fromWire function
+ // can be used to decode the binary message data.
+ D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+ // When using invalid Opcode, the fromWire function should
+ // throw NotUpdateMessage exception.
+ EXPECT_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)),
+ isc::d2::NotUpdateMessage);
+}
+
+// This test verifies that the fromWire function throws appropriate exception
+// if the message being parsed comprises invalid QR flag. The QR bit is
+// expected to be set to indicate that the message is a RESPONSE.
+TEST_F(D2UpdateMessageTest, fromWireInvalidQRFlag) {
+ // This is a binary representation of the DNS message.
+ // It comprises invalid QR flag = 0.
+ const uint8_t bin_msg[] = {
+ 0x05, 0xAF, // ID=0x05AF
+ 0x28, 0x6, // QR=0, Opcode=6, RCODE=YXDOMAIN
+ 0x0, 0x0, // ZOCOUNT=0
+ 0x0, 0x0, // PRCOUNT=0
+ 0x0, 0x0, // UPCOUNT=0
+ 0x0, 0x0 // ADCOUNT=0
+ };
+ // The 'true' argument passed to the constructor turns the
+ // message into the parse mode in which the fromWire function
+ // can be used to decode the binary message data.
+ D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+ // When using invalid QR flag, the fromWire function should
+ // throw InvalidQRFlag exception.
+ EXPECT_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)),
+ isc::d2::InvalidQRFlag);
+}
+
+// This test verifies that the fromWire function throws appropriate exception
+// if the message being parsed comprises more than one (two in this case)
+// Zone records.
+TEST_F(D2UpdateMessageTest, fromWireTooManyZones) {
+ // This is a binary representation of the DNS message. This message
+ // comprises two Zone records.
+ const uint8_t bin_msg[] = {
+ 0x05, 0xAF, // ID=0x05AF
+ 0xA8, 0x6, // QR=1, Opcode=6, RCODE=YXDOMAIN
+ 0x0, 0x2, // ZOCOUNT=2
+ 0x0, 0x0, // PRCOUNT=0
+ 0x0, 0x0, // UPCOUNT=0
+ 0x0, 0x0, // ADCOUNT=0
+
+ // Start first Zone record.
+ 0x7, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example (7 is length)
+ 0x3, 0x63, 0x6F, 0x6D, //.com. (0x3 is a length)
+ 0x0, // NULL character terminates the Zone name.
+ 0x0, 0x6, // ZTYPE='SOA'
+ 0x0, 0x1, // ZCLASS='IN'
+
+ // Start second Zone record. Presence of this record should result
+ // in error when parsing this message.
+ 0x3, 0x63, 0x6F, 0x6D, // com. (0x3 is a length)
+ 0x0, // NULL character terminates the Zone name.
+ 0x0, 0x6, // ZTYPE='SOA'
+ 0x0, 0x1 // ZCLASS='IN'
+ };
+
+ // The 'true' argument passed to the constructor turns the
+ // message into the parse mode in which the fromWire function
+ // can be used to decode the binary message data.
+ D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+ // When parsing a message with more than one Zone record,
+ // exception should be thrown.
+ EXPECT_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)),
+ isc::d2::InvalidZoneSection);
+}
+
+// This test verifies that the wire format of the message is produced
+// in the render mode.
+TEST_F(D2UpdateMessageTest, toWire) {
+ D2UpdateMessage msg;
+ // Set message ID.
+ msg.setId(0x1234);
+ // Rcode to NOERROR.
+ msg.setRcode(Rcode(Rcode::NOERROR_CODE));
+
+ // Set Zone section. This section must comprise exactly
+ // one Zone. toWire function would fail if Zone is not set.
+ msg.setZone(Name("example.com"), RRClass::IN());
+
+ // Set prerequisites.
+
+ // 'Name Is Not In Use' prerequisite (RFC 2136, section 2.4.5)
+ RRsetPtr prereq1(new RRset(Name("foo.example.com"), RRClass::NONE(),
+ RRType::ANY(), RRTTL(0)));
+ msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq1);
+
+ // 'Name is In Use' prerequisite (RFC 2136, section 2.4.4)
+ RRsetPtr prereq2(new RRset(Name("bar.example.com"), RRClass::ANY(),
+ RRType::ANY(), RRTTL(0)));
+ msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq2);
+
+ // Set Update Section.
+
+ // Create RR holding a name being added. This RR is constructed
+ // in conformance to RFC 2136, section 2.5.1.
+ RRsetPtr updaterr1(new RRset(Name("foo.example.com"), RRClass::IN(),
+ RRType::A(), RRTTL(10)));
+ // RR record is of the type A, thus RDATA holds 4 octet Internet
+ // address. This address is 10.10.1.1.
+ char rdata1[] = {
+ 0xA, 0xA , 0x1, 0x1
+ };
+ InputBuffer buf_rdata1(rdata1, 4);
+ updaterr1->addRdata(createRdata(RRType::A(), RRClass::IN(), buf_rdata1,
+ buf_rdata1.getLength()));
+ // Add the RR to the message.
+ msg.addRRset(D2UpdateMessage::SECTION_UPDATE, updaterr1);
+
+ // Render message into the wire format.
+ MessageRenderer renderer;
+ ASSERT_NO_THROW(msg.toWire(renderer));
+
+ // Make sure that created packet is not truncated.
+ ASSERT_EQ(77, renderer.getLength());
+
+ // Create input buffer from the rendered data. InputBuffer
+ // is handy to validate the byte contents of the rendered
+ // message.
+ InputBuffer buf(renderer.getData(), renderer.getLength());
+
+ // Start validating the message header.
+
+ // Verify message ID.
+ EXPECT_EQ(0x1234, buf.readUint16());
+ // The 2-bytes following message ID comprise the following fields:
+ // - QR - 1 bit indicating that it is REQUEST. Should be 0.
+ // - Opcode - 4 bits which should hold value of 5 indicating this is
+ // an Update message. Binary form is "0101".
+ // - Z - These bits are unused for Update Message and should be 0.
+ // - RCODE - Response code, set to NOERROR for REQUEST. It is 0.
+ //8706391835
+ // The binary value is:
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ // | QR| Opcode | Z | RCODE |
+ // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ // | 0 | 0 1 0 1 | 0 0 0 0 0 0 0 | 0 0 0 0 |
+ // +---+---+---+-------+---+---+---+---+---+---+---+---+---+---+---+
+ // and the hexadecimal representation is 0x2800.
+ EXPECT_EQ(0x2800, buf.readUint16());
+
+ // ZOCOUNT - holds the number of zones for the update. For Request
+ // message it must be exactly one record (RFC2136, section 2.3).
+ EXPECT_EQ(1, buf.readUint16());
+
+ // PRCOUNT - holds the number of prerequisites. Earlier we have added
+ // two prerequisites. Thus, expect that this counter is 2.
+ EXPECT_EQ(2, buf.readUint16());
+
+ // UPCOUNT - holds the number of RRs in the Update Section. We have
+ // added 1 RR, which adds the name foo.example.com to the Zone.
+ EXPECT_EQ(1, buf.readUint16());
+
+ // ADCOUNT - holds the number of RRs in the Additional Data Section.
+ EXPECT_EQ(0, buf.readUint16());
+
+ // Start validating the Zone section. This section comprises the
+ // following data:
+ // - ZNAME
+ // - ZTYPE
+ // - ZCLASS
+
+ // ZNAME holds 'example.com.' encoded as set of labels. Each label
+ // is preceded by its length. The name is ended with the byte holding
+ // zero value. This yields the total size of the name in wire format
+ // of 13 bytes.
+
+ // The simplest way to convert the name from wire format to a string
+ // is to use dns::Name class. It should be ok to rely on the Name class
+ // to decode the name, because it is unit tested elsewhere.
+ std::string zone_name = readNameFromWire(buf, 13);
+ EXPECT_EQ("example.com.", zone_name);
+
+ // ZTYPE of the Zone section must be SOA according to RFC 2136,
+ // section 2.3.
+ EXPECT_EQ(RRType::SOA().getCode(), buf.readUint16());
+
+ // ZCLASS of the Zone section is IN.
+ EXPECT_EQ(RRClass::IN().getCode(), buf.readUint16());
+
+ // Start checks on Prerequisite section. Each prerequisite comprises
+ // the following fields:
+ // - NAME - name of the RR in wire format
+ // - TYPE - two octets with one of the RR TYPE codes
+ // - CLASS - two octets with one of the RR CLASS codes
+ // - TTL - a 32-bit signed integer specifying Time-To-Live
+ // - RDLENGTH - length of the RDATA field
+ // - RDATA - a variable length string of octets containing
+ // resource data.
+ // In case of this message, we expect to have two prerequisite RRs.
+ // Their structure is checked below.
+
+ // First prerequisite should comprise the 'Name is not in use prerequisite'
+ // for 'foo.example.com'.
+
+ // Check the name first. Message renderer is using compression for domain
+ // names as described in RFC 1035, section 4.1.4. The name in this RR is
+ // foo.example.com. The name of the zone is example.com and it has occurred
+ // in this message already at offset 12 (the size of the header is 12).
+ // Therefore, name of this RR is encoded as 'foo', followed by a pointer
+ // to offset in this message where the remainder of this name was used.
+ // This pointer has the following format:
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // | 1 1| OFFSET |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // | 1 1| 0 0 0 0 0 0 0 0 0 0 1 1 0 0|
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // which has a following hexadecimal representation: 0xC00C
+
+ // Let's read the non-compressed part first - 'foo.'
+ std::string name_prereq1 = readNameFromWire(buf, 4, true);
+ EXPECT_EQ("foo.", name_prereq1);
+ // The remaining two bytes hold the pointer to 'example.com'.
+ EXPECT_EQ(0xC00C, buf.readUint16());
+ // TYPE is ANY
+ EXPECT_EQ(RRType::ANY().getCode(), buf.readUint16());
+ // CLASS is NONE
+ EXPECT_EQ(RRClass::NONE().getCode(), buf.readUint16());
+ // TTL is a 32-but value, expecting 0
+ EXPECT_EQ(0, buf.readUint32());
+ // There is no RDATA, so RDLENGTH is 0
+ EXPECT_EQ(0, buf.readUint16());
+
+ // Start checking second prerequisite.
+
+ std::string name_prereq2 = readNameFromWire(buf, 4, true);
+ EXPECT_EQ("bar.", name_prereq2);
+ // The remaining two bytes hold the pointer to 'example.com'.
+ EXPECT_EQ(0xC00C, buf.readUint16());
+ // TYPE is ANY
+ EXPECT_EQ(RRType::ANY().getCode(), buf.readUint16());
+ // CLASS is ANY
+ EXPECT_EQ(RRClass::ANY().getCode(), buf.readUint16());
+ // TTL is a 32-but value, expecting 0
+ EXPECT_EQ(0, buf.readUint32());
+ // There is no RDATA, so RDLENGTH is 0
+ EXPECT_EQ(0, buf.readUint16());
+
+ // Start checking Update section. This section contains RRset with
+ // one A RR.
+
+ // The name of the RR is 'foo.example.com'. It is encoded in the
+ // compressed format - as a pointer to the name of prerequisite 1.
+ // This name is in offset 0x1D in this message.
+ EXPECT_EQ(0xC01D, buf.readUint16());
+ // TYPE is A
+ EXPECT_EQ(RRType::A().getCode(), buf.readUint16());
+ // CLASS is IN (same as zone class)
+ EXPECT_EQ(RRClass::IN().getCode(), buf.readUint16());
+ // TTL is a 32-but value, set here to 10.
+ EXPECT_EQ(10, buf.readUint32());
+ // For A records, the RDATA comprises the 4-byte Internet address.
+ // So, RDLENGTH is 4.
+ EXPECT_EQ(4, buf.readUint16());
+ // We have stored the following address in RDATA field: 10.10.1.1
+ // (which is 0A 0A 01 01) in hexadecimal format.
+ EXPECT_EQ(0x0A0A0101, buf.readUint32());
+
+ // @todo: consider extending this test to verify Additional Data
+ // section.
+}
+
+// This test verifies that an attempt to call toWire function on the
+// received message will result in an exception.
+TEST_F(D2UpdateMessageTest, toWireInvalidQRFlag) {
+ // This is a binary representation of the DNS message.
+ // This message is valid and should be parsed with no
+ // error.
+ const uint8_t bin_msg[] = {
+ 0x05, 0xAF, // ID=0x05AF
+ 0xA8, 0x6, // QR=1, Opcode=6, RCODE=YXDOMAIN
+ 0x0, 0x0, // ZOCOUNT=0
+ 0x0, 0x0, // PRCOUNT=0
+ 0x0, 0x0, // UPCOUNT=0
+ 0x0, 0x0 // ADCOUNT=0
+ };
+
+ // The 'true' argument passed to the constructor turns the
+ // message into the parse mode in which the fromWire function
+ // can be used to decode the binary message data.
+ D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+ ASSERT_NO_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)));
+
+ // The message is parsed. The QR Flag should now indicate that
+ // it is a Response message.
+ ASSERT_EQ(D2UpdateMessage::RESPONSE, msg.getQRFlag());
+
+ // An attempt to call toWire on the Response message should
+ // result in the InvalidQRFlag exception.
+ MessageRenderer renderer;
+ EXPECT_THROW(msg.toWire(renderer), isc::d2::InvalidQRFlag);
+}
+
+// TSIG test
+TEST_F(D2UpdateMessageTest, validTSIG) {
+ // Create a TSIG Key and context
+ std::string secret("this key will match");
+ D2TsigKeyPtr right_key;
+ ASSERT_NO_THROW(right_key.reset(new
+ D2TsigKey(Name("right.com"),
+ TSIGKey::HMACMD5_NAME(),
+ secret.c_str(), secret.size())));
+
+ D2TsigKeyPtr wrong_key;
+ secret = "this key will not match";
+ ASSERT_NO_THROW(wrong_key.reset(new
+ D2TsigKey(Name("wrong.com"),
+ TSIGKey::HMACMD5_NAME(),
+ secret.c_str(), secret.size())));
+
+
+ // Build a request message
+ D2UpdateMessage msg;
+ msg.setId(0x1234);
+ msg.setRcode(Rcode(Rcode::NOERROR_CODE));
+ msg.setZone(Name("example.com"), RRClass::IN());
+ RRsetPtr prereq1(new RRset(Name("foo.example.com"), RRClass::NONE(),
+ RRType::ANY(), RRTTL(0)));
+ msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq1);
+ RRsetPtr prereq2(new RRset(Name("bar.example.com"), RRClass::ANY(),
+ RRType::ANY(), RRTTL(0)));
+ msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq2);
+ RRsetPtr updaterr1(new RRset(Name("foo.example.com"), RRClass::IN(),
+ RRType::A(), RRTTL(10)));
+ char rdata1[] = {
+ 0xA, 0xA , 0x1, 0x1
+ };
+ InputBuffer buf_rdata1(rdata1, 4);
+ updaterr1->addRdata(createRdata(RRType::A(), RRClass::IN(), buf_rdata1,
+ buf_rdata1.getLength()));
+ msg.addRRset(D2UpdateMessage::SECTION_UPDATE, updaterr1);
+
+ // Make a context to send the message with and use it to render
+ // the message into the wire format.
+ TSIGContextPtr context;
+ ASSERT_NO_THROW(context.reset(new TSIGContext(*right_key)));
+ MessageRenderer renderer;
+ ASSERT_NO_THROW(msg.toWire(renderer, context.get()));
+
+ // Grab the wire data from the signed message.
+ const void* wire_data = renderer.getData();
+ const size_t wire_size = renderer.getLength();
+
+ // Make a context with the wrong key and use it to convert the wired data.
+ // Verification should fail.
+ D2UpdateMessage msg2(D2UpdateMessage::INBOUND);
+ ASSERT_NO_THROW(context.reset(new TSIGContext(*wrong_key)));
+ ASSERT_THROW(msg2.fromWire(wire_data, wire_size, context.get()),
+ TSIGVerifyError);
+
+ // Now make a context with the correct key and try again.
+ // If the message passes TSIG verification, then the QR Flag test in
+ // the subsequent call to D2UpdateMessage::validateResponse should
+ // fail because this isn't really received message.
+ ASSERT_NO_THROW(context.reset(new TSIGContext(*right_key)));
+ ASSERT_THROW(msg2.fromWire(wire_data, wire_size, context.get()),
+ InvalidQRFlag);
+}
+
+// Tests message signing and verification for all supported algorithms.
+TEST_F(D2UpdateMessageTest, allValidTSIG) {
+ std::vector<std::string>algorithms;
+ algorithms.push_back(TSIGKeyInfo::HMAC_MD5_STR);
+ algorithms.push_back(TSIGKeyInfo::HMAC_SHA1_STR);
+ algorithms.push_back(TSIGKeyInfo::HMAC_SHA224_STR);
+ algorithms.push_back(TSIGKeyInfo::HMAC_SHA256_STR);
+ algorithms.push_back(TSIGKeyInfo::HMAC_SHA384_STR);
+ algorithms.push_back(TSIGKeyInfo::HMAC_SHA512_STR);
+
+ dns::Name key_name("test_key");
+ std::string secret("random text for secret");
+ for (int i = 0; i < algorithms.size(); ++i) {
+ D2TsigKey key(key_name,
+ TSIGKeyInfo::stringToAlgorithmName(algorithms[i]),
+ secret.c_str(), secret.size());
+
+ // Build a request message
+ D2UpdateMessage msg;
+ msg.setId(0x1234);
+ msg.setRcode(Rcode(Rcode::NOERROR_CODE));
+ msg.setZone(Name("example.com"), RRClass::IN());
+
+ // Make a context to send the message with and use it to render
+ // the message into the wire format.
+ TSIGContextPtr context;
+ ASSERT_NO_THROW(context.reset(new TSIGContext(key)));
+ MessageRenderer renderer;
+ ASSERT_NO_THROW(msg.toWire(renderer, context.get()));
+
+ // Grab the wire data from the signed message.
+ const void* wire_data = renderer.getData();
+ const size_t wire_size = renderer.getLength();
+
+ // Create a fresh context to "receive" the message. (We can't use the
+ // one we signed it with, as its expecting a signed response to its
+ // request. Here we are acting like the server).
+ // If the message passes TSIG verification, then the QR Flag test in
+ // the subsequent call to D2UpdateMessage::validateResponse should
+ // fail because this isn't really received message.
+ ASSERT_NO_THROW(context.reset(new TSIGContext(key)));
+ D2UpdateMessage msg2(D2UpdateMessage::INBOUND);
+ ASSERT_THROW(msg2.fromWire(wire_data, wire_size, context.get()),
+ InvalidQRFlag);
+ }
+}
+
+
+} // End of anonymous namespace
diff --git a/src/lib/d2srv/tests/d2_zone_unittests.cc b/src/lib/d2srv/tests/d2_zone_unittests.cc
new file mode 100644
index 0000000..f252d07
--- /dev/null
+++ b/src/lib/d2srv/tests/d2_zone_unittests.cc
@@ -0,0 +1,83 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <d2srv/d2_zone.h>
+#include <gtest/gtest.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+using namespace isc::dns;
+
+namespace {
+
+// This test verifies that Zone object is created and its constructor sets
+// appropriate values for its members.
+TEST(D2ZoneTest, constructor) {
+ // Create first object.
+ D2Zone zone1(Name("example.com"), RRClass::ANY());
+ EXPECT_EQ("example.com.", zone1.getName().toText());
+ EXPECT_EQ(RRClass::ANY().getCode(), zone1.getClass().getCode());
+ // Create another object to make sure that constructor doesn't assign
+ // fixed values, but they change when constructor's parameters change.
+ D2Zone zone2(Name("foo.example.com"), RRClass::IN());
+ EXPECT_EQ("foo.example.com.", zone2.getName().toText());
+ EXPECT_EQ(RRClass::IN().getCode(), zone2.getClass().getCode());
+}
+
+// This test verifies that toText() function returns text representation of
+// of the zone in expected format.
+TEST(D2ZoneTest, toText) {
+ // Create first object.
+ D2Zone zone1(Name("example.com"), RRClass::ANY());
+ EXPECT_EQ("example.com. ANY SOA\n", zone1.toText());
+ // Create another object with different parameters to make sure that the
+ // function's output changes accordingly.
+ D2Zone zone2(Name("foo.example.com"), RRClass::IN());
+ EXPECT_EQ("foo.example.com. IN SOA\n", zone2.toText());
+}
+
+ // Same than for toText() but using the << operator.
+TEST(D2ZoneTest, output) {
+ // Create first object.
+ D2Zone zone1(Name("example.com"), RRClass::ANY());
+ ostringstream ss;
+ ss << zone1;
+ EXPECT_EQ("example.com. ANY SOA\n", ss.str());
+ // Create another object with different parameters to make sure that the
+ // function's output changes accordingly.
+ D2Zone zone2(Name("foo.example.com"), RRClass::IN());
+ ostringstream ss2;
+ ss2 << zone2;
+ EXPECT_EQ("foo.example.com. IN SOA\n", ss2.str());
+}
+
+// This test verifies that the equality and inequality operators behave as
+// expected.
+TEST(D2ZoneTest, compare) {
+ const Name a("a"), b("b");
+ const RRClass in(RRClass::IN()), any(RRClass::ANY());
+
+ // Equality check
+ EXPECT_TRUE(D2Zone(a, any) == D2Zone(a, any));
+ EXPECT_FALSE(D2Zone(a, any) != D2Zone(a, any));
+
+ // Inequality check, objects differ by class.
+ EXPECT_FALSE(D2Zone(a, any) == D2Zone(a, in));
+ EXPECT_TRUE(D2Zone(a, any) != D2Zone(a, in));
+
+ // Inequality check, objects differ by name.
+ EXPECT_FALSE(D2Zone(a, any) == D2Zone(b, any));
+ EXPECT_TRUE(D2Zone(a, any) != D2Zone(b, any));
+
+ // Inequality check, objects differ by name and class.
+ EXPECT_FALSE(D2Zone(a, any) == D2Zone(b, in));
+ EXPECT_TRUE(D2Zone(a, any) != D2Zone(b, in));
+}
+
+} // End of anonymous namespace
diff --git a/src/lib/d2srv/tests/dns_client_unittests.cc b/src/lib/d2srv/tests/dns_client_unittests.cc
new file mode 100644
index 0000000..a560546
--- /dev/null
+++ b/src/lib/d2srv/tests/dns_client_unittests.cc
@@ -0,0 +1,678 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <d2srv/dns_client.h>
+#include <dns/opcode.h>
+#include <asiodns/io_fetch.h>
+#include <asiodns/logger.h>
+#include <asiolink/interval_timer.h>
+#include <d2srv/testutils/nc_test_utils.h>
+#include <d2srv/testutils/stats_test_utils.h>
+#include <dns/messagerenderer.h>
+
+#include <boost/asio/ip/udp.hpp>
+#include <boost/asio/socket_base.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <functional>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::asiodns;
+using namespace isc::d2;
+using namespace isc::d2::test;
+using namespace isc::data;
+using namespace isc::dns;
+using namespace isc::stats;
+using namespace isc::util;
+using namespace boost::asio;
+using namespace boost::asio::ip;
+namespace ph = std::placeholders;
+
+namespace {
+
+const char* TEST_ADDRESS = "127.0.0.1";
+const uint16_t TEST_PORT = 5381;
+const size_t MAX_SIZE = 1024;
+const long TEST_TIMEOUT = 5 * 1000;
+/// @brief Test Fixture class
+//
+// This test fixture class implements DNSClient::Callback so as it can be
+// installed as a completion callback for tests it implements. This callback
+// is called when a DDNS transaction (send and receive) completes. This allows
+// for the callback function to directly access class members. In particular,
+// the callback function can access IOService on which run() was called and
+// call stop() on it.
+//
+// Many of the tests defined here schedule execution of certain tasks and block
+// until tasks are completed or a timeout is hit. However, if timeout is not
+// properly handled a task may be hanging for a long time. In order to prevent
+// it, the asiolink::IntervalTimer is used to break a running test if test
+// timeout is hit. This will result in test failure.
+class DNSClientTest : public ::testing::Test, DNSClient::Callback,
+ public D2StatTest {
+public:
+ /// @brief The IOService which handles IO operations.
+ IOService service_;
+
+ /// @brief The UDP socket.
+ std::unique_ptr<udp::socket> socket_;
+
+ /// @brief The UDP socket endpoint.
+ std::unique_ptr<udp::endpoint> endpoint_;
+
+ /// @brief DNS client response.
+ D2UpdateMessagePtr response_;
+
+ /// @brief The status of the DNS client update callback.
+ DNSClient::Status status_;
+
+ /// @brief The receive buffer.
+ uint8_t receive_buffer_[MAX_SIZE];
+
+ /// @brief The DNS client performing DNS update.
+ DNSClientPtr dns_client_;
+
+ /// @brief The flag which specifies if the response should be corrupted.
+ bool corrupt_response_;
+
+ /// @brief The flag which specifies if a response is expected.
+ bool expect_response_;
+
+ /// @brief The timeout timer.
+ asiolink::IntervalTimer test_timer_;
+
+ /// @brief The number of received DNS updates.
+ int received_;
+
+ /// @brief The number of expected DNS updates.
+ int expected_;
+
+ /// @brief The flag which specifies if the server should continue with
+ /// receiving DNS updates.
+ bool go_on_;
+
+ /// @brief Constructor
+ ///
+ /// This constructor overrides the default logging level of asiodns logger to
+ /// prevent it from emitting debug messages from IOFetch class. Such an error
+ /// message can be emitted if timeout occurs when DNSClient class is
+ /// waiting for a response. Some of the tests are checking DNSClient behavior
+ /// in case when response from the server is not received. Tests output would
+ /// become messy if such errors were logged.
+ DNSClientTest() : service_(), socket_(), endpoint_(),
+ status_(DNSClient::SUCCESS), corrupt_response_(false),
+ expect_response_(true), test_timer_(service_),
+ received_(0), expected_(0), go_on_(false) {
+ asiodns::logger.setSeverity(isc::log::INFO);
+ response_.reset();
+ dns_client_.reset(new DNSClient(response_, this));
+
+ // Set the test timeout to break any running tasks if they hang.
+ test_timer_.setup(std::bind(&DNSClientTest::testTimeoutHandler, this),
+ TEST_TIMEOUT);
+ }
+
+ /// @brief Destructor
+ ///
+ /// Sets the asiodns logging level back to DEBUG.
+ virtual ~DNSClientTest() {
+ asiodns::logger.setSeverity(isc::log::DEBUG);
+ };
+
+ /// @brief Exchange completion callback
+ ///
+ /// This callback is called when the exchange with the DNS server is
+ /// complete or an error occurred. This includes the occurrence of a timeout.
+ ///
+ /// @param status A status code returned by DNSClient.
+ virtual void operator()(DNSClient::Status status) {
+ status_ = status;
+ if (!expected_ || (expected_ == ++received_)) {
+ service_.stop();
+ }
+
+ if (expect_response_) {
+ if (!corrupt_response_) {
+ // We should have received a response.
+ EXPECT_EQ(DNSClient::SUCCESS, status_);
+
+ ASSERT_TRUE(response_);
+ EXPECT_EQ(D2UpdateMessage::RESPONSE, response_->getQRFlag());
+ ASSERT_EQ(1,
+ response_->getRRCount(D2UpdateMessage::SECTION_ZONE));
+ D2ZonePtr zone = response_->getZone();
+ ASSERT_TRUE(zone);
+ EXPECT_EQ("example.com.", zone->getName().toText());
+ EXPECT_EQ(RRClass::IN().getCode(), zone->getClass().getCode());
+
+ } else {
+ EXPECT_EQ(DNSClient::INVALID_RESPONSE, status_);
+
+ }
+ // If we don't expect a response, the status should indicate a timeout.
+ } else {
+ EXPECT_EQ(DNSClient::TIMEOUT, status_);
+
+ }
+ }
+
+ /// @brief Handler invoked when test timeout is hit
+ ///
+ /// This callback stops all running (hanging) tasks on IO service.
+ void testTimeoutHandler() {
+ service_.stop();
+ FAIL() << "Test timeout hit.";
+ }
+
+ /// @brief Handler invoked when test request is received
+ ///
+ /// This callback handler is installed when performing async read on a
+ /// socket to emulate reception of the DNS Update request by a server.
+ /// As a result, this handler will send an appropriate DNS Update response
+ /// message back to the address from which the request has come.
+ ///
+ /// @param socket A pointer to a socket used to receive a query and send a
+ /// response.
+ /// @param remote A pointer to an object which specifies the host (address
+ /// and port) from which a request has come.
+ /// @param receive_length A length (in bytes) of the received data.
+ /// @param corrupt_response A bool value which specifies if the server's
+ /// response should be invalid (true) or valid (false).
+ void udpReceiveHandler(udp::socket* socket, udp::endpoint* remote,
+ size_t receive_length, const bool corrupt_response) {
+ // The easiest way to create a response message is to copy the entire
+ // request.
+ OutputBuffer response_buf(receive_length);
+ response_buf.writeData(receive_buffer_, receive_length);
+ // If a response is to be valid, we have to modify it slightly. If not,
+ // we leave it as is.
+ if (!corrupt_response) {
+ // For a valid response the QR bit must be set. This bit
+ // differentiates both types of messages. Note that the 3rd byte of
+ // the message header comprises this bit in the front followed by
+ // the message code and reserved zeros. Therefore, this byte
+ // has the following value:
+ // 10101000,
+ // where a leading bit is a QR flag. The hexadecimal value is 0xA8.
+ // Write it at message offset 2.
+ response_buf.writeUint8At(0xA8, 2);
+ }
+ // A response message is now ready to send. Send it!
+ socket->send_to(boost::asio::buffer(response_buf.getData(),
+ response_buf.getLength()),
+ *remote);
+
+ if (go_on_) {
+ socket_->async_receive_from(boost::asio::buffer(receive_buffer_,
+ sizeof(receive_buffer_)),
+ *endpoint_,
+ std::bind(&DNSClientTest::udpReceiveHandler,
+ this, socket_.get(),
+ endpoint_.get(), ph::_2,
+ corrupt_response));
+ }
+ }
+
+ /// @brief Request handler for testing clients using TSIG
+ ///
+ /// This callback handler is installed when performing async read on a
+ /// socket to emulate reception of the DNS Update request with TSIG by a
+ /// server. As a result, this handler will send an appropriate DNS Update
+ /// response message back to the address from which the request has come.
+ ///
+ /// @param socket A pointer to a socket used to receive a query and send a
+ /// response.
+ /// @param remote A pointer to an object which specifies the host (address
+ /// and port) from which a request has come.
+ /// @param receive_length A length (in bytes) of the received data.
+ /// @param client_key TSIG key the server should use to verify the inbound
+ /// request. If the pointer is NULL, the server will not attempt to
+ /// verify the request.
+ /// @param server_key TSIG key the server should use to sign the outbound
+ /// request. If the pointer is NULL, the server will not sign the outbound
+ /// response. If the pointer is not NULL and not the same value as the
+ /// client_key, the server will use a new context to sign the response then
+ /// the one used to verify it. This allows us to simulate the server
+ /// signing with the wrong key.
+ void TSIGReceiveHandler(udp::socket* socket, udp::endpoint* remote,
+ size_t receive_length,
+ D2TsigKeyPtr client_key,
+ D2TsigKeyPtr server_key) {
+
+ TSIGContextPtr context;
+ if (client_key) {
+ context = client_key->createContext();
+ }
+
+ isc::util::InputBuffer received_data_buffer(receive_buffer_,
+ receive_length);
+
+ dns::Message request(Message::PARSE);
+ request.fromWire(received_data_buffer);
+
+ // If context is not NULL, then we need to verify the message.
+ if (context) {
+ TSIGError error = context->verify(request.getTSIGRecord(),
+ receive_buffer_, receive_length);
+ if (error != TSIGError::NOERROR()) {
+ isc_throw(TSIGVerifyError, "TSIG verification failed: "
+ << error.toText());
+ }
+ }
+
+ dns::Message response(Message::RENDER);
+ response.setOpcode(Opcode(Opcode::UPDATE_CODE));
+ response.setHeaderFlag(dns::Message::HEADERFLAG_QR, true);
+ response.setQid(request.getQid());
+ response.setRcode(Rcode::NOERROR());
+ dns::Question question(Name("example.com."),
+ RRClass::IN(), RRType::SOA());
+ response.addQuestion(question);
+
+ MessageRenderer renderer;
+
+ if (!server_key) {
+ // don't sign the response.
+ context.reset();
+ } else if (server_key != client_key) {
+ // use a different key to sign the response.
+ context.reset(new TSIGContext(*server_key));
+ } // otherwise use the context based on client_key.
+
+ response.toWire(renderer, context.get());
+ // A response message is now ready to send. Send it!
+ socket->send_to(boost::asio::buffer(renderer.getData(),
+ renderer.getLength()),
+ *remote);
+ }
+
+ /// @brief This test verifies that when invalid response placeholder object
+ /// is passed to a constructor which throws the appropriate exception.
+ /// It also verifies that the constructor will not throw if the supplied
+ /// callback object is NULL.
+ void runConstructorTest() {
+ EXPECT_NO_THROW(DNSClient(response_, NULL, DNSClient::UDP));
+
+ // The TCP Transport is not supported right now. So, we return exception
+ // if caller specified TCP as a preferred protocol. This test will be
+ // removed once TCP is supported.
+ EXPECT_THROW(DNSClient(response_, NULL, DNSClient::TCP),
+ isc::NotImplemented);
+ }
+
+ /// @brief This test verifies that it accepted timeout values belong to the
+ /// range of <0, DNSClient::getMaxTimeout()>.
+ void runInvalidTimeoutTest() {
+
+ expect_response_ = false;
+
+ // Create outgoing message. Simply set the required message fields:
+ // error code and Zone section. This is enough to create on-wire format
+ // of this message and send it.
+ D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
+ ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
+ ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
+
+ // Start with a valid timeout equal to maximal allowed. This way we will
+ // ensure that doUpdate doesn't throw an exception for valid timeouts.
+ unsigned int timeout = DNSClient::getMaxTimeout();
+ EXPECT_NO_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
+ TEST_PORT, message, timeout));
+
+ // Cross the limit and expect that exception is thrown this time.
+ timeout = DNSClient::getMaxTimeout() + 1;
+ EXPECT_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
+ TEST_PORT, message, timeout),
+ isc::BadValue);
+ }
+
+ /// @brief This test verifies the DNSClient behavior when a server does not
+ /// respond do the DNS Update message. In such case, the callback function
+ /// is expected to be called and the TIME_OUT error code should be returned.
+ void runSendNoReceiveTest() {
+ // We expect no response from a server.
+ expect_response_ = false;
+
+ // Create outgoing message. Simply set the required message fields:
+ // error code and Zone section. This is enough to create on-wire format
+ // of this message and send it.
+ D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
+ ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
+ ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
+
+ /// @todo The timeout value could be set to 0 to trigger timeout
+ /// instantly. However, it may lead to situations that the message sent
+ /// in one test will not be dropped by the kernel by the time, the next
+ /// test starts. This will lead to intermittent unit test errors as
+ /// described in the ticket http://oldkea.isc.org/ticket/3265.
+ /// Increasing the timeout to a non-zero value mitigates this problem.
+ /// The proper way to solve this problem is to receive the packet
+ /// on our own and drop it. Such a fix will need to be applied not only
+ /// to this test but also for other tests that rely on arbitrary timeout
+ /// values.
+ const int timeout = 500;
+ // The doUpdate() function starts asynchronous message exchange with DNS
+ // server. When message exchange is done or timeout occurs, the
+ // completion callback will be triggered. The doUpdate function returns
+ // immediately.
+ EXPECT_NO_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
+ TEST_PORT, message, timeout));
+
+ // This starts the execution of tasks posted to IOService. run() blocks
+ // until stop() is called in the completion callback function.
+ service_.run();
+
+ }
+
+ /// @brief This test verifies that DNSClient can send DNS Update and receive
+ /// a corresponding response from a server.
+ void runSendReceiveTest(const bool corrupt_response,
+ const bool two_sends) {
+ go_on_ = two_sends;
+ corrupt_response_ = corrupt_response;
+
+ // Create a request DNS Update message.
+ D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
+ ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
+ ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
+
+ // In order to perform the full test, when the client sends the request
+ // and receives a response from the server, we have to emulate the
+ // server's response in the test. A request will be sent via loopback
+ // interface to 127.0.0.1 and known test port. Response must be sent
+ // to 127.0.0.1 and the source port which has been used to send the
+ // request. A new socket is created, specifically to handle sending
+ // responses. The reuse address option is set so as both sockets can
+ // use the same address. This new socket is bound to the test address
+ // and port, where requests will be sent.
+ socket_.reset(new udp::socket(service_.get_io_service(),
+ boost::asio::ip::udp::v4()));
+ socket_->set_option(socket_base::reuse_address(true));
+ socket_->bind(udp::endpoint(address::from_string(TEST_ADDRESS),
+ TEST_PORT));
+ // Once socket is created, we can post an IO request to receive some
+ // packet from this socket. This is asynchronous operation and
+ // nothing is received until another IO request to send a query is
+ // posted and the run() is invoked on this IO. A callback function is
+ // attached to this asynchronous read. This callback function requires
+ // that a socket object used to receive the request is passed to it,
+ // because the same socket will be used by the callback function to send
+ // a response. Also, the remote object is passed to the callback,
+ // because it holds a source address and port where request originated.
+ // Callback function will send a response to this address and port.
+ // The last parameter holds a length of the received request. It is
+ // required to construct a response.
+ endpoint_.reset(new udp::endpoint());
+ socket_->async_receive_from(boost::asio::buffer(receive_buffer_,
+ sizeof(receive_buffer_)),
+ *endpoint_,
+ std::bind(&DNSClientTest::udpReceiveHandler,
+ this, socket_.get(),
+ endpoint_.get(), ph::_2,
+ corrupt_response));
+
+ // The socket is now ready to receive the data. Let's post some request
+ // message then. Set timeout to some reasonable value to make sure that
+ // there is sufficient amount of time for the test to generate a
+ // response.
+ const int timeout = 500;
+ expected_++;
+ dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS), TEST_PORT,
+ message, timeout);
+
+ // It is possible to request that two packets are sent concurrently.
+ if (two_sends) {
+ expected_++;
+ dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS), TEST_PORT,
+ message, timeout);
+
+ }
+
+ // Kick of the message exchange by actually running the scheduled
+ // "send" and "receive" operations.
+ service_.run();
+
+ socket_->close();
+
+ // Since the callback, operator(), calls stop() on the io_service,
+ // we must reset it in order for subsequent calls to run() or
+ // run_one() to work.
+ service_.get_io_service().reset();
+ }
+
+ /// @brief Performs a single request-response exchange with or without TSIG.
+ ///
+ /// @param client_key TSIG passed to dns_client and also used by the
+ /// "server" to verify the request.
+ /// @param server_key TSIG key the "server" should use to sign the response.
+ /// If this is NULL, then client_key is used.
+ /// @param should_pass indicates if the test should pass.
+ void runTSIGTest(D2TsigKeyPtr client_key, D2TsigKeyPtr server_key,
+ bool should_pass = true) {
+ // Tell operator() method if we expect an invalid response.
+ corrupt_response_ = !should_pass;
+
+ // Create a request DNS Update message.
+ D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
+ ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
+ ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
+
+ // Setup our "loopback" server.
+ udp::socket udp_socket(service_.get_io_service(), boost::asio::ip::udp::v4());
+ udp_socket.set_option(socket_base::reuse_address(true));
+ udp_socket.bind(udp::endpoint(address::from_string(TEST_ADDRESS),
+ TEST_PORT));
+ udp::endpoint remote;
+ udp_socket.async_receive_from(boost::asio::buffer(receive_buffer_,
+ sizeof(receive_buffer_)),
+ remote,
+ std::bind(&DNSClientTest::
+ TSIGReceiveHandler, this,
+ &udp_socket, &remote, ph::_2,
+ client_key, server_key));
+
+ // The socket is now ready to receive the data. Let's post some request
+ // message then. Set timeout to some reasonable value to make sure that
+ // there is sufficient amount of time for the test to generate a
+ // response.
+ const int timeout = 500;
+ expected_++;
+ dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS), TEST_PORT,
+ message, timeout, client_key);
+
+ // Kick of the message exchange by actually running the scheduled
+ // "send" and "receive" operations.
+ service_.run();
+
+ udp_socket.close();
+
+ // Since the callback, operator(), calls stop() on the io_service,
+ // we must reset it in order for subsequent calls to run() or
+ // run_one() to work.
+ service_.get_io_service().reset();
+ }
+};
+
+// Verify that the DNSClient object can be created if provided parameters are
+// valid. Constructor should throw exceptions when parameters are invalid.
+TEST_F(DNSClientTest, constructor) {
+ runConstructorTest();
+ StatMap stats_upd = {
+ { "update-sent", 0},
+ { "update-signed", 0},
+ { "update-unsigned", 0},
+ { "update-success", 0},
+ { "update-timeout", 0},
+ { "update-error", 0}
+ };
+ checkStats(stats_upd);
+}
+
+// This test verifies that the maximal allowed timeout value is maximal int
+// value.
+TEST_F(DNSClientTest, getMaxTimeout) {
+ EXPECT_EQ(std::numeric_limits<int>::max(), DNSClient::getMaxTimeout());
+}
+
+// Verify that timeout is reported when no response is received from DNS.
+TEST_F(DNSClientTest, timeout) {
+ runSendNoReceiveTest();
+ StatMap stats_upd = {
+ { "update-sent", 1},
+ { "update-signed", 0},
+ { "update-unsigned", 1},
+ { "update-success", 0},
+ { "update-timeout", 1},
+ { "update-error", 0}
+ };
+ checkStats(stats_upd);
+}
+
+// Verify that exception is thrown when invalid (too high) timeout value is
+// specified for asynchronous DNS Update.
+TEST_F(DNSClientTest, invalidTimeout) {
+ runInvalidTimeoutTest();
+}
+
+// Verifies that TSIG can be used to sign requests and verify responses.
+TEST_F(DNSClientTest, runTSIGTest) {
+ std::string secret ("key number one");
+ D2TsigKeyPtr key_one;
+ ASSERT_NO_THROW(key_one.reset(new
+ D2TsigKey(Name("one.com"),
+ TSIGKey::HMACMD5_NAME(),
+ secret.c_str(), secret.size())));
+ StatMap stats_key = {
+ { "update-sent", 0},
+ { "update-success", 0},
+ { "update-timeout", 0},
+ { "update-error", 0}
+ };
+ checkStats("one.com.", stats_key);
+ secret = "key number two";
+ D2TsigKeyPtr key_two;
+ ASSERT_NO_THROW(key_two.reset(new
+ D2TsigKey(Name("two.com"),
+ TSIGKey::HMACMD5_NAME(),
+ secret.c_str(), secret.size())));
+ checkStats("two.com.", stats_key);
+ D2TsigKeyPtr nokey;
+
+ // Should be able to send and receive with no keys.
+ // Neither client nor server will attempt to sign or verify.
+ runTSIGTest(nokey, nokey);
+
+ // Client signs the request, server verifies but doesn't sign.
+ runTSIGTest(key_one, nokey, false);
+
+ // Client and server use the same key to sign and verify.
+ runTSIGTest(key_one, key_one);
+
+ // Server uses different key to sign the response.
+ runTSIGTest(key_one, key_two, false);
+
+ // Client neither signs nor verifies, server responds with a signed answer
+ // Since we are "liberal" in what we accept this should be ok.
+ runTSIGTest(nokey, key_two);
+
+ // Check statistics.
+ StatMap stats_one = {
+ { "update-sent", 3},
+ { "update-success", 1},
+ { "update-timeout", 0},
+ { "update-error", 2}
+ };
+ checkStats("one.com.", stats_one);
+ checkStats("two.com.", stats_key);
+ StatMap stats_upd = {
+ { "update-sent", 5},
+ { "update-signed", 3},
+ { "update-unsigned", 2},
+ { "update-success", 3},
+ { "update-timeout", 0},
+ { "update-error", 2}
+ };
+ checkStats(stats_upd);
+}
+
+// Verify that the DNSClient receives the response from DNS and the received
+// buffer can be decoded as DNS Update Response.
+TEST_F(DNSClientTest, sendReceive) {
+ // false means that server response is not corrupted.
+ runSendReceiveTest(false, false);
+ StatMap stats_upd = {
+ { "update-sent", 1},
+ { "update-signed", 0},
+ { "update-unsigned", 1},
+ { "update-success", 1},
+ { "update-timeout", 0},
+ { "update-error", 0}
+ };
+ checkStats(stats_upd);
+}
+
+// Verify that the DNSClient reports an error when the response is received from
+// a DNS and this response is corrupted.
+TEST_F(DNSClientTest, sendReceiveCorrupted) {
+ // true means that server's response is corrupted.
+ runSendReceiveTest(true, false);
+ StatMap stats_upd = {
+ { "update-sent", 1},
+ { "update-signed", 0},
+ { "update-unsigned", 1},
+ { "update-success", 0},
+ { "update-timeout", 0},
+ { "update-error", 1}
+ };
+ checkStats(stats_upd);
+}
+
+// Verify that it is possible to use the same DNSClient instance to
+// perform the following sequence of message exchanges:
+// 1. send
+// 2. receive
+// 3. send
+// 4. receive
+TEST_F(DNSClientTest, sendReceiveTwice) {
+ runSendReceiveTest(false, false);
+ runSendReceiveTest(false, false);
+ EXPECT_EQ(2, received_);
+ StatMap stats_upd = {
+ { "update-sent", 2},
+ { "update-signed", 0},
+ { "update-unsigned", 2},
+ { "update-success", 2},
+ { "update-timeout", 0},
+ { "update-error", 0}
+ };
+ checkStats(stats_upd);
+}
+
+// Verify that it is possible to use the DNSClient instance to perform the
+// following sequence of message exchanges:
+// 1. send
+// 2. send
+// 3. receive
+// 4. receive
+TEST_F(DNSClientTest, concurrentSendReceive) {
+ runSendReceiveTest(false, true);
+ StatMap stats_upd = {
+ { "update-sent", 2},
+ { "update-signed", 0},
+ { "update-unsigned", 2},
+ { "update-success", 2},
+ { "update-timeout", 0},
+ { "update-error", 0}
+ };
+ checkStats(stats_upd);
+}
+
+} // End of anonymous namespace
diff --git a/src/lib/d2srv/tests/nc_trans_unittests.cc b/src/lib/d2srv/tests/nc_trans_unittests.cc
new file mode 100644
index 0000000..89a23b7
--- /dev/null
+++ b/src/lib/d2srv/tests/nc_trans_unittests.cc
@@ -0,0 +1,1279 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/interval_timer.h>
+#include <d2srv/nc_trans.h>
+#include <d2srv/testutils/nc_test_utils.h>
+#include <dns/opcode.h>
+#include <dns/messagerenderer.h>
+#include <log/logger_support.h>
+#include <log/macros.h>
+#include <util/buffer.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <gtest/gtest.h>
+#include <functional>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+using namespace isc::util;
+using namespace boost::posix_time;
+
+namespace {
+
+/// @brief Test derivation of NameChangeTransaction for exercising state
+/// model mechanics.
+///
+/// This class facilitates testing by making non-public methods accessible so
+/// they can be invoked directly in test routines. It implements a very
+/// rudimentary state model, sufficient to test the state model mechanics
+/// supplied by the base class.
+class NameChangeStub : public NameChangeTransaction {
+public:
+
+ // NameChangeStub states
+ static const int DOING_UPDATE_ST = NCT_DERIVED_STATE_MIN + 1;
+
+ // NameChangeStub events
+ static const int SEND_UPDATE_EVT = NCT_DERIVED_EVENT_MIN + 2;
+
+ /// @brief Flag which specifies if the NameChangeStub's callback should be
+ /// used instead of the NameChangeTransaction's callback.
+ bool use_stub_callback_;
+
+ /// @brief Constructor
+ ///
+ /// Parameters match those needed by NameChangeTransaction.
+ NameChangeStub(asiolink::IOServicePtr& io_service,
+ dhcp_ddns::NameChangeRequestPtr& ncr,
+ DdnsDomainPtr& forward_domain,
+ DdnsDomainPtr& reverse_domain,
+ D2CfgMgrPtr& cfg_mgr)
+ : NameChangeTransaction(io_service, ncr, forward_domain,
+ reverse_domain, cfg_mgr),
+ use_stub_callback_(false) {
+ }
+
+ /// @brief Destructor
+ virtual ~NameChangeStub() {
+ }
+
+ /// @brief DNSClient callback
+ /// Overrides the callback in NameChangeTransaction to allow testing
+ /// sendUpdate without incorporating execution of the state model
+ /// into the test.
+ /// It sets the DNS status update and posts IO_COMPLETED_EVT as does
+ /// the normal callback.
+ virtual void operator()(DNSClient::Status status) {
+ if (use_stub_callback_) {
+ setDnsUpdateStatus(status);
+ postNextEvent(IO_COMPLETED_EVT);
+ } else {
+ // For tests which need to use the real callback.
+ NameChangeTransaction::operator()(status);
+ }
+ }
+
+ /// @brief Some tests require a server to be selected. This method
+ /// selects the first server in the forward domain without involving
+ /// state model execution to do so.
+ bool selectFwdServer() {
+ if (getForwardDomain()) {
+ initServerSelection(getForwardDomain());
+ selectNextServer();
+ return (getCurrentServer().get() != 0);
+ }
+
+ return (false);
+ }
+
+ /// @brief Empty handler used to satisfy map verification.
+ void dummyHandler() {
+ isc_throw(NameChangeTransactionError,
+ "dummyHandler - invalid event: " << getContextStr());
+ }
+
+ /// @brief State handler for the READY_ST.
+ ///
+ /// Serves as the starting state handler, it consumes the
+ /// START_EVT "transitioning" to the state, DOING_UPDATE_ST and
+ /// sets the next event to SEND_UPDATE_EVT.
+ void readyHandler() {
+ switch(getNextEvent()) {
+ case START_EVT:
+ transition(DOING_UPDATE_ST, SEND_UPDATE_EVT);
+ break;
+ default:
+ // its bogus
+ isc_throw(NameChangeTransactionError,
+ "readyHandler - invalid event: " << getContextStr());
+ }
+ }
+
+ /// @brief State handler for the DOING_UPDATE_ST.
+ ///
+ /// Simulates a state that starts some form of asynchronous work.
+ /// When next event is SEND_UPDATE_EVT it sets the status to pending
+ /// and signals the state model must "wait" for an event by setting
+ /// next event to NOP_EVT.
+ ///
+ /// When next event is IO_COMPLETED_EVT, it transitions to the state,
+ /// PROCESS_TRANS_OK_ST, and sets the next event to UPDATE_OK_EVT.
+ void doingUpdateHandler() {
+ switch(getNextEvent()) {
+ case SEND_UPDATE_EVT:
+ setNcrStatus(dhcp_ddns::ST_PENDING);
+ postNextEvent(NOP_EVT);
+ break;
+ case IO_COMPLETED_EVT:
+ if (getDnsUpdateStatus() == DNSClient::SUCCESS) {
+ setForwardChangeCompleted(true);
+ transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
+ } else {
+ transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
+ }
+ break;
+ default:
+ // its bogus
+ isc_throw(NameChangeTransactionError,
+ "doingUpdateHandler - invalid event: "
+ << getContextStr());
+ }
+ }
+
+ /// @brief State handler for the PROCESS_TRANS_OK_ST.
+ ///
+ /// This is the last state in the model. Note that it sets the
+ /// status to completed and next event to NOP_EVT.
+ void processTransDoneHandler() {
+ switch(getNextEvent()) {
+ case UPDATE_OK_EVT:
+ setNcrStatus(dhcp_ddns::ST_COMPLETED);
+ endModel();
+ break;
+ case UPDATE_FAILED_EVT:
+ setNcrStatus(dhcp_ddns::ST_FAILED);
+ endModel();
+ break;
+ default:
+ // its bogus
+ isc_throw(NameChangeTransactionError,
+ "processTransDoneHandler - invalid event: "
+ << getContextStr());
+ }
+ }
+
+ /// @brief Construct the event dictionary.
+ virtual void defineEvents() {
+ // Invoke the base call implementation first.
+ NameChangeTransaction::defineEvents();
+
+ // Define our events.
+ defineEvent(SEND_UPDATE_EVT, "SEND_UPDATE_EVT");
+ }
+
+ /// @brief Verify the event dictionary.
+ virtual void verifyEvents() {
+ // Invoke the base call implementation first.
+ NameChangeTransaction::verifyEvents();
+
+ // Define our events.
+ getEvent(SEND_UPDATE_EVT);
+ }
+
+ /// @brief Construct the state dictionary.
+ virtual void defineStates() {
+ // Invoke the base call implementation first.
+ NameChangeTransaction::defineStates();
+
+ // Define our states.
+ defineState(READY_ST, "READY_ST",
+ std::bind(&NameChangeStub::readyHandler, this));
+
+ defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST",
+ std::bind(&NameChangeStub::dummyHandler, this));
+
+ defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST",
+ std::bind(&NameChangeStub::dummyHandler, this));
+
+ defineState(DOING_UPDATE_ST, "DOING_UPDATE_ST",
+ std::bind(&NameChangeStub::doingUpdateHandler,
+ this));
+
+ defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST",
+ std::bind(&NameChangeStub::
+ processTransDoneHandler, this));
+
+ defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST",
+ std::bind(&NameChangeStub::
+ processTransDoneHandler, this));
+ }
+
+ /// @brief Verify the event dictionary.
+ virtual void verifyStates() {
+ // Invoke the base call implementation first.
+ NameChangeTransaction::verifyStates();
+
+ // Check our states.
+ getStateInternal(DOING_UPDATE_ST);
+ }
+
+ // Expose the protected methods to be tested.
+ using StateModel::runModel;
+ using StateModel::postNextEvent;
+ using StateModel::setState;
+ using StateModel::initDictionaries;
+ using NameChangeTransaction::initServerSelection;
+ using NameChangeTransaction::selectNextServer;
+ using NameChangeTransaction::getCurrentServer;
+ using NameChangeTransaction::getDNSClient;
+ using NameChangeTransaction::setNcrStatus;
+ using NameChangeTransaction::setDnsUpdateRequest;
+ using NameChangeTransaction::clearDnsUpdateRequest;
+ using NameChangeTransaction::clearUpdateAttempts;
+ using NameChangeTransaction::setDnsUpdateStatus;
+ using NameChangeTransaction::getDnsUpdateResponse;
+ using NameChangeTransaction::setDnsUpdateResponse;
+ using NameChangeTransaction::clearDnsUpdateResponse;
+ using NameChangeTransaction::getForwardChangeCompleted;
+ using NameChangeTransaction::getReverseChangeCompleted;
+ using NameChangeTransaction::setForwardChangeCompleted;
+ using NameChangeTransaction::setReverseChangeCompleted;
+ using NameChangeTransaction::setUpdateAttempts;
+ using NameChangeTransaction::transition;
+ using NameChangeTransaction::retryTransition;
+ using NameChangeTransaction::sendUpdate;
+ using NameChangeTransaction::prepNewRequest;
+ using NameChangeTransaction::addLeaseAddressRdata;
+ using NameChangeTransaction::addDhcidRdata;
+ using NameChangeTransaction::addPtrRdata;
+ using NameChangeTransaction::responseString;
+ using NameChangeTransaction::transactionOutcomeString;
+};
+
+// Declare them so Gtest can see them.
+const int NameChangeStub::DOING_UPDATE_ST;
+const int NameChangeStub::SEND_UPDATE_EVT;
+
+/// @brief Defines a pointer to a NameChangeStubPtr instance.
+typedef boost::shared_ptr<NameChangeStub> NameChangeStubPtr;
+
+/// @brief Test fixture for testing NameChangeTransaction
+///
+/// Note this class uses NameChangeStub class to exercise non-public
+/// aspects of NameChangeTransaction.
+class NameChangeTransactionTest : public TransactionTest {
+public:
+ NameChangeTransactionTest() {
+ }
+
+ virtual ~NameChangeTransactionTest() {
+ }
+
+
+ /// @brief Instantiates a NameChangeStub test transaction
+ /// The transaction is constructed around a predefined (i.e "canned")
+ /// NameChangeRequest. The request has both forward and reverse DNS
+ /// changes requested, and both forward and reverse domains are populated.
+ /// @param tsig_key_info pointer to the TSIGKeyInfo to use, defaults to
+ /// an empty pointer, in which case TSIG will not be used.
+ NameChangeStubPtr makeCannedTransaction(const TSIGKeyInfoPtr&
+ tsig_key_info = TSIGKeyInfoPtr()) {
+ // Creates IPv4 remove request, forward, and reverse domains.
+ setupForIPv4Transaction(dhcp_ddns::CHG_ADD, FWD_AND_REV_CHG,
+ tsig_key_info);
+
+ // Now create the test transaction as would occur in update manager.
+ // Instantiate the transaction as would be done by update manager.
+ return (NameChangeStubPtr(new NameChangeStub(io_service_, ncr_,
+ forward_domain_, reverse_domain_, cfg_mgr_)));
+ }
+
+ /// @brief Instantiates a NameChangeStub test transaction
+ /// The transaction is constructed around a predefined (i.e "canned")
+ /// NameChangeRequest. The request has both forward and reverse DNS
+ /// changes requested, and both forward and reverse domains are populated.
+ /// @param key_name value to use to create TSIG key, if blank TSIG will not
+ /// be used.
+ NameChangeStubPtr makeCannedTransaction(const std::string& key_name) {
+ // Creates IPv4 remove request, forward, and reverse domains.
+ setupForIPv4Transaction(dhcp_ddns::CHG_ADD, FWD_AND_REV_CHG, key_name);
+
+ // Now create the test transaction as would occur in update manager.
+ // Instantiate the transaction as would be done by update manager.
+ return (NameChangeStubPtr(new NameChangeStub(io_service_, ncr_,
+ forward_domain_, reverse_domain_, cfg_mgr_)));
+ }
+
+ /// @brief Builds and then sends an update request
+ ///
+ /// This method is used to build and send and update request. It is used
+ /// in conjunction with FauxServer to test various message response
+ /// scenarios.
+ /// @param name_change Transaction under test
+ /// @param run_time Maximum time to permit IO processing to run before
+ /// timing out (in milliseconds)
+ void doOneExchange(NameChangeStubPtr name_change,
+ unsigned int run_time = 500) {
+ // Create a valid request for the transaction.
+ D2UpdateMessagePtr req;
+ ASSERT_NO_THROW(req.reset(new D2UpdateMessage(D2UpdateMessage::
+ OUTBOUND)));
+ ASSERT_NO_THROW(name_change->setDnsUpdateRequest(req));
+ req->setZone(dns::Name("request.example.com"), dns::RRClass::ANY());
+ req->setRcode(dns::Rcode(dns::Rcode::NOERROR_CODE));
+
+ // Set the flag to use the NameChangeStub's DNSClient callback.
+ name_change->use_stub_callback_ = true;
+
+ // Invoke sendUpdate.
+ ASSERT_NO_THROW(name_change->sendUpdate());
+
+ // Update attempt count should be 1, next event should be NOP_EVT.
+ ASSERT_EQ(1, name_change->getUpdateAttempts());
+ ASSERT_EQ(NameChangeTransaction::NOP_EVT,
+ name_change->getNextEvent());
+
+ while (name_change->getNextEvent() == NameChangeTransaction::NOP_EVT) {
+ int cnt = 0;
+ ASSERT_NO_THROW(cnt = runTimedIO(run_time));
+ if (cnt == 0) {
+ FAIL() << "IO Service stopped unexpectedly";
+ }
+ }
+ }
+};
+
+/// @brief Tests NameChangeTransaction construction.
+/// This test verifies that:
+/// 1. Construction with null NameChangeRequest
+/// 2. Construction with null forward domain is not allowed when the request
+/// requires forward change.
+/// 3. Construction with null reverse domain is not allowed when the request
+/// requires reverse change.
+/// 4. Valid construction functions properly
+TEST(NameChangeTransaction, construction) {
+ asiolink::IOServicePtr io_service(new isc::asiolink::IOService());
+ D2CfgMgrPtr cfg_mgr(new D2CfgMgr());
+
+ const char* msg_str =
+ "{"
+ " \"change-type\" : 0 , "
+ " \"forward-change\" : true , "
+ " \"reverse-change\" : true , "
+ " \"fqdn\" : \"example.com.\" , "
+ " \"ip-address\" : \"192.168.2.1\" , "
+ " \"dhcid\" : \"0102030405060708\" , "
+ " \"lease-expires-on\" : \"20130121132405\" , "
+ " \"lease-length\" : 1300, "
+ " \"use-conflict-resolution\" : true "
+ "}";
+
+ dhcp_ddns::NameChangeRequestPtr ncr;
+
+ dhcp_ddns::NameChangeRequestPtr empty_ncr;
+ DnsServerInfoStoragePtr servers;
+ DdnsDomainPtr forward_domain;
+ DdnsDomainPtr reverse_domain;
+ DdnsDomainPtr empty_domain;
+
+ ASSERT_NO_THROW(ncr = dhcp_ddns::NameChangeRequest::fromJSON(msg_str));
+ ASSERT_NO_THROW(forward_domain.reset(new DdnsDomain("*", servers)));
+ ASSERT_NO_THROW(reverse_domain.reset(new DdnsDomain("*", servers)));
+
+ // Verify that construction with a null IOServicePtr fails.
+ // @todo Subject to change if multi-threading is implemented.
+ asiolink::IOServicePtr empty;
+ EXPECT_THROW(NameChangeTransaction(empty, ncr,
+ forward_domain, reverse_domain, cfg_mgr),
+ NameChangeTransactionError);
+
+ // Verify that construction with an empty NameChangeRequest throws.
+ EXPECT_THROW(NameChangeTransaction(io_service, empty_ncr,
+ forward_domain, reverse_domain, cfg_mgr),
+ NameChangeTransactionError);
+
+ // Verify that construction with an empty D2CfgMgr throws.
+ D2CfgMgrPtr empty_cfg;
+ EXPECT_THROW(NameChangeTransaction(io_service, empty_ncr,
+ forward_domain, reverse_domain,
+ empty_cfg),
+ NameChangeTransactionError);
+
+
+ // Verify that construction with an empty forward domain when the
+ // NameChangeRequest calls for a forward change throws.
+ EXPECT_THROW(NameChangeTransaction(io_service, ncr,
+ empty_domain, reverse_domain, cfg_mgr),
+ NameChangeTransactionError);
+
+ // Verify that construction with an empty reverse domain when the
+ // NameChangeRequest calls for a reverse change throws.
+ EXPECT_THROW(NameChangeTransaction(io_service, ncr,
+ forward_domain, empty_domain, cfg_mgr),
+ NameChangeTransactionError);
+
+ // Verify that a valid construction attempt works.
+ EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
+ forward_domain, reverse_domain,
+ cfg_mgr));
+
+ // Verify that an empty forward domain is allowed when the requests does
+ // not include a forward change.
+ ncr->setForwardChange(false);
+ ncr->setReverseChange(true);
+ EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
+ empty_domain, reverse_domain,
+ cfg_mgr));
+
+ // Verify that an empty reverse domain is allowed when the requests does
+ // not include a reverse change.
+ ncr->setForwardChange(true);
+ ncr->setReverseChange(false);
+ EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
+ forward_domain, empty_domain,
+ cfg_mgr));
+}
+
+/// @brief General testing of member accessors.
+/// Most if not all of these are also tested as a byproduct of larger tests.
+TEST_F(NameChangeTransactionTest, accessors) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+ // Verify that fetching the NameChangeRequest works.
+ dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
+ ASSERT_TRUE(ncr);
+
+ // Verify that getTransactionKey works.
+ EXPECT_EQ(ncr->getDhcid(), name_change->getTransactionKey());
+
+ // Verify that getRequestId works.
+ EXPECT_EQ(ncr->getRequestId(), name_change->getRequestId());
+
+ // Verify that NcrStatus can be set and retrieved.
+ EXPECT_NO_THROW(name_change->setNcrStatus(dhcp_ddns::ST_FAILED));
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, ncr->getStatus());
+
+ // Verify that the forward domain can be retrieved.
+ ASSERT_TRUE(name_change->getForwardDomain());
+ EXPECT_EQ(forward_domain_, name_change->getForwardDomain());
+
+ // Verify that the reverse domain can be retrieved.
+ ASSERT_TRUE(name_change->getReverseDomain());
+ EXPECT_EQ(reverse_domain_, name_change->getReverseDomain());
+
+ // Neither of these have direct setters, but are tested under server
+ // selection.
+ EXPECT_FALSE(name_change->getDNSClient());
+ EXPECT_FALSE(name_change->getCurrentServer());
+
+ // Verify that DNS update status can be set and retrieved.
+ EXPECT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::TIMEOUT));
+ EXPECT_EQ(DNSClient::TIMEOUT, name_change->getDnsUpdateStatus());
+
+ // Verify that the forward change complete flag can be set and fetched.
+ EXPECT_NO_THROW(name_change->setForwardChangeCompleted(true));
+ EXPECT_TRUE(name_change->getForwardChangeCompleted());
+
+ // Verify that the reverse change complete flag can be set and fetched.
+ EXPECT_NO_THROW(name_change->setReverseChangeCompleted(true));
+ EXPECT_TRUE(name_change->getReverseChangeCompleted());
+}
+
+/// @brief Tests DNS update request accessor methods.
+TEST_F(NameChangeTransactionTest, dnsUpdateRequestAccessors) {
+ // Create a transaction.
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+ // Post transaction construction, there should not be an update request.
+ EXPECT_FALSE(name_change->getDnsUpdateRequest());
+
+ // Create a request.
+ D2UpdateMessagePtr req;
+ ASSERT_NO_THROW(req.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));
+
+ // Use the setter and then verify we can fetch the request.
+ ASSERT_NO_THROW(name_change->setDnsUpdateRequest(req));
+
+ // Post set, we should be able to fetch it.
+ ASSERT_TRUE(name_change->getDnsUpdateRequest());
+
+ // Should be able to clear it.
+ ASSERT_NO_THROW(name_change->clearDnsUpdateRequest());
+
+ // Should be empty again.
+ EXPECT_FALSE(name_change->getDnsUpdateRequest());
+}
+
+/// @brief Tests DNS update request accessor methods.
+TEST_F(NameChangeTransactionTest, dnsUpdateResponseAccessors) {
+ // Create a transaction.
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+ // Post transaction construction, there should not be an update response.
+ EXPECT_FALSE(name_change->getDnsUpdateResponse());
+
+ // Create a response.
+ D2UpdateMessagePtr resp;
+ ASSERT_NO_THROW(resp.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND)));
+
+ // Use the setter and then verify we can fetch the response.
+ ASSERT_NO_THROW(name_change->setDnsUpdateResponse(resp));
+
+ // Post set, we should be able to fetch it.
+ EXPECT_TRUE(name_change->getDnsUpdateResponse());
+
+ // Should be able to clear it.
+ ASSERT_NO_THROW(name_change->clearDnsUpdateResponse());
+
+ // Should be empty again.
+ EXPECT_FALSE(name_change->getDnsUpdateResponse());
+
+}
+
+/// @brief Tests responseString method.
+TEST_F(NameChangeTransactionTest, responseString) {
+ // Create a transaction.
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+ // Make sure it is safe to call when status says success but there
+ // is no update response.
+ ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::SUCCESS));
+ EXPECT_EQ("SUCCESS, rcode: update response is NULL",
+ name_change->responseString());
+
+ // Create a response. (We use an OUTBOUND message so we can set RCODE)
+ D2UpdateMessagePtr resp;
+ ASSERT_NO_THROW(resp.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));
+ ASSERT_NO_THROW(name_change->setDnsUpdateResponse(resp));
+
+ // Make sure we decode Rcode when status is successful.
+ ASSERT_NO_THROW(resp->setRcode(dns::Rcode::NXDOMAIN()));
+ EXPECT_EQ("SUCCESS, rcode: NXDOMAIN", name_change->responseString());
+
+ // Test all of the non-success values for status.
+ ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::TIMEOUT));
+ EXPECT_EQ("TIMEOUT", name_change->responseString());
+
+ ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::IO_STOPPED));
+ EXPECT_EQ("IO_STOPPED", name_change->responseString());
+
+ ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::
+ INVALID_RESPONSE));
+ EXPECT_EQ("INVALID_RESPONSE", name_change->responseString());
+
+ ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::OTHER));
+ EXPECT_EQ("OTHER", name_change->responseString());
+}
+
+/// @brief Tests transactionOutcomeString method.
+TEST_F(NameChangeTransactionTest, transactionOutcomeString) {
+ // Create a transaction.
+ NameChangeStubPtr name_change;
+ dhcp_ddns::NameChangeRequestPtr ncr;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ ncr = name_change->getNcr();
+
+ // Check case of failed transaction in both directions
+ std::string exp_str("Status: Failed, Event: UNDEFINED, Forward change:"
+ " failed, Reverse change: failed, request: ");
+ exp_str += ncr->toText();
+
+ std::string tstring = name_change->transactionOutcomeString();
+ std::cout << "tstring is: [" << tstring << "]" << std::endl;
+
+ EXPECT_EQ(exp_str, name_change->transactionOutcomeString());
+
+ // Check case of success all around
+ name_change->setNcrStatus(dhcp_ddns::ST_COMPLETED);
+ name_change->setForwardChangeCompleted(true);
+ name_change->setReverseChangeCompleted(true);
+
+ exp_str = "Status: Completed, Event: UNDEFINED, Forward change: completed,"
+ " Reverse change: completed, request: " + ncr->toText();
+ EXPECT_EQ(exp_str, name_change->transactionOutcomeString());
+
+ // Check case of success, with no forward change
+ name_change->setNcrStatus(dhcp_ddns::ST_COMPLETED);
+ ncr->setForwardChange(false);
+ exp_str = "Status: Completed, Event: UNDEFINED, "
+ " Reverse change: completed, request: " + ncr->toText();
+ EXPECT_EQ(exp_str, name_change->transactionOutcomeString());
+
+ // Check case of success, with no reverse change
+ name_change->setNcrStatus(dhcp_ddns::ST_COMPLETED);
+ ncr->setForwardChange(true);
+ ncr->setReverseChange(false);
+ exp_str = "Status: Completed, Event: UNDEFINED, "
+ " Forward change: completed, request: " + ncr->toText();
+ EXPECT_EQ(exp_str, name_change->transactionOutcomeString());
+}
+
+/// @brief Tests event and state dictionary construction and verification.
+TEST_F(NameChangeTransactionTest, dictionaryCheck) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+ // Verify that the event and state dictionary validation fails prior
+ // dictionary construction.
+ ASSERT_THROW(name_change->verifyEvents(), StateModelError);
+ ASSERT_THROW(name_change->verifyStates(), StateModelError);
+
+ // Construct both dictionaries.
+ ASSERT_NO_THROW(name_change->defineEvents());
+ ASSERT_NO_THROW(name_change->defineStates());
+
+ // Verify both event and state dictionaries now pass validation.
+ ASSERT_NO_THROW(name_change->verifyEvents());
+ ASSERT_NO_THROW(name_change->verifyStates());
+}
+
+/// @brief Tests server selection methods.
+/// Each transaction has a list of one or more servers for each DNS direction
+/// it is required to update. The transaction must be able to start at the
+/// beginning of a server list and cycle through them one at time, as needed,
+/// when a DNS exchange fails due to an IO error. This test verifies the
+/// ability to iteratively select a server from the list as the current server.
+TEST_F(NameChangeTransactionTest, serverSelectionTest) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+ // Verify that the forward domain and its list of servers can be retrieved.
+ DdnsDomainPtr& domain = name_change->getForwardDomain();
+ ASSERT_TRUE(domain);
+ DnsServerInfoStoragePtr servers = domain->getServers();
+ ASSERT_TRUE(servers);
+
+ // Get the number of entries in the server list.
+ int num_servers = servers->size();
+ ASSERT_TRUE(num_servers > 0);
+
+ // Verify that we can initialize server selection. This "resets" the
+ // selection process to start over using the list of servers in the
+ // given domain.
+ ASSERT_NO_THROW(name_change->initServerSelection(domain));
+
+ // The server selection process determines the current server,
+ // instantiates a new DNSClient, and a DNS response message buffer.
+ // We need to save the values before each selection, so we can verify
+ // they are correct after each selection.
+ DnsServerInfoPtr prev_server = name_change->getCurrentServer();
+ DNSClientPtr prev_client = name_change->getDNSClient();
+
+ // Verify response pointer is empty.
+ EXPECT_FALSE(name_change->getDnsUpdateResponse());
+
+ // Create dummy response so we can verify it is cleared at each
+ // new server select.
+ D2UpdateMessagePtr dummyResp;
+ dummyResp.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND));
+ ASSERT_NO_THROW(name_change->setDnsUpdateResponse(dummyResp));
+ ASSERT_TRUE(name_change->getDnsUpdateResponse());
+
+ // Iteratively select through the list of servers.
+ int passes = 0;
+ while (name_change->selectNextServer()) {
+ // Get the new values after the selection has been made.
+ DnsServerInfoPtr server = name_change->getCurrentServer();
+ DNSClientPtr client = name_change->getDNSClient();
+ D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
+
+ // Verify that the new values are not empty.
+ EXPECT_TRUE(server);
+ EXPECT_TRUE(client);
+
+ // Verify response pointer is now empty.
+ EXPECT_FALSE(name_change->getDnsUpdateResponse());
+
+ // Verify that the new values are indeed new.
+ EXPECT_NE(server, prev_server);
+ EXPECT_NE(client, prev_client);
+
+ // Remember the selected values for the next pass.
+ prev_server = server;
+ prev_client = client;
+
+ // Create new dummy response.
+ dummyResp.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND));
+ ASSERT_NO_THROW(name_change->setDnsUpdateResponse(dummyResp));
+ ASSERT_TRUE(name_change->getDnsUpdateResponse());
+
+ ++passes;
+ }
+
+ // Verify that the number of passes made equal the number of servers.
+ EXPECT_EQ (passes, num_servers);
+
+ // Repeat the same test using the reverse domain.
+ // Verify that the reverse domain and its list of servers can be retrieved.
+ domain = name_change->getReverseDomain();
+ ASSERT_TRUE(domain);
+ servers = domain->getServers();
+ ASSERT_TRUE(servers);
+
+ // Get the number of entries in the server list.
+ num_servers = servers->size();
+ ASSERT_TRUE(num_servers > 0);
+
+ // Verify that we can initialize server selection. This "resets" the
+ // selection process to start over using the list of servers in the
+ // given domain.
+ ASSERT_NO_THROW(name_change->initServerSelection(domain));
+
+ // The server selection process determines the current server,
+ // instantiates a new DNSClient, and resets the DNS response message buffer.
+ // We need to save the values before each selection, so we can verify
+ // they are correct after each selection.
+ prev_server = name_change->getCurrentServer();
+ prev_client = name_change->getDNSClient();
+
+ // Iteratively select through the list of servers.
+ passes = 0;
+ while (name_change->selectNextServer()) {
+ // Get the new values after the selection has been made.
+ DnsServerInfoPtr server = name_change->getCurrentServer();
+ DNSClientPtr client = name_change->getDNSClient();
+ D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
+
+ // Verify that the new values are not empty.
+ EXPECT_TRUE(server);
+ EXPECT_TRUE(client);
+
+ // Verify response pointer is now empty.
+ EXPECT_FALSE(name_change->getDnsUpdateResponse());
+
+ // Verify that the new values are indeed new.
+ EXPECT_NE(server, prev_server);
+ EXPECT_NE(client, prev_client);
+
+ // Remember the selected values for the next pass.
+ prev_server = server;
+ prev_client = client;
+
+ // Create new dummy response.
+ dummyResp.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND));
+ ASSERT_NO_THROW(name_change->setDnsUpdateResponse(dummyResp));
+ ASSERT_TRUE(name_change->getDnsUpdateResponse());
+
+ ++passes;
+ }
+
+ // Verify that the number of passes made equal the number of servers.
+ EXPECT_EQ (passes, num_servers);
+}
+
+/// @brief Tests that the transaction will be "failed" upon model errors.
+TEST_F(NameChangeTransactionTest, modelFailure) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+ // Now call runModel() with an undefined event which should not throw,
+ // but should result in a failed model and failed transaction.
+ EXPECT_NO_THROW(name_change->runModel(9999));
+
+ // Verify that the model reports are done but failed.
+ EXPECT_TRUE(name_change->isModelDone());
+ EXPECT_TRUE(name_change->didModelFail());
+
+ // Verify that the transaction has failed.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_change->getNcrStatus());
+}
+
+/// @brief Tests the ability to use startTransaction to initiate the state
+/// model execution, and DNSClient callback, operator(), to resume the
+/// model with a update successful outcome.
+TEST_F(NameChangeTransactionTest, successfulUpdateTest) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ ASSERT_TRUE(name_change->selectFwdServer());
+
+ EXPECT_TRUE(name_change->isModelNew());
+ EXPECT_FALSE(name_change->getForwardChangeCompleted());
+
+ // Launch the transaction by calling startTransaction. The state model
+ // should run up until the "IO" operation is initiated in DOING_UPDATE_ST.
+ ASSERT_NO_THROW(name_change->startTransaction());
+
+ // Verify that the model is running but waiting, and that forward change
+ // completion is still false.
+ EXPECT_TRUE(name_change->isModelRunning());
+ EXPECT_TRUE(name_change->isModelWaiting());
+ EXPECT_FALSE(name_change->getForwardChangeCompleted());
+
+ // Simulate completion of DNSClient exchange by invoking the callback, as
+ // DNSClient would. This should cause the state model to progress through
+ // completion.
+ EXPECT_NO_THROW((*name_change)(DNSClient::SUCCESS));
+
+ // The model should have worked through to completion.
+ // Verify that the model is done and not failed.
+ EXPECT_TRUE(name_change->isModelDone());
+ EXPECT_FALSE(name_change->didModelFail());
+
+ // Verify that NCR status is completed, and that the forward change
+ // was completed.
+ EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_change->getNcrStatus());
+ EXPECT_TRUE(name_change->getForwardChangeCompleted());
+}
+
+/// @brief Tests the ability to use startTransaction to initiate the state
+/// model execution, and DNSClient callback, operator(), to resume the
+/// model with a update failure outcome.
+TEST_F(NameChangeTransactionTest, failedUpdateTest) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ ASSERT_TRUE(name_change->selectFwdServer());
+
+ // Launch the transaction by calling startTransaction. The state model
+ // should run up until the "IO" operation is initiated in DOING_UPDATE_ST.
+ ASSERT_NO_THROW(name_change->startTransaction());
+
+ // Verify that the model is running but waiting, and that the forward
+ // change has not been completed.
+ EXPECT_TRUE(name_change->isModelRunning());
+ EXPECT_TRUE(name_change->isModelWaiting());
+ EXPECT_FALSE(name_change->getForwardChangeCompleted());
+
+ // Simulate completion of DNSClient exchange by invoking the callback, as
+ // DNSClient would. This should cause the state model to progress through
+ // to completion.
+ EXPECT_NO_THROW((*name_change)(DNSClient::TIMEOUT));
+
+ // The model should have worked through to completion.
+ // Verify that the model is done and not failed.
+ EXPECT_TRUE(name_change->isModelDone());
+ EXPECT_FALSE(name_change->didModelFail());
+
+ // Verify that the NCR status is failed and that the forward change
+ // was not completed.
+ EXPECT_EQ(dhcp_ddns::ST_FAILED, name_change->getNcrStatus());
+ EXPECT_FALSE(name_change->getForwardChangeCompleted());
+}
+
+/// @brief Tests update attempt accessors.
+TEST_F(NameChangeTransactionTest, updateAttempts) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+ // Post transaction construction, update attempts should be 0.
+ EXPECT_EQ(0, name_change->getUpdateAttempts());
+
+ // Set it to a known value.
+ name_change->setUpdateAttempts(5);
+
+ // Verify that the value is as expected.
+ EXPECT_EQ(5, name_change->getUpdateAttempts());
+
+ // Clear it.
+ name_change->clearUpdateAttempts();
+
+ // Verify that it was cleared as expected.
+ EXPECT_EQ(0, name_change->getUpdateAttempts());
+}
+
+/// @brief Tests retryTransition method
+///
+/// Verifies that while the maximum number of update attempts has not
+/// been exceeded, the method will leave the state unchanged but post a
+/// SERVER_SELECTED_EVT. Once the maximum is exceeded, the method should
+/// transition to the state given with a next event of SERVER_IO_ERROR_EVT.
+TEST_F(NameChangeTransactionTest, retryTransition) {
+ // Create the transaction.
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+
+ // Define dictionaries.
+ ASSERT_NO_THROW(name_change->initDictionaries());
+
+ // Transition to a known spot.
+ ASSERT_NO_THROW(name_change->transition(
+ NameChangeStub::DOING_UPDATE_ST,
+ NameChangeStub::SEND_UPDATE_EVT));
+
+ // Verify we are at the known spot.
+ ASSERT_EQ(NameChangeStub::DOING_UPDATE_ST,
+ name_change->getCurrState());
+ ASSERT_EQ(NameChangeStub::SEND_UPDATE_EVT,
+ name_change->getNextEvent());
+
+ // Verify that we have not exceeded maximum number of attempts.
+ ASSERT_LT(name_change->getUpdateAttempts(),
+ NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER);
+
+ // Call retryTransition.
+ ASSERT_NO_THROW(name_change->retryTransition(
+ NameChangeTransaction::PROCESS_TRANS_FAILED_ST));
+
+ // Since the number of update attempts is less than the maximum allowed
+ // we should remain in our current state but with next event of
+ // SERVER_SELECTED_EVT posted.
+ ASSERT_EQ(NameChangeStub::DOING_UPDATE_ST,
+ name_change->getCurrState());
+ ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
+ name_change->getNextEvent());
+
+ // Now set the number of attempts to the maximum.
+ name_change->setUpdateAttempts(NameChangeTransaction::
+ MAX_UPDATE_TRIES_PER_SERVER);
+ // Call retryTransition.
+ ASSERT_NO_THROW(name_change->retryTransition(
+ NameChangeTransaction::PROCESS_TRANS_FAILED_ST));
+
+ // Since we have exceeded maximum attempts, we should transition to
+ // PROCESS_UPDATE_FAILED_ST with a next event of SERVER_IO_ERROR_EVT.
+ ASSERT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_change->getCurrState());
+ ASSERT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
+ name_change->getNextEvent());
+}
+
+/// @brief Tests sendUpdate method when underlying doUpdate throws.
+///
+/// DNSClient::doUpdate can throw for a variety of reasons. This tests
+/// sendUpdate handling of such a throw by passing doUpdate a request
+/// that will not render.
+TEST_F(NameChangeTransactionTest, sendUpdateDoUpdateFailure) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ ASSERT_NO_THROW(name_change->initDictionaries());
+ ASSERT_TRUE(name_change->selectFwdServer());
+
+ // Set the transaction's request to an empty DNS update.
+ D2UpdateMessagePtr req;
+ ASSERT_NO_THROW(req.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));
+ ASSERT_NO_THROW(name_change->setDnsUpdateRequest(req));
+
+ // Verify that sendUpdate does not throw, but it should fail because
+ // the request won't render.
+ ASSERT_NO_THROW(name_change->sendUpdate());
+
+ // Verify that we transition to failed state and event.
+ ASSERT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
+ name_change->getCurrState());
+ ASSERT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
+ name_change->getNextEvent());
+}
+
+/// @brief Tests sendUpdate method when underlying doUpdate times out.
+TEST_F(NameChangeTransactionTest, sendUpdateTimeout) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ ASSERT_NO_THROW(name_change->initDictionaries());
+ ASSERT_TRUE(name_change->selectFwdServer());
+
+ // Build a valid request, call sendUpdate and process the response.
+ // Note we have to wait for DNSClient timeout plus a bit more to allow
+ // DNSClient to timeout.
+ // The method, doOneExchange, can suffer fatal assertions which invalidate
+ // not only it but the invoking test as well. In other words, if the
+ // doOneExchange blows up the rest of test is pointless. I use
+ // ASSERT_NO_FATAL_FAILURE to abort the test immediately.
+
+ D2ParamsPtr d2_params = cfg_mgr_->getD2Params();
+ size_t timeout = d2_params->getDnsServerTimeout() + 100;
+
+ ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change, timeout));
+
+ // Verify that next event is IO_COMPLETED_EVT and DNS status is TIMEOUT.
+ ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
+ name_change->getNextEvent());
+ ASSERT_EQ(DNSClient::TIMEOUT, name_change->getDnsUpdateStatus());
+}
+
+/// @brief Tests sendUpdate method when it receives a corrupt response from
+/// the server.
+TEST_F(NameChangeTransactionTest, sendUpdateCorruptResponse) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ ASSERT_NO_THROW(name_change->initDictionaries());
+ ASSERT_TRUE(name_change->selectFwdServer());
+
+ // Create a server and start it listening.
+ FauxServer server(*io_service_, *(name_change->getCurrentServer()));
+ server.receive(FauxServer::CORRUPT_RESP);
+
+ // Build a valid request, call sendUpdate and process the response.
+ ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));
+
+ // Verify that next event is IO_COMPLETED_EVT and DNS status is INVALID.
+ ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
+ name_change->getNextEvent());
+ ASSERT_EQ(DNSClient::INVALID_RESPONSE, name_change->getDnsUpdateStatus());
+}
+
+/// @brief Tests sendUpdate method when the exchange succeeds.
+TEST_F(NameChangeTransactionTest, sendUpdate) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ ASSERT_NO_THROW(name_change->initDictionaries());
+ ASSERT_TRUE(name_change->selectFwdServer());
+
+ // Create a server and start it listening.
+ FauxServer server(*io_service_, *(name_change->getCurrentServer()));
+ server.receive (FauxServer::USE_RCODE, dns::Rcode::NOERROR());
+
+ // Build a valid request, call sendUpdate and process the response.
+ ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));
+
+ // Verify that next event is IO_COMPLETED_EVT and DNS status is SUCCESS.
+ ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
+ name_change->getNextEvent());
+ ASSERT_EQ(DNSClient::SUCCESS, name_change->getDnsUpdateStatus());
+
+ // Verify that we have a response and it's Rcode is NOERROR,
+ // and the zone is as expected.
+ D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
+ ASSERT_TRUE(response);
+ ASSERT_EQ(dns::Rcode::NOERROR().getCode(), response->getRcode().getCode());
+ D2ZonePtr zone = response->getZone();
+ EXPECT_TRUE(zone);
+ EXPECT_EQ("response.example.com.", zone->getName().toText());
+}
+
+/// @brief Tests that an unsigned response to a signed request is an error
+TEST_F(NameChangeTransactionTest, tsigUnsignedResponse) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction("key_one"));
+ ASSERT_NO_THROW(name_change->initDictionaries());
+ ASSERT_TRUE(name_change->selectFwdServer());
+
+ // Create a server and start it listening.
+ FauxServer server(*io_service_, *(name_change->getCurrentServer()));
+ server.receive (FauxServer::USE_RCODE, dns::Rcode::NOERROR());
+
+ // Do the update.
+ ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));
+
+ // Verify that next event is IO_COMPLETED_EVT and DNS status is
+ // INVALID_RESPONSE.
+ ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
+ name_change->getNextEvent());
+
+ ASSERT_EQ(DNSClient::INVALID_RESPONSE, name_change->getDnsUpdateStatus());
+
+ // When TSIG errors occur, only the message header (including Rcode) is
+ // unpacked. In this case, it should be NOERROR but have no other
+ // information.
+ D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
+ ASSERT_TRUE(response);
+ ASSERT_EQ(dns::Rcode::NOERROR().getCode(), response->getRcode().getCode());
+ EXPECT_FALSE(response->getZone());
+}
+
+/// @brief Tests that a response signed with the wrong key is an error
+TEST_F(NameChangeTransactionTest, tsigInvalidResponse) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction("key_one"));
+ ASSERT_NO_THROW(name_change->initDictionaries());
+ ASSERT_TRUE(name_change->selectFwdServer());
+
+ // Create a server, tell it to sign responses with a "random" key,
+ // then start it listening.
+ FauxServer server(*io_service_, *(name_change->getCurrentServer()));
+ server.receive (FauxServer::INVALID_TSIG, dns::Rcode::NOERROR());
+
+ // Do the update.
+ ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));
+
+ // Verify that next event is IO_COMPLETED_EVT and DNS status is
+ // INVALID_RESPONSE.
+ ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
+ name_change->getNextEvent());
+
+ ASSERT_EQ(DNSClient::INVALID_RESPONSE, name_change->getDnsUpdateStatus());
+
+ // When TSIG errors occur, only the message header (including Rcode) is
+ // unpacked. In this case, it should be NOERROR but have no other
+ // information.
+ D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
+ ASSERT_TRUE(response);
+ ASSERT_EQ(dns::Rcode::NOERROR().getCode(), response->getRcode().getCode());
+ EXPECT_FALSE(response->getZone());
+}
+
+/// @brief Tests that a signed response to an unsigned request is ok.
+/// Currently our policy is to accept a signed response to an unsigned request
+/// even though the spec says a server MUST not do that.
+TEST_F(NameChangeTransactionTest, tsigUnexpectedSignedResponse) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ ASSERT_NO_THROW(name_change->initDictionaries());
+ ASSERT_TRUE(name_change->selectFwdServer());
+
+ // Create a server, tell it to sign responses with a "random" key,
+ // then start it listening.
+ FauxServer server(*io_service_, *(name_change->getCurrentServer()));
+ server.receive (FauxServer::INVALID_TSIG, dns::Rcode::NOERROR());
+
+ // Perform an update without TSIG.
+ ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));
+
+ // Verify that next event is IO_COMPLETED_EVT and DNS status is SUCCESS.
+ ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
+ name_change->getNextEvent());
+
+ ASSERT_EQ(DNSClient::SUCCESS, name_change->getDnsUpdateStatus());
+
+ D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
+ ASSERT_TRUE(response);
+ ASSERT_EQ(dns::Rcode::NOERROR().getCode(), response->getRcode().getCode());
+ D2ZonePtr zone = response->getZone();
+ EXPECT_TRUE(zone);
+ EXPECT_EQ("response.example.com.", zone->getName().toText());
+}
+
+/// @brief Tests that a TSIG update succeeds when client and server both use
+/// the right key. Runs the test for all supported algorithms.
+TEST_F(NameChangeTransactionTest, tsigAllValid) {
+ std::vector<std::string>algorithms;
+ algorithms.push_back(TSIGKeyInfo::HMAC_MD5_STR);
+ algorithms.push_back(TSIGKeyInfo::HMAC_SHA1_STR);
+ algorithms.push_back(TSIGKeyInfo::HMAC_SHA224_STR);
+ algorithms.push_back(TSIGKeyInfo::HMAC_SHA256_STR);
+ algorithms.push_back(TSIGKeyInfo::HMAC_SHA384_STR);
+ algorithms.push_back(TSIGKeyInfo::HMAC_SHA512_STR);
+
+ for (int i = 0; i < algorithms.size(); ++i) {
+ SCOPED_TRACE (algorithms[i]);
+ TSIGKeyInfoPtr key;
+ ASSERT_NO_THROW(key.reset(new TSIGKeyInfo("test_key",
+ algorithms[i],
+ "GWG/Xfbju4O2iXGqkSu4PQ==")));
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction(key));
+ ASSERT_NO_THROW(name_change->initDictionaries());
+ ASSERT_TRUE(name_change->selectFwdServer());
+
+ // Create a server, set its TSIG key, and then start it listening.
+ FauxServer server(*io_service_, *(name_change->getCurrentServer()));
+ // Since we create a new server instance each time we need to tell
+ // it not reschedule receives automatically.
+ server.perpetual_receive_ = false;
+ server.setTSIGKey(key->getTSIGKey());
+ server.receive (FauxServer::USE_RCODE, dns::Rcode::NOERROR());
+
+ // Do the update.
+ ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));
+
+ // Verify that next event is IO_COMPLETED_EVT and DNS status is SUCCESS.
+ ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
+ name_change->getNextEvent());
+
+ ASSERT_EQ(DNSClient::SUCCESS, name_change->getDnsUpdateStatus());
+
+ D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
+ ASSERT_TRUE(response);
+ ASSERT_EQ(dns::Rcode::NOERROR().getCode(),
+ response->getRcode().getCode());
+ D2ZonePtr zone = response->getZone();
+ EXPECT_TRUE(zone);
+ EXPECT_EQ("response.example.com.", zone->getName().toText());
+ }
+}
+
+
+/// @brief Tests the prepNewRequest method
+TEST_F(NameChangeTransactionTest, prepNewRequest) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ D2UpdateMessagePtr request;
+
+ // prepNewRequest should fail on empty domain.
+ ASSERT_THROW(request = name_change->prepNewRequest(DdnsDomainPtr()),
+ NameChangeTransactionError);
+
+ // Verify that prepNewRequest fails on invalid zone name.
+ // @todo This test becomes obsolete if/when DdnsDomain enforces valid
+ // names as is done in dns::Name.
+ DdnsDomainPtr bsDomain = makeDomain(".badname","");
+ ASSERT_THROW(request = name_change->prepNewRequest(bsDomain),
+ NameChangeTransactionError);
+
+ // Verify that prepNewRequest properly constructs a message given
+ // valid input.
+ ASSERT_NO_THROW(request = name_change->prepNewRequest(forward_domain_));
+ checkZone(request, forward_domain_->getName());
+
+ // The query id is random so 0 is not impossible
+ for (unsigned i = 0; i < 10; ++i) {
+ if (request->getId() == 0) {
+ request = name_change->prepNewRequest(forward_domain_);
+ }
+ }
+
+ EXPECT_NE(0, request->getId());
+}
+
+/// @brief Tests the addLeaseAddressRData method
+TEST_F(NameChangeTransactionTest, addLeaseAddressRData) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
+
+ // Verify we can add a lease RData to an valid RRset.
+ dns::RRsetPtr rrset(new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
+ name_change->getAddressRRType(),
+ dns::RRTTL(0)));
+ ASSERT_NO_THROW(name_change->addLeaseAddressRdata(rrset));
+
+ // Verify the Rdata was added and the value is correct.
+ ASSERT_EQ(1, rrset->getRdataCount());
+ dns::RdataIteratorPtr rdata_it = rrset->getRdataIterator();
+ ASSERT_TRUE(rdata_it);
+ EXPECT_EQ(ncr->getIpAddress(), rdata_it->getCurrent().toText());
+
+}
+
+/// @brief Tests the addDhcidRData method
+TEST_F(NameChangeTransactionTest, addDhcidRdata) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
+
+ // Verify we can add a lease RData to an valid RRset.
+ dns::RRsetPtr rrset(new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
+ dns::RRType::DHCID(), dns::RRTTL(0)));
+ ASSERT_NO_THROW(name_change->addDhcidRdata(rrset));
+
+ // Verify the Rdata was added and the value is correct.
+ ASSERT_EQ(1, rrset->getRdataCount());
+ dns::RdataIteratorPtr rdata_it = rrset->getRdataIterator();
+ ASSERT_TRUE(rdata_it);
+
+ const std::vector<uint8_t>& ncr_dhcid = ncr->getDhcid().getBytes();
+ util::InputBuffer buffer(ncr_dhcid.data(), ncr_dhcid.size());
+ dns::rdata::in::DHCID rdata_ref(buffer, ncr_dhcid.size());
+ EXPECT_EQ(0, rdata_ref.compare(rdata_it->getCurrent()));
+}
+
+/// @brief Tests the addPtrData method
+TEST_F(NameChangeTransactionTest, addPtrRdata) {
+ NameChangeStubPtr name_change;
+ ASSERT_NO_THROW(name_change = makeCannedTransaction());
+ dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
+
+ // Verify we can add a PTR RData to an valid RRset.
+ dns::RRsetPtr rrset (new dns::RRset(dns::Name("bs"), dns::RRClass::IN(),
+ dns::RRType::PTR(), dns::RRTTL(0)));
+ ASSERT_NO_THROW(name_change->addPtrRdata(rrset));
+
+ // Verify the Rdata was added and the value is correct.
+ ASSERT_EQ(1, rrset->getRdataCount());
+ dns::RdataIteratorPtr rdata_it = rrset->getRdataIterator();
+ ASSERT_TRUE(rdata_it);
+
+ EXPECT_EQ(ncr->getFqdn(), rdata_it->getCurrent().toText());
+}
+
+}; // anonymous namespace
diff --git a/src/lib/d2srv/tests/run_unittests.cc b/src/lib/d2srv/tests/run_unittests.cc
new file mode 100644
index 0000000..768319e
--- /dev/null
+++ b/src/lib/d2srv/tests/run_unittests.cc
@@ -0,0 +1,20 @@
+// Copyright (C) 2012-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <log/logger_support.h>
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
+
+ int result = RUN_ALL_TESTS();
+
+ return (result);
+}