diff options
Diffstat (limited to 'src/lib/d2srv/tests')
-rw-r--r-- | src/lib/d2srv/tests/Makefile.am | 53 | ||||
-rw-r--r-- | src/lib/d2srv/tests/Makefile.in | 1088 | ||||
-rw-r--r-- | src/lib/d2srv/tests/d2_tsig_key_unittest.cc | 98 | ||||
-rw-r--r-- | src/lib/d2srv/tests/d2_update_message_unittests.cc | 696 | ||||
-rw-r--r-- | src/lib/d2srv/tests/d2_zone_unittests.cc | 83 | ||||
-rw-r--r-- | src/lib/d2srv/tests/dns_client_unittests.cc | 678 | ||||
-rw-r--r-- | src/lib/d2srv/tests/nc_trans_unittests.cc | 1279 | ||||
-rw-r--r-- | src/lib/d2srv/tests/run_unittests.cc | 20 |
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='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + 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); +} |