From 040eee1aa49b49df4698d83a05af57c220127fd1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 13:36:04 +0200 Subject: Adding upstream version 2.2.0. Signed-off-by: Daniel Baumann --- src/lib/d2srv/Makefile.am | 93 ++ src/lib/d2srv/Makefile.in | 1117 +++++++++++++++++ src/lib/d2srv/d2_cfg_mgr.cc | 326 +++++ src/lib/d2srv/d2_cfg_mgr.h | 339 ++++++ src/lib/d2srv/d2_config.cc | 674 +++++++++++ src/lib/d2srv/d2_config.h | 926 ++++++++++++++ src/lib/d2srv/d2_log.cc | 23 + src/lib/d2srv/d2_log.h | 25 + src/lib/d2srv/d2_messages.cc | 175 +++ src/lib/d2srv/d2_messages.h | 91 ++ src/lib/d2srv/d2_messages.mes | 405 +++++++ src/lib/d2srv/d2_simple_parser.cc | 310 +++++ src/lib/d2srv/d2_simple_parser.h | 96 ++ src/lib/d2srv/d2_stats.cc | 58 + src/lib/d2srv/d2_stats.h | 53 + src/lib/d2srv/d2_tsig_key.cc | 72 ++ src/lib/d2srv/d2_tsig_key.h | 76 ++ src/lib/d2srv/d2_update_message.cc | 230 ++++ src/lib/d2srv/d2_update_message.h | 357 ++++++ src/lib/d2srv/d2_zone.cc | 30 + src/lib/d2srv/d2_zone.h | 109 ++ src/lib/d2srv/d2srv.dox | 21 + src/lib/d2srv/dns_client.cc | 324 +++++ src/lib/d2srv/dns_client.h | 157 +++ src/lib/d2srv/nc_trans.cc | 578 +++++++++ src/lib/d2srv/nc_trans.h | 602 +++++++++ src/lib/d2srv/tests/Makefile.am | 53 + src/lib/d2srv/tests/Makefile.in | 1088 +++++++++++++++++ src/lib/d2srv/tests/d2_tsig_key_unittest.cc | 98 ++ src/lib/d2srv/tests/d2_update_message_unittests.cc | 696 +++++++++++ src/lib/d2srv/tests/d2_zone_unittests.cc | 83 ++ src/lib/d2srv/tests/dns_client_unittests.cc | 678 +++++++++++ src/lib/d2srv/tests/nc_trans_unittests.cc | 1279 ++++++++++++++++++++ src/lib/d2srv/tests/run_unittests.cc | 20 + src/lib/d2srv/testutils/Makefile.am | 23 + src/lib/d2srv/testutils/Makefile.in | 852 +++++++++++++ src/lib/d2srv/testutils/nc_test_utils.cc | 987 +++++++++++++++ src/lib/d2srv/testutils/nc_test_utils.h | 497 ++++++++ src/lib/d2srv/testutils/stats_test_utils.cc | 41 + src/lib/d2srv/testutils/stats_test_utils.h | 48 + 40 files changed, 13710 insertions(+) create mode 100644 src/lib/d2srv/Makefile.am create mode 100644 src/lib/d2srv/Makefile.in create mode 100644 src/lib/d2srv/d2_cfg_mgr.cc create mode 100644 src/lib/d2srv/d2_cfg_mgr.h create mode 100644 src/lib/d2srv/d2_config.cc create mode 100644 src/lib/d2srv/d2_config.h create mode 100644 src/lib/d2srv/d2_log.cc create mode 100644 src/lib/d2srv/d2_log.h create mode 100644 src/lib/d2srv/d2_messages.cc create mode 100644 src/lib/d2srv/d2_messages.h create mode 100644 src/lib/d2srv/d2_messages.mes create mode 100644 src/lib/d2srv/d2_simple_parser.cc create mode 100644 src/lib/d2srv/d2_simple_parser.h create mode 100644 src/lib/d2srv/d2_stats.cc create mode 100644 src/lib/d2srv/d2_stats.h create mode 100644 src/lib/d2srv/d2_tsig_key.cc create mode 100644 src/lib/d2srv/d2_tsig_key.h create mode 100644 src/lib/d2srv/d2_update_message.cc create mode 100644 src/lib/d2srv/d2_update_message.h create mode 100644 src/lib/d2srv/d2_zone.cc create mode 100644 src/lib/d2srv/d2_zone.h create mode 100644 src/lib/d2srv/d2srv.dox create mode 100644 src/lib/d2srv/dns_client.cc create mode 100644 src/lib/d2srv/dns_client.h create mode 100644 src/lib/d2srv/nc_trans.cc create mode 100644 src/lib/d2srv/nc_trans.h create mode 100644 src/lib/d2srv/tests/Makefile.am create mode 100644 src/lib/d2srv/tests/Makefile.in create mode 100644 src/lib/d2srv/tests/d2_tsig_key_unittest.cc create mode 100644 src/lib/d2srv/tests/d2_update_message_unittests.cc create mode 100644 src/lib/d2srv/tests/d2_zone_unittests.cc create mode 100644 src/lib/d2srv/tests/dns_client_unittests.cc create mode 100644 src/lib/d2srv/tests/nc_trans_unittests.cc create mode 100644 src/lib/d2srv/tests/run_unittests.cc create mode 100644 src/lib/d2srv/testutils/Makefile.am create mode 100644 src/lib/d2srv/testutils/Makefile.in create mode 100644 src/lib/d2srv/testutils/nc_test_utils.cc create mode 100644 src/lib/d2srv/testutils/nc_test_utils.h create mode 100644 src/lib/d2srv/testutils/stats_test_utils.cc create mode 100644 src/lib/d2srv/testutils/stats_test_utils.h (limited to 'src/lib/d2srv') diff --git a/src/lib/d2srv/Makefile.am b/src/lib/d2srv/Makefile.am new file mode 100644 index 0000000..a1e8333 --- /dev/null +++ b/src/lib/d2srv/Makefile.am @@ -0,0 +1,93 @@ +SUBDIRS = . testutils tests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +EXTRA_DIST = d2srv.dox + +CLEANFILES = *.gcno *.gcda + +lib_LTLIBRARIES = libkea-d2srv.la +libkea_d2srv_la_SOURCES = +libkea_d2srv_la_SOURCES += d2_cfg_mgr.cc d2_cfg_mgr.h +libkea_d2srv_la_SOURCES += d2_config.cc d2_config.h +libkea_d2srv_la_SOURCES += d2_log.cc d2_log.h +libkea_d2srv_la_SOURCES += d2_messages.cc d2_messages.h +libkea_d2srv_la_SOURCES += d2_update_message.cc d2_update_message.h +libkea_d2srv_la_SOURCES += d2_simple_parser.cc d2_simple_parser.h +libkea_d2srv_la_SOURCES += d2_stats.cc d2_stats.h +libkea_d2srv_la_SOURCES += d2_tsig_key.cc d2_tsig_key.h +libkea_d2srv_la_SOURCES += d2_zone.cc d2_zone.h +libkea_d2srv_la_SOURCES += dns_client.cc dns_client.h +libkea_d2srv_la_SOURCES += nc_trans.cc nc_trans.h +EXTRA_DIST += d2_messages.mes + +libkea_d2srv_la_CXXFLAGS = $(AM_CXXFLAGS) +libkea_d2srv_la_CPPFLAGS = $(AM_CPPFLAGS) + +libkea_d2srv_la_LIBADD = +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/process/libkea-process.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/asiodns/libkea-asiodns.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/stats/libkea-stats.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/http/libkea-http.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/database/libkea-database.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libkea_d2srv_la_LIBADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS) + +libkea_d2srv_la_LDFLAGS = -no-undefined -version-info 16:0:0 +libkea_d2srv_la_LDFLAGS += $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) + +# If we want to get rid of all generated messages files, we need to use +# make maintainer-clean. The proper way to introduce custom commands for +# that operation is to define maintainer-clean-local target. However, +# make maintainer-clean also removes Makefile, so running configure script +# is required. To make it easy to rebuild messages without going through +# reconfigure, a new target messages-clean has been added. +maintainer-clean-local: + rm -f d2_messages.h d2_messages.cc + +# To regenerate messages files, one can do: +# +# make messages-clean +# make messages +# +# This is needed only when a .mes file is modified. +messages-clean: maintainer-clean-local + +if GENERATE_MESSAGES + +# Define rule to build logging source files from message file +messages: d2_messages.h d2_messages.cc + @echo Message files regenerated + +d2_messages.h d2_messages.cc: d2_messages.mes + $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/d2srv/d2_messages.mes + +else + +messages d2_messages.h d2_messages.cc: + @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. + +endif + +# Specify the headers for copying into the installation directory tree. +libkea_d2srv_includedir = $(pkgincludedir)/d2srv +libkea_d2srv_include_HEADERS = \ + d2_cfg_mgr.h \ + d2_config.h \ + d2_log.h \ + d2_messages.h \ + d2_simple_parser.h diff --git a/src/lib/d2srv/Makefile.in b/src/lib/d2srv/Makefile.in new file mode 100644 index 0000000..555a0c4 --- /dev/null +++ b/src/lib/d2srv/Makefile.in @@ -0,0 +1,1117 @@ +# 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@ +subdir = src/lib/d2srv +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 $(libkea_d2srv_include_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(libkea_d2srv_includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libkea_d2srv_la_DEPENDENCIES = \ + $(top_builddir)/src/lib/process/libkea-process.la \ + $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \ + $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \ + $(top_builddir)/src/lib/asiodns/libkea-asiodns.la \ + $(top_builddir)/src/lib/stats/libkea-stats.la \ + $(top_builddir)/src/lib/config/libkea-cfgclient.la \ + $(top_builddir)/src/lib/http/libkea-http.la \ + $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \ + $(top_builddir)/src/lib/hooks/libkea-hooks.la \ + $(top_builddir)/src/lib/database/libkea-database.la \ + $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/dns/libkea-dns++.la \ + $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ + $(top_builddir)/src/lib/log/libkea-log.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_libkea_d2srv_la_OBJECTS = libkea_d2srv_la-d2_cfg_mgr.lo \ + libkea_d2srv_la-d2_config.lo libkea_d2srv_la-d2_log.lo \ + libkea_d2srv_la-d2_messages.lo \ + libkea_d2srv_la-d2_update_message.lo \ + libkea_d2srv_la-d2_simple_parser.lo \ + libkea_d2srv_la-d2_stats.lo libkea_d2srv_la-d2_tsig_key.lo \ + libkea_d2srv_la-d2_zone.lo libkea_d2srv_la-dns_client.lo \ + libkea_d2srv_la-nc_trans.lo +libkea_d2srv_la_OBJECTS = $(am_libkea_d2srv_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libkea_d2srv_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) \ + $(libkea_d2srv_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libkea_d2srv_la-d2_cfg_mgr.Plo \ + ./$(DEPDIR)/libkea_d2srv_la-d2_config.Plo \ + ./$(DEPDIR)/libkea_d2srv_la-d2_log.Plo \ + ./$(DEPDIR)/libkea_d2srv_la-d2_messages.Plo \ + ./$(DEPDIR)/libkea_d2srv_la-d2_simple_parser.Plo \ + ./$(DEPDIR)/libkea_d2srv_la-d2_stats.Plo \ + ./$(DEPDIR)/libkea_d2srv_la-d2_tsig_key.Plo \ + ./$(DEPDIR)/libkea_d2srv_la-d2_update_message.Plo \ + ./$(DEPDIR)/libkea_d2srv_la-d2_zone.Plo \ + ./$(DEPDIR)/libkea_d2srv_la-dns_client.Plo \ + ./$(DEPDIR)/libkea_d2srv_la-nc_trans.Plo +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 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libkea_d2srv_la_SOURCES) +DIST_SOURCES = $(libkea_d2srv_la_SOURCES) +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 +HEADERS = $(libkea_d2srv_include_HEADERS) +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 +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 = . testutils tests +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib \ + $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) +EXTRA_DIST = d2srv.dox d2_messages.mes +CLEANFILES = *.gcno *.gcda +lib_LTLIBRARIES = libkea-d2srv.la +libkea_d2srv_la_SOURCES = d2_cfg_mgr.cc d2_cfg_mgr.h d2_config.cc \ + d2_config.h d2_log.cc d2_log.h d2_messages.cc d2_messages.h \ + d2_update_message.cc d2_update_message.h d2_simple_parser.cc \ + d2_simple_parser.h d2_stats.cc d2_stats.h d2_tsig_key.cc \ + d2_tsig_key.h d2_zone.cc d2_zone.h dns_client.cc dns_client.h \ + nc_trans.cc nc_trans.h +libkea_d2srv_la_CXXFLAGS = $(AM_CXXFLAGS) +libkea_d2srv_la_CPPFLAGS = $(AM_CPPFLAGS) +libkea_d2srv_la_LIBADD = \ + $(top_builddir)/src/lib/process/libkea-process.la \ + $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \ + $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \ + $(top_builddir)/src/lib/asiodns/libkea-asiodns.la \ + $(top_builddir)/src/lib/stats/libkea-stats.la \ + $(top_builddir)/src/lib/config/libkea-cfgclient.la \ + $(top_builddir)/src/lib/http/libkea-http.la \ + $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \ + $(top_builddir)/src/lib/hooks/libkea-hooks.la \ + $(top_builddir)/src/lib/database/libkea-database.la \ + $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/dns/libkea-dns++.la \ + $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ + $(top_builddir)/src/lib/log/libkea-log.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS) +libkea_d2srv_la_LDFLAGS = -no-undefined -version-info 16:0:0 \ + $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) + +# Specify the headers for copying into the installation directory tree. +libkea_d2srv_includedir = $(pkgincludedir)/d2srv +libkea_d2srv_include_HEADERS = \ + d2_cfg_mgr.h \ + d2_config.h \ + d2_log.h \ + d2_messages.h \ + d2_simple_parser.h + +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/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/d2srv/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): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libkea-d2srv.la: $(libkea_d2srv_la_OBJECTS) $(libkea_d2srv_la_DEPENDENCIES) $(EXTRA_libkea_d2srv_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libkea_d2srv_la_LINK) -rpath $(libdir) $(libkea_d2srv_la_OBJECTS) $(libkea_d2srv_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_d2srv_la-d2_cfg_mgr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_d2srv_la-d2_config.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_d2srv_la-d2_log.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_d2srv_la-d2_messages.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_d2srv_la-d2_simple_parser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_d2srv_la-d2_stats.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_d2srv_la-d2_tsig_key.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_d2srv_la-d2_update_message.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_d2srv_la-d2_zone.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_d2srv_la-dns_client.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_d2srv_la-nc_trans.Plo@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 $@ $< + +libkea_d2srv_la-d2_cfg_mgr.lo: d2_cfg_mgr.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_d2srv_la-d2_cfg_mgr.lo -MD -MP -MF $(DEPDIR)/libkea_d2srv_la-d2_cfg_mgr.Tpo -c -o libkea_d2srv_la-d2_cfg_mgr.lo `test -f 'd2_cfg_mgr.cc' || echo '$(srcdir)/'`d2_cfg_mgr.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_d2srv_la-d2_cfg_mgr.Tpo $(DEPDIR)/libkea_d2srv_la-d2_cfg_mgr.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_cfg_mgr.cc' object='libkea_d2srv_la-d2_cfg_mgr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_d2srv_la-d2_cfg_mgr.lo `test -f 'd2_cfg_mgr.cc' || echo '$(srcdir)/'`d2_cfg_mgr.cc + +libkea_d2srv_la-d2_config.lo: d2_config.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_d2srv_la-d2_config.lo -MD -MP -MF $(DEPDIR)/libkea_d2srv_la-d2_config.Tpo -c -o libkea_d2srv_la-d2_config.lo `test -f 'd2_config.cc' || echo '$(srcdir)/'`d2_config.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_d2srv_la-d2_config.Tpo $(DEPDIR)/libkea_d2srv_la-d2_config.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_config.cc' object='libkea_d2srv_la-d2_config.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_d2srv_la-d2_config.lo `test -f 'd2_config.cc' || echo '$(srcdir)/'`d2_config.cc + +libkea_d2srv_la-d2_log.lo: d2_log.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_d2srv_la-d2_log.lo -MD -MP -MF $(DEPDIR)/libkea_d2srv_la-d2_log.Tpo -c -o libkea_d2srv_la-d2_log.lo `test -f 'd2_log.cc' || echo '$(srcdir)/'`d2_log.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_d2srv_la-d2_log.Tpo $(DEPDIR)/libkea_d2srv_la-d2_log.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_log.cc' object='libkea_d2srv_la-d2_log.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_d2srv_la-d2_log.lo `test -f 'd2_log.cc' || echo '$(srcdir)/'`d2_log.cc + +libkea_d2srv_la-d2_messages.lo: d2_messages.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_d2srv_la-d2_messages.lo -MD -MP -MF $(DEPDIR)/libkea_d2srv_la-d2_messages.Tpo -c -o libkea_d2srv_la-d2_messages.lo `test -f 'd2_messages.cc' || echo '$(srcdir)/'`d2_messages.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_d2srv_la-d2_messages.Tpo $(DEPDIR)/libkea_d2srv_la-d2_messages.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_messages.cc' object='libkea_d2srv_la-d2_messages.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_d2srv_la-d2_messages.lo `test -f 'd2_messages.cc' || echo '$(srcdir)/'`d2_messages.cc + +libkea_d2srv_la-d2_update_message.lo: d2_update_message.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_d2srv_la-d2_update_message.lo -MD -MP -MF $(DEPDIR)/libkea_d2srv_la-d2_update_message.Tpo -c -o libkea_d2srv_la-d2_update_message.lo `test -f 'd2_update_message.cc' || echo '$(srcdir)/'`d2_update_message.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_d2srv_la-d2_update_message.Tpo $(DEPDIR)/libkea_d2srv_la-d2_update_message.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_update_message.cc' object='libkea_d2srv_la-d2_update_message.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_d2srv_la-d2_update_message.lo `test -f 'd2_update_message.cc' || echo '$(srcdir)/'`d2_update_message.cc + +libkea_d2srv_la-d2_simple_parser.lo: d2_simple_parser.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_d2srv_la-d2_simple_parser.lo -MD -MP -MF $(DEPDIR)/libkea_d2srv_la-d2_simple_parser.Tpo -c -o libkea_d2srv_la-d2_simple_parser.lo `test -f 'd2_simple_parser.cc' || echo '$(srcdir)/'`d2_simple_parser.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_d2srv_la-d2_simple_parser.Tpo $(DEPDIR)/libkea_d2srv_la-d2_simple_parser.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_simple_parser.cc' object='libkea_d2srv_la-d2_simple_parser.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_d2srv_la-d2_simple_parser.lo `test -f 'd2_simple_parser.cc' || echo '$(srcdir)/'`d2_simple_parser.cc + +libkea_d2srv_la-d2_stats.lo: d2_stats.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_d2srv_la-d2_stats.lo -MD -MP -MF $(DEPDIR)/libkea_d2srv_la-d2_stats.Tpo -c -o libkea_d2srv_la-d2_stats.lo `test -f 'd2_stats.cc' || echo '$(srcdir)/'`d2_stats.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_d2srv_la-d2_stats.Tpo $(DEPDIR)/libkea_d2srv_la-d2_stats.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_stats.cc' object='libkea_d2srv_la-d2_stats.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_d2srv_la-d2_stats.lo `test -f 'd2_stats.cc' || echo '$(srcdir)/'`d2_stats.cc + +libkea_d2srv_la-d2_tsig_key.lo: d2_tsig_key.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_d2srv_la-d2_tsig_key.lo -MD -MP -MF $(DEPDIR)/libkea_d2srv_la-d2_tsig_key.Tpo -c -o libkea_d2srv_la-d2_tsig_key.lo `test -f 'd2_tsig_key.cc' || echo '$(srcdir)/'`d2_tsig_key.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_d2srv_la-d2_tsig_key.Tpo $(DEPDIR)/libkea_d2srv_la-d2_tsig_key.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_tsig_key.cc' object='libkea_d2srv_la-d2_tsig_key.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_d2srv_la-d2_tsig_key.lo `test -f 'd2_tsig_key.cc' || echo '$(srcdir)/'`d2_tsig_key.cc + +libkea_d2srv_la-d2_zone.lo: d2_zone.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_d2srv_la-d2_zone.lo -MD -MP -MF $(DEPDIR)/libkea_d2srv_la-d2_zone.Tpo -c -o libkea_d2srv_la-d2_zone.lo `test -f 'd2_zone.cc' || echo '$(srcdir)/'`d2_zone.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_d2srv_la-d2_zone.Tpo $(DEPDIR)/libkea_d2srv_la-d2_zone.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_zone.cc' object='libkea_d2srv_la-d2_zone.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_d2srv_la-d2_zone.lo `test -f 'd2_zone.cc' || echo '$(srcdir)/'`d2_zone.cc + +libkea_d2srv_la-dns_client.lo: dns_client.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_d2srv_la-dns_client.lo -MD -MP -MF $(DEPDIR)/libkea_d2srv_la-dns_client.Tpo -c -o libkea_d2srv_la-dns_client.lo `test -f 'dns_client.cc' || echo '$(srcdir)/'`dns_client.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_d2srv_la-dns_client.Tpo $(DEPDIR)/libkea_d2srv_la-dns_client.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dns_client.cc' object='libkea_d2srv_la-dns_client.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_d2srv_la-dns_client.lo `test -f 'dns_client.cc' || echo '$(srcdir)/'`dns_client.cc + +libkea_d2srv_la-nc_trans.lo: nc_trans.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_d2srv_la-nc_trans.lo -MD -MP -MF $(DEPDIR)/libkea_d2srv_la-nc_trans.Tpo -c -o libkea_d2srv_la-nc_trans.lo `test -f 'nc_trans.cc' || echo '$(srcdir)/'`nc_trans.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_d2srv_la-nc_trans.Tpo $(DEPDIR)/libkea_d2srv_la-nc_trans.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nc_trans.cc' object='libkea_d2srv_la-nc_trans.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_d2srv_la_CPPFLAGS) $(CPPFLAGS) $(libkea_d2srv_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_d2srv_la-nc_trans.lo `test -f 'nc_trans.cc' || echo '$(srcdir)/'`nc_trans.cc + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-libkea_d2srv_includeHEADERS: $(libkea_d2srv_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(libkea_d2srv_include_HEADERS)'; test -n "$(libkea_d2srv_includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libkea_d2srv_includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libkea_d2srv_includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libkea_d2srv_includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libkea_d2srv_includedir)" || exit $$?; \ + done + +uninstall-libkea_d2srv_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libkea_d2srv_include_HEADERS)'; test -n "$(libkea_d2srv_includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libkea_d2srv_includedir)'; $(am__uninstall_files_from_dir) + +# 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 + +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 +check: check-recursive +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libkea_d2srv_includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +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-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_cfg_mgr.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_config.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_log.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_messages.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_simple_parser.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_stats.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_tsig_key.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_update_message.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_zone.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-dns_client.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-nc_trans.Plo + -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-libkea_d2srv_includeHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +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)/libkea_d2srv_la-d2_cfg_mgr.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_config.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_log.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_messages.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_simple_parser.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_stats.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_tsig_key.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_update_message.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-d2_zone.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-dns_client.Plo + -rm -f ./$(DEPDIR)/libkea_d2srv_la-nc_trans.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic \ + maintainer-clean-local + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES \ + uninstall-libkea_d2srv_includeHEADERS + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool 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-libLTLIBRARIES \ + install-libkea_d2srv_includeHEADERS 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 \ + maintainer-clean-local mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \ + uninstall-libkea_d2srv_includeHEADERS + +.PRECIOUS: Makefile + + +# If we want to get rid of all generated messages files, we need to use +# make maintainer-clean. The proper way to introduce custom commands for +# that operation is to define maintainer-clean-local target. However, +# make maintainer-clean also removes Makefile, so running configure script +# is required. To make it easy to rebuild messages without going through +# reconfigure, a new target messages-clean has been added. +maintainer-clean-local: + rm -f d2_messages.h d2_messages.cc + +# To regenerate messages files, one can do: +# +# make messages-clean +# make messages +# +# This is needed only when a .mes file is modified. +messages-clean: maintainer-clean-local + +# Define rule to build logging source files from message file +@GENERATE_MESSAGES_TRUE@messages: d2_messages.h d2_messages.cc +@GENERATE_MESSAGES_TRUE@ @echo Message files regenerated + +@GENERATE_MESSAGES_TRUE@d2_messages.h d2_messages.cc: d2_messages.mes +@GENERATE_MESSAGES_TRUE@ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/d2srv/d2_messages.mes + +@GENERATE_MESSAGES_FALSE@messages d2_messages.h d2_messages.cc: +@GENERATE_MESSAGES_FALSE@ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. + +# 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/d2_cfg_mgr.cc b/src/lib/d2srv/d2_cfg_mgr.cc new file mode 100644 index 0000000..351b9ca --- /dev/null +++ b/src/lib/d2srv/d2_cfg_mgr.cc @@ -0,0 +1,326 @@ +// Copyright (C) 2014-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 + +#include +#include +#include +#include +#include + +#include + +using namespace isc::asiolink; +using namespace isc::config; +using namespace isc::data; +using namespace isc::process; + +namespace isc { +namespace d2 { + +namespace { + +typedef std::vector ByteAddress; + +} // end of unnamed namespace + +// *********************** D2CfgContext ************************* + +D2CfgContext::D2CfgContext() + : d2_params_(new D2Params()), + forward_mgr_(new DdnsDomainListMgr("forward-ddns")), + reverse_mgr_(new DdnsDomainListMgr("reverse-ddns")), + keys_(new TSIGKeyInfoMap()), + control_socket_(ConstElementPtr()) { +} + +D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : ConfigBase(rhs) { + d2_params_ = rhs.d2_params_; + if (rhs.forward_mgr_) { + forward_mgr_.reset(new DdnsDomainListMgr(rhs.forward_mgr_->getName())); + forward_mgr_->setDomains(rhs.forward_mgr_->getDomains()); + } + + if (rhs.reverse_mgr_) { + reverse_mgr_.reset(new DdnsDomainListMgr(rhs.reverse_mgr_->getName())); + reverse_mgr_->setDomains(rhs.reverse_mgr_->getDomains()); + } + + keys_ = rhs.keys_; + + control_socket_ = rhs.control_socket_; + + hooks_config_ = rhs.hooks_config_; +} + +D2CfgContext::~D2CfgContext() { +} + +ElementPtr +D2CfgContext::toElement() const { + ElementPtr d2 = ConfigBase::toElement(); + // Set user-context + contextToElement(d2); + // Set ip-address + const IOAddress& ip_address = d2_params_->getIpAddress(); + d2->set("ip-address", Element::create(ip_address.toText())); + // Set port + size_t port = d2_params_->getPort(); + d2->set("port", Element::create(static_cast(port))); + // Set dns-server-timeout + size_t dns_server_timeout = d2_params_->getDnsServerTimeout(); + d2->set("dns-server-timeout", + Element::create(static_cast(dns_server_timeout))); + // Set ncr-protocol + const dhcp_ddns::NameChangeProtocol& ncr_protocol = + d2_params_->getNcrProtocol(); + d2->set("ncr-protocol", + Element::create(dhcp_ddns::ncrProtocolToString(ncr_protocol))); + // Set ncr-format + const dhcp_ddns::NameChangeFormat& ncr_format = d2_params_->getNcrFormat(); + d2->set("ncr-format", + Element::create(dhcp_ddns::ncrFormatToString(ncr_format))); + // Set forward-ddns + ElementPtr forward_ddns = Element::createMap(); + forward_ddns->set("ddns-domains", forward_mgr_->toElement()); + d2->set("forward-ddns", forward_ddns); + // Set reverse-ddns + ElementPtr reverse_ddns = Element::createMap(); + reverse_ddns->set("ddns-domains", reverse_mgr_->toElement()); + d2->set("reverse-ddns", reverse_ddns); + // Set tsig-keys + ElementPtr tsig_keys = Element::createList(); + for (TSIGKeyInfoMap::const_iterator key = keys_->begin(); + key != keys_->end(); ++key) { + tsig_keys->add(key->second->toElement()); + } + d2->set("tsig-keys", tsig_keys); + // Set control-socket (skip if null as empty is not legal) + if (!isNull(control_socket_)) { + d2->set("control-socket", UserContext::toElement(control_socket_)); + } + // Set hooks-libraries + d2->set("hooks-libraries", hooks_config_.toElement()); + // Set DhcpDdns + ElementPtr result = Element::createMap(); + result->set("DhcpDdns", d2); + + return (result); +} + +// *********************** D2CfgMgr ************************* + +const char* D2CfgMgr::IPV4_REV_ZONE_SUFFIX = "in-addr.arpa."; + +const char* D2CfgMgr::IPV6_REV_ZONE_SUFFIX = "ip6.arpa."; + +D2CfgMgr::D2CfgMgr() : DCfgMgrBase(ConfigPtr(new D2CfgContext())) { +} + +D2CfgMgr::~D2CfgMgr() { +} + +ConfigPtr +D2CfgMgr::createNewContext() { + return (ConfigPtr(new D2CfgContext())); +} + +bool +D2CfgMgr::forwardUpdatesEnabled() { + // Forward updates are not enabled if no forward servers are defined. + return (getD2CfgContext()->getForwardMgr()->size() > 0); +} + +bool +D2CfgMgr::reverseUpdatesEnabled() { + // Reverse updates are not enabled if no reverse servers are defined. + return (getD2CfgContext()->getReverseMgr()->size() > 0); +} + +bool +D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) { + if (fqdn.empty()) { + // This is a programmatic error and should not happen. + isc_throw(D2CfgError, "matchForward passed an empty fqdn"); + } + + // Fetch the forward manager from the D2 context. + DdnsDomainListMgrPtr mgr = getD2CfgContext()->getForwardMgr(); + + // Call the manager's match method and return the result. + return (mgr->matchDomain(fqdn, domain)); +} + +bool +D2CfgMgr::matchReverse(const std::string& ip_address, DdnsDomainPtr& domain) { + // Note, reverseIpAddress will throw if the ip_address is invalid. + std::string reverse_address = reverseIpAddress(ip_address); + + // Fetch the reverse manager from the D2 context. + DdnsDomainListMgrPtr mgr = getD2CfgContext()->getReverseMgr(); + + return (mgr->matchDomain(reverse_address, domain)); +} + +std::string +D2CfgMgr::reverseIpAddress(const std::string& address) { + try { + // Convert string address into an IOAddress and invoke the + // appropriate reverse method. + isc::asiolink::IOAddress ioaddr(address); + if (ioaddr.isV4()) { + return (reverseV4Address(ioaddr)); + } + + return (reverseV6Address(ioaddr)); + + } catch (const isc::Exception& ex) { + isc_throw(D2CfgError, "D2CfgMgr cannot reverse address: " + << address << " : " << ex.what()); + } +} + +std::string +D2CfgMgr::reverseV4Address(const isc::asiolink::IOAddress& ioaddr) { + if (!ioaddr.isV4()) { + isc_throw(D2CfgError, "D2CfgMgr address is not IPv4 address :" + << ioaddr); + } + + // Get the address in byte vector form. + const ByteAddress bytes = ioaddr.toBytes(); + + // Walk backwards through vector outputting each octet and a dot. + std::ostringstream stream; + + // We have to set the following variable to get + // const_reverse_iterator type of rend(), otherwise Solaris GCC + // complains on operator!= by trying to use the non-const variant. + const ByteAddress::const_reverse_iterator end = bytes.rend(); + + for (ByteAddress::const_reverse_iterator rit = bytes.rbegin(); + rit != end; + ++rit) + { + stream << static_cast(*rit) << "."; + } + + // Tack on the suffix and we're done. + stream << IPV4_REV_ZONE_SUFFIX; + return(stream.str()); +} + +std::string +D2CfgMgr::reverseV6Address(const isc::asiolink::IOAddress& ioaddr) { + if (!ioaddr.isV6()) { + isc_throw(D2CfgError, "D2Cfg address is not IPv6 address: " << ioaddr); + } + + // Turn the address into a string of digits. + const ByteAddress bytes = ioaddr.toBytes(); + const std::string digits = isc::util::encode::encodeHex(bytes); + + // Walk backwards through string outputting each digits and a dot. + std::ostringstream stream; + + // We have to set the following variable to get + // const_reverse_iterator type of rend(), otherwise Solaris GCC + // complains on operator!= by trying to use the non-const variant. + const std::string::const_reverse_iterator end = digits.rend(); + + for (std::string::const_reverse_iterator rit = digits.rbegin(); + rit != end; + ++rit) + { + stream << static_cast(*rit) << "."; + } + + // Tack on the suffix and we're done. + stream << IPV6_REV_ZONE_SUFFIX; + return(stream.str()); +} + +const D2ParamsPtr& +D2CfgMgr::getD2Params() { + return (getD2CfgContext()->getD2Params()); +} + +const isc::data::ConstElementPtr +D2CfgMgr::getControlSocketInfo() { + return (getD2CfgContext()->getControlSocketInfo()); +} + +std::string +D2CfgMgr::getConfigSummary(const uint32_t) { + return (getD2Params()->getConfigSummary()); +} + +void +D2CfgMgr::setCfgDefaults(ElementPtr mutable_config) { + D2SimpleParser::setAllDefaults(mutable_config); +} + +isc::data::ConstElementPtr +D2CfgMgr::parse(isc::data::ConstElementPtr config_set, bool check_only) { + // Do a sanity check first. + if (!config_set) { + isc_throw(D2CfgError, "Mandatory config parameter not provided"); + } + + D2CfgContextPtr ctx = getD2CfgContext(); + + // Set the defaults + ElementPtr cfg = boost::const_pointer_cast(config_set); + D2SimpleParser::setAllDefaults(cfg); + + // And parse the configuration. + ConstElementPtr answer; + std::string excuse; + try { + // Do the actual parsing + D2SimpleParser parser; + parser.parse(ctx, cfg, check_only); + } catch (const isc::Exception& ex) { + excuse = ex.what(); + answer = createAnswer(CONTROL_RESULT_ERROR, excuse); + } catch (...) { + excuse = "undefined configuration parsing error"; + answer = createAnswer(CONTROL_RESULT_ERROR, excuse); + } + + // At this stage the answer was created only in case of exception. + if (answer) { + if (check_only) { + LOG_ERROR(d2_logger, DHCP_DDNS_CONFIG_CHECK_FAIL).arg(excuse); + } else { + LOG_ERROR(d2_logger, DHCP_DDNS_CONFIG_FAIL).arg(excuse); + } + return (answer); + } + + if (check_only) { + answer = createAnswer(CONTROL_RESULT_SUCCESS, + "Configuration check successful"); + } else { + answer = createAnswer(CONTROL_RESULT_SUCCESS, + "Configuration applied successfully."); + } + + return (answer); +} + +std::list> +D2CfgMgr::jsonPathsToRedact() const { + static std::list> const list({ + {"tsig-keys", "[]"}, + {"hooks-libraries", "[]", "parameters", "*"}, + }); + return list; +} + +} // namespace d2 +} // namespace isc diff --git a/src/lib/d2srv/d2_cfg_mgr.h b/src/lib/d2srv/d2_cfg_mgr.h new file mode 100644 index 0000000..eade86c --- /dev/null +++ b/src/lib/d2srv/d2_cfg_mgr.h @@ -0,0 +1,339 @@ +// Copyright (C) 2014-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/. + +#ifndef D2_CFG_MGR_H +#define D2_CFG_MGR_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace isc { +namespace d2 { + +class D2CfgContext; +/// @brief Pointer to a configuration context. +typedef boost::shared_ptr D2CfgContextPtr; + +/// @brief DHCP-DDNS Configuration Context +/// +/// Implements the storage container for configuration context. +/// It provides a single enclosure for the storage of configuration parameters +/// and any other DHCP-DDNS specific information that needs to be accessible +/// during configuration parsing as well as to the application as a whole. +/// It is derived from the context base class, ConfigBase. +class D2CfgContext : public process::ConfigBase { +public: + /// @brief Constructor + D2CfgContext(); + + /// @brief Destructor + virtual ~D2CfgContext(); + + /// @brief Creates a clone of this context object. + /// + /// @return returns a pointer to the new clone. + virtual process::ConfigPtr clone() { + return (process::ConfigPtr(new D2CfgContext(*this))); + } + + /// @brief Fetches a reference to the D2Params + D2ParamsPtr& getD2Params() { + return (d2_params_); + } + + /// @brief Fetches the forward DNS domain list manager. + /// + /// @return returns a pointer to the forward manager. + DdnsDomainListMgrPtr getForwardMgr() { + return (forward_mgr_); + } + + /// @brief Sets the forward domain list manager + /// @param forward_mgr pointer to the new forward manager + void setForwardMgr(DdnsDomainListMgrPtr forward_mgr) { + forward_mgr_ = forward_mgr; + } + + /// @brief Fetches the reverse DNS domain list manager. + /// + /// @return returns a pointer to the reverse manager. + DdnsDomainListMgrPtr getReverseMgr() { + return (reverse_mgr_); + } + + /// @brief Sets the reverse domain list manager + /// @param reverse_mgr pointer to the new reverse manager + void setReverseMgr(DdnsDomainListMgrPtr reverse_mgr) { + reverse_mgr_ = reverse_mgr; + } + + /// @brief Fetches the map of TSIG keys. + /// + /// @return returns a pointer to the key map. + TSIGKeyInfoMapPtr getKeys() { + return (keys_); + } + + /// @brief Sets the map of TSIG keys + /// + /// @param keys pointer to the new TSIG key map + void setKeys(const TSIGKeyInfoMapPtr& keys) { + keys_ = keys; + } + + /// @brief Returns information about control socket + /// @return pointer to the Element that holds control-socket map + const isc::data::ConstElementPtr getControlSocketInfo() const { + return (control_socket_); + } + + /// @brief Sets information about the control socket + /// @param control_socket Element that holds control-socket map + void setControlSocketInfo(const isc::data::ConstElementPtr& control_socket) { + control_socket_ = control_socket; + } + + /// @brief Returns non-const reference to configured hooks libraries. + /// + /// @return non-const reference to configured hooks libraries. + isc::hooks::HooksConfig& getHooksConfig() { + return (hooks_config_); + } + + /// @brief Returns const reference to configured hooks libraries. + /// + /// @return const reference to configured hooks libraries. + const isc::hooks::HooksConfig& getHooksConfig() const { + return (hooks_config_); + } + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + +protected: + /// @brief Copy constructor for use by derivations in clone(). + D2CfgContext(const D2CfgContext& rhs); + +private: + /// @brief Private assignment operator to avoid potential for slicing. + D2CfgContext& operator=(const D2CfgContext& rhs); + + /// @brief Global level parameter storage + D2ParamsPtr d2_params_; + + /// @brief Forward domain list manager. + DdnsDomainListMgrPtr forward_mgr_; + + /// @brief Reverse domain list manager. + DdnsDomainListMgrPtr reverse_mgr_; + + /// @brief Storage for the map of TSIGKeyInfos. + TSIGKeyInfoMapPtr keys_; + + /// @brief Pointer to the control-socket information. + isc::data::ConstElementPtr control_socket_; + + /// @brief Configured hooks libraries. + isc::hooks::HooksConfig hooks_config_; +}; + +/// @brief Defines a pointer for DdnsDomain instances. +typedef boost::shared_ptr DdnsDomainListMgrPtr; + +/// @brief DHCP-DDNS Configuration Manager +/// +/// Provides the mechanisms for managing the DHCP-DDNS application's +/// configuration. This includes services for parsing sets of configuration +/// values, storing the parsed information in its converted form, +/// and retrieving the information on demand. +class D2CfgMgr : public process::DCfgMgrBase { +public: + /// @brief Reverse zone suffix added to IPv4 addresses for reverse lookups + /// @todo This should be configurable. + static const char* IPV4_REV_ZONE_SUFFIX; + + /// @brief Reverse zone suffix added to IPv6 addresses for reverse lookups + /// @todo This should be configurable. + static const char* IPV6_REV_ZONE_SUFFIX; + + /// @brief Constructor + D2CfgMgr(); + + /// @brief Destructor + virtual ~D2CfgMgr(); + + /// @brief Convenience method that returns the D2 configuration context. + /// + /// @return returns a pointer to the configuration context. + D2CfgContextPtr getD2CfgContext() { + return (boost::dynamic_pointer_cast(getContext())); + } + + /// @brief Returns whether or not forward updates are enabled. + /// + /// This method currently uses the presence or absence of Forward DDNS + /// Domains to determine if forward updates are enabled or disabled. + /// @todo This could be expanded to include the check of a configurable + /// boolean value. + /// + /// @return true if forward updates are enabled, false otherwise. + bool forwardUpdatesEnabled(); + + /// @brief Returns whether or not reverse updates are enabled. + /// + /// This method currently uses the presence or absence of Reverse DDNS + /// Domains to determine if reverse updates are enabled or disabled. + /// @todo This could be expanded to include the check of a configurable + /// boolean value. + /// + /// @return true if reverse updates are enabled, false otherwise. + bool reverseUpdatesEnabled(); + + /// @brief Matches a given FQDN to a forward domain. + /// + /// This calls the matchDomain method of the forward domain manager to + /// match the given FQDN to a forward domain. + /// + /// @param fqdn is the name for which to look. + /// @param domain receives the matching domain. Note that it will be reset + /// upon entry and only set if a match is subsequently found. + /// + /// @return returns true if a match is found, false otherwise. + /// @throw throws D2CfgError if given an invalid fqdn. + bool matchForward(const std::string& fqdn, DdnsDomainPtr& domain); + + /// @brief Matches a given IP address to a reverse domain. + /// + /// This calls the matchDomain method of the reverse domain manager to + /// match the given IPv4 or IPv6 address to a reverse domain. + /// + /// @param ip_address is the name for which to look. + /// @param domain receives the matching domain. Note that it will be reset + /// upon entry and only set if a match is subsequently found. + /// + /// @return returns true if a match is found, false otherwise. + /// @throw throws D2CfgError if given an invalid fqdn. + bool matchReverse(const std::string& ip_address, DdnsDomainPtr& domain); + + /// @brief Generate a reverse order string for the given IP address + /// + /// This method creates a string containing the given IP address + /// contents in reverse order. This format is used for matching + /// against reverse DDNS domains in DHCP_DDNS configuration. + /// After reversing the syllables of the address, it appends the + /// appropriate suffix. + /// + /// @param address string containing a valid IPv4 or IPv6 address. + /// + /// @return a std::string containing the reverse order address. + /// + /// @throw D2CfgError if given an invalid address. + static std::string reverseIpAddress(const std::string& address); + + /// @brief Generate a reverse order string for the given IP address + /// + /// This method creates a string containing the given IP address + /// contents in reverse order. This format is used for matching + /// against reverse DDNS domains in DHCP_DDNS configuration. + /// After reversing the syllables of the address, it appends the + /// appropriate suffix. + /// + /// Example: + /// input: 192.168.1.15 + /// output: 15.1.168.192.in-addr.arpa. + /// + /// @param ioaddr is the IPv4 IOaddress to convert + /// + /// @return a std::string containing the reverse order address. + /// + /// @throw D2CfgError if not given an IPv4 address. + static std::string reverseV4Address(const isc::asiolink::IOAddress& ioaddr); + + /// @brief Generate a reverse order string for the given IP address + /// + /// This method creates a string containing the given IPv6 address + /// contents in reverse order. This format is used for matching + /// against reverse DDNS domains in DHCP_DDNS configuration. + /// After reversing the syllables of the address, it appends the + /// appropriate suffix. + /// + /// IPv6 example: + /// input: 2001:db8:302:99:: + /// output: + ///0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.9.0.0.2.0.3.0.8.B.D.0.1.0.0.2.ip6.arpa. + /// + /// @param ioaddr string containing a valid IPv6 address. + /// + /// @return a std::string containing the reverse order address. + /// + /// @throw D2CfgError if not given an IPv6 address. + static std::string reverseV6Address(const isc::asiolink::IOAddress& ioaddr); + + /// @brief Convenience method fetches the D2Params from context + /// @return reference to const D2ParamsPtr + const D2ParamsPtr& getD2Params(); + + /// @brief Convenience method fetches information about control socket + /// from context + /// @return pointer to the Element that holds control-socket map + const isc::data::ConstElementPtr getControlSocketInfo(); + + /// @brief Returns configuration summary in the textual format. + /// + /// @param selection Bitfield which describes the parts of the configuration + /// to be returned. This parameter is ignored for the D2. + /// + /// @return Summary of the configuration in the textual format. + virtual std::string getConfigSummary(const uint32_t selection) override; + + std::list> jsonPathsToRedact() const final override; + +protected: + /// @brief Parses configuration of the D2. + /// + /// @param config Pointer to a configuration specified for D2. + /// @param check_only Boolean flag indicating if this method should + /// only verify correctness of the provided configuration. + /// @return Pointer to a result of configuration parsing. + virtual isc::data::ConstElementPtr + parse(isc::data::ConstElementPtr config, bool check_only) override; + + /// @brief Adds default values to the given config + /// + /// Adds the D2 default values to the configuration Element map. This + /// method is invoked by @c DCfgMgrBase::parseConfig(). + /// + /// @param mutable_config - configuration to which defaults should be added + virtual void setCfgDefaults(isc::data::ElementPtr mutable_config) override; + + /// @brief Creates an new, blank D2CfgContext context + /// + /// This method is used at the beginning of configuration process to + /// create a fresh, empty copy of a D2CfgContext. This new context will + /// be populated during the configuration process and will replace the + /// existing context provided the configuration process completes without + /// error. + /// + /// @return Returns a ConfigPtr to the new context instance. + virtual process::ConfigPtr createNewContext() override; +}; + +/// @brief Defines a shared pointer to D2CfgMgr. +typedef boost::shared_ptr D2CfgMgrPtr; + +} // end of isc::d2 namespace +} // end of isc namespace + +#endif // D2_CFG_MGR_H diff --git a/src/lib/d2srv/d2_config.cc b/src/lib/d2srv/d2_config.cc new file mode 100644 index 0000000..21c7bf4 --- /dev/null +++ b/src/lib/d2srv/d2_config.cc @@ -0,0 +1,674 @@ +// 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 + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +using namespace isc::process; +using namespace isc::data; + +namespace isc { +namespace d2 { + +// *********************** D2Params ************************* + +D2Params::D2Params(const isc::asiolink::IOAddress& ip_address, + const size_t port, + const size_t dns_server_timeout, + const dhcp_ddns::NameChangeProtocol& ncr_protocol, + const dhcp_ddns::NameChangeFormat& ncr_format) + : ip_address_(ip_address), + port_(port), + dns_server_timeout_(dns_server_timeout), + ncr_protocol_(ncr_protocol), + ncr_format_(ncr_format) { + validateContents(); +} + +D2Params::D2Params() + : ip_address_(isc::asiolink::IOAddress("127.0.0.1")), + port_(53001), dns_server_timeout_(100), + ncr_protocol_(dhcp_ddns::NCR_UDP), + ncr_format_(dhcp_ddns::FMT_JSON) { + validateContents(); +} + +D2Params::~D2Params(){}; + +void +D2Params::validateContents() { + if ((ip_address_.toText() == "0.0.0.0") || (ip_address_.toText() == "::")) { + isc_throw(D2CfgError, + "D2Params: IP address cannot be \"" << ip_address_ << "\""); + } + + if (port_ == 0) { + isc_throw(D2CfgError, "D2Params: port cannot be 0"); + } + + if (dns_server_timeout_ < 1) { + isc_throw(D2CfgError, + "D2Params: DNS server timeout must be larger than 0"); + } + + if (ncr_format_ != dhcp_ddns::FMT_JSON) { + isc_throw(D2CfgError, "D2Params: NCR Format:" + << dhcp_ddns::ncrFormatToString(ncr_format_) + << " is not yet supported"); + } + + if (ncr_protocol_ != dhcp_ddns::NCR_UDP) { + isc_throw(D2CfgError, "D2Params: NCR Protocol:" + << dhcp_ddns::ncrProtocolToString(ncr_protocol_) + << " is not yet supported"); + } +} + +std::string +D2Params::getConfigSummary() const { + std::ostringstream s; + s << "listening on " << getIpAddress() << ", port " << getPort() + << ", using " << ncrProtocolToString(ncr_protocol_); + return (s.str()); +} + +bool +D2Params::operator == (const D2Params& other) const { + return ((ip_address_ == other.ip_address_) && + (port_ == other.port_) && + (dns_server_timeout_ == other.dns_server_timeout_) && + (ncr_protocol_ == other.ncr_protocol_) && + (ncr_format_ == other.ncr_format_)); +} + +bool +D2Params::operator != (const D2Params& other) const { + return (!(*this == other)); +} + +std::string +D2Params::toText() const { + std::ostringstream stream; + + stream << ", ip-address: " << ip_address_.toText() + << ", port: " << port_ + << ", dns-server-timeout_: " << dns_server_timeout_ + << ", ncr-protocol: " + << dhcp_ddns::ncrProtocolToString(ncr_protocol_) + << ", ncr-format: " << ncr_format_ + << dhcp_ddns::ncrFormatToString(ncr_format_); + + return (stream.str()); +} + +std::ostream& +operator<<(std::ostream& os, const D2Params& config) { + os << config.toText(); + return (os); +} + +// *********************** TSIGKeyInfo ************************* +// Note these values match corresponding values for Bind9's +// dnssec-keygen +const char* TSIGKeyInfo::HMAC_MD5_STR = "HMAC-MD5"; +const char* TSIGKeyInfo::HMAC_SHA1_STR = "HMAC-SHA1"; +const char* TSIGKeyInfo::HMAC_SHA224_STR = "HMAC-SHA224"; +const char* TSIGKeyInfo::HMAC_SHA256_STR = "HMAC-SHA256"; +const char* TSIGKeyInfo::HMAC_SHA384_STR = "HMAC-SHA384"; +const char* TSIGKeyInfo::HMAC_SHA512_STR = "HMAC-SHA512"; + +TSIGKeyInfo::TSIGKeyInfo(const std::string& name, const std::string& algorithm, + const std::string& secret, uint32_t digestbits) + :name_(name), algorithm_(algorithm), secret_(secret), + digestbits_(digestbits), tsig_key_() { + remakeKey(); +} + +TSIGKeyInfo::~TSIGKeyInfo() { +} + +const dns::Name& +TSIGKeyInfo::stringToAlgorithmName(const std::string& algorithm_id) { + if (boost::iequals(algorithm_id, HMAC_MD5_STR)) { + return (dns::TSIGKey::HMACMD5_NAME()); + } else if (boost::iequals(algorithm_id, HMAC_SHA1_STR)) { + return (dns::TSIGKey::HMACSHA1_NAME()); + } else if (boost::iequals(algorithm_id, HMAC_SHA224_STR)) { + return (dns::TSIGKey::HMACSHA224_NAME()); + } else if (boost::iequals(algorithm_id, HMAC_SHA256_STR)) { + return (dns::TSIGKey::HMACSHA256_NAME()); + } else if (boost::iequals(algorithm_id, HMAC_SHA384_STR)) { + return (dns::TSIGKey::HMACSHA384_NAME()); + } else if (boost::iequals(algorithm_id, HMAC_SHA512_STR)) { + return (dns::TSIGKey::HMACSHA512_NAME()); + } + + isc_throw(BadValue, "Unknown TSIG Key algorithm: " << algorithm_id); +} + +void +TSIGKeyInfo::remakeKey() { + try { + // Since our secret value is base64 encoded already, we need to + // build the input string for the appropriate D2TsigKey constructor. + // If secret isn't a valid base64 value, the constructor will throw. + std::ostringstream stream; + stream << dns::Name(name_).toText() << ":" + << secret_ << ":" + << stringToAlgorithmName(algorithm_); + if (digestbits_ > 0) { + stream << ":" << digestbits_; + } + + tsig_key_.reset(new D2TsigKey(stream.str())); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, "Cannot make D2TsigKey: " << ex.what()); + } +} + +ElementPtr +TSIGKeyInfo::toElement() const { + ElementPtr result = Element::createMap(); + // Set user-context + contextToElement(result); + // Set name + result->set("name", Element::create(name_)); + // Set algorithm + result->set("algorithm", Element::create(algorithm_)); + // Set secret + result->set("secret", Element::create(secret_)); + // Set digest-bits + result->set("digest-bits", + Element::create(static_cast(digestbits_))); + + return (result); +} + +// *********************** DnsServerInfo ************************* +DnsServerInfo::DnsServerInfo(const std::string& hostname, + isc::asiolink::IOAddress ip_address, + uint32_t port, + bool enabled, + const TSIGKeyInfoPtr& tsig_key_info, + bool inherited_key) + : hostname_(hostname), ip_address_(ip_address), port_(port), + enabled_(enabled), tsig_key_info_(tsig_key_info), + inherited_key_(inherited_key) { +} + +DnsServerInfo::~DnsServerInfo() { +} + +const std::string +DnsServerInfo::getKeyName() const { + if (tsig_key_info_) { + return (tsig_key_info_->getName()); + } + + return (""); +} + +std::string +DnsServerInfo::toText() const { + std::ostringstream stream; + stream << (getIpAddress().toText()) << " port:" << getPort(); + return (stream.str()); +} + +ElementPtr +DnsServerInfo::toElement() const { + ElementPtr result = Element::createMap(); + // Set user-context + contextToElement(result); + // Set hostname + result->set("hostname", Element::create(hostname_)); + // Set ip-address + result->set("ip-address", Element::create(ip_address_.toText())); + // Set port + result->set("port", Element::create(static_cast(port_))); + // Set key-name + if (tsig_key_info_ && !inherited_key_) { + result->set("key-name", Element::create(tsig_key_info_->getName())); + } + + return (result); +} + +std::ostream& +operator<<(std::ostream& os, const DnsServerInfo& server) { + os << server.toText(); + return (os); +} + +// *********************** DdnsDomain ************************* + +DdnsDomain::DdnsDomain(const std::string& name, + DnsServerInfoStoragePtr servers, + const std::string& key_name) + : name_(name), servers_(servers), key_name_(key_name) { +} + +DdnsDomain::~DdnsDomain() { +} + +ElementPtr +DdnsDomain::toElement() const { + ElementPtr result = Element::createMap(); + // Set user-context + contextToElement(result); + // Set name + result->set("name", Element::create(name_)); + // Set servers + ElementPtr servers = Element::createList(); + for (DnsServerInfoStorage::const_iterator server = servers_->begin(); + server != servers_->end(); ++server) { + ElementPtr dns_server = (*server)->toElement(); + servers->add(dns_server); + } + // the dns server list may not be empty + if (!servers->empty()) { + result->set("dns-servers", servers); + } + // Set key-name + if (!key_name_.empty()) { + result->set("key-name", Element::create(key_name_)); + } + + return (result); +} + +// *********************** DdnsDomainLstMgr ************************* + +const char* DdnsDomainListMgr::wildcard_domain_name_ = "*"; + +DdnsDomainListMgr::DdnsDomainListMgr(const std::string& name) : name_(name), + domains_(new DdnsDomainMap()) { +} + + +DdnsDomainListMgr::~DdnsDomainListMgr () { +} + +void +DdnsDomainListMgr::setDomains(DdnsDomainMapPtr domains) { + if (!domains) { + isc_throw(D2CfgError, + "DdnsDomainListMgr::setDomains: Domain list may not be null"); + } + + domains_ = domains; + + // Look for the wild card domain. If present, set the member variable + // to remember it. This saves us from having to look for it every time + // we attempt a match. + DdnsDomainMap::iterator gotit = domains_->find(wildcard_domain_name_); + if (gotit != domains_->end()) { + wildcard_domain_ = gotit->second; + } +} + +bool +DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) { + // First check the case of one domain to rule them all. + if ((size() == 1) && (wildcard_domain_)) { + domain = wildcard_domain_; + return (true); + } + + // Iterate over the domain map looking for the domain which matches + // the longest portion of the given fqdn. + + size_t req_len = fqdn.size(); + size_t match_len = 0; + DdnsDomainMapPair map_pair; + DdnsDomainPtr best_match; + BOOST_FOREACH (map_pair, *domains_) { + std::string domain_name = map_pair.first; + size_t dom_len = domain_name.size(); + + // If the domain name is longer than the fqdn, then it cant be match. + if (req_len < dom_len) { + continue; + } + + // If the lengths are identical and the names match we're done. + if (req_len == dom_len) { + if (boost::iequals(fqdn, domain_name)) { + // exact match, done + domain = map_pair.second; + return (true); + } + } else { + // The fqdn is longer than the domain name. Adjust the start + // point of comparison by the excess in length. Only do the + // comparison if the adjustment lands on a boundary. This + // prevents "onetwo.net" from matching "two.net". + size_t offset = req_len - dom_len; + if ((fqdn[offset - 1] == '.') && + (boost::iequals(fqdn.substr(offset), domain_name))) { + // Fqdn contains domain name, keep it if its better than + // any we have matched so far. + if (dom_len > match_len) { + match_len = dom_len; + best_match = map_pair.second; + } + } + } + } + + if (!best_match) { + // There's no match. If they specified a wild card domain use it + // otherwise there's no domain for this entry. + if (wildcard_domain_) { + domain = wildcard_domain_; + return (true); + } + + LOG_WARN(dhcp_to_d2_logger, DHCP_DDNS_NO_MATCH).arg(fqdn); + return (false); + } + + domain = best_match; + return (true); +} + +ElementPtr +DdnsDomainListMgr::toElement() const { + ElementPtr result = Element::createList(); + // Iterate on ddns domains + for (DdnsDomainMap::const_iterator domain = domains_->begin(); + domain != domains_->end(); ++domain) { + ElementPtr ddns_domain = domain->second->toElement(); + result->add(ddns_domain); + } + + return (result); +} + +// *************************** PARSERS *********************************** + +// *********************** TSIGKeyInfoParser ************************* + +TSIGKeyInfoPtr +TSIGKeyInfoParser::parse(ConstElementPtr key_config) { + std::string name = getString(key_config, "name"); + std::string algorithm = getString(key_config, "algorithm"); + uint32_t digestbits = getInteger(key_config, "digest-bits"); + std::string secret = getString(key_config, "secret"); + ConstElementPtr user_context = key_config->get("user-context"); + + // Algorithm must be valid. + try { + TSIGKeyInfo::stringToAlgorithmName(algorithm); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, "tsig-key : " << ex.what() + << " (" << getPosition("algorithm", key_config) << ")"); + } + + // Non-zero digest-bits must be an integral number of octets, greater + // than 80 and at least half of the algorithm key length. It defaults + // to zero and JSON parsing ensures it's a multiple of 8. + if ((digestbits > 0) && + ((digestbits < 80) || + (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA224_STR) + && (digestbits < 112)) || + (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA256_STR) + && (digestbits < 128)) || + (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA384_STR) + && (digestbits < 192)) || + (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA512_STR) + && (digestbits < 256)))) { + isc_throw(D2CfgError, "tsig-key: digest-bits too small : (" + << getPosition("digest-bits", key_config) + << ")"); + } + + // Everything should be valid, so create the key instance. + // It is possible for the D2TsigKey constructor to fail such as + // with an invalid secret content. + TSIGKeyInfoPtr key_info; + try { + key_info.reset(new TSIGKeyInfo(name, algorithm, secret, digestbits)); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, ex.what() << " (" + << key_config->getPosition() << ")"); + } + + // Add user-context + if (user_context) { + key_info->setContext(user_context); + } + + return (key_info); +} + +// *********************** TSIGKeyInfoListParser ************************* + +TSIGKeyInfoMapPtr +TSIGKeyInfoListParser::parse(ConstElementPtr key_list) { + TSIGKeyInfoMapPtr keys(new TSIGKeyInfoMap()); + ConstElementPtr key_config; + TSIGKeyInfoParser key_parser; + BOOST_FOREACH(key_config, key_list->listValue()) { + TSIGKeyInfoPtr key = key_parser.parse(key_config); + + // Duplicates are not allowed and should be flagged as an error. + if (keys->find(key->getName()) != keys->end()) { + isc_throw(D2CfgError, "Duplicate TSIG key name specified : " + << key->getName() + << " (" << getPosition("name", key_config) << ")"); + } + + (*keys)[key->getName()] = key; + } + + return (keys); +} + +// *********************** DnsServerInfoParser ************************* + +DnsServerInfoPtr +DnsServerInfoParser::parse(ConstElementPtr server_config, + ConstElementPtr domain_config, + const TSIGKeyInfoMapPtr keys) { + std::string hostname = getString(server_config, "hostname"); + std::string ip_address = getString(server_config, "ip-address"); + uint32_t port = getInteger(server_config, "port"); + std::string key_name = getString(server_config, "key-name"); + ConstElementPtr user_context = server_config->get("user-context"); + + // Key name is optional. If it is not blank, then find the key in the + // list of defined keys. + TSIGKeyInfoPtr tsig_key_info; + bool inherited_key = true; + if (key_name.empty()) { + std::string domain_key_name = getString(domain_config, "key-name"); + if (!domain_key_name.empty()) { + key_name = domain_key_name; + } + } else { + inherited_key = false; + } + if (!key_name.empty()) { + if (keys) { + TSIGKeyInfoMap::iterator kit = keys->find(key_name); + if (kit != keys->end()) { + tsig_key_info = kit->second; + } + } + + if (!tsig_key_info) { + if (inherited_key) { + isc_throw(D2CfgError, "DdnsDomain : specifies an " + << "undefined key: " << key_name << " (" + << getPosition("key-name", domain_config) << ")"); + } else { + isc_throw(D2CfgError, "Dns Server : specifies an " + << "undefined key: " << key_name << " (" + << getPosition("key-name", server_config) << ")"); + } + } + } + + // The configuration must specify one or the other. + if (hostname.empty() == ip_address.empty()) { + isc_throw(D2CfgError, "Dns Server must specify one or the other" + " of hostname or IP address" + << " (" << server_config->getPosition() << ")"); + } + + DnsServerInfoPtr server_info; + if (!hostname.empty()) { + /// @todo when resolvable hostname is supported we create the entry + /// as follows: + /// + /// @code + /// // When hostname is specified, create a valid, blank IOAddress + /// // and then create the DnsServerInfo. + /// serverInfo.reset(new DnsServerInfo(hostname, io_addr, port)); + /// + /// @endcode + /// + /// Resolution will be done prior to connection during transaction + /// processing. + /// Until then we'll throw unsupported. + isc_throw(D2CfgError, "Dns Server : hostname is not yet supported" + << " (" << getPosition("hostname", server_config) << ")"); + } else { + try { + // Create an IOAddress from the IP address string given and then + // create the DnsServerInfo. + isc::asiolink::IOAddress io_addr(ip_address); + server_info.reset(new DnsServerInfo(hostname, io_addr, port, + true, tsig_key_info, + inherited_key)); + } catch (const isc::asiolink::IOError& ex) { + isc_throw(D2CfgError, "Dns Server : invalid IP address : " + << ip_address + << " (" << getPosition("ip-address", server_config) << ")"); + } + } + + // Add user-context + if (user_context) { + server_info->setContext(user_context); + } + + return (server_info); +} + +// *********************** DnsServerInfoListParser ************************* + +DnsServerInfoStoragePtr +DnsServerInfoListParser::parse(ConstElementPtr server_list, + ConstElementPtr domain_config, + const TSIGKeyInfoMapPtr keys) { + DnsServerInfoStoragePtr servers(new DnsServerInfoStorage()); + ConstElementPtr server_config; + DnsServerInfoParser parser; + BOOST_FOREACH(server_config, server_list->listValue()) { + DnsServerInfoPtr server = + parser.parse(server_config, domain_config, keys); + servers->push_back(server); + } + + return (servers); +} + +// *********************** DdnsDomainParser ************************* + +DdnsDomainPtr DdnsDomainParser::parse(ConstElementPtr domain_config, + const TSIGKeyInfoMapPtr keys) { + std::string name = getString(domain_config, "name"); + std::string key_name = getString(domain_config, "key-name"); + ConstElementPtr user_context = domain_config->get("user-context"); + + // Parse the list of DNS servers + ConstElementPtr servers_config; + try { + servers_config = domain_config->get("dns-servers"); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, "DdnsDomain : missing dns-server list" + << " (" << servers_config->getPosition() << ")"); + } + + DnsServerInfoListParser server_parser; + DnsServerInfoStoragePtr servers = + server_parser.parse(servers_config, domain_config, keys); + if (servers->size() == 0) { + isc_throw(D2CfgError, "DNS server list cannot be empty" + << servers_config->getPosition()); + } + + // Instantiate the new domain and add it to domain storage. + DdnsDomainPtr domain(new DdnsDomain(name, servers, key_name)); + + // Add user-context + if (user_context) { + domain->setContext(user_context); + } + + return (domain); +} + +// *********************** DdnsDomainListParser ************************* + +DdnsDomainMapPtr DdnsDomainListParser::parse(ConstElementPtr domain_list, + const TSIGKeyInfoMapPtr keys) { + DdnsDomainMapPtr domains(new DdnsDomainMap()); + DdnsDomainParser parser; + ConstElementPtr domain_config; + BOOST_FOREACH(domain_config, domain_list->listValue()) { + DdnsDomainPtr domain = parser.parse(domain_config, keys); + + // Duplicates are not allowed + if (domains->find(domain->getName()) != domains->end()) { + isc_throw(D2CfgError, "Duplicate domain specified:" + << domain->getName() + << " (" << getPosition("name", domain_config) << ")"); + } + + (*domains)[domain->getName()] = domain; + } + + return (domains); +} + +// *********************** DdnsDomainListMgrParser ************************* + +DdnsDomainListMgrPtr +DdnsDomainListMgrParser::parse(ConstElementPtr mgr_config, + const std::string& mgr_name, + const TSIGKeyInfoMapPtr keys) { + DdnsDomainListMgrPtr mgr(new DdnsDomainListMgr(mgr_name)); + + // Parse the list of domains + ConstElementPtr domains_config = mgr_config->get("ddns-domains"); + if (domains_config) { + DdnsDomainListParser domain_parser; + DdnsDomainMapPtr domains = domain_parser.parse(domains_config, keys); + + // Add the new domain to the domain storage. + mgr->setDomains(domains); + } + + return(mgr); +} + +} // end of isc::dhcp namespace +} // end of isc namespace diff --git a/src/lib/d2srv/d2_config.h b/src/lib/d2srv/d2_config.h new file mode 100644 index 0000000..6162ec5 --- /dev/null +++ b/src/lib/d2srv/d2_config.h @@ -0,0 +1,926 @@ +// 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/. + +#ifndef D2_CONFIG_H +#define D2_CONFIG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace isc { +namespace d2 { + +/// @file d2_config.h +/// @brief A collection of classes for housing and parsing the application +/// configuration necessary for the DHCP-DDNS application (aka D2). +/// +/// This file contains the class declarations for the class hierarchy created +/// from the D2 configuration and the parser classes used to create it. +/// The application configuration consists of a set of scalar parameters, +/// a list of TSIG keys, and two managed lists of domains: one list for +/// forward domains and one list for reverse domains. +/// +/// The key list consists of one or more TSIG keys, each entry described by +/// a name, the algorithm method name, optionally the minimum truncated +/// length, and its secret key component. +/// +/// Each managed domain list consists of a list one or more domains and is +/// represented by the class DdnsDomainListMgr. +/// +/// Each domain consists of a set of scalars parameters and a list of DNS +/// servers which support that domain. Among its scalars, is key_name, which +/// is the name of the TSIG Key to use for with this domain. This value should +/// map to one of the TSIG Keys in the key list. Domains are represented by +/// the class, DdnsDomain. +/// +/// Each server consists of a set of scalars used to describe the server such +/// that the application can carry out DNS update exchanges with it. Servers +/// are represented by the class, DnsServerInfo. +/// +/// The parsing class hierarchy reflects this same scheme. Working top down: +/// +/// A DdnsDomainListMgrParser parses a managed domain list entry. It handles +/// any scalars which belong to the manager as well as creating and invoking a +/// DdnsDomainListParser to parse its list of domain entries. +/// +/// A DdnsDomainListParser creates and invokes a DdnsDomainParser for each +/// domain entry in its list. +/// +/// A DdnsDomainParser handles the scalars which belong to the domain as well as +/// creating and invoking a DnsSeverInfoListParser to parse its list of server +/// entries. +/// +/// A DnsServerInfoListParser creates and invokes a DnsServerInfoParser for +/// each server entry in its list. +/// +/// A DdnsServerInfoParser handles the scalars which belong to the server. +/// The following is sample configuration in JSON form with extra spacing +/// for clarity: +/// +/// @code +/// { +/// "interface" : "eth1" , +/// "ip-address" : "192.168.1.33" , +/// "port" : 88 , +/// "control-socket": +/// { +/// "socket-type": "unix" , +/// "socket-name": "/tmp/kea-ddns-ctrl-socket" +//// }, +/// "tsig-keys": +//// [ +/// { +/// "name": "d2_key.tmark.org" , +/// "algorithm": "md5" , +/// "secret": "0123456989" +/// } +/// ], +/// "forward-ddns" : +/// { +/// "ddns-domains": +/// [ +/// { +/// "name": "tmark.org." , +/// "key-name": "d2_key.tmark.org" , +/// "dns-servers" : +/// [ +/// { "hostname": "fserver.tmark.org" }, +/// { "hostname": "f2server.tmark.org" } +/// ] +/// }, +/// { +/// "name": "pub.tmark.org." , +/// "key-name": "d2_key.tmark.org" , +/// "dns-servers" : +/// [ +/// { "hostname": "f3server.tmark.org" } +/// ] +/// } +/// ] +/// }, +/// "reverse-ddns" : +/// { +/// "ddns-domains": +/// [ +/// { +/// "name": " 0.168.192.in.addr.arpa." , +/// "key-name": "d2_key.tmark.org" , +/// "dns-servers" : +/// [ +/// { "ip-address": "127.0.0.101" , "port": 100 , +/// "key-name": "d2_key.tmark.org" } +/// ] +/// } +/// ] +/// } +/// } +/// @endcode + +/// @brief Exception thrown when the error during configuration handling +/// occurs. +class D2CfgError : public isc::Exception { +public: + D2CfgError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Acts as a storage vault for D2 global scalar parameters +class D2Params { +public: + /// @brief Constructor + /// + /// @param ip_address IP address at which D2 should listen for NCRs + /// @param port port on which D2 should listen NCRs + /// @param dns_server_timeout maximum amount of time in milliseconds to + /// wait for a response to a single DNS update request. + /// @param ncr_protocol socket protocol D2 should use to receive NCRS + /// @param ncr_format packet format of the inbound NCRs + /// + /// @throw D2CfgError if: + /// -# ip_address is 0.0.0.0 or :: + /// -# port is 0 + /// -# dns_server_timeout is < 1 + /// -# ncr_protocol is invalid, currently only NCR_UDP is supported + /// -# ncr_format is invalid, currently only FMT_JSON is supported + D2Params(const isc::asiolink::IOAddress& ip_address, + const size_t port, + const size_t dns_server_timeout, + const dhcp_ddns::NameChangeProtocol& ncr_protocol, + const dhcp_ddns::NameChangeFormat& ncr_format); + + /// @brief Default constructor + /// The default constructor creates an instance that has updates disabled. + D2Params(); + + /// @brief Destructor + virtual ~D2Params(); + + /// @brief Return the IP address D2 listens on. + const isc::asiolink::IOAddress& getIpAddress() const { + return(ip_address_); + } + + /// @brief Return the TCP/UPD port D2 listens on. + size_t getPort() const { + return(port_); + } + + /// @brief Return the DNS server timeout value. + size_t getDnsServerTimeout() const { + return(dns_server_timeout_); + } + + /// @brief Return the socket protocol in use. + const dhcp_ddns::NameChangeProtocol& getNcrProtocol() const { + return(ncr_protocol_); + } + + /// @brief Return the expected format of inbound requests (NCRs). + const dhcp_ddns::NameChangeFormat& getNcrFormat() const { + return(ncr_format_); + } + + /// @brief Return summary of the configuration used by D2. + /// + /// The returned summary of the configuration is meant to be appended to + /// the log message informing about the successful completion of the + /// D2 configuration. + /// + /// @return Configuration summary in the textual format. + std::string getConfigSummary() const; + + /// @brief Compares two D2Params's for equality + bool operator == (const D2Params& other) const; + + /// @brief Compares two D2Params's for inequality + bool operator != (const D2Params& other) const; + + /// @brief Generates a string representation of the class contents. + std::string toText() const; + +protected: + /// @brief Validates member values. + /// + /// Method is used by the constructor to validate member contents. + /// Currently checks: + /// -# ip_address is not 0.0.0.0 or :: + /// -# port is not 0 + /// -# dns_server_timeout is 0 + /// -# ncr_protocol is UDP + /// -# ncr_format is JSON + /// + /// @throw D2CfgError if contents are invalid + virtual void validateContents(); + +private: + /// @brief IP address D2 listens on. + isc::asiolink::IOAddress ip_address_; + + /// @brief IP port D2 listens on. + size_t port_; + + /// @brief Timeout for a single DNS packet exchange in milliseconds. + size_t dns_server_timeout_; + + /// @brief The socket protocol to use. + /// Currently only UDP is supported. + dhcp_ddns::NameChangeProtocol ncr_protocol_; + + /// @brief Format of the inbound requests (NCRs). + /// Currently only JSON format is supported. + dhcp_ddns::NameChangeFormat ncr_format_; +}; + +/// @brief Dumps the contents of a D2Params as text to an output stream +/// +/// @param os output stream to which text should be sent +/// @param config D2Param instance to dump +std::ostream& +operator<<(std::ostream& os, const D2Params& config); + +/// @brief Defines a pointer for D2Params instances. +typedef boost::shared_ptr D2ParamsPtr; + +/// @brief Represents a TSIG Key. +/// +/// Acts as both a storage class containing the basic attributes which +/// describe a TSIG Key, as well as owning and providing access to an +/// instance of the actual key (@ref isc::dns::TSIGKey) that can be used +/// by the IO layer for signing and verifying messages. +/// +class TSIGKeyInfo : public isc::data::UserContext, public isc::data::CfgToElement { +public: + /// @brief Defines string values for the supported TSIG algorithms + //@{ + static const char* HMAC_MD5_STR; + static const char* HMAC_SHA1_STR; + static const char* HMAC_SHA256_STR; + static const char* HMAC_SHA224_STR; + static const char* HMAC_SHA384_STR; + static const char* HMAC_SHA512_STR; + //}@ + + /// @brief Constructor + /// + /// @param name the unique label used to identify this key + /// @param algorithm the id of the encryption algorithm this key uses. + /// Currently supported values are (case insensitive): + /// -# "HMAC-MD5" + /// -# "HMAC-SHA1" + /// -# "HMAC-SHA224" + /// -# "HMAC-SHA256" + /// -# "HMAC-SHA384" + /// -# "HMAC-SHA512" + /// + /// @param secret The base-64 encoded secret component for this key. + /// (A suitable string for use here could be obtained by running the + /// BIND 9 dnssec-keygen program; the contents of resulting key file + /// will look similar to: + /// @code + /// Private-key-format: v1.3 + /// Algorithm: 157 (HMAC_MD5) + /// Key: LSWXnfkKZjdPJI5QxlpnfQ== + /// Bits: AAA= + /// Created: 20140515143700 + /// Publish: 20140515143700 + /// Activate: 20140515143700 + /// @endcode + /// where the value the "Key:" entry is the secret component of the key.) + /// @param digestbits the minimum truncated length in bits + /// + /// @throw D2CfgError if values supplied are invalid: + /// name cannot be blank, algorithm must be a supported value, + /// secret must be a non-blank, base64 encoded string. + TSIGKeyInfo(const std::string& name, const std::string& algorithm, + const std::string& secret, uint32_t digestbits = 0); + + /// @brief Destructor + virtual ~TSIGKeyInfo(); + + /// @brief Getter which returns the key's name. + /// + /// @return returns the name as a std::string. + const std::string getName() const { + return (name_); + } + + /// @brief Getter which returns the key's algorithm string ID + /// + /// @return returns the algorithm as a std::string. + const std::string getAlgorithm() const { + return (algorithm_); + } + + /// @brief Getter which returns the key's minimum truncated length + /// + /// @return returns the minimum truncated length or 0 as an uint32_t + uint32_t getDigestbits() const { + return (digestbits_); + } + + /// @brief Getter which returns the key's secret. + /// + /// @return returns the secret as a std::string. + const std::string getSecret() const { + return (secret_); + } + + /// @brief Getter which returns the TSIG key used to sign and verify + /// messages + /// + /// @return const pointer reference to @c D2TsigKeyPtr + const D2TsigKeyPtr& getTSIGKey() const { + return (tsig_key_); + } + + /// @brief Converts algorithm id to dns::TSIGKey algorithm dns::Name + /// + /// @param algorithm_id string value to translate into an algorithm name. + /// Currently supported values are (case insensitive): + /// -# "HMAC-MD5" + /// -# "HMAC-SHA1" + /// -# "HMAC-SHA224" + /// -# "HMAC-SHA256" + /// -# "HMAC-SHA384" + /// -# "HMAC-SHA512" + /// + /// @return const reference to a dns::Name containing the algorithm name + /// @throw BadValue if ID isn't recognized. + static const dns::Name& stringToAlgorithmName(const std::string& + algorithm_id); + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + +private: + /// @brief Creates the actual TSIG key instance member + /// + /// Replaces this tsig_key member with a key newly created using the key + /// name, algorithm id, digest bits, and secret. + /// This method is currently only called by the constructor, however it + /// could be called post-construction should keys ever support expiration. + /// + /// @throw D2CfgError with an explanation if the key could not be created. + void remakeKey(); + + /// @brief The name of the key. + /// + /// This value is the unique identifier that domains use to + /// to specify which TSIG key they need. + std::string name_; + + /// @brief The string ID of the algorithm that should be used for this key. + std::string algorithm_; + + /// @brief The base64 encoded string secret value component of this key. + std::string secret_; + + /// @brief The minimum truncated length in bits + /// (0 means no truncation is allowed and is the default) + uint32_t digestbits_; + + /// @brief The actual TSIG key. + D2TsigKeyPtr tsig_key_; +}; + +/// @brief Defines a pointer for TSIGKeyInfo instances. +typedef boost::shared_ptr TSIGKeyInfoPtr; + +/// @brief Defines a map of TSIGKeyInfos, keyed by the name. +typedef std::map TSIGKeyInfoMap; + +/// @brief Defines a iterator pairing of name and TSIGKeyInfo +typedef std::pair TSIGKeyInfoMapPair; + +/// @brief Defines a pointer to map of TSIGkeyInfos +typedef boost::shared_ptr TSIGKeyInfoMapPtr; + + +/// @brief Represents a specific DNS Server. +/// It provides information about the server's network identity and typically +/// belongs to a list of servers supporting DNS for a given domain. It will +/// be used to establish communications with the server to carry out DNS +/// updates. +class DnsServerInfo : public isc::data::UserContext, public isc::data::CfgToElement { +public: + /// @brief defines DNS standard port value + static const uint32_t STANDARD_DNS_PORT = 53; + + /// @brief Constructor + /// + /// @param hostname is the resolvable name of the server. If not blank, + /// then the server address should be resolved at runtime. + /// @param ip_address is the static IP address of the server. If hostname + /// is blank, then this address should be used to connect to the server. + /// @param port is the port number on which the server listens. + /// primarily meant for testing purposes. Normally, DNS traffic is on + /// is port 53. (NOTE the constructing code is responsible for setting + /// the default.) + /// @param enabled is a flag that indicates whether this server is + /// enabled for use. It defaults to true. + /// @param tsig_key_info pointer to the TSIGKeyInfo for the server's key + /// It defaults to an empty pointer, signifying the server has no key. + /// @param inherited_key is a flag that indicates whether the key was + /// inherited from the domain or not. It defaults to true i.e. inherited. + DnsServerInfo(const std::string& hostname, + isc::asiolink::IOAddress ip_address, + uint32_t port = STANDARD_DNS_PORT, + bool enabled = true, + const TSIGKeyInfoPtr& tsig_key_info = TSIGKeyInfoPtr(), + bool inherited_key = true); + + /// @brief Destructor + virtual ~DnsServerInfo(); + + /// @brief Getter which returns the server's hostname. + /// + /// @return the hostname as a std::string. + const std::string getHostname() const { + return (hostname_); + } + + /// @brief Getter which returns the server's port number. + /// + /// @return the port number as a unsigned integer. + uint32_t getPort() const { + return (port_); + } + + /// @brief Getter which returns the server's ip_address. + /// + /// @return the address as an IOAddress reference. + const isc::asiolink::IOAddress& getIpAddress() const { + return (ip_address_); + } + + /// @brief Convenience method which returns whether or not the + /// server is enabled. + /// + /// @return true if the server is enabled, false otherwise. + bool isEnabled() const { + return (enabled_); + } + + /// @brief Sets the server's enabled flag to true. + void enable() { + enabled_ = true; + } + + /// @brief Sets the server's enabled flag to false. + void disable() { + enabled_ = false; + } + + /// @brief Convenience method which returns the server's TSIG key name. + /// + /// @return the key name in a std::string. If server has no TSIG key, + /// the string will be empty. + const std::string getKeyName() const; + + /// @brief Getter which returns the server's TSIGKey info. + /// + /// @return the pointer to the server storage. If the server + /// is not configured to use TSIG the pointer will be empty. + const TSIGKeyInfoPtr& getTSIGKeyInfo() { + return (tsig_key_info_); + } + + /// @brief Returns a text representation for the server. + std::string toText() const; + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + +private: + /// @brief The resolvable name of the server. If not blank, then the + /// server's IP address should be dynamically resolved at runtime. + std::string hostname_; + + /// @brief The static IP address of the server. When hostname is blank, + /// then this address should be used to connect to the server. + isc::asiolink::IOAddress ip_address_; + + /// @brief The port number on which the server listens for DNS traffic. + uint32_t port_; + + /// @param enabled is a flag that indicates whether this server is + /// enabled for use. It defaults to true. + bool enabled_; + + /// @brief Pointer to the server TSIGKeyInfo. + /// Value is empty if the server is not configured for TSIG. + TSIGKeyInfoPtr tsig_key_info_; + + /// @brief Inherited key. When true the key was inherited from the domain, + /// false otherwise. + bool inherited_key_; +}; + +std::ostream& +operator<<(std::ostream& os, const DnsServerInfo& server); + +/// @brief Defines a pointer for DnsServerInfo instances. +typedef boost::shared_ptr DnsServerInfoPtr; + +/// @brief Defines a storage container for DnsServerInfo pointers. +typedef std::vector DnsServerInfoStorage; + +/// @brief Defines a pointer to DnsServerInfo storage containers. +typedef boost::shared_ptr DnsServerInfoStoragePtr; + + +/// @brief Represents a DNS domain that is may be updated dynamically. +/// This class specifies a DNS domain and the list of DNS servers that support +/// it. Its primary use is to map a domain to the DNS server(s) responsible +/// for it. +/// @todo Currently the name entry for a domain is just a std::string. It +/// may be worthwhile to change this to a dns::Name for purposes of better +/// validation and matching capabilities. +class DdnsDomain : public isc::data::UserContext, public isc::data::CfgToElement { +public: + /// @brief Constructor + /// + /// @param name is the domain name of the domain. + /// @param servers is the list of server(s) supporting this domain. + /// @param key_name is the TSIG key name of the domain. + DdnsDomain(const std::string& name, DnsServerInfoStoragePtr servers, + const std::string& key_name = ""); + + /// @brief Destructor + virtual ~DdnsDomain(); + + /// @brief Getter which returns the domain's name. + /// + /// @return returns the name in a std::string. + const std::string getName() const { + return (name_); + } + + /// @brief Getter which returns the domain's TSIG key name. + /// + /// @note: TSIG key infos are in servers. + /// + /// @return returns the key name in a std::string. If domain has no + /// TSIG key, the string will be empty. + const std::string getKeyName() const { + return (key_name_); + } + + /// @brief Getter which returns the domain's list of servers. + /// + /// @return returns the pointer to the server storage. + const DnsServerInfoStoragePtr& getServers() { + return (servers_); + } + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + +private: + /// @brief The domain name of the domain. + std::string name_; + + /// @brief The list of server(s) supporting this domain. + DnsServerInfoStoragePtr servers_; + + /// @brief The TSIG key name (empty when there is no key for the domain). + std::string key_name_; +}; + +/// @brief Defines a pointer for DdnsDomain instances. +typedef boost::shared_ptr DdnsDomainPtr; + +/// @brief Defines a map of DdnsDomains, keyed by the domain name. +typedef std::map DdnsDomainMap; + +/// @brief Defines a iterator pairing domain name and DdnsDomain +typedef std::pair DdnsDomainMapPair; + +/// @brief Defines a pointer to DdnsDomain storage containers. +typedef boost::shared_ptr DdnsDomainMapPtr; + +/// @brief Provides storage for and management of a list of DNS domains. +/// In addition to housing the domain list storage, it provides domain matching +/// services. These services are used to match a FQDN to a domain. Currently +/// it supports a single matching service, which will return the matching +/// domain or a wild card domain if one is specified. The wild card domain is +/// specified as a domain whose name is "*". The wild card domain will match +/// any entry and is provided for flexibility in FQDNs If for instance, all +/// forward requests are handled by the same servers, the configuration could +/// specify the wild card domain as the only forward domain. All forward DNS +/// updates would be sent to that one list of servers, regardless of the FQDN. +/// As matching capabilities evolve this class is expected to expand. +class DdnsDomainListMgr : public isc::data::CfgToElement { +public: + /// @brief defines the domain name for denoting the wildcard domain. + static const char* wildcard_domain_name_; + + /// @brief Constructor + /// + /// @param name is an arbitrary label assigned to this manager. + DdnsDomainListMgr(const std::string& name); + + /// @brief Destructor + virtual ~DdnsDomainListMgr (); + + /// @brief Matches a given name to a domain based on a longest match + /// scheme. + /// + /// Given a FQDN, search the list of domains, successively removing a + /// sub-domain from the FQDN until a match is found. If no match is found + /// and the wild card domain is present in the list, then return it as the + /// match. If the wild card domain is the only domain in the list, then + /// it will be returned immediately for any FQDN. + /// + /// @param fqdn is the name for which to look. + /// @param domain receives the matching domain. If no match is found its + /// contents will be unchanged. + /// + /// @return returns true if a match is found, false otherwise. + /// @todo This is a very basic match method, which expects valid FQDNs + /// both as input and for the DdnsDomain::getName(). Currently both are + /// simple strings and there is no normalization (i.e. added trailing dots + /// if missing). + virtual bool matchDomain(const std::string& fqdn, DdnsDomainPtr& domain); + + /// @brief Fetches the manager's name. + /// + /// @return returns a std::string containing the name of the manager. + const std::string getName() const { + return (name_); + } + + /// @brief Returns the number of domains in the domain list. + /// + /// @brief returns an unsigned int containing the domain count. + uint32_t size() const { + return (domains_->size()); + } + + /// @brief Fetches the wild card domain. + /// + /// @return returns a pointer reference to the domain. The pointer will + /// empty if the wild card domain is not present. + const DdnsDomainPtr& getWildcardDomain() { + return (wildcard_domain_); + } + + /// @brief Fetches the domain list. + /// + /// @return returns a pointer reference to the list of domains. + const DdnsDomainMapPtr &getDomains() { + return (domains_); + } + + /// @brief Sets the manger's domain list to the given list of domains. + /// This method will scan the inbound list for the wild card domain and + /// set the internal wild card domain pointer accordingly. + void setDomains(DdnsDomainMapPtr domains); + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + +private: + /// @brief An arbitrary label assigned to this manager. + std::string name_; + + /// @brief Map of the domains, keyed by name. + DdnsDomainMapPtr domains_; + + /// @brief Pointer to the wild card domain. + DdnsDomainPtr wildcard_domain_; +}; + +/// @brief Defines a pointer for DdnsDomain instances. +typedef boost::shared_ptr DdnsDomainListMgrPtr; + +/// @brief Storage container for scalar configuration parameters. +/// +/// This class is useful for implementing parsers for more complex configuration +/// elements (e.g. those of item type "map"). It provides a convenient way to +/// add storage to the parser for an arbitrary number and variety of scalar +/// configuration items (e.g. ints, bools, strings...) without explicitly adding +/// storage for each individual type needed by the parser. +/// +/// This class implements a concrete version of the base class by supplying a +/// "clone" method. +class DScalarContext : public process::ConfigBase { +public: + + /// @brief Constructor + DScalarContext() { + }; + + /// @brief Destructor + virtual ~DScalarContext() { + } + + /// @brief Creates a clone of a DStubContext. + /// + /// @return returns a pointer to the new clone. + virtual process::ConfigPtr clone() { + return (process::ConfigPtr(new DScalarContext(*this))); + } + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const { + isc_throw(isc::NotImplemented, "DScalarContext::ElementPtr"); + } + +protected: + /// @brief Copy constructor + DScalarContext(const DScalarContext& rhs) : ConfigBase(rhs) { + } + +private: + /// @brief Private assignment operator, not implemented. + DScalarContext& operator=(const DScalarContext& rhs); +}; + +/// @brief Defines a pointer for DScalarContext instances. +typedef boost::shared_ptr DScalarContextPtr; + +/// @brief Parser for TSIGKeyInfo +/// +/// This class parses the configuration element "tsig-key" +/// and creates an instance of a TSIGKeyInfo. +class TSIGKeyInfoParser : public data::SimpleParser { +public: + /// @brief Performs the actual parsing of the given "tsig-key" element. + /// + /// Parses a configuration for the elements needed to instantiate a + /// TSIGKeyInfo, validates those entries, creates a TSIGKeyInfo instance + /// + /// @param key_config is the "tsig-key" configuration to parse + /// + /// @return pointer to the new TSIGKeyInfo instance + TSIGKeyInfoPtr parse(data::ConstElementPtr key_config); + +}; + +/// @brief Parser for a list of TSIGKeyInfos +/// +/// This class parses a list of "tsig-key" configuration elements. +/// The TSIGKeyInfo instances are added to the given storage upon commit. +class TSIGKeyInfoListParser : public data::SimpleParser { +public: + /// @brief Performs the parsing of the given list "tsig-key" elements. + /// + /// Creates an empty TSIGKeyInfoMap + /// + /// Instantiates a TSIGKeyInfoParser + /// It iterates over each key entry in the list: + /// 2. Pass the element configuration to the parser's parse method + /// 3. Add the new TSIGKeyInfo instance to the key map + /// + /// @param key_list_config is the list of "tsig_key" elements to parse. + /// + /// @return a map containing the TSIGKeyInfo instances + TSIGKeyInfoMapPtr parse(data::ConstElementPtr key_list_config); +}; + +/// @brief Parser for DnsServerInfo +/// +/// This class parses the configuration element "dns-server" +/// and creates an instance of a DnsServerInfo. +class DnsServerInfoParser : public data::SimpleParser { +public: + /// @brief Performs the actual parsing of the given "dns-server" element. + /// + /// Parses a configuration for the elements needed to instantiate a + /// DnsServerInfo, validates those entries, creates a DnsServerInfo instance + /// and returns it. + /// + /// @param server_config is the "dns-server" configuration to parse + /// @param domain_config the parent domain's configuration + /// @param keys map of defined TSIG keys + /// + /// @return a pointer to the newly created server instance + /// + /// @throw D2CfgError if: + /// -# hostname is not blank, hostname is not yet supported + /// -# ip_address is invalid + /// -# port is 0 + DnsServerInfoPtr parse(data::ConstElementPtr server_config, + data::ConstElementPtr domain_config, + const TSIGKeyInfoMapPtr keys); +}; + +/// @brief Parser for a list of DnsServerInfos +/// +/// This class parses a list of "dns-server" configuration elements. +/// The DnsServerInfo instances are added +/// to the given storage upon commit. +class DnsServerInfoListParser : public data::SimpleParser { +public: + /// @brief Performs the actual parsing of the given list "dns-server" + /// elements. + /// + /// Creates an empty server list + /// It iterates over each server entry in the list: + /// 1. Creates a server instance by passing the entry to @c + /// DnsSeverInfoParser::parse() + /// 2. Adds the server to the server list + /// + /// @param server_list_config is the list of "dns-server" elements to parse. + /// @param domain_config the parent domain's configuration + /// @param keys map of defined TSIG keys + /// @return A pointer to the new, populated server list + DnsServerInfoStoragePtr parse(data::ConstElementPtr server_list_config, + data::ConstElementPtr domain_config, + const TSIGKeyInfoMapPtr keys); +}; + +/// @brief Parser for DdnsDomain +/// +/// This class parses the configuration element "ddns-domain" +/// and creates an instance of a DdnsDomain. +class DdnsDomainParser : public data::SimpleParser { +public: + /// @brief Performs the actual parsing of the given "ddns-domain" element. + /// + /// Parses a configuration for the elements needed to instantiate a + /// DdnsDomain, validates those entries, and creates a DdnsDomain instance. + /// + /// @param domain_config is the "ddns-domain" configuration to parse + /// @param keys map of defined TSIG keys + /// + /// @return a pointer to the new domain instance + DdnsDomainPtr parse(data::ConstElementPtr domain_config, + const TSIGKeyInfoMapPtr keys); +}; + +/// @brief Parser for a list of DdnsDomains +/// +/// This class parses a list of "ddns-domain" configuration elements +/// into a map of DdnsDomains. +class DdnsDomainListParser : public data::SimpleParser { +public: + /// @brief Performs the actual parsing of the given list "ddns-domain" + /// elements. + /// Creates a new DdnsDomain map + /// It iterates over each domain entry in the list: + /// 1. Creates a DdnsDomain instance by passing the entry into @c + /// DdnsDomainParser::parser() + /// 2. Adds the DdnsDomain instance to the domain map + /// + /// @param domain_list_config is the list of "ddns-domain" elements to + /// parse. + /// @param keys map of defined TSIG keys + /// @return a pointer to the newly populated domain map + DdnsDomainMapPtr parse(data::ConstElementPtr domain_list_config, + const TSIGKeyInfoMapPtr keys); +}; + +/// @brief Parser for DdnsDomainListMgr +/// +/// This class parses the configuration elements "forward-ddns" and +/// "reverse-ddns". It populates the given DdnsDomainListMgr with parsed +/// information. +class DdnsDomainListMgrParser : public data::SimpleParser { +public: + /// @brief Performs the actual parsing of the given manager element. + /// + /// Parses a configuration for the elements needed to instantiate a + /// DdnsDomainListMgr, validates those entries, then creates a + /// DdnsDomainListMgr. + /// + /// @param mgr_config manager configuration to parse + /// @param mgr_name convenience label for the manager instance + /// @param keys map of defined TSIG keys + /// + /// @return a pointer to the new manager instance + DdnsDomainListMgrPtr parse(data::ConstElementPtr mgr_config, + const std::string& mgr_name, + const TSIGKeyInfoMapPtr keys); +}; + +} // end of isc::d2 namespace +} // end of isc namespace + +#endif // D2_CONFIG_H diff --git a/src/lib/d2srv/d2_log.cc b/src/lib/d2srv/d2_log.cc new file mode 100644 index 0000000..a56640b --- /dev/null +++ b/src/lib/d2srv/d2_log.cc @@ -0,0 +1,23 @@ +// 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/. + +/// Defines the logger used by the top-level component of kea-dhcp-ddns. + +#include + +#include + +namespace isc { +namespace d2 { + +/// @brief Defines the logger used within D2. +isc::log::Logger d2_logger("dhcpddns"); +isc::log::Logger dhcp_to_d2_logger("dhcp-to-d2"); +isc::log::Logger d2_to_dns_logger("d2-to-dns"); + +} // namespace d2 +} // namespace isc + diff --git a/src/lib/d2srv/d2_log.h b/src/lib/d2srv/d2_log.h new file mode 100644 index 0000000..2b408e0 --- /dev/null +++ b/src/lib/d2srv/d2_log.h @@ -0,0 +1,25 @@ +// 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/. + +#ifndef D2_LOG_H +#define D2_LOG_H + +#include +#include +#include + +namespace isc { +namespace d2 { + +/// Define the loggers for the "d2" logging. +extern isc::log::Logger d2_logger; +extern isc::log::Logger dhcp_to_d2_logger; +extern isc::log::Logger d2_to_dns_logger; + +} // namespace d2 +} // namespace isc + +#endif // D2_LOG_H diff --git a/src/lib/d2srv/d2_messages.cc b/src/lib/d2srv/d2_messages.cc new file mode 100644 index 0000000..9fd85e0 --- /dev/null +++ b/src/lib/d2srv/d2_messages.cc @@ -0,0 +1,175 @@ +// File created from ../../../src/lib/d2srv/d2_messages.mes + +#include +#include +#include + +namespace isc { +namespace d2 { + +extern const isc::log::MessageID DHCP_DDNS_ADD_FAILED = "DHCP_DDNS_ADD_FAILED"; +extern const isc::log::MessageID DHCP_DDNS_ADD_SUCCEEDED = "DHCP_DDNS_ADD_SUCCEEDED"; +extern const isc::log::MessageID DHCP_DDNS_ALREADY_RUNNING = "DHCP_DDNS_ALREADY_RUNNING"; +extern const isc::log::MessageID DHCP_DDNS_AT_MAX_TRANSACTIONS = "DHCP_DDNS_AT_MAX_TRANSACTIONS"; +extern const isc::log::MessageID DHCP_DDNS_CLEARED_FOR_SHUTDOWN = "DHCP_DDNS_CLEARED_FOR_SHUTDOWN"; +extern const isc::log::MessageID DHCP_DDNS_COMMAND = "DHCP_DDNS_COMMAND"; +extern const isc::log::MessageID DHCP_DDNS_CONFIGURE = "DHCP_DDNS_CONFIGURE"; +extern const isc::log::MessageID DHCP_DDNS_CONFIGURED_CALLOUT_DROP = "DHCP_DDNS_CONFIGURED_CALLOUT_DROP"; +extern const isc::log::MessageID DHCP_DDNS_CONFIG_CHECK_FAIL = "DHCP_DDNS_CONFIG_CHECK_FAIL"; +extern const isc::log::MessageID DHCP_DDNS_CONFIG_FAIL = "DHCP_DDNS_CONFIG_FAIL"; +extern const isc::log::MessageID DHCP_DDNS_CONFIG_SYNTAX_WARNING = "DHCP_DDNS_CONFIG_SYNTAX_WARNING"; +extern const isc::log::MessageID DHCP_DDNS_FAILED = "DHCP_DDNS_FAILED"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE = "DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_IO_ERROR = "DHCP_DDNS_FORWARD_ADD_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_REJECTED = "DHCP_DDNS_FORWARD_ADD_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT = "DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE = "DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR = "DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED = "DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT = "DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE = "DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR = "DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED = "DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT = "DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE = "DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_IO_ERROR = "DHCP_DDNS_FORWARD_REPLACE_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_REJECTED = "DHCP_DDNS_FORWARD_REPLACE_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT = "DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_FWD_REQUEST_IGNORED = "DHCP_DDNS_FWD_REQUEST_IGNORED"; +extern const isc::log::MessageID DHCP_DDNS_INVALID_RESPONSE = "DHCP_DDNS_INVALID_RESPONSE"; +extern const isc::log::MessageID DHCP_DDNS_NOT_ON_LOOPBACK = "DHCP_DDNS_NOT_ON_LOOPBACK"; +extern const isc::log::MessageID DHCP_DDNS_NO_ELIGIBLE_JOBS = "DHCP_DDNS_NO_ELIGIBLE_JOBS"; +extern const isc::log::MessageID DHCP_DDNS_NO_FWD_MATCH_ERROR = "DHCP_DDNS_NO_FWD_MATCH_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_NO_MATCH = "DHCP_DDNS_NO_MATCH"; +extern const isc::log::MessageID DHCP_DDNS_NO_REV_MATCH_ERROR = "DHCP_DDNS_NO_REV_MATCH_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_PROCESS_INIT = "DHCP_DDNS_PROCESS_INIT"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_FULL = "DHCP_DDNS_QUEUE_MGR_QUEUE_FULL"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE = "DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECONFIGURING = "DHCP_DDNS_QUEUE_MGR_RECONFIGURING"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECOVERING = "DHCP_DDNS_QUEUE_MGR_RECOVERING"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECV_ERROR = "DHCP_DDNS_QUEUE_MGR_RECV_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUME_ERROR = "DHCP_DDNS_QUEUE_MGR_RESUME_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUMING = "DHCP_DDNS_QUEUE_MGR_RESUMING"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STARTED = "DHCP_DDNS_QUEUE_MGR_STARTED"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_START_ERROR = "DHCP_DDNS_QUEUE_MGR_START_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPED = "DHCP_DDNS_QUEUE_MGR_STOPPED"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPING = "DHCP_DDNS_QUEUE_MGR_STOPPING"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOP_ERROR = "DHCP_DDNS_QUEUE_MGR_STOP_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR = "DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP = "DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP"; +extern const isc::log::MessageID DHCP_DDNS_REMOVE_FAILED = "DHCP_DDNS_REMOVE_FAILED"; +extern const isc::log::MessageID DHCP_DDNS_REMOVE_SUCCEEDED = "DHCP_DDNS_REMOVE_SUCCEEDED"; +extern const isc::log::MessageID DHCP_DDNS_REQUEST_DROPPED = "DHCP_DDNS_REQUEST_DROPPED"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE = "DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_IO_ERROR = "DHCP_DDNS_REVERSE_REMOVE_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_REJECTED = "DHCP_DDNS_REVERSE_REMOVE_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT = "DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE = "DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_IO_ERROR = "DHCP_DDNS_REVERSE_REPLACE_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_REJECTED = "DHCP_DDNS_REVERSE_REPLACE_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT = "DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_REV_REQUEST_IGNORED = "DHCP_DDNS_REV_REQUEST_IGNORED"; +extern const isc::log::MessageID DHCP_DDNS_RUN_EXIT = "DHCP_DDNS_RUN_EXIT"; +extern const isc::log::MessageID DHCP_DDNS_SHUTDOWN_COMMAND = "DHCP_DDNS_SHUTDOWN_COMMAND"; +extern const isc::log::MessageID DHCP_DDNS_STARTED = "DHCP_DDNS_STARTED"; +extern const isc::log::MessageID DHCP_DDNS_STARTING_TRANSACTION = "DHCP_DDNS_STARTING_TRANSACTION"; +extern const isc::log::MessageID DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR = "DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_TRANS_SEND_ERROR = "DHCP_DDNS_TRANS_SEND_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_UPDATE_REQUEST_SENT = "DHCP_DDNS_UPDATE_REQUEST_SENT"; +extern const isc::log::MessageID DHCP_DDNS_UPDATE_RESPONSE_RECEIVED = "DHCP_DDNS_UPDATE_RESPONSE_RECEIVED"; + +} // namespace d2 +} // namespace isc + +namespace { + +const char* values[] = { + "DHCP_DDNS_ADD_FAILED", "DHCP_DDNS Request ID %1: Transaction outcome %2", + "DHCP_DDNS_ADD_SUCCEEDED", "DHCP_DDNS Request ID %1: successfully added the DNS mapping addition for this request: %2", + "DHCP_DDNS_ALREADY_RUNNING", "%1 already running? %2", + "DHCP_DDNS_AT_MAX_TRANSACTIONS", "application has %1 queued requests but has reached maximum number of %2 concurrent transactions", + "DHCP_DDNS_CLEARED_FOR_SHUTDOWN", "application has met shutdown criteria for shutdown type: %1", + "DHCP_DDNS_COMMAND", "command directive received, command: %1 - args: %2", + "DHCP_DDNS_CONFIGURE", "configuration %1 received: %2", + "DHCP_DDNS_CONFIGURED_CALLOUT_DROP", "configuration was rejected because a callout set the next step to 'drop': %1", + "DHCP_DDNS_CONFIG_CHECK_FAIL", "DHCP-DDNS server configuration check failed: %1", + "DHCP_DDNS_CONFIG_FAIL", "DHCP-DDNS server configuration failed: %1", + "DHCP_DDNS_CONFIG_SYNTAX_WARNING", "DHCP-DDNS server configuration syntax warning: %1", + "DHCP_DDNS_FAILED", "application experienced a fatal error: %1", + "DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while adding a forward address mapping for FQDN %3 to DNS server %4", + "DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE", "DNS Request ID %1: update message to add a forward DNS entry could not be constructed for this request: %2, reason: %3", + "DHCP_DDNS_FORWARD_ADD_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping add for FQDN %2 to DNS server %3", + "DHCP_DDNS_FORWARD_ADD_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to add the address mapping for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while adding forward address mapping for FQDN, %3", + "DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing a forward address mapping for FQDN %3 to DNS server %4", + "DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE", "DNS Request ID %1: update message to remove a forward DNS Address entry could not be constructed for this request: %2, reason: %3", + "DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping address removal for FQDN %2 to DNS server %3", + "DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to remove the forward address mapping for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing forward address mapping for FQDN, %3", + "DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing forward RRs for FQDN %3 to DNS server %4", + "DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE", "DNS Request ID %1: update message to remove forward DNS RR entries could not be constructed for this request: %2, reason: %3", + "DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a forward RR removal for FQDN %2 to DNS server %3", + "DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to remove forward RR entries for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing forward RRs for FQDN, %3", + "DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while replacing forward address mapping for FQDN %3 to DNS server %4", + "DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE", "DNS Request ID %1: update message to replace a forward DNS entry could not be constructed from this request: %2, reason: %3", + "DHCP_DDNS_FORWARD_REPLACE_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping replace for FQDN %2 to DNS server %3", + "DHCP_DDNS_FORWARD_REPLACE_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to replace the address mapping for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while replacing forward address mapping for FQDN, %3", + "DHCP_DDNS_FWD_REQUEST_IGNORED", "Request ID %1: Forward updates are disabled, the forward portion of request will be ignored: %2", + "DHCP_DDNS_INVALID_RESPONSE", "received response to DNS Update message is malformed: %1", + "DHCP_DDNS_NOT_ON_LOOPBACK", "the DHCP-DDNS server has been configured to listen on %1 which is not the local loopback. This is an insecure configuration supported for testing purposes only", + "DHCP_DDNS_NO_ELIGIBLE_JOBS", "although there are queued requests, there are pending transactions for each, Queue count: %1 Transaction count: %2", + "DHCP_DDNS_NO_FWD_MATCH_ERROR", "Request ID %1: the configured list of forward DDNS domains does not contain a match for: %2 The request has been discarded.", + "DHCP_DDNS_NO_MATCH", "No DNS servers match FQDN %1", + "DHCP_DDNS_NO_REV_MATCH_ERROR", "Request ID %1: the configured list of reverse DDNS domains does not contain a match for: %2 The request has been discarded.", + "DHCP_DDNS_PROCESS_INIT", "application init invoked", + "DHCP_DDNS_QUEUE_MGR_QUEUE_FULL", "application request queue has reached maximum number of entries %1", + "DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE", "Request ID %1: received and queued a request.", + "DHCP_DDNS_QUEUE_MGR_RECONFIGURING", "application is reconfiguring the queue manager", + "DHCP_DDNS_QUEUE_MGR_RECOVERING", "application is attempting to recover from a queue manager IO error", + "DHCP_DDNS_QUEUE_MGR_RECV_ERROR", "application's queue manager was notified of a request receive error by its listener.", + "DHCP_DDNS_QUEUE_MGR_RESUME_ERROR", "application could not restart the queue manager, reason: %1", + "DHCP_DDNS_QUEUE_MGR_RESUMING", "application is resuming listening for requests now that the request queue size has reached %1 of a maximum %2 allowed", + "DHCP_DDNS_QUEUE_MGR_STARTED", "application's queue manager has begun listening for requests.", + "DHCP_DDNS_QUEUE_MGR_START_ERROR", "application could not start the queue manager, reason: %1", + "DHCP_DDNS_QUEUE_MGR_STOPPED", "application's queue manager has stopped listening for requests.", + "DHCP_DDNS_QUEUE_MGR_STOPPING", "application is stopping the queue manager for %1", + "DHCP_DDNS_QUEUE_MGR_STOP_ERROR", "application encountered an error stopping the queue manager: %1", + "DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR", "application's queue manager request receive handler experienced an unexpected exception %1:", + "DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP", "application's queue manager receive was", + "DHCP_DDNS_REMOVE_FAILED", "DHCP_DDNS Request ID %1: Transaction outcome: %2", + "DHCP_DDNS_REMOVE_SUCCEEDED", "DHCP_DDNS Request ID %1: successfully removed the DNS mapping addition for this request: %2", + "DHCP_DDNS_REQUEST_DROPPED", "Request ID %1: Request contains no enabled update requests and will be dropped: %2", + "DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing reverse address mapping for FQDN %3 to DNS server %4", + "DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE", "DNS Request ID %1: update message to remove a reverse DNS entry could not be constructed from this request: %2, reason: %3", + "DHCP_DDNS_REVERSE_REMOVE_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a reverse mapping remove for FQDN %2 to DNS server %3", + "DHCP_DDNS_REVERSE_REMOVE_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to remove the reverse mapping for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing reverse address mapping for FQDN, %3", + "DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while replacing reverse address mapping for FQDN %3 to DNS server %4", + "DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE", "DNS Request ID %1: update message to replace a reverse DNS entry could not be constructed from this request: %2, reason: %3", + "DHCP_DDNS_REVERSE_REPLACE_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a reverse mapping replacement for FQDN %2 to DNS server %3", + "DHCP_DDNS_REVERSE_REPLACE_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to replace the reverse mapping for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while replacing reverse address mapping for FQDN, %3", + "DHCP_DDNS_REV_REQUEST_IGNORED", "Request ID %1: Reverse updates are disabled, the reverse portion of request will be ignored: %2", + "DHCP_DDNS_RUN_EXIT", "application is exiting the event loop", + "DHCP_DDNS_SHUTDOWN_COMMAND", "application received shutdown command with args: %1", + "DHCP_DDNS_STARTED", "Kea DHCP-DDNS server version %1 started", + "DHCP_DDNS_STARTING_TRANSACTION", "Request ID %1:", + "DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR", "Request ID %1: application encountered an unexpected error while carrying out a NameChangeRequest: %2", + "DHCP_DDNS_TRANS_SEND_ERROR", "Request ID %1: application encountered an unexpected error while attempting to send a DNS update: %2", + "DHCP_DDNS_UPDATE_REQUEST_SENT", "Request ID %1: %2 to server: %3", + "DHCP_DDNS_UPDATE_RESPONSE_RECEIVED", "Request ID %1: to server: %2 status: %3", + NULL +}; + +const isc::log::MessageInitializer initializer(values); + +} // Anonymous namespace + diff --git a/src/lib/d2srv/d2_messages.h b/src/lib/d2srv/d2_messages.h new file mode 100644 index 0000000..cb1a95a --- /dev/null +++ b/src/lib/d2srv/d2_messages.h @@ -0,0 +1,91 @@ +// File created from ../../../src/lib/d2srv/d2_messages.mes + +#ifndef D2_MESSAGES_H +#define D2_MESSAGES_H + +#include + +namespace isc { +namespace d2 { + +extern const isc::log::MessageID DHCP_DDNS_ADD_FAILED; +extern const isc::log::MessageID DHCP_DDNS_ADD_SUCCEEDED; +extern const isc::log::MessageID DHCP_DDNS_ALREADY_RUNNING; +extern const isc::log::MessageID DHCP_DDNS_AT_MAX_TRANSACTIONS; +extern const isc::log::MessageID DHCP_DDNS_CLEARED_FOR_SHUTDOWN; +extern const isc::log::MessageID DHCP_DDNS_COMMAND; +extern const isc::log::MessageID DHCP_DDNS_CONFIGURE; +extern const isc::log::MessageID DHCP_DDNS_CONFIGURED_CALLOUT_DROP; +extern const isc::log::MessageID DHCP_DDNS_CONFIG_CHECK_FAIL; +extern const isc::log::MessageID DHCP_DDNS_CONFIG_FAIL; +extern const isc::log::MessageID DHCP_DDNS_CONFIG_SYNTAX_WARNING; +extern const isc::log::MessageID DHCP_DDNS_FAILED; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_FWD_REQUEST_IGNORED; +extern const isc::log::MessageID DHCP_DDNS_INVALID_RESPONSE; +extern const isc::log::MessageID DHCP_DDNS_NOT_ON_LOOPBACK; +extern const isc::log::MessageID DHCP_DDNS_NO_ELIGIBLE_JOBS; +extern const isc::log::MessageID DHCP_DDNS_NO_FWD_MATCH_ERROR; +extern const isc::log::MessageID DHCP_DDNS_NO_MATCH; +extern const isc::log::MessageID DHCP_DDNS_NO_REV_MATCH_ERROR; +extern const isc::log::MessageID DHCP_DDNS_PROCESS_INIT; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_FULL; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECONFIGURING; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECOVERING; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECV_ERROR; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUME_ERROR; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUMING; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STARTED; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_START_ERROR; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPED; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPING; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOP_ERROR; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP; +extern const isc::log::MessageID DHCP_DDNS_REMOVE_FAILED; +extern const isc::log::MessageID DHCP_DDNS_REMOVE_SUCCEEDED; +extern const isc::log::MessageID DHCP_DDNS_REQUEST_DROPPED; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_REV_REQUEST_IGNORED; +extern const isc::log::MessageID DHCP_DDNS_RUN_EXIT; +extern const isc::log::MessageID DHCP_DDNS_SHUTDOWN_COMMAND; +extern const isc::log::MessageID DHCP_DDNS_STARTED; +extern const isc::log::MessageID DHCP_DDNS_STARTING_TRANSACTION; +extern const isc::log::MessageID DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR; +extern const isc::log::MessageID DHCP_DDNS_TRANS_SEND_ERROR; +extern const isc::log::MessageID DHCP_DDNS_UPDATE_REQUEST_SENT; +extern const isc::log::MessageID DHCP_DDNS_UPDATE_RESPONSE_RECEIVED; + +} // namespace d2 +} // namespace isc + +#endif // D2_MESSAGES_H diff --git a/src/lib/d2srv/d2_messages.mes b/src/lib/d2srv/d2_messages.mes new file mode 100644 index 0000000..b86eb7b --- /dev/null +++ b/src/lib/d2srv/d2_messages.mes @@ -0,0 +1,405 @@ +# 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/. + +$NAMESPACE isc::d2 + +% DHCP_DDNS_ADD_FAILED DHCP_DDNS Request ID %1: Transaction outcome %2 +This is an error message issued after DHCP_DDNS attempts to submit DNS mapping +entry additions have failed. The precise reason for the failure should be +documented in preceding log entries. + +% DHCP_DDNS_ADD_SUCCEEDED DHCP_DDNS Request ID %1: successfully added the DNS mapping addition for this request: %2 +This is an informational message issued after DHCP_DDNS has submitted DNS +mapping additions which were received and accepted by an appropriate DNS server. + +% DHCP_DDNS_ALREADY_RUNNING %1 already running? %2 +This is an error message that occurs when DHCP_DDNS encounters a pre-existing +PID file which contains the PID of a running process. This most likely +indicates an attempt to start a second instance of DHCP_DDNS using the +same configuration file. It is possible, though unlikely, that the PID file +is a remnant left behind by a server crash or power failure and the PID +it contains refers to a process other than DHCP_DDNS. In such an event, +it would be necessary to manually remove the PID file. The first argument is +the DHCP_DDNS process name, the second contains the PID and PID file. + +% DHCP_DDNS_AT_MAX_TRANSACTIONS application has %1 queued requests but has reached maximum number of %2 concurrent transactions +This is a debug message that indicates that the application has DHCP_DDNS +requests in the queue but is working as many concurrent requests as allowed. + +% DHCP_DDNS_CLEARED_FOR_SHUTDOWN application has met shutdown criteria for shutdown type: %1 +This is a debug message issued when the application has been instructed +to shutdown and has met the required criteria to exit. + +% DHCP_DDNS_COMMAND command directive received, command: %1 - args: %2 +This is a debug message issued when the DHCP-DDNS application command method +has been invoked. + +% DHCP_DDNS_CONFIGURE configuration %1 received: %2 +This is a debug message issued when the DHCP-DDNS application configure method +has been invoked. + +% DHCP_DDNS_CONFIGURED_CALLOUT_DROP configuration was rejected because a callout set the next step to 'drop': %1 +This error message indicates that the DHCP-DDNS had failed configuration +attempt because the next step of the configured callout was set to 'drop' +by a hook library. The error message provided by the hook library is displayed. + +% DHCP_DDNS_CONFIG_CHECK_FAIL DHCP-DDNS server configuration check failed: %1 +This error message indicates that the DHCP-DDNS had failed configuration +check. Details are provided. Additional details may be available +in earlier log entries, possibly on lower levels. + +% DHCP_DDNS_CONFIG_FAIL DHCP-DDNS server configuration failed: %1 +This error message indicates that the DHCP-DDNS had failed configuration +attempt. Details are provided. Additional details may be available +in earlier log entries, possibly on lower levels. + +% DHCP_DDNS_CONFIG_SYNTAX_WARNING DHCP-DDNS server configuration syntax warning: %1 +This warning message indicates that the DHCP-DDNS configuration had a minor +syntax error. The error was displayed and the configuration parsing resumed. + +% DHCP_DDNS_FAILED application experienced a fatal error: %1 +This is a debug message issued when the DHCP-DDNS application encounters an +unrecoverable error from within the event loop. + +% DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while adding a forward address mapping for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was adding a forward address mapping. The request will be +aborted. This is most likely a programmatic issue and should be reported. + +% DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE DNS Request ID %1: update message to add a forward DNS entry could not be constructed for this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting a forward address addition. This is due +to invalid data contained in the NameChangeRequest. The request will be aborted. +This is most likely a configuration issue. + +% DHCP_DDNS_FORWARD_ADD_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping add for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a forward address update. The application will +retry against the same server or others as appropriate. + +% DHCP_DDNS_FORWARD_ADD_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to add the address mapping for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while adding forward address mapping for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to add a forward address mapping, is mangled or malformed. +The application will retry against the same server or others as appropriate. + +% DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing a forward address mapping for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was removing a forward address mapping. The request will be +aborted. This is most likely a programmatic issue and should be reported. + +% DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE DNS Request ID %1: update message to remove a forward DNS Address entry could not be constructed for this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting a forward address (A or AAAA) removal. This +is due to invalid data contained in the NameChangeRequest. The request will be +aborted. This is most likely a configuration issue. +/*sar*/ + +% DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping address removal for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a forward address remove. The application will retry +against the same server or others as appropriate. + +% DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to remove the forward address mapping for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing forward address mapping for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to remove a forward address mapping, is mangled or malformed. +The application will retry against the same server or others as appropriate. + +% DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing forward RRs for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was removing forward RRs. The request will be aborted. This is +most likely a programmatic issue and should be reported. + +% DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE DNS Request ID %1: update message to remove forward DNS RR entries could not be constructed for this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting forward RR (DHCID RR) removal. This is due +to invalid data contained in the NameChangeRequest. The request will be aborted. +This is most likely a configuration issue. + +% DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a forward RR removal for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a forward RR remove. The application will retry +against the same server. + +% DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to remove forward RR entries for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing forward RRs for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to remove forward RRs mapping, is mangled or malformed. +The application will retry against the same server or others as appropriate. +/*sar*/ + +% DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while replacing forward address mapping for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was replacing a forward address mapping. The request will be +aborted. This is most likely a programmatic issue and should be reported. + +% DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE DNS Request ID %1: update message to replace a forward DNS entry could not be constructed from this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting a forward address replacement. This is +due to invalid data contained in the NameChangeRequest. The request will be +aborted. This is most likely a configuration issue. + +% DHCP_DDNS_FORWARD_REPLACE_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping replace for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a forward address update. The application will +retry against the same server or others as appropriate. + +% DHCP_DDNS_FORWARD_REPLACE_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to replace the address mapping for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while replacing forward address mapping for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to replace a forward address mapping, is mangled or malformed. +The application will retry against the same server or others as appropriate. + +% DHCP_DDNS_FWD_REQUEST_IGNORED Request ID %1: Forward updates are disabled, the forward portion of request will be ignored: %2 +This is a debug message issued when forward DNS updates are disabled and +DHCP_DDNS receives an update request containing a forward DNS update. The +forward update will not performed. + +% DHCP_DDNS_INVALID_RESPONSE received response to DNS Update message is malformed: %1 +This is a debug message issued when the DHCP-DDNS application encountered an +error while decoding a response to DNS Update message. Typically, this error +will be encountered when a response message is malformed. + +% DHCP_DDNS_NOT_ON_LOOPBACK the DHCP-DDNS server has been configured to listen on %1 which is not the local loopback. This is an insecure configuration supported for testing purposes only +This is a warning message issued when the DHCP-DDNS server is configured to +listen at an address other than the loopback address (127.0.0.1 or ::1). It is +possible for a malicious attacker to send bogus NameChangeRequests to it and +change entries in the DNS. For this reason, addresses other than the IPv4 or +IPv6 loopback addresses should only be used for testing purposes. A future +version of Kea will implement authentication to guard against such attacks. + +% DHCP_DDNS_NO_ELIGIBLE_JOBS although there are queued requests, there are pending transactions for each, Queue count: %1 Transaction count: %2 +This is a debug message issued when all of the queued requests represent clients +for which there is an update already in progress. This may occur under +normal operations but should be temporary situation. + +% DHCP_DDNS_NO_FWD_MATCH_ERROR Request ID %1: the configured list of forward DDNS domains does not contain a match for: %2 The request has been discarded. +This is an error message that indicates that DHCP_DDNS received a request to +update the forward DNS information for the given FQDN but for which there are +no configured DDNS domains in the DHCP_DDNS configuration. Either the DHCP_DDNS +configuration needs to be updated or the source of the FQDN itself should be +investigated. + +% DHCP_DDNS_NO_MATCH No DNS servers match FQDN %1 +This is warning message issued when there are no domains in the configuration +which match the cited fully qualified domain name (FQDN). The DNS Update +request for the FQDN cannot be processed. + +% DHCP_DDNS_NO_REV_MATCH_ERROR Request ID %1: the configured list of reverse DDNS domains does not contain a match for: %2 The request has been discarded. +This is an error message that indicates that DHCP_DDNS received a request to +update the reverse DNS information for the given FQDN but for which there are +no configured DDNS domains in the DHCP_DDNS configuration. Either the DHCP_DDNS +configuration needs to be updated or the source of the FQDN itself should be +investigated. + +% DHCP_DDNS_PROCESS_INIT application init invoked +This is a debug message issued when the DHCP-DDNS application enters +its initialization method. + +% DHCP_DDNS_QUEUE_MGR_QUEUE_FULL application request queue has reached maximum number of entries %1 +This an error message indicating that DHCP-DDNS is receiving DNS update +requests faster than they can be processed. This may mean the maximum queue +needs to be increased, the DHCP-DDNS clients are simply generating too many +requests too quickly, or perhaps upstream DNS servers are experiencing +load issues. + +% DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE Request ID %1: received and queued a request. +This is an informational message indicating that the NameChangeRequest listener used +by DHCP-DDNS to receive a request has received a request and queued it for further +processing. + +% DHCP_DDNS_QUEUE_MGR_RECONFIGURING application is reconfiguring the queue manager +This is an informational message indicating that DHCP_DDNS is reconfiguring the queue manager as part of normal startup or in response to a new configuration. + +% DHCP_DDNS_QUEUE_MGR_RECOVERING application is attempting to recover from a queue manager IO error +This is an informational message indicating that DHCP_DDNS is attempting to +restart the queue manager after it suffered an IO error while receiving +requests. + +% DHCP_DDNS_QUEUE_MGR_RECV_ERROR application's queue manager was notified of a request receive error by its listener. +This is an error message indicating that the NameChangeRequest listener used by +DHCP-DDNS to receive requests encountered an IO error. There should be +corresponding log messages from the listener layer with more details. This may +indicate a network connectivity or system resource issue. + +% DHCP_DDNS_QUEUE_MGR_RESUME_ERROR application could not restart the queue manager, reason: %1 +This is an error message indicating that DHCP_DDNS's Queue Manager could not +be restarted after stopping due to a full receive queue. This means that +the application cannot receive requests. This is most likely due to DHCP_DDNS +configuration parameters referring to resources such as an IP address or port, +that is no longer unavailable. DHCP_DDNS will attempt to restart the queue +manager if given a new configuration. + +% DHCP_DDNS_QUEUE_MGR_RESUMING application is resuming listening for requests now that the request queue size has reached %1 of a maximum %2 allowed +This is an informational message indicating that DHCP_DDNS, which had stopped +accepting new requests, has processed enough entries from the receive queue to +resume accepting requests. + +% DHCP_DDNS_QUEUE_MGR_STARTED application's queue manager has begun listening for requests. +This is a debug message indicating that DHCP_DDNS's Queue Manager has +successfully started and is now listening for NameChangeRequests. + +% DHCP_DDNS_QUEUE_MGR_START_ERROR application could not start the queue manager, reason: %1 +This is an error message indicating that DHCP_DDNS's Queue Manager could not +be started. This means that the application cannot receive requests. This is +most likely due to DHCP_DDNS configuration parameters referring to resources +such as an IP address or port, that are unavailable. DHCP_DDNS will attempt to +restart the queue manager if given a new configuration. + +% DHCP_DDNS_QUEUE_MGR_STOPPED application's queue manager has stopped listening for requests. +This is a debug message indicating that DHCP_DDNS's Queue Manager has +stopped listening for NameChangeRequests. This may be because of normal event +such as reconfiguration or as a result of an error. There should be log +messages preceding this one to indicate why it has stopped. + +% DHCP_DDNS_QUEUE_MGR_STOPPING application is stopping the queue manager for %1 +This is an informational message indicating that DHCP_DDNS is stopping the +queue manager either to reconfigure it or as part of application shutdown. + +% DHCP_DDNS_QUEUE_MGR_STOP_ERROR application encountered an error stopping the queue manager: %1 +This is an error message indicating that DHCP_DDNS encountered an error while +trying to stop the queue manager. This error is unlikely to occur or to +impair the application's ability to function but it should be reported for +analysis. + +% DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR application's queue manager request receive handler experienced an unexpected exception %1: +This is an error message indicating that an unexpected error occurred within the +DHCP_DDNS's Queue Manager request receive completion handler. This is most +likely a programmatic issue that should be reported. The application may +recover on its own. + +% DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP application's queue manager receive was +aborted unexpectedly while queue manager state is: %1 +This is an error message indicating that DHCP_DDNS's Queue Manager request +receive was unexpected interrupted. Normally, the read is receive is only +interrupted as a normal part of stopping the queue manager. This is most +likely a programmatic issue that should be reported. + +% DHCP_DDNS_REMOVE_FAILED DHCP_DDNS Request ID %1: Transaction outcome: %2 +This is an error message issued after DHCP_DDNS attempts to submit DNS mapping +entry removals have failed. The precise reason for the failure should be +documented in preceding log entries. + +% DHCP_DDNS_REMOVE_SUCCEEDED DHCP_DDNS Request ID %1: successfully removed the DNS mapping addition for this request: %2 +This is an informational message issued after DHCP_DDNS has submitted DNS +mapping removals which were received and accepted by an appropriate DNS server. + +% DHCP_DDNS_REQUEST_DROPPED Request ID %1: Request contains no enabled update requests and will be dropped: %2 +This is a debug message issued when DHCP_DDNS receives a request which does not +contain updates in a direction that is enabled. In other words, if only forward +updates are enabled and request is received that asks only for reverse updates +then the request is dropped. + +% DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing reverse address mapping for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was removing a reverse address mapping. The request will be +aborted. This is most likely a programmatic issue and should be reported. + +% DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE DNS Request ID %1: update message to remove a reverse DNS entry could not be constructed from this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting a reverse PTR removal. This is +due to invalid data contained in the NameChangeRequest. The request will be +aborted. This is most likely a configuration issue. + +% DHCP_DDNS_REVERSE_REMOVE_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a reverse mapping remove for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a reverse address update. The application will +retry against the same server or others as appropriate. + +% DHCP_DDNS_REVERSE_REMOVE_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to remove the reverse mapping for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing reverse address mapping for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to remove a reverse address, is mangled or malformed. +The application will retry against the same server or others as appropriate. + +% DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while replacing reverse address mapping for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was replacing a reverse address mapping. The request will be +aborted. This is most likely a programmatic issue and should be reported. + +% DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE DNS Request ID %1: update message to replace a reverse DNS entry could not be constructed from this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting a reverse PTR replacement. This is +due to invalid data contained in the NameChangeRequest. The request will be +aborted. This is most likely a configuration issue. + +% DHCP_DDNS_REVERSE_REPLACE_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a reverse mapping replacement for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a reverse address update. The application will +retry against the same server or others as appropriate. + +% DHCP_DDNS_REVERSE_REPLACE_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to replace the reverse mapping for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while replacing reverse address mapping for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to replace a reverse address, is mangled or malformed. +The application will retry against the same server or others as appropriate. + +% DHCP_DDNS_REV_REQUEST_IGNORED Request ID %1: Reverse updates are disabled, the reverse portion of request will be ignored: %2 +This is a debug message issued when reverse DNS updates are disabled and +DHCP_DDNS receives an update request containing a reverse DNS update. The +reverse update will not performed. + +% DHCP_DDNS_RUN_EXIT application is exiting the event loop +This is a debug message issued when the DHCP-DDNS server exits its +event lo + +% DHCP_DDNS_SHUTDOWN_COMMAND application received shutdown command with args: %1 +This is a debug message issued when the application has been instructed +to shut down by the controller. + +% DHCP_DDNS_STARTED Kea DHCP-DDNS server version %1 started +This informational message indicates that the DHCP-DDNS server has +processed all configuration information and is ready to begin processing. +The version is also printed. + +% DHCP_DDNS_STARTING_TRANSACTION Request ID %1: +This is a debug message issued when DHCP-DDNS has begun a transaction for +a given request. + +% DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR Request ID %1: application encountered an unexpected error while carrying out a NameChangeRequest: %2 +This is error message issued when the application fails to process a +NameChangeRequest correctly. Some or all of the DNS updates requested as part +of this update did not succeed. This is a programmatic error and should be +reported. + +% DHCP_DDNS_TRANS_SEND_ERROR Request ID %1: application encountered an unexpected error while attempting to send a DNS update: %2 +This is error message issued when the application is able to construct an update +message but the attempt to send it suffered an unexpected error. This is most +likely a programmatic error, rather than a communications issue. Some or all +of the DNS updates requested as part of this request did not succeed. + +% DHCP_DDNS_UPDATE_REQUEST_SENT Request ID %1: %2 to server: %3 +This is a debug message issued when DHCP_DDNS sends a DNS request to a DNS +server. + +% DHCP_DDNS_UPDATE_RESPONSE_RECEIVED Request ID %1: to server: %2 status: %3 +This is a debug message issued when DHCP_DDNS receives sends a DNS update +response from a DNS server. diff --git a/src/lib/d2srv/d2_simple_parser.cc b/src/lib/d2srv/d2_simple_parser.cc new file mode 100644 index 0000000..81bab9c --- /dev/null +++ b/src/lib/d2srv/d2_simple_parser.cc @@ -0,0 +1,310 @@ +// Copyright (C) 2017-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 + +#include +#include +#include +#include +#include +#include + +using namespace isc::data; +using namespace isc::d2; +using namespace isc; + +namespace { + +dhcp_ddns::NameChangeProtocol +getProtocol(ConstElementPtr map, const std::string& name) { + ConstElementPtr value = map->get(name); + if (!value) { + isc_throw(D2CfgError, "Mandatory parameter " << name + << " not found (" << map->getPosition() << ")"); + } + std::string str = value->stringValue(); + try { + return (dhcp_ddns::stringToNcrProtocol(str)); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, + "invalid NameChangeRequest protocol (" << str + << ") specified for parameter '" << name + << "' (" << value->getPosition() << ")"); + } +} + +dhcp_ddns::NameChangeFormat +getFormat(ConstElementPtr map, const std::string& name) { + ConstElementPtr value = map->get(name); + if (!value) { + isc_throw(D2CfgError, "Mandatory parameter " << name + << " not found (" << map->getPosition() << ")"); + } + std::string str = value->stringValue(); + try { + return (dhcp_ddns::stringToNcrFormat(str)); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, + "invalid NameChangeRequest format (" << str + << ") specified for parameter '" << name + << "' (" << value->getPosition() << ")"); + } +} + +} // anon + +namespace isc { +namespace d2 { +/// @brief This sets of arrays define the default values and +/// values inherited (derived) between various scopes. +/// +/// Each of those is documented in @file d2_simple_parser.cc. This +/// is different than most other comments in Kea code. The reason +/// for placing those in .cc rather than .h file is that it +/// is expected to be one centralized place to look at for +/// the default values. This is expected to be looked at also by +/// people who are not skilled in C or C++, so they may be +/// confused with the differences between declaration and definition. +/// As such, there's one file to look at that hopefully is readable +/// without any C or C++ skills. +/// +/// @{ + +/// @brief This table defines default global values for D2 +/// +/// Some of the global parameters defined in the global scope (i.e. directly +/// in DhcpDdns) are optional. If not defined, the following values will be +/// used. +const SimpleDefaults D2SimpleParser::D2_GLOBAL_DEFAULTS = { + { "ip-address", Element::string, "127.0.0.1" }, + { "port", Element::integer, "53001" }, + { "dns-server-timeout", Element::integer, "100" }, // in milliseconds + { "ncr-protocol", Element::string, "UDP" }, + { "ncr-format", Element::string, "JSON" } +}; + +/// Supplies defaults for ddns-domains list elements (i.e. DdnsDomains) +const SimpleDefaults D2SimpleParser::TSIG_KEY_DEFAULTS = { + { "digest-bits", Element::integer, "0" } +}; + +/// Supplies defaults for optional values in DDNS domain managers +/// (e.g. "forward-ddns" and "reverse-ddns"). +/// @note While there are none yet defined, it is highly likely +/// there will be domain manager defaults added in the future. +/// This code to set defaults already uses this list, so supporting +/// values will simply require adding them to this list. +const SimpleDefaults D2SimpleParser::DDNS_DOMAIN_MGR_DEFAULTS = { +}; + +/// Supplies defaults for ddns-domains list elements (i.e. DdnsDomains) +const SimpleDefaults D2SimpleParser::DDNS_DOMAIN_DEFAULTS = { + { "key-name", Element::string, "" } +}; + +/// Supplies defaults for optional values DdnsDomain entries. +const SimpleDefaults D2SimpleParser::DNS_SERVER_DEFAULTS = { + { "hostname", Element::string, "" }, + { "port", Element::integer, "53" }, + { "key-name", Element::string, "" } +}; + +/// @} + +/// --------------------------------------------------------------------------- +/// --- end of default values ------------------------------------------------- +/// --------------------------------------------------------------------------- + +size_t +D2SimpleParser::setAllDefaults(isc::data::ElementPtr global) { + size_t cnt = 0; + // Set global defaults first. + cnt = setDefaults(global, D2_GLOBAL_DEFAULTS); + + // If the key list is present, set its members' defaults + if (global->find("tsig-keys")) { + ConstElementPtr keys = global->get("tsig-keys"); + cnt += setListDefaults(keys, TSIG_KEY_DEFAULTS); + } else { + // Not present, so add an empty list. + ConstElementPtr list(new ListElement()); + global->set("tsig-keys", list); + cnt++; + } + + // Set the forward domain manager defaults. + cnt += setManagerDefaults(global, "forward-ddns", DDNS_DOMAIN_MGR_DEFAULTS); + + // Set the reverse domain manager defaults. + cnt += setManagerDefaults(global, "reverse-ddns", DDNS_DOMAIN_MGR_DEFAULTS); + return (cnt); +} + +size_t +D2SimpleParser::setDdnsDomainDefaults(ElementPtr domain, + const SimpleDefaults& domain_defaults) { + size_t cnt = 0; + + // Set the domain's scalar defaults + cnt += setDefaults(domain, domain_defaults); + if (domain->find("dns-servers")) { + // Now add the defaults to its server list. + ConstElementPtr servers = domain->get("dns-servers"); + cnt += setListDefaults(servers, DNS_SERVER_DEFAULTS); + } + + return (cnt); +} + + +size_t +D2SimpleParser::setManagerDefaults(ElementPtr global, + const std::string& mgr_name, + const SimpleDefaults& mgr_defaults) { + size_t cnt = 0; + + if (!global->find(mgr_name)) { + // If it's not present, then default is an empty map + ConstElementPtr map(new MapElement()); + global->set(mgr_name, map); + ++cnt; + } else { + // Get a writable copy of the manager element map + ElementPtr mgr = + boost::const_pointer_cast(global->get(mgr_name)); + + // Set the manager's scalar defaults first + cnt += setDefaults(mgr, mgr_defaults); + + // Get the domain list and set defaults for them. + // The domain list may not be present ddns for this + // manager is disabled. + if (mgr->find("ddns-domains")) { + ConstElementPtr domains = mgr->get("ddns-domains"); + BOOST_FOREACH(ElementPtr domain, domains->listValue()) { + // Set the domain's defaults. We can't use setListDefaults() + // as this does not handle sub-lists or maps, like server list. + cnt += setDdnsDomainDefaults(domain, DDNS_DOMAIN_DEFAULTS); + } + } + + } + + return (cnt); +} + +void D2SimpleParser::parse(const D2CfgContextPtr& ctx, + const isc::data::ConstElementPtr& config, + bool check_only) { + // TSIG keys need to parse before the Domains, so we can catch Domains + // that specify undefined keys. Create the necessary parsing order now. + // addToParseOrder("tsig-keys"); + // addToParseOrder("forward-ddns"); + // addToParseOrder("reverse-ddns"); + + ConstElementPtr keys = config->get("tsig-keys"); + if (keys) { + TSIGKeyInfoListParser parser; + ctx->setKeys(parser.parse(keys)); + } + + ConstElementPtr fwd = config->get("forward-ddns"); + if (fwd) { + DdnsDomainListMgrParser parser; + DdnsDomainListMgrPtr mgr = parser.parse(fwd, "forward-ddns", + ctx->getKeys()); + ctx->setForwardMgr(mgr); + } + + ConstElementPtr rev = config->get("reverse-ddns"); + if (rev) { + DdnsDomainListMgrParser parser; + DdnsDomainListMgrPtr mgr = parser.parse(rev, "reverse-ddns", + ctx->getKeys()); + ctx->setReverseMgr(mgr); + } + + // Fetch the parameters in the config, performing any logical + // validation required. + asiolink::IOAddress ip_address(0); + uint32_t port = 0; + uint32_t dns_server_timeout = 0; + dhcp_ddns::NameChangeProtocol ncr_protocol = dhcp_ddns::NCR_UDP; + dhcp_ddns::NameChangeFormat ncr_format = dhcp_ddns::FMT_JSON; + + ip_address = SimpleParser::getAddress(config, "ip-address"); + + if ((ip_address.toText() == "0.0.0.0") || + (ip_address.toText() == "::")) { + isc_throw(D2CfgError, "IP address cannot be \"" + << ip_address << "\"" + << " (" << config->get("ip-address")->getPosition() << ")"); + } + + port = SimpleParser::getUint32(config, "port"); + + dns_server_timeout = SimpleParser::getUint32(config, "dns-server-timeout"); + + ncr_protocol = getProtocol(config, "ncr-protocol"); + if (ncr_protocol != dhcp_ddns::NCR_UDP) { + isc_throw(D2CfgError, "ncr-protocol : " + << dhcp_ddns::ncrProtocolToString(ncr_protocol) + << " is not yet supported (" + << config->get("ncr-protocol")->getPosition() << ")"); + } + + ncr_format = getFormat(config, "ncr-format"); + if (ncr_format != dhcp_ddns::FMT_JSON) { + isc_throw(D2CfgError, "NCR Format:" + << dhcp_ddns::ncrFormatToString(ncr_format) + << " is not yet supported" + << " (" << config->get("ncr-format")->getPosition() << ")"); + } + + ConstElementPtr user = config->get("user-context"); + if (user) { + ctx->setContext(user); + } + + ConstElementPtr socket = config->get("control-socket"); + if (socket) { + if (socket->getType() != Element::map) { + isc_throw(D2CfgError, "Specified control-socket is expected to be a map" + ", i.e. a structure defined within { }"); + } + ctx->setControlSocketInfo(socket); + } + + // Finally, let's get the hook libs! + using namespace isc::hooks; + HooksConfig& libraries = ctx->getHooksConfig(); + ConstElementPtr hooks = config->get("hooks-libraries"); + if (hooks) { + HooksLibrariesParser hooks_parser; + hooks_parser.parse(libraries, hooks); + libraries.verifyLibraries(hooks->getPosition()); + } + + // Attempt to create the new client config. This ought to fly as + // we already validated everything. + D2ParamsPtr params(new D2Params(ip_address, port, dns_server_timeout, + ncr_protocol, ncr_format)); + + ctx->getD2Params() = params; + + if (!check_only) { + // This occurs last as if it succeeds, there is no easy way + // revert it. As a result, the failure to commit a subsequent + // change causes problems when trying to roll back. + HooksManager::prepareUnloadLibraries(); + static_cast(HooksManager::unloadLibraries()); + libraries.loadLibraries(); + } +} + +} +} diff --git a/src/lib/d2srv/d2_simple_parser.h b/src/lib/d2srv/d2_simple_parser.h new file mode 100644 index 0000000..49f0d8a --- /dev/null +++ b/src/lib/d2srv/d2_simple_parser.h @@ -0,0 +1,96 @@ +// Copyright (C) 2017-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/. + +#ifndef D2_SIMPLE_PARSER_H +#define D2_SIMPLE_PARSER_H + +#include +#include + +namespace isc { +namespace d2 { + +/// @brief SimpleParser specialized for D2 +/// +/// This class is a @ref isc::data::SimpleParser dedicated to D2. +/// In particular, it contains all the default values and names of the +/// parameters that are to be derived (inherited) between scopes. +/// For the actual values, see @file d2_simple_parser.cc +class D2SimpleParser : public data::SimpleParser { +public: + + /// @brief Sets all defaults for D2 configuration + /// + /// This method sets global and element defaults. + /// + /// @param global scope to be filled in with defaults. + /// @return number of default values added + static size_t setAllDefaults(data::ElementPtr global); + + // see d2_simple_parser.cc for comments for those parameters + static const data::SimpleDefaults D2_GLOBAL_DEFAULTS; + + // Defaults for tsig-keys list elements, TSIGKeyInfos + static const data::SimpleDefaults TSIG_KEY_DEFAULTS; + + // Defaults for -ddns elements, DdnsDomainListMgrs + static const data::SimpleDefaults DDNS_DOMAIN_MGR_DEFAULTS; + + // Defaults for ddns-domains list elements, DdnsDomains + static const data::SimpleDefaults DDNS_DOMAIN_DEFAULTS; + + // Defaults for dns-servers list elements, DnsServerInfos + static const data::SimpleDefaults DNS_SERVER_DEFAULTS; + + /// @brief Adds default values to a DDNS Domain element + /// + /// Adds the scalar default values to the given DDNS domain + /// element, and then adds the DNS Server defaults to the domain's + /// server list, "dns-servers". + /// + /// @param domain DDNS domain element to which defaults should be added + /// @param domain_defaults list of default values from which to add + /// @return returns the number of default values added + static size_t setDdnsDomainDefaults(data::ElementPtr domain, + const data::SimpleDefaults& + domain_defaults); + + /// @brief Adds default values to a DDNS Domain List Manager + /// + /// This function looks for the named DDNS domain manager element within + /// the given element tree. If it is found, it adds the scalar default + /// values to the manager element and then adds the DDNS Domain defaults + /// to its domain list, "ddns-domains". If the manager element is not + /// found, then an empty map entry is added for it, thus defaulting the + /// manager to "disabled". + /// + /// @param global element tree containing the DDNS domain manager element + /// to which defaults should be + /// added + /// @param mgr_name name of the manager element within the element tree + /// (e.g. "forward-ddns", "reverse-ddns") + /// @param mgr_defaults list of default values from which to add + /// @return returns the number of default values added + static size_t setManagerDefaults(data::ElementPtr global, + const std::string& mgr_name, + const data::SimpleDefaults& mgr_defaults); + + /// @brief Parses the whole D2 configuration + /// + /// @param ctx - parsed information will be stored here + /// @param config - Element tree structure that holds configuration + /// @param check_only - if true the configuration is verified only, not applied + /// + /// @throw ConfigError if any issues are encountered. + void parse(const D2CfgContextPtr& ctx, + const isc::data::ConstElementPtr& config, + bool check_only); +}; + +}; +}; + +#endif diff --git a/src/lib/d2srv/d2_stats.cc b/src/lib/d2srv/d2_stats.cc new file mode 100644 index 0000000..8534f13 --- /dev/null +++ b/src/lib/d2srv/d2_stats.cc @@ -0,0 +1,58 @@ +// 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/. + +/// Defines the logger used by the top-level component of kea-dhcp-ddns. + +#include + +#include +#include + +using namespace std; +using namespace isc::stats; + +namespace isc { +namespace d2 { + +const list +D2Stats::ncr = { + "ncr-received", + "ncr-invalid", + "ncr-error" +}; + +const list +D2Stats::update = { + "update-sent", + "update-signed", + "update-unsigned", + "update-success", + "update-timeout", + "update-error" +}; + +const list +D2Stats::key = { + "update-sent", + "update-success", + "update-timeout", + "update-error" +}; + +void +D2Stats::init() { + StatsMgr& stats_mgr = isc::stats::StatsMgr::instance(); + stats_mgr.setMaxSampleCountDefault(0); + for (const auto& name : D2Stats::ncr) { + stats_mgr.setValue(name, static_cast(0)); + } + for (const auto& name : D2Stats::update) { + stats_mgr.setValue(name, static_cast(0)); + } +}; + +} // namespace d2 +} // namespace isc diff --git a/src/lib/d2srv/d2_stats.h b/src/lib/d2srv/d2_stats.h new file mode 100644 index 0000000..b4122b4 --- /dev/null +++ b/src/lib/d2srv/d2_stats.h @@ -0,0 +1,53 @@ +// 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/. + +#ifndef D2_STATS_H +#define D2_STATS_H + +#include +#include + +namespace isc { +namespace d2 { + +/// @brief Statistics Names. +class D2Stats { +public: + /// @brief Global NCR statistics names. + /// + /// - ncr-received + /// - ncr-invalid + /// - ncr-error + static const std::list ncr; + + /// @brief Global DNS update statistics names. + /// + /// - update-sent + /// - update-signed + /// - update-unsigned + /// - update-success + /// - update-timeout + /// - update-error + static const std::list update; + + /// @brief Key DNS update statistics names. + /// + /// - update-sent + /// - update-success + /// - update-timeout + /// - update-error + static const std::list key; + + /// @brief Initialize D2 statistics. + /// + /// @note: Add default samples if needed. + static void init(); +}; + +} // namespace d2 +} // namespace isc + +#endif // D2_STATS_H diff --git a/src/lib/d2srv/d2_tsig_key.cc b/src/lib/d2srv/d2_tsig_key.cc new file mode 100644 index 0000000..75dc527 --- /dev/null +++ b/src/lib/d2srv/d2_tsig_key.cc @@ -0,0 +1,72 @@ +// 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/. + +/// Defines the logger used by the top-level component of kea-dhcp-ddns. + +#include + +#include +#include +#include + +using namespace isc::dns; +using namespace isc::stats; +using namespace std; + +namespace isc { +namespace d2 { + +D2TsigKey::D2TsigKey(const std::string& key_spec) : TSIGKey(key_spec) { + initStats(); +} + +D2TsigKey::D2TsigKey(const Name& key_name, const Name& algorithm_name, + const void* secret, size_t secret_len, size_t digestbits) + : TSIGKey(key_name, algorithm_name, secret, secret_len, digestbits) { + initStats(); +} + +D2TsigKey::~D2TsigKey() { + removeStats(); +} + +void +D2TsigKey::initStats() { + StatsMgr& stats_mgr = StatsMgr::instance(); + const string& kname = getKeyName().toText(); + for (const auto& name : D2Stats::key) { + const string& sname = StatsMgr::generateName("key", kname, name); + stats_mgr.setValue(sname, static_cast(0)); + } +} + +void +D2TsigKey::removeStats() { + StatsMgr& stats_mgr = StatsMgr::instance(); + const string& kname = getKeyName().toText(); + for (const auto& name : D2Stats::key) { + string sname = StatsMgr::generateName("key", kname, name); + stats_mgr.del(sname); + } +} + +void +D2TsigKey::resetStats() { + StatsMgr& stats_mgr = StatsMgr::instance(); + const string& kname = getKeyName().toText(); + for (const auto& name : D2Stats::key) { + string sname = StatsMgr::generateName("key", kname, name); + stats_mgr.reset(sname); + } +} + +TSIGContextPtr +D2TsigKey::createContext() { + return (TSIGContextPtr(new TSIGContext(*this))); +} + +} // namespace d2 +} // namespace isc diff --git a/src/lib/d2srv/d2_tsig_key.h b/src/lib/d2srv/d2_tsig_key.h new file mode 100644 index 0000000..4cdb036 --- /dev/null +++ b/src/lib/d2srv/d2_tsig_key.h @@ -0,0 +1,76 @@ +// 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/. + +#ifndef D2_TSIG_KEY_H +#define D2_TSIG_KEY_H + +#include +#include +#include + +namespace isc { +namespace d2 { + +/// @brief Statistics keeping extension of the DNS TSIGKey class. +/// +/// Implements a TSIGKey derived class which can be used as the value +/// of TSIGKeyPtr so with minimal or no update to the DNS++ library. +class D2TsigKey : public dns::TSIGKey { +public: + /// @brief Constructor. + /// + /// Initialize the key statistics. + /// + /// @param key_spec Specification of the key + /// (name:secret[:algorithm][:digestbits]) + explicit D2TsigKey(const std::string& key_spec); + + /// @brief Constructor. + /// + /// Initialize the key statistics. + /// + /// @param key_name The name of the key as a domain name. + /// @param algorithm_name The hash algorithm used for this key in the + /// form of domain name. + /// @param secret Point to a binary sequence of the shared secret to be + /// used for this key. + /// @param secret_len The size of the binary %data (@c secret) in bytes. + /// @param digestbits The number of bits to include in the digest + /// (0 means to include all) + D2TsigKey(const dns::Name& key_name, const dns::Name& algorithm_name, + const void* secret, size_t secret_len, size_t digestbits = 0); + + /// @brief Destructor. + /// + /// Remove the key statistics. + virtual ~D2TsigKey(); + + /// @brief Reset statistics. + /// + virtual void resetStats(); + + /// @brief Create TSIG context. + /// + /// @note Derived classes can implement their own specific context. + /// + /// @return The specific @ref dns::TSIGContext of the @ref dns::TSIGKey. + virtual dns::TSIGContextPtr createContext(); + +private: + /// @brief Initialize key statistics. + void initStats(); + + /// @brief Remove key statistics. + void removeStats(); +}; + +/// @brief Type of pointer to a D2 TSIG key. +typedef boost::shared_ptr D2TsigKeyPtr; + +} // namespace d2 +} // namespace isc + +#endif // D2_TSIG_KEY_H diff --git a/src/lib/d2srv/d2_update_message.cc b/src/lib/d2srv/d2_update_message.cc new file mode 100644 index 0000000..1917d6e --- /dev/null +++ b/src/lib/d2srv/d2_update_message.cc @@ -0,0 +1,230 @@ +// 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 + +#include +#include +#include +#include +#include + +namespace isc { +namespace d2 { + +using namespace isc::dns; + +D2UpdateMessage::D2UpdateMessage(const Direction direction) + : message_(direction == INBOUND ? + dns::Message::PARSE : dns::Message::RENDER) { + // If this object is to create an outgoing message, we have to + // set the proper Opcode field and QR flag here. + if (direction == OUTBOUND) { + message_.setOpcode(Opcode(Opcode::UPDATE_CODE)); + message_.setHeaderFlag(dns::Message::HEADERFLAG_QR, false); + message_.setRcode(Rcode::NOERROR()); + } +} + +D2UpdateMessage::QRFlag +D2UpdateMessage::getQRFlag() const { + return (message_.getHeaderFlag(dns::Message::HEADERFLAG_QR) ? + RESPONSE : REQUEST); +} + +uint16_t +D2UpdateMessage::getId() const { + return (message_.getQid()); +} + +void +D2UpdateMessage::setId(const uint16_t id) { + message_.setQid(id); +} + + +const dns::Rcode& +D2UpdateMessage::getRcode() const { + return (message_.getRcode()); +} + +void +D2UpdateMessage::setRcode(const dns::Rcode& rcode) { + message_.setRcode(rcode); +} + +unsigned int +D2UpdateMessage::getRRCount(const UpdateMsgSection section) const { + return (message_.getRRCount(ddnsToDnsSection(section))); +} + +const dns::RRsetIterator +D2UpdateMessage::beginSection(const UpdateMsgSection section) const { + return (message_.beginSection(ddnsToDnsSection(section))); +} + +const dns::RRsetIterator +D2UpdateMessage::endSection(const UpdateMsgSection section) const { + return (message_.endSection(ddnsToDnsSection(section))); +} + +void +D2UpdateMessage::setZone(const Name& zone, const RRClass& rrclass) { + // The Zone data is kept in the underlying Question class. If there + // is a record stored there already, we need to remove it, because + // we may have at most one Zone record in the DNS Update message. + if (message_.getRRCount(dns::Message::SECTION_QUESTION) > 0) { + message_.clearSection(dns::Message::SECTION_QUESTION); + } + // Add the new record... + Question question(zone, rrclass, RRType::SOA()); + message_.addQuestion(question); + // ... and update the local class member holding the D2Zone object. + zone_.reset(new D2Zone(question.getName(), question.getClass())); +} + +D2ZonePtr +D2UpdateMessage::getZone() const { + return (zone_); +} + +void +D2UpdateMessage::addRRset(const UpdateMsgSection section, + const dns::RRsetPtr& rrset) { + if (section == SECTION_ZONE) { + isc_throw(isc::BadValue, "unable to add RRset to the Zone section" + " of the DNS Update message, use setZone instead"); + } + message_.addRRset(ddnsToDnsSection(section), rrset); +} + +void +D2UpdateMessage::toWire(AbstractMessageRenderer& renderer, + TSIGContext* const tsig_context) { + // We are preparing the wire format of the message, meaning + // that this message will be sent as a request to the DNS. + // Therefore, we expect that this message is a REQUEST. + if (getQRFlag() != REQUEST) { + isc_throw(InvalidQRFlag, "QR flag must be cleared for the outgoing" + " DNS Update message"); + } + // According to RFC2136, the ZONE section may contain exactly one + // record. + if (getRRCount(SECTION_ZONE) != 1) { + isc_throw(InvalidZoneSection, "Zone section of the DNS Update message" + " must comprise exactly one record (RFC2136, section 2.3)"); + } + message_.toWire(renderer, tsig_context); +} + +void +D2UpdateMessage::fromWire(const void* received_data, size_t bytes_received, + dns::TSIGContext* const tsig_context) { + // First, use the underlying dns::Message implementation to get the + // contents of the DNS response message. Note that it may or may + // not be the message that we are interested in, but needs to be + // parsed so as we can check its ID, Opcode etc. + isc::util::InputBuffer received_data_buffer(received_data, bytes_received); + message_.fromWire(received_data_buffer); + + // If tsig_context is not NULL, then we need to verify the message. + if (tsig_context) { + TSIGError error = tsig_context->verify(message_.getTSIGRecord(), + received_data, bytes_received); + if (error != TSIGError::NOERROR()) { + isc_throw(TSIGVerifyError, "TSIG verification failed: " + << error.toText()); + } + } + + // This class exposes the getZone() function. This function will return + // pointer to the D2Zone object if non-empty Zone section exists in the + // received message. It will return NULL pointer if it doesn't exist. + // The pointer is held in the D2UpdateMessage class member. We need to + // update this pointer every time we parse the message. + if (getRRCount(D2UpdateMessage::SECTION_ZONE) > 0) { + // There is a Zone section in the received message. Replace + // Zone pointer with the new value. + QuestionPtr question = *message_.beginQuestion(); + // If the Zone counter is greater than 0 (which we have checked) + // there must be a valid Question pointer stored in the message_ + // object. If there isn't, it is a programming error. + if (!question) { + isc_throw(isc::Unexpected, "question is null?!"); + } + zone_.reset(new D2Zone(question->getName(), question->getClass())); + + } else { + // Zone section doesn't hold any pointers, so set the pointer to NULL. + zone_.reset(); + + } + // Check that the content of the received message is sane. + // One of the basic checks to do is to verify that we have + // received the DNS update message. If not, it can be dropped + // or an error message can be printed. Other than that, we + // will check that there is at most one Zone record and QR flag + // is set. + validateResponse(); +} + +dns::Message::Section +D2UpdateMessage::ddnsToDnsSection(const UpdateMsgSection section) { + /// The following switch maps the enumerator values from the + /// DNS Update message to the corresponding enumerator values + /// representing fields of the DNS message. + switch(section) { + case SECTION_ZONE : + return (dns::Message::SECTION_QUESTION); + + case SECTION_PREREQUISITE: + return (dns::Message::SECTION_ANSWER); + + case SECTION_UPDATE: + return (dns::Message::SECTION_AUTHORITY); + + case SECTION_ADDITIONAL: + return (dns::Message::SECTION_ADDITIONAL); + + default: + ; + } + isc_throw(dns::InvalidMessageSection, + "unknown message section " << section); +} + +void +D2UpdateMessage::validateResponse() const { + // Verify that we are dealing with the DNS Update message. According to + // RFC 2136, section 3.8 server will copy the Opcode from the query. + // If we are dealing with a different type of message, we may simply + // stop further processing, because it is likely that the message was + // directed to someone else. + if (message_.getOpcode() != Opcode::UPDATE()) { + isc_throw(NotUpdateMessage, "received message is not a DDNS update," + << " received message code is " + << message_.getOpcode().getCode()); + } + // Received message should have QR flag set, which indicates that it is + // a RESPONSE. + if (getQRFlag() == REQUEST) { + isc_throw(InvalidQRFlag, "received message should have QR flag set," + " to indicate that it is a RESPONSE message; the QR" + << " flag in received message is unset"); + } + // DNS server may copy a Zone record from the query message. Since query + // must comprise exactly one Zone record (RFC 2136, section 2.3), the + // response message may contain 1 record at most. It may also contain no + // records if a server chooses not to copy Zone section. + if (getRRCount(SECTION_ZONE) > 1) { + isc_throw(InvalidZoneSection, "received message contains " + << getRRCount(SECTION_ZONE) << " Zone records," + << " it should contain at most 1 record"); + } +} + +} // namespace d2 +} // namespace isc diff --git a/src/lib/d2srv/d2_update_message.h b/src/lib/d2srv/d2_update_message.h new file mode 100644 index 0000000..8365128 --- /dev/null +++ b/src/lib/d2srv/d2_update_message.h @@ -0,0 +1,357 @@ +// 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/. + +#ifndef D2_UPDATE_MESSAGE_H +#define D2_UPDATE_MESSAGE_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace isc { +namespace d2 { + +/// @brief Exception indicating that Zone section contains invalid content. +/// +/// This exception is thrown when ZONE section of the DNS Update message +/// is invalid. According to RFC2136, section 2.3, the zone section is +/// allowed to contain exactly one record. When Request message contains +/// more records or is empty, this exception is thrown. +class InvalidZoneSection : public Exception { +public: + InvalidZoneSection(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Exception indicating that QR flag has invalid value. +/// +/// This exception is thrown when QR flag has invalid value for +/// the operation performed on the particular message. For instance, +/// the QR flag must be set to indicate that the given message is +/// a RESPONSE when @c D2UpdateMessage::fromWire is performed. +/// The QR flag must be cleared when @c D2UpdateMessage::toWire +/// is executed. +class InvalidQRFlag : public Exception { +public: + InvalidQRFlag(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Exception indicating that the parsed message is not DNS Update. +/// +/// This exception is thrown when decoding the DNS message which is not +/// a DNS Update. +class NotUpdateMessage : public Exception { +public: + NotUpdateMessage(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Exception indicating that a signed, inbound message failed to verify +/// +/// This exception is thrown when TSIG verification of a DNS server's response +/// fails. +class TSIGVerifyError : public Exception { +public: + TSIGVerifyError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + + +class D2UpdateMessage; + +/// @brief Pointer to the DNS Update Message. +typedef boost::shared_ptr D2UpdateMessagePtr; + +/// @brief The @c D2UpdateMessage encapsulates a DNS Update message. +/// +/// This class represents the DNS Update message. Functions exposed by this +/// class allow to specify the data sections carried by the message and create +/// an on-wire format of this message. This class is also used to decode +/// messages received from the DNS server in the on-wire format. +/// +/// Design choice: A dedicated class has been created to encapsulate +/// DNS Update message because existing @c isc::dns::Message is designed to +/// support regular DNS messages (described in RFC 1035) only. Although DNS +/// Update has the same format, particular sections serve different purposes. +/// In order to avoid rewrite of significant portions of @c isc::dns::Message +/// class, this class is implemented in-terms-of @c isc::dns::Message class +/// to reuse its functionality where possible. +class D2UpdateMessage { +public: + + /// @brief Indicates if the @c D2UpdateMessage object encapsulates Inbound + /// or Outbound message. + enum Direction { + INBOUND, + OUTBOUND + }; + + /// @brief Indicates whether DNS Update message is a REQUEST or RESPONSE. + enum QRFlag { + REQUEST, + RESPONSE + }; + + /// @brief Identifies sections in the DNS Update Message. + /// + /// Each message comprises message Header and may contain the following + /// sections: + /// - ZONE + /// - PREREQUISITE + /// - UPDATE + /// - ADDITIONAL + /// + /// The enum elements are used by functions such as @c getRRCount (to get + /// the number of records in a corresponding section) and @c beginSection + /// and @c endSection (to access data in the corresponding section). + enum UpdateMsgSection { + SECTION_ZONE, + SECTION_PREREQUISITE, + SECTION_UPDATE, + SECTION_ADDITIONAL + }; + +public: + /// @brief Constructor used to create an instance of the DNS Update Message + /// (either outgoing or incoming). + /// + /// This constructor is used to create an instance of either incoming or + /// outgoing DNS Update message. The boolean argument indicates whether it + /// is incoming (true) or outgoing (false) message. For incoming messages + /// the @c D2UpdateMessage::fromWire function is used to parse on-wire data. + /// For outgoing messages, modifier functions should be used to set the + /// message contents and @c D2UpdateMessage::toWire function to create + /// on-wire data. + /// + /// @param direction indicates if this is an inbound or outbound message. + D2UpdateMessage(const Direction direction = OUTBOUND); + + /// + /// @name Copy constructor and assignment operator + /// + /// Copy constructor and assignment operator are private because we assume + /// there will be no need to copy messages on the client side. + //@{ +private: + D2UpdateMessage(const D2UpdateMessage& source); + D2UpdateMessage& operator=(const D2UpdateMessage& source); + //@} + +public: + + /// @brief Returns enum value indicating if the message is a + /// REQUEST or RESPONSE + /// + /// The returned value is REQUEST if the message is created as an outgoing + /// message. In such case the QR flag bit in the message header is cleared. + /// The returned value is RESPONSE if the message is created as an incoming + /// message and the QR flag bit was set in the received message header. + /// + /// @return An enum value indicating whether the message is a + /// REQUEST or RESPONSE. + QRFlag getQRFlag() const; + + /// @brief Returns message ID. + /// + /// @return message ID. + uint16_t getId() const; + + /// @brief Sets message ID. + /// + /// @param id 16-bit value of the message id. + void setId(const uint16_t id); + + /// @brief Returns an object representing message RCode. + /// + /// @return An object representing message RCode. + const dns::Rcode& getRcode() const; + + /// @brief Sets message RCode. + /// + /// @param rcode An object representing message RCode. + void setRcode(const dns::Rcode& rcode); + + /// @brief Returns number of RRsets in the specified message section. + /// + /// @param section An @c UpdateMsgSection enum specifying a message section + /// for which the number of RRsets is to be returned. + /// + /// @return A number of RRsets in the specified message section. + unsigned int getRRCount(const UpdateMsgSection section) const; + + /// @name Functions returning iterators to RRsets in message sections. + /// + //@{ + /// @brief Return iterators pointing to the beginning of the list of RRsets, + /// which belong to the specified section. + /// + /// @param section An @c UpdateMsgSection enum specifying a message section + /// for which the iterator should be returned. + /// + /// @return An iterator pointing to the beginning of the list of the + /// RRsets, which belong to the specified section. + const dns::RRsetIterator beginSection(const UpdateMsgSection section) const; + + /// @brief Return iterators pointing to the end of the list of RRsets, + /// which belong to the specified section. + /// + /// @param section An @c UpdateMsgSection enum specifying a message section + /// for which the iterator should be returned. + /// + /// @return An iterator pointing to the end of the list of the + /// RRsets, which belong to the specified section. + const dns::RRsetIterator endSection(const UpdateMsgSection section) const; + //@} + + /// @brief Sets the Zone record. + /// + /// This function creates the @c D2Zone object, representing a Zone record + /// for the outgoing message. If the Zone record is already set, it is + /// replaced by the new record being set by this function. The RRType for + /// the record is always SOA. + /// + /// @param zone A name of the zone being updated. + /// @param rrclass A class of the zone record. + void setZone(const dns::Name& zone, const dns::RRClass& rrclass); + + /// @brief Returns a pointer to the object representing Zone record. + /// + /// @return A pointer to the object representing Zone record. + D2ZonePtr getZone() const; + + /// @brief Adds an RRset to the specified section. + /// + /// This function may throw exception if the specified section is + /// out of bounds or Zone section update is attempted. For Zone + /// section @c D2UpdateMessage::setZone function should be used instead. + /// Also, this function expects that @c rrset argument is non-NULL. + /// + /// @param section A message section where the RRset should be added. + /// @param rrset A reference to a RRset which should be added. + void addRRset(const UpdateMsgSection section, const dns::RRsetPtr& rrset); + + /// @name Functions to handle message encoding and decoding. + /// + //@{ + /// @brief Encode outgoing message into wire format. + /// + /// This function encodes the DNS Update into the wire format. The format of + /// such a message is described in the RFC2136, section 2. Some of the + /// sections which belong to encoded message may be empty. If a particular + /// message section is empty (does not comprise any RRs), the corresponding + /// counter in the message header is set to 0. These counters are: PRCOUNT, + /// UPCOUNT, ADCOUNT for the Prerequisites, Update RRs and Additional Data + /// RRs respectively. The ZOCOUNT must be equal to 1 because RFC2136 + /// requires that the message comprises exactly one Zone record. + /// + /// If given a TSIG context, this method will pass the context down into + /// dns::Message.toWire() method which signs the message using the context. + /// + /// This function does not guarantee exception safety. However, exceptions + /// should be rare because @c D2UpdateMessage class API prevents invalid + /// use of the class. The typical case, when this function may throw an + /// exception is when this it is called on the object representing + /// incoming (instead of outgoing) message. In such case, the QR field + /// will be set to RESPONSE, which is invalid setting when calling this + /// function. + /// + /// @param renderer A renderer object used to generate the message wire + /// format. + /// @param tsig_ctx A TSIG context that is to be used for signing the + /// message. If NULL the message will not be signed. + void toWire(dns::AbstractMessageRenderer& renderer, + dns::TSIGContext* const tsig_ctx = NULL); + + /// @brief Decode incoming message from the wire format. + /// + /// This function decodes the DNS Update message stored in the buffer + /// specified by the function argument. If given a TSIG context, then + /// the function will first attempt to use that context to verify the + /// message signature. If verification fails a TSIGVerifyError exception + /// will be thrown. The function then parses message header and extracts + /// the section counters: ZOCOUNT, PRCOUNT, UPCOUNT and ADCOUNT. Using + /// these counters, function identifies message sections, which follow + /// message header. These sections can be later accessed using: + /// @c D2UpdateMessage::getZone, @c D2UpdateMessage::beginSection and + /// @c D2UpdateMessage::endSection functions. + /// + /// This function is NOT exception safe. It signals message decoding errors + /// through exceptions. Message decoding error may occur if the received + /// message does not conform to the general DNS Message format, specified in + /// RFC 1035. Errors which are specific to DNS Update messages include: + /// - Invalid Opcode - not an UPDATE. + /// - Invalid QR flag - the QR bit should be set to indicate that the + /// message is the server response. + /// - The number of records in the Zone section is greater than 1. + /// + /// @param received_data buffer holding DNS Update message to be parsed. + /// @param bytes_received the number of bytes in received_data + /// @param tsig_context A TSIG context that is to be used to verify the + /// message. If NULL TSIG verification will not be attempted. + void fromWire(const void* received_data, size_t bytes_received, + dns::TSIGContext* const tsig_context = NULL); + //@} + +private: + /// Maps the values of the @c UpdateMessageSection field to the + /// corresponding values in the @c isc::dns::Message class. This + /// mapping is required here because this class uses @c isc::dns::Message + /// class to do the actual processing of the DNS Update message. + /// + /// @param section An enum indicating the section for which the + /// corresponding enum value from @c isc::dns::Message will be returned. + /// + /// @return The enum value indicating the section in the DNS message + /// represented by the @c isc::dns::Message class. + static + dns::Message::Section ddnsToDnsSection(const UpdateMsgSection section); + + /// @brief Checks received response message for correctness. + /// + /// This function verifies that the received response from a server is + /// correct. Currently this function checks the following: + /// - Opcode is 'DNS Update', + /// - QR flag is RESPONSE (flag bit is set), + /// - Zone section comprises at most one record. + /// + /// The function will throw exception if any of the conditions above are + /// not met. + /// + /// @throw isc::d2::NotUpdateMessage if invalid Opcode. + /// @throw isc::d2::InvalidQRFlag if QR flag is not set to RESPONSE + /// @throw isc::d2::InvalidZone section, if Zone section comprises more + /// than one record. + void validateResponse() const; + + /// @brief An object representing DNS Message which is used by the + /// implementation of @c D2UpdateMessage to perform low level. + /// + /// Declaration of this object pollutes the header with the details + /// of @c D2UpdateMessage implementation. It might be cleaner to use + /// Pimpl idiom to hide this object in an D2UpdateMessageImpl. However, + /// it would bring additional complications to the implementation + /// while the benefit would low - this header is not a part of any + /// common library. Therefore, if implementation is changed, modification of + /// private members of this class in the header has low impact. + dns::Message message_; + + /// @brief Holds a pointer to the object, representing Zone in the DNS + /// Update. + D2ZonePtr zone_; + +}; + +} // namespace d2 +} // namespace isc + +#endif // D2_UPDATE_MESSAGE_H diff --git a/src/lib/d2srv/d2_zone.cc b/src/lib/d2srv/d2_zone.cc new file mode 100644 index 0000000..c48b2c5 --- /dev/null +++ b/src/lib/d2srv/d2_zone.cc @@ -0,0 +1,30 @@ +// 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 + +#include + +namespace isc { +namespace d2 { + +D2Zone::D2Zone(const dns::Name& name, const dns::RRClass& rrclass) + : name_(name), rrclass_(rrclass) { +} + +std::string D2Zone::toText() const { + return (name_.toText() + " " + rrclass_.toText() + " SOA\n"); +} + +std::ostream& +operator<<(std::ostream& os, const D2Zone& zone) { + os << zone.toText(); + return (os); +} + +} // namespace d2 +} // namespace isc + diff --git a/src/lib/d2srv/d2_zone.h b/src/lib/d2srv/d2_zone.h new file mode 100644 index 0000000..077ad16 --- /dev/null +++ b/src/lib/d2srv/d2_zone.h @@ -0,0 +1,109 @@ +// 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/. + +#ifndef D2_ZONE_H +#define D2_ZONE_H + +#include +#include + +#include + +namespace isc { +namespace d2 { + +/// @brief The @c D2Zone encapsulates the Zone section in DNS Update message. +/// +/// This class is used by the @c D2UpdateMessage to encapsulate the Zone section +/// of the DNS Update message. Class members hold corresponding values of +/// section's fields: NAME, CLASS. This class does not hold the RTYPE field +/// value because RTYPE is always equal to SOA for DNS Update message (see +/// RFC 2136, section 2.3). +/// +/// Note, that this @c D2Zone class neither exposes functions to decode messages +/// from wire format nor to encode to wire format. This is not needed, because +/// @c isc::d2::D2UpdateMessage class uses @c D2Zone only to return the parsed +/// Zone information to the caller. Internally, D2UpdateMessage parses and +/// stores Zone section using @c isc::dns::Question class, and the @c toWire +/// and @c fromWire functions of the @c isc::dns::Question class are used. +class D2Zone { +public: + /// @brief Constructor from Name and RRClass. + /// + /// @param name The name of the Zone. + /// @param rrclass The RR class of the Zone. + D2Zone(const dns::Name& name, const dns::RRClass& rrclass); + + /// + /// @name Getters + /// + //@{ + /// @brief Returns the Zone name. + /// + /// @return A reference to the Zone name. + const dns::Name& getName() const { return (name_); } + + /// @brief Returns the Zone class. + /// + /// @return A reference to the Zone class. + const dns::RRClass& getClass() const { return (rrclass_); } + //@} + + /// @brief Returns text representation of the Zone. + /// + /// This function concatenates the name of the Zone, Class and Type. + /// The type is always SOA. + /// + /// @return A text representation of the Zone. + std::string toText() const; + + /// + /// @name Comparison Operators + /// + //@{ + /// @brief Equality operator to compare @c D2Zone objects in query and + /// response messages. + /// + /// @param rhs Zone to compare against. + /// + /// @return true if name and class are equal, false otherwise. + bool operator==(const D2Zone& rhs) const { + return ((rrclass_ == rhs.rrclass_) && (name_ == rhs.name_)); + } + + /// @brief Inequality operator to compare @c D2Zone objects in query and + /// response messages. + /// + /// @param rhs Zone to compare against. + /// + /// @return true if any of name or class are unequal, false otherwise. + bool operator!=(const D2Zone& rhs) const { + return (!operator==(rhs)); + } + //@} + +private: + dns::Name name_; ///< Holds the Zone name. + dns::RRClass rrclass_; ///< Holds the Zone class. +}; + +typedef boost::shared_ptr D2ZonePtr; + +/// @brief Insert the @c D2Zone as a string into stream. +/// +/// @param os A @c std::ostream object on which the insertion operation is +/// performed. +/// @param zone A reference to the @c D2Zone object output by the +/// operation. +/// +/// @return A reference to the same @c std::ostream object referenced by +/// parameter @c os after the insertion operation. +std::ostream& operator<<(std::ostream& os, const D2Zone& zone); + +} // namespace d2 +} // namespace isc + +#endif // D2_ZONE_H diff --git a/src/lib/d2srv/d2srv.dox b/src/lib/d2srv/d2srv.dox new file mode 100644 index 0000000..71d88d5 --- /dev/null +++ b/src/lib/d2srv/d2srv.dox @@ -0,0 +1,21 @@ +// Copyright (C) 2020-2022 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/. + +/** + @page libd2srv libkea-d2srv - Kea D2 Server Library + +This library contains most of the DNS classes used by the Kea D2 server +(kea-dhcp-ddns) but is organized as an independent library so that it +can be reused as needed by other components. + +For a more detailed documentation about the design and class separation +see @ref src/bin/d2/d2.dox. + +@section d2srvMTConsiderations Multi-Threading Consideration for D2 Server Library + +By default this library is not thread safe. + +*/ diff --git a/src/lib/d2srv/dns_client.cc b/src/lib/d2srv/dns_client.cc new file mode 100644 index 0000000..5798207 --- /dev/null +++ b/src/lib/d2srv/dns_client.cc @@ -0,0 +1,324 @@ +// 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 + +#include +#include +#include +#include +#include + +namespace isc { +namespace d2 { + +namespace { + +// OutputBuffer objects are pre-allocated before data is written to them. +// This is a default number of bytes for the buffers we create within +// DNSClient class. +const size_t DEFAULT_BUFFER_SIZE = 128; + +} + +using namespace isc::util; +using namespace isc::asiolink; +using namespace isc::asiodns; +using namespace isc::dns; +using namespace isc::stats; + +// This class provides the implementation for the DNSClient. This allows for +// the separation of the DNSClient interface from the implementation details. +// Currently, implementation uses IOFetch object to handle asynchronous +// communication with the DNS. This design may be revisited in the future. If +// implementation is changed, the DNSClient API will remain unchanged thanks +// to this separation. +class DNSClientImpl : public asiodns::IOFetch::Callback { +public: + /// @brief A buffer holding response from a DNS. + util::OutputBufferPtr in_buf_; + + /// A caller-supplied object which will hold the parsed response from DNS. + /// The response object is (or descends from) isc::dns::Message and is + /// populated using Message::fromWire(). This method may only be called + /// once in the lifetime of a Message instance. Therefore, response_ is a + /// pointer reference thus allowing this class to replace the object + /// pointed to with a new Message instance each time a message is + /// received. This allows a single DNSClientImpl instance to be used for + /// multiple, sequential IOFetch calls. (@todo Trac# 3286 has been opened + /// against dns::Message::fromWire. Should the behavior of fromWire change + /// the behavior here with could be reexamined). + D2UpdateMessagePtr& response_; + + /// @brief A caller-supplied external callback which is invoked when DNS + /// message exchange is complete or interrupted. + DNSClient::Callback* callback_; + + /// @brief A Transport Layer protocol used to communicate with a DNS. + DNSClient::Protocol proto_; + + /// @brief TSIG context used to sign outbound and verify inbound messages. + dns::TSIGContextPtr tsig_context_; + + /// @brief TSIG key name for stats. + std::string tsig_key_name_; + + /// @brief Constructor. + /// + /// @param response_placeholder Message object pointer which will be updated + /// with dynamically allocated object holding the DNS server's response. + /// @param callback Pointer to an object implementing @c DNSClient::Callback + /// class. This object will be called when DNS message exchange completes or + /// if an error occurs. NULL value disables callback invocation. + /// @param proto caller's preference regarding Transport layer protocol to + /// be used by DNS Client to communicate with a server. + DNSClientImpl(D2UpdateMessagePtr& response_placeholder, + DNSClient::Callback* callback, + const DNSClient::Protocol proto); + + /// @brief Destructor. + virtual ~DNSClientImpl(); + + /// @brief This internal callback is called when the DNS update message + /// exchange is complete. It further invokes the external callback provided + /// by a caller. Before external callback is invoked, an object of the + /// D2UpdateMessage type, representing a response from the server is set. + virtual void operator()(asiodns::IOFetch::Result result); + + /// @brief Starts asynchronous DNS Update using TSIG. + /// + /// @param io_service IO service to be used to run the message exchange. + /// @param ns_addr DNS server address. + /// @param ns_port DNS server port. + /// @param update A DNS Update message to be sent to the server. + /// @param wait A timeout (in milliseconds) for the response. If a response + /// is not received within the timeout, exchange is interrupted. This value + /// must not exceed maximal value for 'int' data type. + /// @param tsig_key A pointer to an @c D2TsigKeyPtr object that will + /// (if not null) be used to sign the DNS Update message and verify the + /// response. + void doUpdate(asiolink::IOService& io_service, + const asiolink::IOAddress& ns_addr, + const uint16_t ns_port, + D2UpdateMessage& update, + const unsigned int wait, + const D2TsigKeyPtr& tsig_key); + + /// @brief This function maps the IO error to the DNSClient error. + /// + /// @param result The IOFetch result to be converted to DNSClient status. + /// @return The DNSClient status corresponding to the IOFetch result. + DNSClient::Status getStatus(const asiodns::IOFetch::Result result); + + /// @brief This function updates statistics. + /// + /// @param stat The statistic name to be incremented. + /// @param update_key The flag indicating if the key statistics should also + /// be updated. + void incrStats(const std::string& stat, bool update_key = true); +}; + +DNSClientImpl::DNSClientImpl(D2UpdateMessagePtr& response_placeholder, + DNSClient::Callback* callback, + const DNSClient::Protocol proto) + : in_buf_(new OutputBuffer(DEFAULT_BUFFER_SIZE)), + response_(response_placeholder), callback_(callback), proto_(proto) { + + // Response should be an empty pointer. It gets populated by the + // operator() method. + if (response_) { + isc_throw(isc::BadValue, "Response buffer pointer should be null"); + } + + // @todo Currently we only support UDP. The support for TCP is planned for + // the future release. + if (proto_ == DNSClient::TCP) { + isc_throw(isc::NotImplemented, "TCP is currently not supported as a" + << " Transport protocol for DNS Updates; please use UDP"); + } + + // Given that we already eliminated the possibility that TCP is used, it + // would be sufficient to check that (proto != DNSClient::UDP). But, once + // support TCP is added the check above will disappear and the extra check + // will be needed here anyway. + // Note that cascaded check is used here instead of: + // if (proto_ != DNSClient::TCP && proto_ != DNSClient::UDP).. + // because some versions of GCC compiler complain that check above would + // be always 'false' due to limited range of enumeration. In fact, it is + // possible to pass out of range integral value through enum and it should + // be caught here. + if (proto_ != DNSClient::TCP) { + if (proto_ != DNSClient::UDP) { + isc_throw(isc::NotImplemented, "invalid transport protocol type '" + << proto_ << "' specified for DNS Updates"); + } + } +} + +DNSClientImpl::~DNSClientImpl() { +} + +void +DNSClientImpl::operator()(asiodns::IOFetch::Result result) { + // Get the status from IO. If no success, we just call user's callback + // and pass the status code. + DNSClient::Status status = getStatus(result); + if (status == DNSClient::SUCCESS) { + // Allocate a new response message. (Note that Message::fromWire + // may only be run once per message, so we need to start fresh + // each time.) + response_.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND)); + + // Server's response may be corrupted. In such case, fromWire will + // throw an exception. We want to catch this exception to return + // appropriate status code to the caller and log this event. + try { + response_->fromWire(in_buf_->getData(), in_buf_->getLength(), + tsig_context_.get()); + incrStats("update-success"); + } catch (const isc::Exception& ex) { + status = DNSClient::INVALID_RESPONSE; + LOG_DEBUG(d2_to_dns_logger, isc::log::DBGLVL_TRACE_DETAIL, + DHCP_DDNS_INVALID_RESPONSE).arg(ex.what()); + incrStats("update-error"); + } + + if (tsig_context_) { + // Context is a one-shot deal, get rid of it. + tsig_context_.reset(); + } + } else if (status == DNSClient::TIMEOUT) { + incrStats("update-timeout"); + } else { + incrStats("update-error"); + } + + // Once we are done with internal business, let's call a callback supplied + // by a caller. + if (callback_ != NULL) { + (*callback_)(status); + } +} + +DNSClient::Status +DNSClientImpl::getStatus(const asiodns::IOFetch::Result result) { + switch (result) { + case IOFetch::SUCCESS: + return (DNSClient::SUCCESS); + + case IOFetch::TIME_OUT: + return (DNSClient::TIMEOUT); + + case IOFetch::STOPPED: + return (DNSClient::IO_STOPPED); + + default: + ; + } + return (DNSClient::OTHER); +} + +void +DNSClientImpl::doUpdate(asiolink::IOService& io_service, + const IOAddress& ns_addr, + const uint16_t ns_port, + D2UpdateMessage& update, + const unsigned int wait, + const D2TsigKeyPtr& tsig_key) { + // The underlying implementation which we use to send DNS Updates uses + // signed integers for timeout. If we want to avoid overflows we need to + // respect this limitation here. + if (wait > DNSClient::getMaxTimeout()) { + isc_throw(isc::BadValue, "A timeout value for DNS Update request must" + " not exceed " << DNSClient::getMaxTimeout() + << ". Provided timeout value is '" << wait << "'"); + } + + // Create a TSIG context if we have a key, otherwise clear the context + // pointer. Message marshalling uses non-null context is the indicator + // that TSIG should be used. + if (tsig_key) { + tsig_context_ = tsig_key->createContext(); + tsig_key_name_ = tsig_key->getKeyName().toText(); + } else { + tsig_context_.reset(); + tsig_key_name_.clear(); + } + + // A renderer is used by the toWire function which creates the on-wire data + // from the DNS Update message. A renderer has its internal buffer where it + // renders data by default. However, this buffer can't be directly accessed. + // Fortunately, the renderer's API accepts user-supplied buffers. So, let's + // create our own buffer and pass it to the renderer so as the message is + // rendered to this buffer. Finally, we pass this buffer to IOFetch. + dns::MessageRenderer renderer; + OutputBufferPtr msg_buf(new OutputBuffer(DEFAULT_BUFFER_SIZE)); + renderer.setBuffer(msg_buf.get()); + + // Render DNS Update message. This may throw a bunch of exceptions if + // invalid message object is given. + update.toWire(renderer, tsig_context_.get()); + + // IOFetch has all the mechanisms that we need to perform asynchronous + // communication with the DNS server. The last but one argument points to + // this object as a completion callback for the message exchange. As a + // result operator()(Status) will be called. + + // Timeout value is explicitly cast to the int type to avoid warnings about + // overflows when doing implicit cast. It should have been checked by the + // caller that the unsigned timeout value will fit into int. + IOFetch io_fetch(IOFetch::UDP, io_service, msg_buf, ns_addr, ns_port, + in_buf_, this, static_cast(wait)); + + // Post the task to the task queue in the IO service. Caller will actually + // run these tasks by executing IOService::run. + io_service.post(io_fetch); + + // Update sent statistics. + incrStats("update-sent"); + if (tsig_key) { + incrStats("update-signed", false); + } else { + incrStats("update-unsigned", false); + } +} + +void +DNSClientImpl::incrStats(const std::string& stat, bool update_key) { + StatsMgr& mgr = StatsMgr::instance(); + mgr.addValue(stat, static_cast(1)); + if (update_key && !tsig_key_name_.empty()) { + mgr.addValue(StatsMgr::generateName("key", tsig_key_name_, stat), + static_cast(1)); + } +} + +DNSClient::DNSClient(D2UpdateMessagePtr& response_placeholder, + Callback* callback, const DNSClient::Protocol proto) + : impl_(new DNSClientImpl(response_placeholder, callback, proto)) { +} + +DNSClient::~DNSClient() { +} + +unsigned int +DNSClient::getMaxTimeout() { + static const unsigned int max_timeout = std::numeric_limits::max(); + return (max_timeout); +} + +void +DNSClient::doUpdate(asiolink::IOService& io_service, + const IOAddress& ns_addr, + const uint16_t ns_port, + D2UpdateMessage& update, + const unsigned int wait, + const D2TsigKeyPtr& tsig_key) { + impl_->doUpdate(io_service, ns_addr, ns_port, update, wait, tsig_key); +} + +} // namespace d2 +} // namespace isc diff --git a/src/lib/d2srv/dns_client.h b/src/lib/d2srv/dns_client.h new file mode 100644 index 0000000..c63a7a0 --- /dev/null +++ b/src/lib/d2srv/dns_client.h @@ -0,0 +1,157 @@ +// 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/. + +#ifndef DNS_CLIENT_H +#define DNS_CLIENT_H + +#include +#include +#include +#include +#include + +namespace isc { +namespace d2 { + +class DNSClient; +typedef boost::shared_ptr DNSClientPtr; + +/// DNSClient class implementation. +class DNSClientImpl; + +/// @brief The @c DNSClient class handles communication with the DNS server. +/// +/// Communication with the DNS server is asynchronous. Caller must provide a +/// callback, which will be invoked when the response from the DNS server is +/// received, a timeout has occurred or IO service has been stopped for any +/// reason. The caller-supplied callback is called by the internal callback +/// operator implemented by @c DNSClient. This callback is responsible for +/// initializing the @c D2UpdateMessage instance which encapsulates the response +/// from the DNS. This initialization does not take place if the response from +/// DNS is not received. +/// +/// Caller must supply a pointer to the @c D2UpdateMessage object, which will +/// encapsulate DNS response, through class constructor. An exception will be +/// thrown if the pointer is not initialized by the caller. +/// +/// @todo Ultimately, this class will support both TCP and UDP Transport. +/// Currently only UDP is supported and can be specified as a preferred +/// protocol. @c DNSClient constructor will throw an exception if TCP is +/// specified. Once both protocols are supported, the @c DNSClient logic will +/// try to obey caller's preference. However, it may use the other protocol if +/// on its own discretion, when there is a legitimate reason to do so. For +/// example, if communication with the server using preferred protocol fails. +class DNSClient { +public: + + /// @brief Transport layer protocol used by a DNS Client to communicate + /// with a server. + enum Protocol { + UDP, + TCP + }; + + /// @brief A status code of the DNSClient. + enum Status { + SUCCESS, ///< Response received and is ok. + TIMEOUT, ///< No response, timeout. + IO_STOPPED, ///< IO was stopped. + INVALID_RESPONSE, ///< Response received but invalid. + OTHER ///< Other, unclassified error. + }; + + /// @brief Callback for the @c DNSClient class. + /// + /// This is an abstract class which represents the external callback for the + /// @c DNSClient. Caller must implement this class and supply its instance + /// in the @c DNSClient constructor to get callbacks when the DNS Update + /// exchange is complete (@see @c DNSClient). + class Callback { + public: + /// @brief Virtual destructor. + virtual ~Callback() { } + + /// @brief Function operator implementing a callback. + /// + /// @param status a @c DNSClient::Status enum representing status code + /// of DNSClient operation. + virtual void operator()(DNSClient::Status status) = 0; + }; + + /// @brief Constructor. + /// + /// @param response_placeholder Message object pointer which will be updated + /// with dynamically allocated object holding the DNS server's response. + /// @param callback Pointer to an object implementing @c DNSClient::Callback + /// class. This object will be called when DNS message exchange completes or + /// if an error occurs. NULL value disables callback invocation. + /// @param proto caller's preference regarding Transport layer protocol to + /// be used by DNS Client to communicate with a server. + DNSClient(D2UpdateMessagePtr& response_placeholder, Callback* callback, + const Protocol proto = UDP); + + /// @brief Virtual destructor, does nothing. + ~DNSClient(); + + /// + /// @name Copy constructor and assignment operator + /// + /// Copy constructor and assignment operator are private because there are + /// no use cases when a DNSClient instance will need to be copied. Also, it + /// is desired to avoid copying a DNSClient::impl_ pointer and external + /// callbacks. + /// + //@{ +private: + DNSClient(const DNSClient& source); + DNSClient& operator=(const DNSClient& source); + //@} + +public: + + /// @brief Returns maximal allowed timeout value accepted by + /// @c DNSClient::doUpdate. + /// + /// @return maximal allowed timeout value accepted by @c DNSClient::doUpdate + static unsigned int getMaxTimeout(); + + /// @brief Start asynchronous DNS Update with TSIG. + /// + /// This function starts asynchronous DNS Update and returns. The DNS Update + /// will be executed by the specified IO service. Once the message exchange + /// with a DNS server is complete, timeout occurs or IO operation is + /// interrupted, the caller-supplied callback function will be invoked. + /// + /// An address and port of the DNS server is specified through the function + /// arguments so as the same instance of the @c DNSClient can be used to + /// initiate multiple message exchanges. + /// + /// @param io_service IO service to be used to run the message exchange. + /// @param ns_addr DNS server address. + /// @param ns_port DNS server port. + /// @param update A DNS Update message to be sent to the server. + /// @param wait A timeout (in milliseconds) for the response. If a response + /// is not received within the timeout, exchange is interrupted. This value + /// must not exceed maximal value for 'int' data type. + /// @param tsig_key A pointer to an @c D2TsigKeyPtr object that will + /// (if not null) be used to sign the DNS Update message and verify the + /// response. + void doUpdate(asiolink::IOService& io_service, + const asiolink::IOAddress& ns_addr, + const uint16_t ns_port, + D2UpdateMessage& update, + const unsigned int wait, + const D2TsigKeyPtr& tsig_key = D2TsigKeyPtr()); + +private: + /// @brief Pointer to DNSClient implementation. + std::unique_ptr impl_; +}; + +} // namespace d2 +} // namespace isc + +#endif // DNS_CLIENT_H diff --git a/src/lib/d2srv/nc_trans.cc b/src/lib/d2srv/nc_trans.cc new file mode 100644 index 0000000..e9534c2 --- /dev/null +++ b/src/lib/d2srv/nc_trans.cc @@ -0,0 +1,578 @@ +// 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 + +#include +#include +#include +#include +#include +#include + +#include + +using namespace isc::hooks; +using namespace isc::util; + +namespace { + +/// Structure that holds registered hook indexes. +struct NcTransHooks { + int hooks_index_select_key_; + + /// Constructor that registers hook points for the D2 server. + NcTransHooks() { + hooks_index_select_key_ = HooksManager::registerHook("select_key"); + } +}; + +// Declare a Hooks object. As this is outside any function or method, it +// will be instantiated (and the constructor run) when the module is loaded. +// As a result, the hook indexes will be defined before any method in this +// module is called. + +NcTransHooks Hooks; + +} + +namespace isc { +namespace d2 { + +// Common transaction states +const int NameChangeTransaction::READY_ST; +const int NameChangeTransaction::SELECTING_FWD_SERVER_ST; +const int NameChangeTransaction::SELECTING_REV_SERVER_ST; +const int NameChangeTransaction::PROCESS_TRANS_OK_ST; +const int NameChangeTransaction::PROCESS_TRANS_FAILED_ST; + +const int NameChangeTransaction::NCT_DERIVED_STATE_MIN; + +// Common transaction events +const int NameChangeTransaction::SELECT_SERVER_EVT; +const int NameChangeTransaction::SERVER_SELECTED_EVT; +const int NameChangeTransaction::SERVER_IO_ERROR_EVT; +const int NameChangeTransaction::NO_MORE_SERVERS_EVT; +const int NameChangeTransaction::IO_COMPLETED_EVT; +const int NameChangeTransaction::UPDATE_OK_EVT; +const int NameChangeTransaction::UPDATE_FAILED_EVT; + +const int NameChangeTransaction::NCT_DERIVED_EVENT_MIN; + +const unsigned int NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + +NameChangeTransaction:: +NameChangeTransaction(asiolink::IOServicePtr& io_service, + dhcp_ddns::NameChangeRequestPtr& ncr, + DdnsDomainPtr& forward_domain, + DdnsDomainPtr& reverse_domain, + D2CfgMgrPtr& cfg_mgr) + : io_service_(io_service), ncr_(ncr), forward_domain_(forward_domain), + reverse_domain_(reverse_domain), dns_client_(), dns_update_request_(), + dns_update_status_(DNSClient::OTHER), dns_update_response_(), + forward_change_completed_(false), reverse_change_completed_(false), + current_server_list_(), current_server_(), next_server_pos_(0), + update_attempts_(0), cfg_mgr_(cfg_mgr), tsig_key_() { + /// @todo if io_service is NULL we are multi-threading and should + /// instantiate our own + if (!io_service_) { + isc_throw(NameChangeTransactionError, "IOServicePtr cannot be null"); + } + + if (!ncr_) { + isc_throw(NameChangeTransactionError, + "NameChangeRequest cannot be null"); + } + + if (ncr_->isForwardChange() && !(forward_domain_)) { + isc_throw(NameChangeTransactionError, + "Forward change must have a forward domain"); + } + + if (ncr_->isReverseChange() && !(reverse_domain_)) { + isc_throw(NameChangeTransactionError, + "Reverse change must have a reverse domain"); + } + + if (!cfg_mgr_) { + isc_throw(NameChangeTransactionError, + "Configuration manager cannot be null"); + } +} + +NameChangeTransaction::~NameChangeTransaction(){ +} + +void +NameChangeTransaction::startTransaction() { + LOG_DEBUG(d2_to_dns_logger, isc::log::DBGLVL_TRACE_DETAIL, + DHCP_DDNS_STARTING_TRANSACTION) + .arg(getRequestId()); + + setNcrStatus(dhcp_ddns::ST_PENDING); + startModel(READY_ST); +} + +void +NameChangeTransaction::operator()(DNSClient::Status status) { + // Stow the completion status and re-enter the run loop with the event + // set to indicate IO completed. + // runModel is exception safe so we are good to call it here. + // It won't exit until we hit the next IO wait or the state model ends. + setDnsUpdateStatus(status); + LOG_DEBUG(d2_to_dns_logger, isc::log::DBGLVL_TRACE_DETAIL, + DHCP_DDNS_UPDATE_RESPONSE_RECEIVED) + .arg(getRequestId()) + .arg(current_server_->toText()) + .arg(responseString()); + + runModel(IO_COMPLETED_EVT); +} + +std::string +NameChangeTransaction::responseString() const { + std::ostringstream stream; + switch (getDnsUpdateStatus()) { + case DNSClient::SUCCESS: + stream << "SUCCESS, rcode: "; + if (getDnsUpdateResponse()) { + stream << getDnsUpdateResponse()->getRcode().toText(); + } else { + stream << " update response is NULL"; + } + break; + case DNSClient::TIMEOUT: + stream << "TIMEOUT"; + break; + case DNSClient::IO_STOPPED: + stream << "IO_STOPPED"; + break; + case DNSClient::INVALID_RESPONSE: + stream << "INVALID_RESPONSE"; + break; + case DNSClient::OTHER: + stream << "OTHER"; + break; + default: + stream << "UNKNOWN(" + << static_cast(getDnsUpdateStatus()) << ")"; + break; + + } + + return (stream.str()); +} + +std::string +NameChangeTransaction::transactionOutcomeString() const { + std::ostringstream stream; + stream << "Status: " << (getNcrStatus() == dhcp_ddns::ST_COMPLETED + ? "Completed, " : "Failed, ") + << "Event: " << getEventLabel(getNextEvent()) << ", "; + + if (ncr_->isForwardChange()) { + stream << " Forward change:" << (getForwardChangeCompleted() + ? " completed, " : " failed, "); + } + + if (ncr_->isReverseChange()) { + stream << " Reverse change:" << (getReverseChangeCompleted() + ? " completed, " : " failed, "); + } + + stream << " request: " << ncr_->toText(); + return (stream.str()); +} + + +void +NameChangeTransaction::sendUpdate(const std::string& comment) { + try { + ++update_attempts_; + // @todo add logic to add/replace TSIG key info in request if + // use_tsig_ is true. We should be able to navigate to the TSIG key + // for the current server. If not we would need to add that. + + D2ParamsPtr d2_params = cfg_mgr_->getD2Params(); + dns_client_->doUpdate(*io_service_, current_server_->getIpAddress(), + current_server_->getPort(), *dns_update_request_, + d2_params->getDnsServerTimeout(), tsig_key_); + // Message is on its way, so the next event should be NOP_EVT. + postNextEvent(NOP_EVT); + LOG_DEBUG(d2_to_dns_logger, isc::log::DBGLVL_TRACE_DETAIL, + DHCP_DDNS_UPDATE_REQUEST_SENT) + .arg(getRequestId()) + .arg(comment) + .arg(current_server_->toText()); + } catch (const std::exception& ex) { + // We were unable to initiate the send. + // It is presumed that any throw from doUpdate is due to a programmatic + // error, such as an unforeseen permutation of data, rather than an IO + // failure. IO errors should be caught by the underlying asiolink + // mechanisms and manifested as an unsuccessful IO status in the + // DNSClient callback. Any problem here most likely means the request + // is corrupt in some way and cannot be completed, therefore we will + // log it and transition it to failure. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_TRANS_SEND_ERROR) + .arg(getRequestId()) + .arg(ex.what()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + } +} + +void +NameChangeTransaction::defineEvents() { + // Call superclass impl first. + StateModel::defineEvents(); + + // Define NCT events. + defineEvent(SELECT_SERVER_EVT, "SELECT_SERVER_EVT"); + defineEvent(SERVER_SELECTED_EVT, "SERVER_SELECTED_EVT"); + defineEvent(SERVER_IO_ERROR_EVT, "SERVER_IO_ERROR_EVT"); + defineEvent(NO_MORE_SERVERS_EVT, "NO_MORE_SERVERS_EVT"); + defineEvent(IO_COMPLETED_EVT, "IO_COMPLETED_EVT"); + defineEvent(UPDATE_OK_EVT, "UPDATE_OK_EVT"); + defineEvent(UPDATE_FAILED_EVT, "UPDATE_FAILED_EVT"); +} + +void +NameChangeTransaction::verifyEvents() { + // Call superclass impl first. + StateModel::verifyEvents(); + + // Verify NCT events. + getEvent(SELECT_SERVER_EVT); + getEvent(SERVER_SELECTED_EVT); + getEvent(SERVER_IO_ERROR_EVT); + getEvent(NO_MORE_SERVERS_EVT); + getEvent(IO_COMPLETED_EVT); + getEvent(UPDATE_OK_EVT); + getEvent(UPDATE_FAILED_EVT); +} + +void +NameChangeTransaction::defineStates() { + // Call superclass impl first. + StateModel::defineStates(); + // This class is "abstract" in that it does not supply handlers for its + // states, derivations must do that therefore they must define them. +} + +void +NameChangeTransaction::verifyStates() { + // Call superclass impl first. + StateModel::verifyStates(); + + // Verify NCT states. This ensures that derivations provide the handlers. + getStateInternal(READY_ST); + getStateInternal(SELECTING_FWD_SERVER_ST); + getStateInternal(SELECTING_REV_SERVER_ST); + getStateInternal(PROCESS_TRANS_OK_ST); + getStateInternal(PROCESS_TRANS_FAILED_ST); +} + +void +NameChangeTransaction::onModelFailure(const std::string& explanation) { + setNcrStatus(dhcp_ddns::ST_FAILED); + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR) + .arg(getRequestId()) + .arg(explanation); +} + +void +NameChangeTransaction::retryTransition(const int fail_to_state) { + if (update_attempts_ < MAX_UPDATE_TRIES_PER_SERVER) { + // Re-enter the current state with same server selected. + transition(getCurrState(), SERVER_SELECTED_EVT); + } else { + // Transition to given fail_to_state state if we are out + // of retries. + transition(fail_to_state, SERVER_IO_ERROR_EVT); + } +} + +void +NameChangeTransaction::setDnsUpdateRequest(D2UpdateMessagePtr& request) { + dns_update_request_ = request; +} + +void +NameChangeTransaction::clearDnsUpdateRequest() { + dns_update_request_.reset(); +} + +void +NameChangeTransaction::clearUpdateAttempts() { + update_attempts_ = 0; +} + +void +NameChangeTransaction::setDnsUpdateStatus(const DNSClient::Status& status) { + dns_update_status_ = status; +} + +void +NameChangeTransaction::setDnsUpdateResponse(D2UpdateMessagePtr& response) { + dns_update_response_ = response; +} + +void +NameChangeTransaction::clearDnsUpdateResponse() { + dns_update_response_.reset(); +} + +void +NameChangeTransaction::setForwardChangeCompleted(const bool value) { + forward_change_completed_ = value; +} + +void +NameChangeTransaction::setReverseChangeCompleted(const bool value) { + reverse_change_completed_ = value; +} + +void +NameChangeTransaction::setUpdateAttempts(const size_t value) { + update_attempts_ = value; +} + +D2UpdateMessagePtr +NameChangeTransaction::prepNewRequest(DdnsDomainPtr domain) { + if (!domain) { + isc_throw(NameChangeTransactionError, + "prepNewRequest - domain cannot be null"); + } + + try { + // Create a "blank" update request. + D2UpdateMessagePtr request(new D2UpdateMessage(D2UpdateMessage:: + OUTBOUND)); + // Set the query id + request->setId(dns::QidGenerator::getInstance().generateQid()); + // Construct the Zone Section. + dns::Name zone_name(domain->getName()); + request->setZone(zone_name, dns::RRClass::IN()); + return (request); + } catch (const std::exception& ex) { + isc_throw(NameChangeTransactionError, "Cannot create new request :" + << ex.what()); + } +} + +void +NameChangeTransaction::addLeaseAddressRdata(dns::RRsetPtr& rrset) { + if (!rrset) { + isc_throw(NameChangeTransactionError, + "addLeaseAddressRdata - RRset cannot cannot be null"); + } + + try { + // Manufacture an RData from the lease address then add it to the RR. + dns::rdata::ConstRdataPtr rdata; + if (ncr_->isV4()) { + rdata.reset(new dns::rdata::in::A(ncr_->getIpAddress())); + } else { + rdata.reset(new dns::rdata::in::AAAA(ncr_->getIpAddress())); + } + rrset->addRdata(rdata); + } catch (const std::exception& ex) { + isc_throw(NameChangeTransactionError, "Cannot add address rdata: " + << ex.what()); + } +} + +void +NameChangeTransaction::addDhcidRdata(dns::RRsetPtr& rrset) { + if (!rrset) { + isc_throw(NameChangeTransactionError, + "addDhcidRdata - RRset cannot cannot be null"); + } + + try { + const std::vector& ncr_dhcid = ncr_->getDhcid().getBytes(); + util::InputBuffer buffer(ncr_dhcid.data(), ncr_dhcid.size()); + dns::rdata::ConstRdataPtr rdata (new dns::rdata::in:: + DHCID(buffer, ncr_dhcid.size())); + rrset->addRdata(rdata); + } catch (const std::exception& ex) { + isc_throw(NameChangeTransactionError, "Cannot add DCHID rdata: " + << ex.what()); + } + +} + +void +NameChangeTransaction::addPtrRdata(dns::RRsetPtr& rrset) { + if (!rrset) { + isc_throw(NameChangeTransactionError, + "addPtrRdata - RRset cannot cannot be null"); + } + + try { + dns::rdata::ConstRdataPtr rdata(new dns::rdata::generic:: + PTR(getNcr()->getFqdn())); + rrset->addRdata(rdata); + } catch (const std::exception& ex) { + isc_throw(NameChangeTransactionError, "Cannot add PTR rdata: " + << ex.what()); + } +} + +const dhcp_ddns::NameChangeRequestPtr& +NameChangeTransaction::getNcr() const { + return (ncr_); +} + +const TransactionKey& +NameChangeTransaction::getTransactionKey() const { + return (ncr_->getDhcid()); +} + +std::string +NameChangeTransaction::getRequestId() const { + return (ncr_->getRequestId()); +} + +dhcp_ddns::NameChangeStatus +NameChangeTransaction::getNcrStatus() const { + return (ncr_->getStatus()); +} + +DdnsDomainPtr& +NameChangeTransaction::getForwardDomain() { + return (forward_domain_); +} + +DdnsDomainPtr& +NameChangeTransaction::getReverseDomain() { + return (reverse_domain_); +} + +void +NameChangeTransaction::initServerSelection(const DdnsDomainPtr& domain) { + if (!domain) { + isc_throw(NameChangeTransactionError, + "initServerSelection called with an empty domain"); + } + + current_server_list_ = domain->getServers(); + next_server_pos_ = 0; + current_server_.reset(); +} + +bool +NameChangeTransaction::selectNextServer() { + for (;;) { + if ((current_server_list_) && + (next_server_pos_ < current_server_list_->size())) { + current_server_ = (*current_server_list_)[next_server_pos_]; + // Toss out any previous response. + dns_update_response_.reset(); + + // Set the tsig_key to that of the current server. + if (!selectTSIGKey()) { + ++next_server_pos_; + continue; + } + + // @todo Protocol is set on DNSClient constructor. We need + // to propagate a configuration value downward, probably starting + // at global, then domain, finishing by server. + // Once that is supported we need to add it here. + dns_client_.reset(new DNSClient(dns_update_response_, this, + DNSClient::UDP)); + ++next_server_pos_; + return (true); + } + + return (false); + } +} + +bool +NameChangeTransaction::selectTSIGKey() { + TSIGKeyInfoPtr tsig_key_info = current_server_->getTSIGKeyInfo(); + if (tsig_key_info) { + tsig_key_ = tsig_key_info->getTSIGKey(); + } else { + tsig_key_.reset(); + } + + // This hook point allows hooks libraries to overwrite the TSIG key. + if (HooksManager::calloutsPresent(Hooks.hooks_index_select_key_)) { + CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle(); + + callout_handle->setArgument("current_server", current_server_); + callout_handle->setArgument("tsig_key", tsig_key_); + + HooksManager::callCallouts(Hooks.hooks_index_select_key_, + *callout_handle); + + // This server is skipped because the status is not NEXT_STEP_CONTINUE. + if (callout_handle->getStatus() != CalloutHandle::NEXT_STEP_CONTINUE) { + return (false); + } + + // Reread the TSIG key. + callout_handle->getArgument("tsig_key", tsig_key_); + } + + return (true); +} + + +const DNSClientPtr& +NameChangeTransaction::getDNSClient() const { + return (dns_client_); +} + +const DnsServerInfoPtr& +NameChangeTransaction::getCurrentServer() const { + return (current_server_); +} + +void +NameChangeTransaction::setNcrStatus(const dhcp_ddns::NameChangeStatus& status) { + return (ncr_->setStatus(status)); +} + +const D2UpdateMessagePtr& +NameChangeTransaction::getDnsUpdateRequest() const { + return (dns_update_request_); +} + +DNSClient::Status +NameChangeTransaction::getDnsUpdateStatus() const { + return (dns_update_status_); +} + +const D2UpdateMessagePtr& +NameChangeTransaction::getDnsUpdateResponse() const { + return (dns_update_response_); +} + +bool +NameChangeTransaction::getForwardChangeCompleted() const { + return (forward_change_completed_); +} + +bool +NameChangeTransaction::getReverseChangeCompleted() const { + return (reverse_change_completed_); +} + +size_t +NameChangeTransaction::getUpdateAttempts() const { + return (update_attempts_); +} + +const dns::RRType& +NameChangeTransaction::getAddressRRType() const { + return (ncr_->isV4() ? dns::RRType::A() : dns::RRType::AAAA()); +} + +} // namespace isc::d2 +} // namespace isc diff --git a/src/lib/d2srv/nc_trans.h b/src/lib/d2srv/nc_trans.h new file mode 100644 index 0000000..2aee7ec --- /dev/null +++ b/src/lib/d2srv/nc_trans.h @@ -0,0 +1,602 @@ +// 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/. + +#ifndef NC_TRANS_H +#define NC_TRANS_H + +/// @file nc_trans.h This file defines the class NameChangeTransaction. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace isc { +namespace d2 { + +/// @brief Thrown if the transaction encounters a general error. +class NameChangeTransactionError : public isc::Exception { +public: + NameChangeTransactionError(const char* file, size_t line, + const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Defines the type used as the unique key for transactions. +typedef isc::dhcp_ddns::D2Dhcid TransactionKey; + +/// @brief Embodies the "life-cycle" required to carry out a DDNS update. +/// +/// NameChangeTransaction is the base class that provides the common state +/// model mechanics and services performing the DNS updates needed to carry out +/// a DHCP_DDNS request as described by a NameChangeRequest. It is derived +/// from StateModel which supplies a simple, general purpose FSM implementation. +/// +/// Upon construction, each transaction has all of the information and +/// resources required to carry out its assigned request, including the list(s) +/// of DNS server(s) needed. It is responsible for knowing what conversations +/// it must have with which servers and in the order necessary to fulfill the +/// request. Upon fulfillment of the request, the transaction's work is complete +/// and it is destroyed. +/// +/// Fulfillment of the request is carried out through the performance of the +/// transaction's state model. Using a state driven implementation accounts +/// for the conditional processing flow necessary to meet the DDNS RFCs as well +/// as the asynchronous nature of IO with DNS servers. +/// +/// Derivations of the class are responsible for defining the state model and +/// conversations necessary to carry out the specific of request. +/// +/// Conversations with DNS servers are done through the use of the DNSClient +/// class. The DNSClient provides a IOService-based means a service which +/// performs a single, packet exchange with a given DNS server. It sends a +/// single update to the server and returns the response, asynchronously, +/// through a callback. At each point in a transaction's state model, where +/// an update is to be sent, the model "suspends" until notified by the +/// DNSClient via the callback. Suspension is done by posting a +/// StateModel::NOP_EVT as the next event, stopping the state model execution. +/// +/// Resuming state model execution when a DNS update completes is done by a +/// call to StateModel::runStateModel() from within the DNSClient callback, +/// with an event value of IO_COMPLETED_EVT (described below). +/// +/// This class defines a set of events and states that are a common to all +/// transactions. Each derivation may add define additional states and events +/// as needed, but it must support the common set. NameChangeTransaction +/// does not supply any state handlers. These are the sole responsibility of +/// derivations. +class NameChangeTransaction : public DNSClient::Callback, public util::StateModel { +public: + + //@{ States common to all transactions. + + /// @brief State from which a transaction is started. + static const int READY_ST = SM_DERIVED_STATE_MIN + 1; + + /// @brief State in which forward DNS server selection is done. + /// + /// Within this state, the actual selection of the next forward server + /// to use is conducted. Upon conclusion of this state the next server + /// is either selected or it should transition out with NO_MORE_SERVERS_EVT + /// event. + static const int SELECTING_FWD_SERVER_ST = SM_DERIVED_STATE_MIN + 2; + + /// @brief State in which reverse DNS server selection is done. + /// + /// Within this state, the actual selection of the next reverse server + /// to use is conducted. Upon conclusion of this state the next server + /// is either selected or it should transition out with NO_MORE_SERVERS_EVT + /// event. + static const int SELECTING_REV_SERVER_ST = SM_DERIVED_STATE_MIN + 3; + + /// @brief State which processes successful transaction conclusion. + static const int PROCESS_TRANS_OK_ST = SM_DERIVED_STATE_MIN + 4; + + /// @brief State which processes an unsuccessful transaction conclusion. + static const int PROCESS_TRANS_FAILED_ST = SM_DERIVED_STATE_MIN + 5; + + /// @brief Value at which custom states in a derived class should begin. + static const int NCT_DERIVED_STATE_MIN = SM_DERIVED_STATE_MIN + 101; + //@} + + //@{ Events common to all transactions. + /// @brief Issued when a server needs to be selected. + static const int SELECT_SERVER_EVT = SM_DERIVED_EVENT_MIN + 1; + + /// @brief Issued when a server has been selected. + static const int SERVER_SELECTED_EVT = SM_DERIVED_EVENT_MIN + 2; + + /// @brief Issued when an update fails due to an IO error. + static const int SERVER_IO_ERROR_EVT = SM_DERIVED_EVENT_MIN + 3; + + /// @brief Issued when there are no more servers from which to select. + /// This occurs when none of the servers in the list can be reached to + /// perform the update. + + static const int NO_MORE_SERVERS_EVT =SM_DERIVED_EVENT_MIN + 4; + /// @brief Issued when a DNS update packet exchange has completed. + /// This occurs whenever the DNSClient callback is invoked whether the + /// exchange was successful or not. + + static const int IO_COMPLETED_EVT = SM_DERIVED_EVENT_MIN + 5; + /// @brief Issued when the attempted update successfully completed. + /// This occurs when an DNS update packet was successfully processed + /// by the server. + + static const int UPDATE_OK_EVT = SM_DERIVED_EVENT_MIN + 6; + + /// @brief Issued when the attempted update fails to complete. + /// This occurs when an DNS update packet fails to process. The nature of + /// the failure is given by the DNSClient return status and the response + /// packet (if one was received). + static const int UPDATE_FAILED_EVT = SM_DERIVED_EVENT_MIN + 7; + + /// @brief Value at which custom events in a derived class should begin. + static const int NCT_DERIVED_EVENT_MIN = SM_DERIVED_EVENT_MIN + 101; + //@} + + /// @brief Default time to assign to a single DNS update. + /// @todo This value will be made configurable in the very near future + /// under trac3268. For now we will define it to 100 milliseconds + /// so unit tests will run within a reasonable amount of time. + static const unsigned int DNS_UPDATE_DEFAULT_TIMEOUT = 100; + + /// @brief Maximum times to attempt a single update on a given server. + static const unsigned int MAX_UPDATE_TRIES_PER_SERVER = 3; + + /// @brief Constructor + /// + /// Instantiates a transaction that is ready to be started. + /// + /// @param io_service IO service to be used for IO processing + /// @param ncr is the NameChangeRequest to fulfill + /// @param forward_domain is the domain to use for forward DNS updates + /// @param reverse_domain is the domain to use for reverse DNS updates + /// @param cfg_mgr reference to the current configuration manager + /// + /// @throw NameChangeTransactionError if given an null request, + /// if forward change is enabled but forward domain is null, if + /// reverse change is enabled but reverse domain is null. + NameChangeTransaction(asiolink::IOServicePtr& io_service, + dhcp_ddns::NameChangeRequestPtr& ncr, + DdnsDomainPtr& forward_domain, + DdnsDomainPtr& reverse_domain, + D2CfgMgrPtr& cfg_mgr); + + /// @brief Destructor + virtual ~NameChangeTransaction(); + + /// @brief Begins execution of the transaction. + /// + /// This method invokes StateModel::startModel() with a value of READY_ST. + /// This causes transaction's state model to attempt to begin execution + /// with the state handler for READY_ST. + void startTransaction(); + + /// @brief Serves as the DNSClient IO completion event handler. + /// + /// This is the implementation of the method inherited by our derivation + /// from DNSClient::Callback. When the DNSClient completes an update it + /// invokes this method as the completion handler. This method stores + /// the given status and invokes runStateModel() with an event value of + /// IO_COMPLETED_EVT. + /// + /// @param status is the outcome of the DNS update packet exchange. + /// This method is exception safe. + virtual void operator()(DNSClient::Status status); + +protected: + /// @brief Send the update request to the current server. + /// + /// This method increments the update attempt count and then passes the + /// current update request to the DNSClient instance to be sent to the + /// currently selected server. Since the send is asynchronous, the method + /// posts NOP_EVT as the next event and then returns. + /// + /// If tsig_key_ is not NULL, then the update will be conducted using + /// the key to sign the request and verify the response, otherwise it + /// will be conducted without TSIG. + /// + /// @param comment text to include in log detail + /// + /// If an exception occurs it will be logged and and the transaction will + /// be failed. + virtual void sendUpdate(const std::string& comment = ""); + + /// @brief Adds events defined by NameChangeTransaction to the event set. + /// + /// This method adds the events common to NCR transaction processing to + /// the set of define events. It invokes the superclass's implementation + /// first to maintain the hierarchical chain of event definition. + /// Derivations of NameChangeTransaction must invoke its implementation + /// in like fashion. + /// + /// @throw StateModelError if an event definition is invalid or a duplicate. + virtual void defineEvents(); + + /// @brief Validates the contents of the set of events. + /// + /// This method verifies that the events defined by both the superclass and + /// this class are defined. As with defineEvents, this method calls the + /// superclass's implementation first, to verify events defined by it and + /// then this implementation to verify events defined by + /// NameChangeTransaction. + /// + /// @throw StateModelError if an event value is undefined. + virtual void verifyEvents(); + + /// @brief Adds states defined by NameChangeTransaction to the state set. + /// + /// This method adds the states common to NCR transaction processing to + /// the dictionary of states. It invokes the superclass's implementation + /// first to maintain the hierarchical chain of state definition. + /// Derivations of NameChangeTransaction must invoke its implementation + /// in like fashion. + /// + /// @throw StateModelError if an state definition is invalid or a duplicate. + virtual void defineStates(); + + /// @brief Validates the contents of the set of states. + /// + /// This method verifies that the states defined by both the superclass and + /// this class are defined. As with defineStates, this method calls the + /// superclass's implementation first, to verify states defined by it and + /// then this implementation to verify states defined by + /// NameChangeTransaction. + /// + /// @throw StateModelError if an event value is undefined. + virtual void verifyStates(); + + /// @brief Handler for fatal model execution errors. + /// + /// This handler is called by the StateModel implementation when the model + /// execution encounters a model violation: attempt to call an unmapped + /// state, an event not valid for the current state, or an uncaught + /// exception thrown during a state handler invocation. When such an + /// error occurs the transaction is deemed inoperable, and further model + /// execution cannot be performed. It marks the transaction as failed by + /// setting the NCR status to dhcp_ddns::ST_FAILED + /// + /// @param explanation is text detailing the error + virtual void onModelFailure(const std::string& explanation); + + /// @brief Determines the state and next event based on update attempts. + /// + /// This method will post a next event of SERVER_SELECTED_EVT to the + /// current state if the number of update attempts has not reached the + /// maximum allowed. + /// + /// If the maximum number of attempts has been reached, it will transition + /// to the given state with a next event of SERVER_IO_ERROR_EVT. + /// + /// @param fail_to_state State to transition to if maximum attempts + /// have been tried. + /// + void retryTransition(const int fail_to_state); + + /// @brief Sets the update request packet to the given packet. + /// + /// @param request is the new request packet to assign. + void setDnsUpdateRequest(D2UpdateMessagePtr& request); + + /// @brief Destroys the current update request packet. + void clearDnsUpdateRequest(); + + /// @brief Resets the update attempts count. + void clearUpdateAttempts(); + + /// @brief Sets the update status to the given status value. + /// + /// @param status is the new value for the update status. + void setDnsUpdateStatus(const DNSClient::Status& status); + + /// @brief Sets the update response packet to the given packet. + /// + /// @param response is the new response packet to assign. + void setDnsUpdateResponse(D2UpdateMessagePtr& response); + + /// @brief Destroys the current update response packet. + void clearDnsUpdateResponse(); + + /// @brief Sets the forward change completion flag to the given value. + /// + /// @param value is the new value to assign to the flag. + void setForwardChangeCompleted(const bool value); + + /// @brief Sets the reverse change completion flag to the given value. + /// + /// @param value is the new value to assign to the flag. + void setReverseChangeCompleted(const bool value); + + /// @brief Sets the status of the transaction's NameChangeRequest + /// + /// @param status is the new value to assign to the NCR status. + void setNcrStatus(const dhcp_ddns::NameChangeStatus& status); + + /// @brief Initializes server selection from the given DDNS domain. + /// + /// Method prepares internal data to conduct server selection from the + /// list of servers supplied by the given domain. This method should be + /// called when a transaction is ready to begin selecting servers from + /// a new list. Typically this will be prior to starting the updates for + /// a given DNS direction. + /// + /// @param domain is the domain from which server selection is to be + /// conducted. + void initServerSelection(const DdnsDomainPtr& domain); + + /// @brief Selects the next server in the current server list. + /// + /// This method is used to iterate over the list of servers. If there are + /// no more servers in the list, it returns false. Otherwise it sets the + /// current server to the next server and creates a new DNSClient + /// instance. + /// + /// @return True if a server has been selected, false if there are no more + /// servers from which to select. + bool selectNextServer(); + + /// @brief Selects the TSIG key. + /// + /// This method uses the current server and the select_key callout. + /// When no TSIG key is selected the tsig_key_ pointer is null. + /// + /// @return False when the current server should be skipped. + bool selectTSIGKey(); + + /// @brief Sets the update attempt count to the given value. + /// + /// @param value is the new value to assign. + void setUpdateAttempts(const size_t value); + + /// @brief Fetches the IOService the transaction uses for IO processing. + /// + /// @return returns a const pointer to the IOService. + const asiolink::IOServicePtr& getIOService() { + return (io_service_); + } + + /// @brief Creates a new DNS update request based on the given domain. + /// + /// Constructs a new "empty", OUTBOUND, request with the message id set + /// and zone section populated based on the given domain. + /// It is declared virtual for test purposes. + /// + /// @return A D2UpdateMessagePtr to the new request. + /// + /// @throw NameChangeTransactionError if request cannot be constructed. + virtual D2UpdateMessagePtr prepNewRequest(DdnsDomainPtr domain); + + /// @brief Adds an RData for the lease address to the given RRset. + /// + /// Creates an in::A() or in:AAAA() RData instance from the NCR + /// lease address and adds it to the given RRset. + /// + /// @param rrset RRset to which to add the RData + /// + /// @throw NameChangeTransactionError if RData cannot be constructed or + /// the RData cannot be added to the given RRset. + void addLeaseAddressRdata(dns::RRsetPtr& rrset); + + /// @brief Adds an RData for the lease client's DHCID to the given RRset. + /// + /// Creates an in::DHCID() RData instance from the NCR DHCID and adds + /// it to the given RRset. + /// + /// @param rrset RRset to which to add the RData + /// + /// @throw NameChangeTransactionError if RData cannot be constructed or + /// the RData cannot be added to the given RRset. + void addDhcidRdata(dns::RRsetPtr& rrset); + + /// @brief Adds an RData for the lease FQDN to the given RRset. + /// + /// Creates an in::PTR() RData instance from the NCR FQDN and adds + /// it to the given RRset. + /// + /// @param rrset RRset to which to add the RData + /// + /// @throw NameChangeTransactionError if RData cannot be constructed or + /// the RData cannot be added to the given RRset. + void addPtrRdata(dns::RRsetPtr& rrset); + + /// @brief Returns a string version of the current response status and rcode + /// + /// Renders a string containing the text label of current DNS update status + /// and RCODE (if status is DNSClient::SUCCESS) + /// + /// @return std::string containing constructed text + std::string responseString() const; + + /// @brief Returns a string version of transaction outcome. + /// + /// Renders a string containing summarizes the outcome of the + /// transaction. The information includes the overall status, + /// the last event, whether not forward and reverse changes were + /// done, as well as the NCR serviced. + /// + /// @return std::string containing constructed text + std::string transactionOutcomeString() const; + +public: + /// @brief Fetches the NameChangeRequest for this transaction. + /// + /// @return A const pointer reference to the NameChangeRequest. + const dhcp_ddns::NameChangeRequestPtr& getNcr() const; + + /// @brief Fetches the unique key that identifies this transaction. + /// + /// Transactions are uniquely identified by a TransactionKey. Currently + /// this is wrapper around a D2Dhcid. + /// + /// @return A const reference to the TransactionKey. + const TransactionKey& getTransactionKey() const; + + /// @brief Fetches the request id that identifies this transaction. + /// + /// This is a wrapper around getRequestId from the NCR which currently + /// returns DHCID. In the future we may include a distinct request id. + /// The primary purpose of this function is to provide a consistent way + /// to identify requests for logging purposes. + /// + /// @return a string with the request's request ID (currently DHCID) + std::string getRequestId() const; + + /// @brief Fetches the NameChangeRequest status of the transaction. + /// + /// This is the current status of the NameChangeRequest, not to + /// be confused with the state of the transaction. Once the transaction + /// is reached its conclusion, the request will end up with a final + /// status. + /// + /// @return A dhcp_ddns::NameChangeStatus representing the current + /// status of the transaction. + dhcp_ddns::NameChangeStatus getNcrStatus() const; + + /// @brief Fetches the forward DdnsDomain. + /// + /// @return A pointer reference to the forward DdnsDomain. If + /// the request does not include a forward change, the pointer will empty. + DdnsDomainPtr& getForwardDomain(); + + /// @brief Fetches the reverse DdnsDomain. + /// + /// @return A pointer reference to the reverse DdnsDomain. If + /// the request does not include a reverse change, the pointer will empty. + DdnsDomainPtr& getReverseDomain(); + + /// @brief Fetches the currently selected server. + /// + /// @return A const pointer reference to the DnsServerInfo of the current + /// server. + const DnsServerInfoPtr& getCurrentServer() const; + + /// @brief Fetches the DNSClient instance + /// + /// @return A const pointer reference to the DNSClient + const DNSClientPtr& getDNSClient() const; + + /// @brief Fetches the current DNS update request packet. + /// + /// @return A const pointer reference to the current D2UpdateMessage + /// request. + const D2UpdateMessagePtr& getDnsUpdateRequest() const; + + /// @brief Fetches the most recent DNS update status. + /// + /// @return A DNSClient::Status indicating the result of the most recent + /// DNS update to complete. + DNSClient::Status getDnsUpdateStatus() const; + + /// @brief Fetches the most recent DNS update response packet. + /// + /// @return A const pointer reference to the D2UpdateMessage most recently + /// received. + const D2UpdateMessagePtr& getDnsUpdateResponse() const; + + /// @brief Returns whether the forward change has completed or not. + /// + /// The value returned is only meaningful if the NameChangeRequest calls + /// for a forward change to be done. The value returned indicates if + /// forward change has been completed successfully. + /// + /// @return True if the forward change has been completed, false otherwise. + bool getForwardChangeCompleted() const; + + /// @brief Returns whether the reverse change has completed or not. + /// + /// The value returned is only meaningful if the NameChangeRequest calls + /// for a reverse change to be done. The value returned indicates if + /// reverse change has been completed successfully. + /// + /// @return True if the reverse change has been completed, false otherwise. + bool getReverseChangeCompleted() const; + + /// @brief Fetches the update attempt count for the current update. + /// + /// @return size_t which is the number of times the current request has + /// been attempted against the current server. + size_t getUpdateAttempts() const; + + /// @brief Returns the DHCP data type for the lease address + /// + /// @return constant reference to dns::RRType::A() if the lease address + /// is IPv4 or dns::RRType::AAAA() if the lease address is IPv6. + const dns::RRType& getAddressRRType() const; + +private: + /// @brief The IOService which should be used to for IO processing. + asiolink::IOServicePtr io_service_; + + /// @brief The NameChangeRequest that the transaction is to fulfill. + dhcp_ddns::NameChangeRequestPtr ncr_; + + /// @brief The forward domain that matches the request. + /// + /// The forward "domain" is DdnsDomain which contains all of the information + /// necessary, including the list of DNS servers to be used for a forward + /// change. + DdnsDomainPtr forward_domain_; + + /// @brief The reverse domain that matches the request. + /// + /// The reverse "domain" is DdnsDomain which contains all of the information + /// necessary, including the list of DNS servers to be used for a reverse + /// change. + DdnsDomainPtr reverse_domain_; + + /// @brief The DNSClient instance that will carry out DNS packet exchanges. + DNSClientPtr dns_client_; + + /// @brief The DNS current update request packet. + D2UpdateMessagePtr dns_update_request_; + + /// @brief The outcome of the most recently completed DNS packet exchange. + DNSClient::Status dns_update_status_; + + /// @brief The DNS update response packet most recently received. + D2UpdateMessagePtr dns_update_response_; + + /// @brief Indicator for whether or not the forward change completed ok. + bool forward_change_completed_; + + /// @brief Indicator for whether or not the reverse change completed ok. + bool reverse_change_completed_; + + /// @brief Pointer to the current server selection list. + DnsServerInfoStoragePtr current_server_list_; + + /// @brief Pointer to the currently selected server. + DnsServerInfoPtr current_server_; + + /// @brief Next server position in the list. + /// + /// This value is always the position of the next selection in the server + /// list, which may be beyond the end of the list. + size_t next_server_pos_; + + /// @brief Number of transmit attempts for the current request. + size_t update_attempts_; + + /// @brief Pointer to the configuration manager. + D2CfgMgrPtr cfg_mgr_; + + /// @brief Pointer to the TSIG key which should be used (if any). + D2TsigKeyPtr tsig_key_; +}; + +/// @brief Defines a pointer to a NameChangeTransaction. +typedef boost::shared_ptr NameChangeTransactionPtr; + +} // namespace isc::d2 +} // namespace isc +#endif diff --git a/src/lib/d2srv/tests/Makefile.am b/src/lib/d2srv/tests/Makefile.am new file mode 100644 index 0000000..436cb21 --- /dev/null +++ b/src/lib/d2srv/tests/Makefile.am @@ -0,0 +1,53 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +CLEANFILES = *.gcno *.gcda + +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +TESTS = +if HAVE_GTEST +TESTS += libd2srv_unittests + +libd2srv_unittests_SOURCES = run_unittests.cc +libd2srv_unittests_SOURCES += d2_tsig_key_unittest.cc +libd2srv_unittests_SOURCES += d2_update_message_unittests.cc +libd2srv_unittests_SOURCES += d2_zone_unittests.cc +libd2srv_unittests_SOURCES += dns_client_unittests.cc +libd2srv_unittests_SOURCES += nc_trans_unittests.cc + +libd2srv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +libd2srv_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) + +libd2srv_unittests_LDADD = $(top_builddir)/src/lib/d2srv/libkea-d2srv.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/d2srv/testutils/libd2srvtest.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/process/libkea-process.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libkea-asiodns.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/database/libkea-database.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libd2srv_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(GTEST_LDADD) + +endif + +noinst_PROGRAMS = $(TESTS) diff --git a/src/lib/d2srv/tests/Makefile.in b/src/lib/d2srv/tests/Makefile.in new file mode 100644 index 0000000..3dc72db --- /dev/null +++ b/src/lib/d2srv/tests/Makefile.in @@ -0,0 +1,1088 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +TESTS = $(am__EXEEXT_1) +@HAVE_GTEST_TRUE@am__append_1 = libd2srv_unittests +noinst_PROGRAMS = $(am__EXEEXT_2) +subdir = src/lib/d2srv/tests +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ + $(top_srcdir)/m4macros/ax_cpp11.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_sysrepo.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@HAVE_GTEST_TRUE@am__EXEEXT_1 = libd2srv_unittests$(EXEEXT) +am__EXEEXT_2 = $(am__EXEEXT_1) +PROGRAMS = $(noinst_PROGRAMS) +am__libd2srv_unittests_SOURCES_DIST = run_unittests.cc \ + d2_tsig_key_unittest.cc d2_update_message_unittests.cc \ + d2_zone_unittests.cc dns_client_unittests.cc \ + nc_trans_unittests.cc +@HAVE_GTEST_TRUE@am_libd2srv_unittests_OBJECTS = \ +@HAVE_GTEST_TRUE@ libd2srv_unittests-run_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libd2srv_unittests-d2_tsig_key_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libd2srv_unittests-d2_update_message_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libd2srv_unittests-d2_zone_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libd2srv_unittests-dns_client_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libd2srv_unittests-nc_trans_unittests.$(OBJEXT) +libd2srv_unittests_OBJECTS = $(am_libd2srv_unittests_OBJECTS) +am__DEPENDENCIES_1 = +@HAVE_GTEST_TRUE@libd2srv_unittests_DEPENDENCIES = $(top_builddir)/src/lib/d2srv/libkea-d2srv.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/d2srv/testutils/libd2srvtest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiodns/libkea-asiodns.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/database/libkea-database.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libd2srv_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libd2srv_unittests_LDFLAGS) \ + $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = \ + ./$(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Po \ + ./$(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po \ + ./$(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po \ + ./$(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po \ + ./$(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po \ + ./$(DEPDIR)/libd2srv_unittests-run_unittests.Po +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libd2srv_unittests_SOURCES) +DIST_SOURCES = $(am__libd2srv_unittests_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_SYSREPO = @HAVE_SYSREPO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) +@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static +CLEANFILES = *.gcno *.gcda +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +@HAVE_GTEST_TRUE@libd2srv_unittests_SOURCES = run_unittests.cc \ +@HAVE_GTEST_TRUE@ d2_tsig_key_unittest.cc \ +@HAVE_GTEST_TRUE@ d2_update_message_unittests.cc \ +@HAVE_GTEST_TRUE@ d2_zone_unittests.cc dns_client_unittests.cc \ +@HAVE_GTEST_TRUE@ nc_trans_unittests.cc +@HAVE_GTEST_TRUE@libd2srv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +@HAVE_GTEST_TRUE@libd2srv_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) +@HAVE_GTEST_TRUE@libd2srv_unittests_LDADD = $(top_builddir)/src/lib/d2srv/libkea-d2srv.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/d2srv/testutils/libd2srvtest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiodns/libkea-asiodns.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/database/libkea-database.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(LOG4CPLUS_LIBS) $(BOOST_LIBS) \ +@HAVE_GTEST_TRUE@ $(GTEST_LDADD) +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/d2srv/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/d2srv/tests/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +libd2srv_unittests$(EXEEXT): $(libd2srv_unittests_OBJECTS) $(libd2srv_unittests_DEPENDENCIES) $(EXTRA_libd2srv_unittests_DEPENDENCIES) + @rm -f libd2srv_unittests$(EXEEXT) + $(AM_V_CXXLD)$(libd2srv_unittests_LINK) $(libd2srv_unittests_OBJECTS) $(libd2srv_unittests_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srv_unittests-run_unittests.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +libd2srv_unittests-run_unittests.o: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-run_unittests.Tpo -c -o libd2srv_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-run_unittests.Tpo $(DEPDIR)/libd2srv_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='libd2srv_unittests-run_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc + +libd2srv_unittests-run_unittests.obj: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-run_unittests.Tpo -c -o libd2srv_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-run_unittests.Tpo $(DEPDIR)/libd2srv_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='libd2srv_unittests-run_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` + +libd2srv_unittests-d2_tsig_key_unittest.o: d2_tsig_key_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_tsig_key_unittest.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Tpo -c -o libd2srv_unittests-d2_tsig_key_unittest.o `test -f 'd2_tsig_key_unittest.cc' || echo '$(srcdir)/'`d2_tsig_key_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Tpo $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_tsig_key_unittest.cc' object='libd2srv_unittests-d2_tsig_key_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_tsig_key_unittest.o `test -f 'd2_tsig_key_unittest.cc' || echo '$(srcdir)/'`d2_tsig_key_unittest.cc + +libd2srv_unittests-d2_tsig_key_unittest.obj: d2_tsig_key_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_tsig_key_unittest.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Tpo -c -o libd2srv_unittests-d2_tsig_key_unittest.obj `if test -f 'd2_tsig_key_unittest.cc'; then $(CYGPATH_W) 'd2_tsig_key_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/d2_tsig_key_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Tpo $(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_tsig_key_unittest.cc' object='libd2srv_unittests-d2_tsig_key_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_tsig_key_unittest.obj `if test -f 'd2_tsig_key_unittest.cc'; then $(CYGPATH_W) 'd2_tsig_key_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/d2_tsig_key_unittest.cc'; fi` + +libd2srv_unittests-d2_update_message_unittests.o: d2_update_message_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_update_message_unittests.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Tpo -c -o libd2srv_unittests-d2_update_message_unittests.o `test -f 'd2_update_message_unittests.cc' || echo '$(srcdir)/'`d2_update_message_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Tpo $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_update_message_unittests.cc' object='libd2srv_unittests-d2_update_message_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_update_message_unittests.o `test -f 'd2_update_message_unittests.cc' || echo '$(srcdir)/'`d2_update_message_unittests.cc + +libd2srv_unittests-d2_update_message_unittests.obj: d2_update_message_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_update_message_unittests.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Tpo -c -o libd2srv_unittests-d2_update_message_unittests.obj `if test -f 'd2_update_message_unittests.cc'; then $(CYGPATH_W) 'd2_update_message_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_update_message_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Tpo $(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_update_message_unittests.cc' object='libd2srv_unittests-d2_update_message_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_update_message_unittests.obj `if test -f 'd2_update_message_unittests.cc'; then $(CYGPATH_W) 'd2_update_message_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_update_message_unittests.cc'; fi` + +libd2srv_unittests-d2_zone_unittests.o: d2_zone_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_zone_unittests.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Tpo -c -o libd2srv_unittests-d2_zone_unittests.o `test -f 'd2_zone_unittests.cc' || echo '$(srcdir)/'`d2_zone_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Tpo $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_zone_unittests.cc' object='libd2srv_unittests-d2_zone_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_zone_unittests.o `test -f 'd2_zone_unittests.cc' || echo '$(srcdir)/'`d2_zone_unittests.cc + +libd2srv_unittests-d2_zone_unittests.obj: d2_zone_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-d2_zone_unittests.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Tpo -c -o libd2srv_unittests-d2_zone_unittests.obj `if test -f 'd2_zone_unittests.cc'; then $(CYGPATH_W) 'd2_zone_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_zone_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Tpo $(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_zone_unittests.cc' object='libd2srv_unittests-d2_zone_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-d2_zone_unittests.obj `if test -f 'd2_zone_unittests.cc'; then $(CYGPATH_W) 'd2_zone_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_zone_unittests.cc'; fi` + +libd2srv_unittests-dns_client_unittests.o: dns_client_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-dns_client_unittests.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Tpo -c -o libd2srv_unittests-dns_client_unittests.o `test -f 'dns_client_unittests.cc' || echo '$(srcdir)/'`dns_client_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Tpo $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dns_client_unittests.cc' object='libd2srv_unittests-dns_client_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-dns_client_unittests.o `test -f 'dns_client_unittests.cc' || echo '$(srcdir)/'`dns_client_unittests.cc + +libd2srv_unittests-dns_client_unittests.obj: dns_client_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-dns_client_unittests.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Tpo -c -o libd2srv_unittests-dns_client_unittests.obj `if test -f 'dns_client_unittests.cc'; then $(CYGPATH_W) 'dns_client_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/dns_client_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Tpo $(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dns_client_unittests.cc' object='libd2srv_unittests-dns_client_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-dns_client_unittests.obj `if test -f 'dns_client_unittests.cc'; then $(CYGPATH_W) 'dns_client_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/dns_client_unittests.cc'; fi` + +libd2srv_unittests-nc_trans_unittests.o: nc_trans_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-nc_trans_unittests.o -MD -MP -MF $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Tpo -c -o libd2srv_unittests-nc_trans_unittests.o `test -f 'nc_trans_unittests.cc' || echo '$(srcdir)/'`nc_trans_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Tpo $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nc_trans_unittests.cc' object='libd2srv_unittests-nc_trans_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-nc_trans_unittests.o `test -f 'nc_trans_unittests.cc' || echo '$(srcdir)/'`nc_trans_unittests.cc + +libd2srv_unittests-nc_trans_unittests.obj: nc_trans_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libd2srv_unittests-nc_trans_unittests.obj -MD -MP -MF $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Tpo -c -o libd2srv_unittests-nc_trans_unittests.obj `if test -f 'nc_trans_unittests.cc'; then $(CYGPATH_W) 'nc_trans_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/nc_trans_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Tpo $(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nc_trans_unittests.cc' object='libd2srv_unittests-nc_trans_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srv_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libd2srv_unittests-nc_trans_unittests.obj `if test -f 'nc_trans_unittests.cc'; then $(CYGPATH_W) 'nc_trans_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/nc_trans_unittests.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Po + -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po + -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po + -rm -f ./$(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po + -rm -f ./$(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po + -rm -f ./$(DEPDIR)/libd2srv_unittests-run_unittests.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_tsig_key_unittest.Po + -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_update_message_unittests.Po + -rm -f ./$(DEPDIR)/libd2srv_unittests-d2_zone_unittests.Po + -rm -f ./$(DEPDIR)/libd2srv_unittests-dns_client_unittests.Po + -rm -f ./$(DEPDIR)/libd2srv_unittests-nc_trans_unittests.Po + -rm -f ./$(DEPDIR)/libd2srv_unittests-run_unittests.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-TESTS check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lib/d2srv/tests/d2_tsig_key_unittest.cc b/src/lib/d2srv/tests/d2_tsig_key_unittest.cc new file mode 100644 index 0000000..2bc6171 --- /dev/null +++ b/src/lib/d2srv/tests/d2_tsig_key_unittest.cc @@ -0,0 +1,98 @@ +// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +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(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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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 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::vectoralgorithms; + 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 +#include +#include +#include + +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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +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 socket_; + + /// @brief The UDP socket endpoint. + std::unique_ptr 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::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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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 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::vectoralgorithms; + 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& 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 +#include + +#include + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); + + int result = RUN_ALL_TESTS(); + + return (result); +} diff --git a/src/lib/d2srv/testutils/Makefile.am b/src/lib/d2srv/testutils/Makefile.am new file mode 100644 index 0000000..c7ae2ad --- /dev/null +++ b/src/lib/d2srv/testutils/Makefile.am @@ -0,0 +1,23 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +CLEANFILES = *.gcno *.gcda + +if HAVE_GTEST + +noinst_LTLIBRARIES = libd2srvtest.la + +libd2srvtest_la_SOURCES = nc_test_utils.cc nc_test_utils.h +libd2srvtest_la_SOURCES += stats_test_utils.cc stats_test_utils.h + +libd2srvtest_la_CXXFLAGS = $(AM_CXXFLAGS) +libd2srvtest_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) + +libd2srvtest_la_LIBADD = $(top_builddir)/src/lib/d2srv/libkea-d2srv.la +libd2srvtest_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la + +endif diff --git a/src/lib/d2srv/testutils/Makefile.in b/src/lib/d2srv/testutils/Makefile.in new file mode 100644 index 0000000..51bfe05 --- /dev/null +++ b/src/lib/d2srv/testutils/Makefile.in @@ -0,0 +1,852 @@ +# 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@ +subdir = src/lib/d2srv/testutils +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 = +LTLIBRARIES = $(noinst_LTLIBRARIES) +@HAVE_GTEST_TRUE@libd2srvtest_la_DEPENDENCIES = $(top_builddir)/src/lib/d2srv/libkea-d2srv.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la +am__libd2srvtest_la_SOURCES_DIST = nc_test_utils.cc nc_test_utils.h \ + stats_test_utils.cc stats_test_utils.h +@HAVE_GTEST_TRUE@am_libd2srvtest_la_OBJECTS = \ +@HAVE_GTEST_TRUE@ libd2srvtest_la-nc_test_utils.lo \ +@HAVE_GTEST_TRUE@ libd2srvtest_la-stats_test_utils.lo +libd2srvtest_la_OBJECTS = $(am_libd2srvtest_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libd2srvtest_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libd2srvtest_la_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +@HAVE_GTEST_TRUE@am_libd2srvtest_la_rpath = +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)/libd2srvtest_la-nc_test_utils.Plo \ + ./$(DEPDIR)/libd2srvtest_la-stats_test_utils.Plo +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 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libd2srvtest_la_SOURCES) +DIST_SOURCES = $(am__libd2srvtest_la_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 +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) +CLEANFILES = *.gcno *.gcda +@HAVE_GTEST_TRUE@noinst_LTLIBRARIES = libd2srvtest.la +@HAVE_GTEST_TRUE@libd2srvtest_la_SOURCES = nc_test_utils.cc \ +@HAVE_GTEST_TRUE@ nc_test_utils.h stats_test_utils.cc \ +@HAVE_GTEST_TRUE@ stats_test_utils.h +@HAVE_GTEST_TRUE@libd2srvtest_la_CXXFLAGS = $(AM_CXXFLAGS) +@HAVE_GTEST_TRUE@libd2srvtest_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +@HAVE_GTEST_TRUE@libd2srvtest_la_LIBADD = $(top_builddir)/src/lib/d2srv/libkea-d2srv.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la +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/testutils/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/d2srv/testutils/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-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libd2srvtest.la: $(libd2srvtest_la_OBJECTS) $(libd2srvtest_la_DEPENDENCIES) $(EXTRA_libd2srvtest_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libd2srvtest_la_LINK) $(am_libd2srvtest_la_rpath) $(libd2srvtest_la_OBJECTS) $(libd2srvtest_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srvtest_la-nc_test_utils.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libd2srvtest_la-stats_test_utils.Plo@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 $@ $< + +libd2srvtest_la-nc_test_utils.lo: nc_test_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srvtest_la_CPPFLAGS) $(CPPFLAGS) $(libd2srvtest_la_CXXFLAGS) $(CXXFLAGS) -MT libd2srvtest_la-nc_test_utils.lo -MD -MP -MF $(DEPDIR)/libd2srvtest_la-nc_test_utils.Tpo -c -o libd2srvtest_la-nc_test_utils.lo `test -f 'nc_test_utils.cc' || echo '$(srcdir)/'`nc_test_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srvtest_la-nc_test_utils.Tpo $(DEPDIR)/libd2srvtest_la-nc_test_utils.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nc_test_utils.cc' object='libd2srvtest_la-nc_test_utils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srvtest_la_CPPFLAGS) $(CPPFLAGS) $(libd2srvtest_la_CXXFLAGS) $(CXXFLAGS) -c -o libd2srvtest_la-nc_test_utils.lo `test -f 'nc_test_utils.cc' || echo '$(srcdir)/'`nc_test_utils.cc + +libd2srvtest_la-stats_test_utils.lo: stats_test_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srvtest_la_CPPFLAGS) $(CPPFLAGS) $(libd2srvtest_la_CXXFLAGS) $(CXXFLAGS) -MT libd2srvtest_la-stats_test_utils.lo -MD -MP -MF $(DEPDIR)/libd2srvtest_la-stats_test_utils.Tpo -c -o libd2srvtest_la-stats_test_utils.lo `test -f 'stats_test_utils.cc' || echo '$(srcdir)/'`stats_test_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libd2srvtest_la-stats_test_utils.Tpo $(DEPDIR)/libd2srvtest_la-stats_test_utils.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='stats_test_utils.cc' object='libd2srvtest_la-stats_test_utils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libd2srvtest_la_CPPFLAGS) $(CPPFLAGS) $(libd2srvtest_la_CXXFLAGS) $(CXXFLAGS) -c -o libd2srvtest_la-stats_test_utils.lo `test -f 'stats_test_utils.cc' || echo '$(srcdir)/'`stats_test_utils.cc + +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 + +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 +check: check-recursive +all-am: Makefile $(LTLIBRARIES) +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-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libd2srvtest_la-nc_test_utils.Plo + -rm -f ./$(DEPDIR)/libd2srvtest_la-stats_test_utils.Plo + -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)/libd2srvtest_la-nc_test_utils.Plo + -rm -f ./$(DEPDIR)/libd2srvtest_la-stats_test_utils.Plo + -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) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir 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/testutils/nc_test_utils.cc b/src/lib/d2srv/testutils/nc_test_utils.cc new file mode 100644 index 0000000..3903535 --- /dev/null +++ b/src/lib/d2srv/testutils/nc_test_utils.cc @@ -0,0 +1,987 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace isc; +using namespace isc::d2; +namespace ph = std::placeholders; + +namespace isc { +namespace d2 { + +const char* valid_d2_config = "{ " + "\"ip-address\" : \"127.0.0.1\" , " + "\"port\" : 5031, " + "\"tsig-keys\": [" + "{ \"name\": \"d2_key.example.com\" , " + " \"algorithm\": \"HMAC-MD5\" ," + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " + "} ]," + "\"forward-ddns\" : {" + "\"ddns-domains\": [ " + "{ \"name\": \"example.com.\" , " + " \"key-name\": \"d2_key.example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.101\" } " + "] } ] }, " + "\"reverse-ddns\" : {" + "\"ddns-domains\": [ " + "{ \"name\": \" 0.168.192.in.addr.arpa.\" , " + " \"key-name\": \"d2_key.example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.101\" , " + " \"port\": 100 } ] } " + "] } }"; + + +const char* TEST_DNS_SERVER_IP = "127.0.0.1"; +size_t TEST_DNS_SERVER_PORT = 5301; + +//const bool HAS_RDATA = true; +const bool NO_RDATA = false; + +//*************************** FauxServer class *********************** + +FauxServer::FauxServer(asiolink::IOService& io_service, + asiolink::IOAddress& address, size_t port) + :io_service_(io_service), address_(address), port_(port), + server_socket_(), receive_pending_(false), perpetual_receive_(true), + tsig_key_() { + + server_socket_.reset(new boost::asio::ip::udp::socket(io_service_.get_io_service(), + boost::asio::ip::udp::v4())); + server_socket_->set_option(boost::asio::socket_base::reuse_address(true)); + + isc::asiolink::UDPEndpoint endpoint(address_, port_); + server_socket_->bind(endpoint.getASIOEndpoint()); +} + +FauxServer::FauxServer(asiolink::IOService& io_service, + DnsServerInfo& server) + :io_service_(io_service), address_(server.getIpAddress()), + port_(server.getPort()), server_socket_(), receive_pending_(false), + perpetual_receive_(true), tsig_key_() { + server_socket_.reset(new boost::asio::ip::udp::socket(io_service_.get_io_service(), + boost::asio::ip::udp::v4())); + server_socket_->set_option(boost::asio::socket_base::reuse_address(true)); + isc::asiolink::UDPEndpoint endpoint(address_, port_); + server_socket_->bind(endpoint.getASIOEndpoint()); +} + + +FauxServer::~FauxServer() { +} + +void +FauxServer::receive (const ResponseMode& response_mode, + const dns::Rcode& response_rcode) { + if (receive_pending_) { + return; + } + + receive_pending_ = true; + server_socket_->async_receive_from(boost::asio::buffer(receive_buffer_, + sizeof(receive_buffer_)), + remote_, + std::bind(&FauxServer::requestHandler, + this, ph::_1, ph::_2, + response_mode, + response_rcode)); +} + +void +FauxServer::requestHandler(const boost::system::error_code& error, + std::size_t bytes_recvd, + const ResponseMode& response_mode, + const dns::Rcode& response_rcode) { + receive_pending_ = false; + // If we encountered an error or received no data then fail. + // We expect the client to send good requests. + if (error.value() != 0 || bytes_recvd < 1) { + ADD_FAILURE() << "FauxServer receive failed: " << error.message(); + return; + } + + // If TSIG key isn't NULL, create a context and use to verify the + // request and sign the response. + dns::TSIGContextPtr context; + if (tsig_key_) { + context.reset(new dns::TSIGContext(*tsig_key_)); + } + + // We have a successfully received data. We need to turn it into + // a request in order to build a proper response. + // Note D2UpdateMessage is geared towards making requests and + // reading responses. This is the opposite perspective so we have + // to a bit of roll-your-own here. + dns::Message request(dns::Message::PARSE); + util::InputBuffer request_buf(receive_buffer_, bytes_recvd); + try { + request.fromWire(request_buf); + + // If context is not NULL, then we need to verify the message. + if (context) { + dns::TSIGError error = context->verify(request.getTSIGRecord(), + receive_buffer_, + bytes_recvd); + if (error != dns::TSIGError::NOERROR()) { + isc_throw(TSIGVerifyError, "TSIG verification failed: " + << error.toText()); + } + } + } catch (const std::exception& ex) { + // If the request cannot be parsed, then fail the test. + // We expect the client to send good requests. + ADD_FAILURE() << "FauxServer request is corrupt:" << ex.what(); + return; + } + + // The request parsed OK, so let's build a response. + // We must use the QID we received in the response or IOFetch will + // toss the response out, resulting in eventual timeout. + // We fill in the zone with data we know is from the "server". + dns::Message response(dns::Message::RENDER); + response.setQid(request.getQid()); + dns::Question question(dns::Name("response.example.com"), + dns::RRClass::ANY(), dns::RRType::SOA()); + response.addQuestion(question); + response.setOpcode(dns::Opcode(dns::Opcode::UPDATE_CODE)); + response.setHeaderFlag(dns::Message::HEADERFLAG_QR, true); + + // Set the response Rcode to value passed in, default is NOERROR. + response.setRcode(response_rcode); + + // Render the response to a buffer. + dns::MessageRenderer renderer; + util::OutputBuffer response_buf(TEST_MSG_MAX); + renderer.setBuffer(&response_buf); + + if (response_mode == INVALID_TSIG) { + // Create a different key to sign the response. + std::string secret ("key that doesn't match"); + D2TsigKeyPtr key; + ASSERT_NO_THROW(key.reset(new + D2TsigKey(dns::Name("badkey"), + dns::TSIGKey::HMACMD5_NAME(), + secret.c_str(), secret.size()))); + context.reset(new dns::TSIGContext(*key)); + } + + response.toWire(renderer, context.get()); + + // If mode is to ship garbage, then stomp on part of the rendered + // message. + if (response_mode == CORRUPT_RESP) { + response_buf.writeUint16At(0xFFFF, 2); + } + + // Ship the response via synchronous send. + try { + int cnt = server_socket_->send_to(boost::asio:: + buffer(response_buf.getData(), + response_buf.getLength()), + remote_); + // Make sure we sent what we expect to send. + if (cnt != response_buf.getLength()) { + ADD_FAILURE() << "FauxServer sent: " << cnt << " expected: " + << response_buf.getLength(); + } + } catch (const std::exception& ex) { + ADD_FAILURE() << "FauxServer send failed: " << ex.what(); + } + + if (perpetual_receive_) { + // Schedule the next receive + receive (response_mode, response_rcode); + } +} + + + +//********************** TimedIO class *********************** + +TimedIO::TimedIO() + : io_service_(new isc::asiolink::IOService()), + timer_(*io_service_), run_time_(0) { +} + +TimedIO::~TimedIO() { +} + +int +TimedIO::runTimedIO(int run_time) { + run_time_ = run_time; + int cnt = io_service_->get_io_service().poll(); + if (cnt == 0) { + timer_.setup(std::bind(&TimedIO::timesUp, this), run_time_); + cnt = io_service_->get_io_service().run_one(); + timer_.cancel(); + } + + return (cnt); +} + +void +TimedIO::timesUp() { + io_service_->stop(); + FAIL() << "Test Time: " << run_time_ << " expired"; +} + +//********************** TransactionTest class *********************** + +const unsigned int TransactionTest::FORWARD_CHG = 0x01; +const unsigned int TransactionTest::REVERSE_CHG = 0x02; +const unsigned int TransactionTest::FWD_AND_REV_CHG = REVERSE_CHG | FORWARD_CHG; + +TransactionTest::TransactionTest() : ncr_(), cfg_mgr_(new D2CfgMgr()) { +} + +TransactionTest::~TransactionTest() { +} + +void +TransactionTest::setupForIPv4Transaction(dhcp_ddns::NameChangeType chg_type, + int change_mask, + const TSIGKeyInfoPtr& tsig_key_info) { + const char* msg_str = + "{" + " \"change-type\" : 0 , " + " \"forward-change\" : true , " + " \"reverse-change\" : true , " + " \"fqdn\" : \"my.forward.example.com.\" , " + " \"ip-address\" : \"192.168.2.1\" , " + " \"dhcid\" : \"0102030405060708\" , " + " \"lease-expires-on\" : \"20130121132405\" , " + " \"lease-length\" : 1300, " + " \"use-conflict-resolution\" : true " + "}"; + + // Create NameChangeRequest from JSON string. + ncr_ = dhcp_ddns::NameChangeRequest::fromJSON(msg_str); + + // Set the change type. + ncr_->setChangeType(chg_type); + + // If the change mask does not include a forward change clear the + // forward domain; otherwise create the domain and its servers. + if (!(change_mask & FORWARD_CHG)) { + ncr_->setForwardChange(false); + forward_domain_.reset(); + } else { + // Create the forward domain and then its servers. + forward_domain_ = makeDomain("example.com.", tsig_key_info); + addDomainServer(forward_domain_, "forward.example.com", + "127.0.0.1", 5301, tsig_key_info); + addDomainServer(forward_domain_, "forward2.example.com", + "127.0.0.1", 5302, tsig_key_info); + } + + // If the change mask does not include a reverse change clear the + // reverse domain; otherwise create the domain and its servers. + if (!(change_mask & REVERSE_CHG)) { + ncr_->setReverseChange(false); + reverse_domain_.reset(); + } else { + // Create the reverse domain and its server. + reverse_domain_ = makeDomain("2.168.192.in.addr.arpa.", tsig_key_info); + addDomainServer(reverse_domain_, "reverse.example.com", + "127.0.0.1", 5301, tsig_key_info); + addDomainServer(reverse_domain_, "reverse2.example.com", + "127.0.0.1", 5302, tsig_key_info); + } +} + +void +TransactionTest::setupForIPv4Transaction(dhcp_ddns::NameChangeType chg_type, + int change_mask, + const std::string& key_name) { + setupForIPv4Transaction(chg_type, change_mask, makeTSIGKeyInfo(key_name)); +} + +void +TransactionTest::setupForIPv6Transaction(dhcp_ddns::NameChangeType chg_type, + int change_mask, + const TSIGKeyInfoPtr& tsig_key_info) { + const char* msg_str = + "{" + " \"change-type\" : 0 , " + " \"forward-change\" : true , " + " \"reverse-change\" : true , " + " \"fqdn\" : \"my6.forward.example.com.\" , " + " \"ip-address\" : \"2001:1::100\" , " + " \"dhcid\" : \"0102030405060708\" , " + " \"lease-expires-on\" : \"20130121132405\" , " + " \"lease-length\" : 1300, " + " \"use-conflict-resolution\" : true " + "}"; + + // Create NameChangeRequest from JSON string. + ncr_ = makeNcrFromString(msg_str); + + // Set the change type. + ncr_->setChangeType(chg_type); + + // If the change mask does not include a forward change clear the + // forward domain; otherwise create the domain and its servers. + if (!(change_mask & FORWARD_CHG)) { + ncr_->setForwardChange(false); + forward_domain_.reset(); + } else { + // Create the forward domain and then its servers. + forward_domain_ = makeDomain("example.com.", tsig_key_info); + addDomainServer(forward_domain_, "fwd6-server.example.com", + "::1", 5301, tsig_key_info); + addDomainServer(forward_domain_, "fwd6-server2.example.com", + "::1", 5302, tsig_key_info); + } + + // If the change mask does not include a reverse change clear the + // reverse domain; otherwise create the domain and its servers. + if (!(change_mask & REVERSE_CHG)) { + ncr_->setReverseChange(false); + reverse_domain_.reset(); + } else { + // Create the reverse domain and its server. + reverse_domain_ = makeDomain("1.2001.ip6.arpa.", tsig_key_info); + addDomainServer(reverse_domain_, "rev6-server.example.com", + "::1", 5301, tsig_key_info); + addDomainServer(reverse_domain_, "rev6-server2.example.com", + "::1", 5302, tsig_key_info); + } +} + +void +TransactionTest::setupForIPv6Transaction(dhcp_ddns::NameChangeType chg_type, + int change_mask, + const std::string& key_name) { + setupForIPv6Transaction(chg_type, change_mask, makeTSIGKeyInfo(key_name)); +} + + +//********************** Functions **************************** + +void +checkRRCount(const D2UpdateMessagePtr& request, + D2UpdateMessage::UpdateMsgSection section, int count) { + dns::RRsetIterator rrset_it = request->beginSection(section); + dns::RRsetIterator rrset_end = request->endSection(section); + + ASSERT_EQ(count, std::distance(rrset_it, rrset_end)); +} + +void +checkZone(const D2UpdateMessagePtr& request, const std::string& exp_zone_name) { + // Verify the zone section info. + D2ZonePtr zone = request->getZone(); + EXPECT_TRUE(zone); + EXPECT_EQ(exp_zone_name, zone->getName().toText()); + EXPECT_EQ(dns::RRClass::IN().getCode(), zone->getClass().getCode()); +} + +void +checkRR(dns::RRsetPtr rrset, const std::string& exp_name, + const dns::RRClass& exp_class, const dns::RRType& exp_type, + unsigned int exp_ttl, dhcp_ddns::NameChangeRequestPtr ncr, + bool has_rdata) { + // Verify the FQDN/DHCID RR fields. + EXPECT_EQ(exp_name, rrset->getName().toText()); + EXPECT_EQ(exp_class.getCode(), rrset->getClass().getCode()); + EXPECT_EQ(exp_type.getCode(), rrset->getType().getCode()); + EXPECT_EQ(exp_ttl, rrset->getTTL().getValue()); + if ((!has_rdata) || + (exp_type == dns::RRType::ANY() || exp_class == dns::RRClass::ANY())) { + // ANY types do not have RData + ASSERT_EQ(0, rrset->getRdataCount()); + return; + } + + ASSERT_EQ(1, rrset->getRdataCount()); + dns::RdataIteratorPtr rdata_it = rrset->getRdataIterator(); + ASSERT_TRUE(rdata_it); + + if ((exp_type == dns::RRType::A()) || + (exp_type == dns::RRType::AAAA())) { + // should have lease rdata + EXPECT_EQ(ncr->getIpAddress(), rdata_it->getCurrent().toText()); + } else if (exp_type == dns::RRType::PTR()) { + // should have PTR rdata + EXPECT_EQ(ncr->getFqdn(), rdata_it->getCurrent().toText()); + } else if (exp_type == dns::RRType::DHCID()) { + // should have DHCID rdata + const std::vector& 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())); + } else { + // we got a problem + FAIL(); + } +} + +dns::RRsetPtr +getRRFromSection(const D2UpdateMessagePtr& request, + D2UpdateMessage::UpdateMsgSection section, int index) { + dns::RRsetIterator rrset_it = request->beginSection(section); + dns::RRsetIterator rrset_end = request->endSection(section); + + if (std::distance(rrset_it, rrset_end) <= index) { + // Return an empty pointer if index is out of range. + return (dns::RRsetPtr()); + } + + std::advance(rrset_it, index); + return (*rrset_it); +} + +dhcp_ddns::NameChangeRequestPtr +makeNcrFromString(const std::string& ncr_str) { + return (dhcp_ddns::NameChangeRequest::fromJSON(ncr_str)); +} + +DdnsDomainPtr +makeDomain(const std::string& zone_name, const std::string& key_name) { + DnsServerInfoStoragePtr servers(new DnsServerInfoStorage()); + DdnsDomainPtr domain(new DdnsDomain(zone_name, servers, key_name)); + return (domain); +} + +DdnsDomainPtr +makeDomain(const std::string& zone_name, const TSIGKeyInfoPtr &tsig_key_info) { + DdnsDomainPtr domain; + DnsServerInfoStoragePtr servers(new DnsServerInfoStorage()); + std::string key_name; + if (tsig_key_info) { + key_name = tsig_key_info->getName(); + } + domain.reset(new DdnsDomain(zone_name, servers, key_name)); + return (domain); +} + +TSIGKeyInfoPtr +makeTSIGKeyInfo(const std::string& key_name, const std::string& secret, + const std::string& algorithm) { + TSIGKeyInfoPtr key_info; + if (!key_name.empty()) { + if (!secret.empty()) { + key_info.reset(new TSIGKeyInfo(key_name, algorithm, secret)); + } else { + // Since secret was left blank, we'll convert key_name into a + // base64 encoded string and use that. + const uint8_t* bytes = reinterpret_cast + (key_name.c_str()); + size_t len = key_name.size(); + const vector key_name_v(bytes, bytes + len); + std::string key_name64 + = isc::util::encode::encodeBase64(key_name_v); + + // Now, make the TSIGKeyInfo with a real base64 secret. + key_info.reset(new TSIGKeyInfo(key_name, algorithm, key_name64)); + } + } + + return (key_info); +} + +void +addDomainServer(DdnsDomainPtr& domain, const std::string& name, + const std::string& ip, const size_t port, + const TSIGKeyInfoPtr &tsig_key_info) { + DnsServerInfoPtr server(new DnsServerInfo(name, asiolink::IOAddress(ip), + port, true, tsig_key_info)); + domain->getServers()->push_back(server); +} + +// Verifies that the contents of the given transaction's DNS update request +// is correct for adding a forward DNS entry +void +checkAddFwdAddressRequest(NameChangeTransaction& tran) { + const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest(); + ASSERT_TRUE(request); + + // Safety check. + dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr(); + ASSERT_TRUE(ncr); + + std::string exp_zone_name = tran.getForwardDomain()->getName(); + std::string exp_fqdn = ncr->getFqdn(); + const dns::RRType& exp_ip_rr_type = tran.getAddressRRType(); + + // Verify the zone section. + checkZone(request, exp_zone_name); + + // Verify the PREREQUISITE SECTION + // Should be 1 which tests for FQDN does not exist. + dns::RRsetPtr rrset; + checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 1); + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_PREREQUISITE, 0)); + checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::ANY(), 0, ncr); + + // Verify the UPDATE SECTION + // Should be 2 RRs: 1 to add the FQDN/IP and one to add the DHCID RR + checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 2); + + // Fetch ttl. + uint32_t ttl = ncr->getLeaseLength(); + + // First, Verify the FQDN/IP add RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 0)); + checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, ttl, ncr); + + // Now, verify the DHCID add RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 1)); + checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(), + ttl, ncr); + + // Verify there are no RRs in the ADDITIONAL Section. + checkRRCount(request, D2UpdateMessage::SECTION_ADDITIONAL, 0); + + // Verify that it will render toWire without throwing. + dns::MessageRenderer renderer; + ASSERT_NO_THROW(request->toWire(renderer)); +} + +// Verifies that the contents of the given transaction's DNS update request +// is correct for replacing a forward DNS entry +void +checkReplaceFwdAddressRequest(NameChangeTransaction& tran) { + const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest(); + ASSERT_TRUE(request); + + // Safety check. + dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr(); + ASSERT_TRUE(ncr); + + std::string exp_zone_name = tran.getForwardDomain()->getName(); + std::string exp_fqdn = ncr->getFqdn(); + const dns::RRType& exp_ip_rr_type = tran.getAddressRRType(); + + // Verify the zone section. + checkZone(request, exp_zone_name); + + // Verify the PREREQUISITE SECTION + // Should be 2, 1 which tests for FQDN does exist and the other + // checks for a matching DHCID. + dns::RRsetPtr rrset; + checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 2); + + // Verify the FQDN test RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_PREREQUISITE, 0)); + checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::ANY(), 0, ncr); + + // Verify the DHCID test RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_PREREQUISITE, 1)); + checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(), 0, ncr); + + // Verify the UPDATE SECTION + // Should be 2, 1 which deletes the existing FQDN/IP and one that + // adds the new one. + checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 2); + + // Fetch ttl. + uint32_t ttl = ncr->getLeaseLength(); + + // Verify the FQDN delete RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 0)); + checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr); + + // Verify the FQDN/IP add RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 1)); + checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, ttl, ncr); + + // Verify there are no RRs in the ADDITIONAL Section. + checkRRCount(request, D2UpdateMessage::SECTION_ADDITIONAL, 0); + + // Verify that it will render toWire without throwing. + dns::MessageRenderer renderer; + ASSERT_NO_THROW(request->toWire(renderer)); +} + +// Verifies that the contents of the given transaction's DNS update request +// is correct for replacing a reverse DNS entry +void +checkReplaceRevPtrsRequest(NameChangeTransaction& tran) { + const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest(); + ASSERT_TRUE(request); + + // Safety check. + dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr(); + ASSERT_TRUE(ncr); + + std::string exp_zone_name = tran.getReverseDomain()->getName(); + std::string exp_rev_addr = D2CfgMgr::reverseIpAddress(ncr->getIpAddress()); + + // Verify the zone section. + checkZone(request, exp_zone_name); + + // Verify there are no RRs in the PREREQUISITE Section. + checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 0); + + // Fetch ttl. + uint32_t ttl = ncr->getLeaseLength(); + + // Verify the UPDATE Section. + // It should contain 4 RRs: + // 1. A delete all PTR RRs for the given IP + // 2. A delete of all DHCID RRs for the given IP + // 3. An add of the new PTR RR + // 4. An add of the new DHCID RR + dns::RRsetPtr rrset; + checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 4); + + // Verify the PTR delete RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 0)); + checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::PTR(), + 0, ncr); + + // Verify the DHCID delete RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 1)); + checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::DHCID(), + 0, ncr); + + // Verify the PTR add RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 2)); + checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::PTR(), + ttl, ncr); + + // Verify the DHCID add RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 3)); + checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::DHCID(), + ttl, ncr); + + // Verify there are no RRs in the ADDITIONAL Section. + checkRRCount(request, D2UpdateMessage::SECTION_ADDITIONAL, 0); + + // Verify that it will render toWire without throwing. + dns::MessageRenderer renderer; + ASSERT_NO_THROW(request->toWire(renderer)); +} + +void +checkRemoveFwdAddressRequest(NameChangeTransaction& tran) { + const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest(); + ASSERT_TRUE(request); + + // Safety check. + dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr(); + ASSERT_TRUE(ncr); + + std::string exp_zone_name = tran.getForwardDomain()->getName(); + std::string exp_fqdn = ncr->getFqdn(); + + // Verify the zone section. + checkZone(request, exp_zone_name); + + // Verify there is 1 RR in the PREREQUISITE Section. + checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 1); + + // Verify the DHCID matching assertion RR. + dns::RRsetPtr rrset; + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_PREREQUISITE, 0)); + checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(), + 0, ncr); + + // Verify there is 1 RR in the UPDATE Section. + checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 1); + + // Verify the FQDN/IP delete RR. + const dns::RRType& exp_ip_rr_type = tran.getAddressRRType(); + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 0)); + checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), exp_ip_rr_type, + 0, ncr); + + // Verify that it will render toWire without throwing. + dns::MessageRenderer renderer; + ASSERT_NO_THROW(request->toWire(renderer)); +} + +void +checkRemoveFwdRRsRequest(NameChangeTransaction& tran) { + const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest(); + ASSERT_TRUE(request); + + // Safety check. + dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr(); + ASSERT_TRUE(ncr); + + std::string exp_zone_name = tran.getForwardDomain()->getName(); + std::string exp_fqdn = ncr->getFqdn(); + + // Verify the zone section. + checkZone(request, exp_zone_name); + + // Verify there is 1 RR in the PREREQUISITE Section. + checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 3); + + // Verify the DHCID matches assertion. + dns::RRsetPtr rrset; + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_PREREQUISITE, 0)); + checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(), + 0, ncr); + + // Verify the NO A RRs assertion. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_PREREQUISITE, 1)); + checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::A(), + 0, ncr, NO_RDATA); + + // Verify the NO AAAA RRs assertion. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_PREREQUISITE, 2)); + checkRR(rrset, exp_fqdn, dns::RRClass::NONE(), dns::RRType::AAAA(), + 0, ncr, NO_RDATA); + + // Verify there is 1 RR in the UPDATE Section. + checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 1); + + // Verify the delete all for the FQDN RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 0)); + checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::ANY(), + 0, ncr); + + // Verify that it will render toWire without throwing. + dns::MessageRenderer renderer; + ASSERT_NO_THROW(request->toWire(renderer)); +} + +void +checkRemoveRevPtrsRequest(NameChangeTransaction& tran) { + const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest(); + ASSERT_TRUE(request); + + // Safety check. + dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr(); + ASSERT_TRUE(ncr); + + std::string exp_zone_name = tran.getReverseDomain()->getName(); + std::string exp_rev_addr = D2CfgMgr::reverseIpAddress(ncr->getIpAddress()); + + // Verify the zone section. + checkZone(request, exp_zone_name); + + // Verify there is 1 RR in the PREREQUISITE Section. + checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 1); + + // Verify the FQDN-PTRNAME assertion RR. + dns::RRsetPtr rrset; + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_PREREQUISITE, 0)); + checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::PTR(), + 0, ncr); + + // Verify there is 1 RR in the UPDATE Section. + checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 1); + + // Verify the delete all for the FQDN RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 0)); + checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::ANY(), + 0, ncr); + + // Verify that it will render toWire without throwing. + dns::MessageRenderer renderer; + ASSERT_NO_THROW(request->toWire(renderer)); +} + +std::string +toHexText(const uint8_t* data, size_t len) { + std::ostringstream stream; + stream << "Data length is: " << len << std::endl; + for (int i = 0; i < len; ++i) { + if (i > 0 && ((i % 16) == 0)) { + stream << std::endl; + } + + stream << setfill('0') << setw(2) << setbase(16) + << static_cast(data[i]) << " "; + } + + return (stream.str()); +} + +// Verifies that the contents of the given transaction's DNS update request +// is correct for replacing a forward DNS entry when not using conflict +// resolution. +void +checkSimpleReplaceFwdAddressRequest(NameChangeTransaction& tran) { + const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest(); + ASSERT_TRUE(request); + + // Safety check. + dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr(); + ASSERT_TRUE(ncr); + + std::string exp_zone_name = tran.getForwardDomain()->getName(); + std::string exp_fqdn = ncr->getFqdn(); + const dns::RRType& exp_ip_rr_type = tran.getAddressRRType(); + + // Verify the zone section. + checkZone(request, exp_zone_name); + + // Verify the PREREQUISITE SECTION + // There should be no prerequisites. + dns::RRsetPtr rrset; + checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 0); + + // Verify the UPDATE SECTION + // Should be 4 + // 1. delete of the FQDN/IP RR + // 2. delete of the DHCID RR + // 3. add of the FQDN/IP RR + // 4. add of the DHCID RR + checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 4); + + // Fetch ttl. + uint32_t ttl = ncr->getLeaseLength(); + + // Verify the FQDN delete RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 0)); + checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr); + + // Verify the DHCID delete RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 1)); + checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::DHCID(), + 0, ncr); + + // Verify the FQDN/IP add RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 2)); + checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, ttl, ncr); + + // Now, verify the DHCID add RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 3)); + checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(), + ttl, ncr); + + // Verify there are no RRs in the ADDITIONAL Section. + checkRRCount(request, D2UpdateMessage::SECTION_ADDITIONAL, 0); + + // Verify that it will render toWire without throwing. + dns::MessageRenderer renderer; + ASSERT_NO_THROW(request->toWire(renderer)); +} + +void +checkSimpleRemoveFwdRRsRequest(NameChangeTransaction& tran) { + const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest(); + ASSERT_TRUE(request); + + // Safety check. + dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr(); + ASSERT_TRUE(ncr); + + std::string exp_zone_name = tran.getForwardDomain()->getName(); + std::string exp_fqdn = ncr->getFqdn(); + const dns::RRType& exp_ip_rr_type = tran.getAddressRRType(); + + // Verify the zone section. + checkZone(request, exp_zone_name); + + // Verify there no prerequisites. + checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 0); + + // Verify there are 2 RRs in the UPDATE Section. + checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 2); + + // Verify the FQDN delete RR. + dns::RRsetPtr rrset; + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 0)); + checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), exp_ip_rr_type, 0, ncr); + + // Verify the DHCID delete RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 1)); + checkRR(rrset, exp_fqdn, dns::RRClass::ANY(), dns::RRType::DHCID(), + 0, ncr); + + // Verify that it will render toWire without throwing. + dns::MessageRenderer renderer; + ASSERT_NO_THROW(request->toWire(renderer)); +} + +// Verifies that the contents of the given transaction's DNS update request +// is correct for removing a reverse DNS entry when not using conflict +// resolution. +void +checkSimpleRemoveRevPtrsRequest(NameChangeTransaction& tran) { + const D2UpdateMessagePtr& request = tran.getDnsUpdateRequest(); + ASSERT_TRUE(request); + + // Safety check. + dhcp_ddns::NameChangeRequestPtr ncr = tran.getNcr(); + ASSERT_TRUE(ncr); + + std::string exp_zone_name = tran.getReverseDomain()->getName(); + std::string exp_rev_addr = D2CfgMgr::reverseIpAddress(ncr->getIpAddress()); + + // Verify the zone section. + checkZone(request, exp_zone_name); + + // Verify there are no RRs in the PREREQUISITE Section. + checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 0); + + // Verify there is 2 RRs in the UPDATE Section. + checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 2); + + // Verify the FQDN delete RR. + dns::RRsetPtr rrset; + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 0)); + checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::PTR(), 0, ncr); + + // Verify the DHCID delete RR. + ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage:: + SECTION_UPDATE, 1)); + checkRR(rrset, exp_rev_addr, dns::RRClass::ANY(), dns::RRType::DHCID(), 0, ncr); + + // Verify that it will render toWire without throwing. + dns::MessageRenderer renderer; + ASSERT_NO_THROW(request->toWire(renderer)); +} + +// Verifies the current state and next event in a transaction +void +checkContext(NameChangeTransactionPtr trans, const int exp_state, + const int exp_evt, const std::string& file, int line) { + ASSERT_TRUE(trans); + ASSERT_TRUE(exp_state == trans->getCurrState() && exp_evt == trans->getNextEvent()) + << "expected state: " << trans->getStateLabel(exp_state) + << " event: " << trans->getEventLabel(exp_evt) + << ", actual context: " << trans->getContextStr() + << " at " << file << ":" << line; +} + +} // namespace isc::d2 +} // namespace isc diff --git a/src/lib/d2srv/testutils/nc_test_utils.h b/src/lib/d2srv/testutils/nc_test_utils.h new file mode 100644 index 0000000..73b6287 --- /dev/null +++ b/src/lib/d2srv/testutils/nc_test_utils.h @@ -0,0 +1,497 @@ +// 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/. + +#ifndef NC_TEST_UTILS_H +#define NC_TEST_UTILS_H + +/// @file nc_test_utils.h prototypes for functions related transaction testing. + +#include +#include +#include +#include + +#include +#include +#include + +namespace isc { +namespace d2 { + +extern const char* valid_d2_config; +extern const char* TEST_DNS_SERVER_IP; +extern size_t TEST_DNS_SERVER_PORT; + +// Not extern'ed to allow use as array size +const int TEST_MSG_MAX = 1024; + +typedef boost::shared_ptr SocketPtr; + +/// @brief This class simulates a DNS server. It is capable of performing +/// an asynchronous read, governed by an IOService, and responding to received +/// requests in a given manner. +class FauxServer { +public: + /// @brief The types of response generated by the server. + enum ResponseMode { + USE_RCODE, // Generate a response with a given RCODE + CORRUPT_RESP, // Generate a corrupt response + INVALID_TSIG // Generate a response with the wrong TSIG key + }; + + /// @brief Reference to IOService to use for IO processing. + asiolink::IOService& io_service_; + + /// @brief IP address at which to listen for requests. + const asiolink::IOAddress& address_; + + /// @brief Port on which to listen for requests. + size_t port_; + + /// @brief Socket on which listening is done. + SocketPtr server_socket_; + + /// @brief Stores the end point of requesting client. + boost::asio::ip::udp::endpoint remote_; + + /// @brief Buffer in which received packets are stuffed. + uint8_t receive_buffer_[TEST_MSG_MAX]; + + /// @brief Flag which indicates if a receive has been initiated but not yet + /// completed. + bool receive_pending_; + /// @brief Flag which indicates if server is in perpetual receive mode. + /// If true once a receive has been completed, a new one will be + /// automatically initiated. + bool perpetual_receive_; + + /// @brief TSIG Key to use to verify requests and sign responses. If it is + /// NULL TSIG is not used. + D2TsigKeyPtr tsig_key_; + + /// @brief Constructor + /// + /// @param io_service IOService to be used for socket IO. + /// @param address IP address at which the server should listen. + /// @param port Port number at which the server should listen. + FauxServer(asiolink::IOService& io_service, asiolink::IOAddress& address, + size_t port); + + /// @brief Constructor + /// + /// @param io_service IOService to be used for socket IO. + /// @param server DnsServerInfo of server the DNS server. This supplies the + /// server's ip address and port. + FauxServer(asiolink::IOService& io_service, DnsServerInfo& server); + + /// @brief Destructor + virtual ~FauxServer(); + + /// @brief Initiates an asynchronous receive + /// + /// Starts the server listening for requests. Upon completion of the + /// listen, the callback method, requestHandler, is invoked. + /// + /// @param response_mode Selects how the server responds to a request + /// @param response_rcode The Rcode value set in the response. Not used + /// for all modes. + void receive (const ResponseMode& response_mode, + const dns::Rcode& response_rcode=dns::Rcode::NOERROR()); + + /// @brief Socket IO Completion callback + /// + /// This method servers as the Server's UDP socket receive callback handler. + /// When the receive completes the handler is invoked with the parameters + /// listed. + /// + /// @param error result code of the receive (determined by asio layer) + /// @param bytes_recvd number of bytes received, if any + /// @param response_mode type of response the handler should produce + /// @param response_rcode value of Rcode in the response constructed by + /// handler + void requestHandler(const boost::system::error_code& error, + std::size_t bytes_recvd, + const ResponseMode& response_mode, + const dns::Rcode& response_rcode); + + /// @brief Returns true if a receive has been started but not completed. + bool isReceivePending() { + return receive_pending_; + } + + /// @brief Sets the TSIG key to the given value. + /// + /// @param tsig_key Pointer to the TSIG key to use. If the pointer is + /// empty, TSIG will not be used. + void setTSIGKey(const D2TsigKeyPtr& tsig_key) { + tsig_key_ = tsig_key; + } +}; + +/// @brief Provides a means to process IOService IO for a finite amount of time. +/// +/// This class instantiates an IOService provides a single method, runTimedIO +/// which will run the IOService for no more than a finite amount of time, +/// at least one event is executed or the IOService is stopped. +/// It provides an virtual handler for timer expiration event. It is +/// intended to be used as a base class for test fixtures that need to process +/// IO by providing them a consistent way to do so while retaining a safety +/// valve so tests do not hang. +class TimedIO { +public: + asiolink::IOServicePtr io_service_; + asiolink::IntervalTimer timer_; + int run_time_; + + /// @brief Constructor + TimedIO(); + + /// @brief Destructor + virtual ~TimedIO(); + + /// @brief IO Timer expiration handler + /// + /// Stops the IOService and fails the current test. + virtual void timesUp(); + + /// @brief Processes IO till time expires or at least one handler executes. + /// + /// This method first polls IOService to run any ready handlers. If no + /// handlers are ready, it starts the internal time to run for the given + /// amount of time and invokes service's run_one method. This method + /// blocks until at least one handler executes or the IO Service is stopped. + /// Upon completion of this method the timer is cancelled. Should the + /// timer expires prior to run_one returning, the timesUp handler will be + /// invoked which stops the IO service and fails the test. + /// + /// Note that this method closely mimics the runIO method in D2Process. + /// + /// @param run_time maximum length of time to run in milliseconds before + /// timing out. + /// + /// @return Returns the number of handlers executed or zero. A return of + /// zero indicates that the IOService has been stopped. + int runTimedIO(int run_time); + +}; + +/// @brief Base class Test fixture for testing transactions. +class TransactionTest : public TimedIO, public ::testing::Test { +public: + dhcp_ddns::NameChangeRequestPtr ncr_; + DdnsDomainPtr forward_domain_; + DdnsDomainPtr reverse_domain_; + D2CfgMgrPtr cfg_mgr_; + + /// @brief constants used to specify change directions for a transaction. + static const unsigned int FORWARD_CHG; // Only forward change. + static const unsigned int REVERSE_CHG; // Only reverse change. + static const unsigned int FWD_AND_REV_CHG; // Both forward and reverse. + + TransactionTest(); + virtual ~TransactionTest(); + + /// @brief Creates a transaction which requests an IPv4 DNS update. + /// + /// The transaction is constructed around a predefined (i.e. "canned") + /// IPv4 NameChangeRequest. The request has both forward and reverse DNS + /// changes requested. Based upon the change mask, the transaction + /// will have either the forward, reverse, or both domains populated. + /// + /// @param change_type selects the type of change requested, CHG_ADD or + /// CHG_REMOVE. + /// @param change_mask determines which change directions are requested + /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG. + /// @param tsig_key_info pointer to the TSIGKeyInfo to assign to both + /// domains in the transaction. This will cause the transaction to + /// use TSIG. If the pointer is empty, TSIG will not be used. + void setupForIPv4Transaction(dhcp_ddns::NameChangeType change_type, + int change_mask, + const TSIGKeyInfoPtr& tsig_key_info = + TSIGKeyInfoPtr()); + + /// @brief Creates a transaction which requests an IPv4 DNS update. + /// + /// Convenience wrapper around the above method which accepts a string + /// key_name from which the TSIGKeyInfo is constructed. Note the string + /// may not be blank. + /// + /// @param change_type selects the type of change requested, CHG_ADD or + /// CHG_REMOVE. + /// @param change_mask determines which change directions are requested + /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG. + /// @param key_name value to use to create TSIG key. The value may not + /// be blank. + void setupForIPv4Transaction(dhcp_ddns::NameChangeType change_type, + int change_mask, const std::string& key_name); + + /// @brief Creates a transaction which requests an IPv6 DNS update. + /// + /// The transaction is constructed around a predefined (i.e. "canned") + /// IPv6 NameChangeRequest. The request has both forward and reverse DNS + /// changes requested. Based upon the change mask, the transaction + /// will have either the forward, reverse, or both domains populated. + /// + /// @param change_type selects the type of change requested, CHG_ADD or + /// CHG_REMOVE. + /// @param change_mask determines which change directions are requested + /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG. + /// @param tsig_key_info pointer to the TSIGKeyInfo to assign to both + /// domains in the transaction. This will cause the transaction to + /// use TSIG. If the pointer is empty, TSIG will not be used. + void setupForIPv6Transaction(dhcp_ddns::NameChangeType change_type, + int change_mask, + const TSIGKeyInfoPtr& tsig_key_info = + TSIGKeyInfoPtr()); + + /// @brief Creates a transaction which requests an IPv6 DNS update. + /// + /// Convenience wrapper around the above method which accepts a string + /// key_name from which the TSIGKeyInfo is constructed. Note the string + /// may not be blank. + /// + /// @param change_type selects the type of change requested, CHG_ADD or + /// CHG_REMOVE. + /// @param change_mask determines which change directions are requested + /// FORWARD_CHG, REVERSE_CHG, or FWD_AND_REV_CHG. + /// @param key_name value to use to create TSIG key, if blank TSIG will not + /// be used. + void setupForIPv6Transaction(dhcp_ddns::NameChangeType change_type, + int change_mask, const std::string& key_name); +}; + + +/// @brief Tests the number of RRs in a request section against a given count. +/// +/// This function actually returns the number of RRsetPtrs in a section. Since +/// D2 only uses RRsets with a single RData in each (i.e. 1 RR), it is used +/// as the number of RRs. The dns::Message::getRRCount() cannot be used for +/// this as it returns the number of RDatas in an RRSet which does NOT equate +/// to the number of RRs. RRs with no RData, those with class or type of ANY, +/// are not counted. +/// +/// @param request DNS update request to test +/// @param section enum value of the section to count +/// @param count the expected number of RRs +extern void checkRRCount(const D2UpdateMessagePtr& request, + D2UpdateMessage::UpdateMsgSection section, int count); + +/// @brief Tests the zone content of a given request. +/// +/// @param request DNS update request to validate +/// @param exp_zone_name expected value of the zone name in the zone section +extern void checkZone(const D2UpdateMessagePtr& request, + const std::string& exp_zone_name); + +/// @brief Tests the contents of an RRset +/// +/// @param rrset Pointer the RRset to test +/// @param exp_name expected value of RRset name (FQDN or reverse ip) +/// @param exp_class expected RRClass value of RRset +/// @param exp_typ expected RRType value of RRset +/// @param exp_ttl expected TTL value of RRset +/// @param ncr NameChangeRequest on which the RRset is based +/// @param has_rdata if true, RRset's rdata will be checked based on it's +/// RRType. Set this to false if the RRset's type supports Rdata but it does +/// not contain it. For instance, prerequisites of type NONE have no Rdata +/// where updates of type NONE may. +extern void checkRR(dns::RRsetPtr rrset, const std::string& exp_name, + const dns::RRClass& exp_class, const dns::RRType& exp_type, + unsigned int exp_ttl, dhcp_ddns::NameChangeRequestPtr ncr, + bool has_rdata=true); + +/// @brief Fetches an RR(set) from a given section of a request +/// +/// @param request DNS update request from which the RR should come +/// @param section enum value of the section from which the RR should come +/// @param index zero-based index of the RR of interest. +/// +/// @return Pointer to the RR of interest, empty pointer if the index is out +/// of range. +extern dns::RRsetPtr getRRFromSection(const D2UpdateMessagePtr& request, + D2UpdateMessage::UpdateMsgSection section, + int index); +/// @brief Creates a NameChangeRequest from a JSON string +/// +/// @param ncr_str JSON string form of a NameChangeRequest. Example: +/// @code +/// const char* msg_str = +/// "{" +/// " \"change-type\" : 0 , " +/// " \"forward-change\" : true , " +/// " \"reverse-change\" : true , " +/// " \"fqdn\" : \"my.example.com.\" , " +/// " \"ip-address\" : \"192.168.2.1\" , " +/// " \"dhcid\" : \"0102030405060708\" , " +/// " \"lease-expires-on\" : \"20130121132405\" , " +/// " \"lease-length\" : 1300 " +/// "}"; +/// +/// @endcode + +/// @brief Verifies a forward mapping addition DNS update request +/// +/// Tests that the DNS Update request for a given transaction, is correct for +/// adding a forward DNS mapping. +/// +/// @param tran Transaction containing the request to be verified. +extern void checkAddFwdAddressRequest(NameChangeTransaction& tran); + +/// @brief Verifies a forward mapping replacement DNS update request +/// +/// Tests that the DNS Update request for a given transaction, is correct for +/// replacing a forward DNS mapping. +/// +/// @param tran Transaction containing the request to be verified. +extern void checkReplaceFwdAddressRequest(NameChangeTransaction& tran); + +/// @brief Verifies a reverse mapping replacement DNS update request +/// +/// Tests that the DNS Update request for a given transaction, is correct for +/// replacing a reverse DNS mapping. +/// +/// @param tran Transaction containing the request to be verified. +extern void checkReplaceRevPtrsRequest(NameChangeTransaction& tran); + +/// @brief Verifies a forward address removal DNS update request +/// +/// Tests that the DNS Update request for a given transaction, is correct for +/// removing the forward address DNS entry. +/// +/// @param tran Transaction containing the request to be verified. +extern void checkRemoveFwdAddressRequest(NameChangeTransaction& tran); + +/// @brief Verifies a forward RR removal DNS update request +/// +/// Tests that the DNS Update request for a given transaction, is correct for +/// removing forward RR DNS entries. +/// +/// @param tran Transaction containing the request to be verified. +extern void checkRemoveFwdRRsRequest(NameChangeTransaction& tran); + +/// @brief Verifies a reverse mapping removal DNS update request +/// +/// Tests that the DNS Update request for a given transaction, is correct for +/// removing a reverse DNS mapping. +/// +/// @param tran Transaction containing the request to be verified. +extern void checkRemoveRevPtrsRequest(NameChangeTransaction& tran); + +/// @brief Verifies a simple forward mapping replacement DNS update request +/// +/// Tests that the DNS Update request for a given transaction, is correct for +/// replacing a forward DNS mapping when not using conflict resolution. +/// +/// @param tran Transaction containing the request to be verified. +extern void checkSimpleReplaceFwdAddressRequest(NameChangeTransaction& tran); + +/// @brief Verifies a simple forward RR removal DNS update request +/// +/// Tests that the DNS Update request for a given transaction, is correct for +/// removing forward RR DNS entries when not using conflict resolution. +/// +/// @param tran Transaction containing the request to be verified. +extern void checkSimpleRemoveFwdRRsRequest(NameChangeTransaction& tran); + +/// @brief Verifies a simple reverse RR removal DNS update request +/// +/// Tests that the DNS Update request for a given transaction, is correct for +/// removing reverse RR DNS entries when not using conflict resolution. +/// +/// @param tran Transaction containing the request to be verified. +extern void checkSimpleRemoveRevPtrsRequest(NameChangeTransaction& tran); + +/// @brief Creates a NameChangeRequest from JSON string. +/// +/// @param ncr_str string of JSON text from which to make the request. +/// +/// @return Pointer to newly created request. +/// +/// @throw Underlying methods may throw. +extern +dhcp_ddns::NameChangeRequestPtr makeNcrFromString(const std::string& ncr_str); + +/// @brief Creates a DdnsDomain with the one server. +/// +/// @param zone_name zone name of the domain +/// @param key_name TSIG key name of the TSIG key for this domain. It will +/// create a TSIGKeyInfo based on the key_name and assign it to the domain. +/// +/// @throw Underlying methods may throw. +extern DdnsDomainPtr makeDomain(const std::string& zone_name, + const std::string& key_name); + +/// @brief Creates a DdnsDomain with the one server. +/// +/// @param zone_name zone name of the domain +/// @param tsig_key_info pointer to the TSIGInfog key for this domain. +/// Defaults to an empty pointer, meaning this domain has no key. +/// +/// @throw Underlying methods may throw. +extern DdnsDomainPtr makeDomain(const std::string& zone_name, + const TSIGKeyInfoPtr& + tsig_key_info = TSIGKeyInfoPtr()); + +/// @brief Creates a TSIGKeyInfo +/// +/// @param key_name name of the key +/// @param secret key secret data as a base64 encoded string. If blank, +/// then the secret value will be generated from key_name. +/// @param algorithm algorithm to use. Defaults to MD5. +/// @return a TSIGKeyInfoPtr for the newly created key. If key_name is blank +/// the pointer will be empty. +/// @throw Underlying methods may throw. +extern +TSIGKeyInfoPtr makeTSIGKeyInfo(const std::string& key_name, + const std::string& secret = "", + const std::string& algorithm + = TSIGKeyInfo::HMAC_MD5_STR); + +/// @brief Creates a DnsServerInfo and adds it to the given DdnsDomain. +/// +/// The server is created and added to the domain, without duplicate entry +/// checking. +/// +/// @param domain DdnsDomain to which to add the server +/// @param name new server's host name of the server +/// @param ip new server's ip address +/// @param port new server's port +/// @param tsig_key_info pointer to the TSIGInfog key for this server. +/// Defaults to an empty pointer, meaning this server has no key. +/// +/// @throw Underlying methods may throw. +extern void addDomainServer(DdnsDomainPtr& domain, const std::string& name, + const std::string& ip = TEST_DNS_SERVER_IP, + const size_t port = TEST_DNS_SERVER_PORT, + const TSIGKeyInfoPtr& + tsig_key_info = TSIGKeyInfoPtr()); + +/// @brief Creates a hex text dump of the given data buffer. +/// +/// This method is not used for testing but is handy for debugging. It creates +/// a pleasantly formatted string of 2-digits per byte separated by spaces with +/// 16 bytes per line. +/// +/// @param data pointer to the data to dump +/// @param len size (in bytes) of data +extern std::string toHexText(const uint8_t* data, size_t len); + +/// @brief Verifies the current state and next event in a transaction +/// @param trans NameChangeTransaction to check +/// @param exp_state expected current state of the transaction +/// @param exp_event expected next event of the transaction +/// @param file source file name +/// @param line source line number +extern void checkContext(NameChangeTransactionPtr trans, const int exp_state, + const int exp_evt, const std::string& file, int line); + +/// @brief Macro for calling checkContext() that supplies invocation location +#define CHECK_CONTEXT(a,b,c) checkContext(a,b,c,__FILE__,__LINE__) + +} // namespace isc::d2 +} // namespace isc + +#endif diff --git a/src/lib/d2srv/testutils/stats_test_utils.cc b/src/lib/d2srv/testutils/stats_test_utils.cc new file mode 100644 index 0000000..262879b --- /dev/null +++ b/src/lib/d2srv/testutils/stats_test_utils.cc @@ -0,0 +1,41 @@ +// 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 + +#include +#include + +using namespace isc::data; +using namespace isc::stats; +using namespace std; + +namespace isc { +namespace d2 { +namespace test { + +D2StatTest::D2StatTest() { + StatsMgr::instance().removeAll(); + D2Stats::init(); +} + +D2StatTest::~D2StatTest() { + StatsMgr::instance().removeAll(); +} + +void +checkStats(const string& key_name, const StatMap& expected_stats) { + StatMap key_stats; + for (const auto& it : expected_stats) { + const string& stat_name = + StatsMgr::generateName("key", key_name, it.first); + key_stats[stat_name] = it.second; + } + checkStats(key_stats); +} + +} +} +} diff --git a/src/lib/d2srv/testutils/stats_test_utils.h b/src/lib/d2srv/testutils/stats_test_utils.h new file mode 100644 index 0000000..ccdad02 --- /dev/null +++ b/src/lib/d2srv/testutils/stats_test_utils.h @@ -0,0 +1,48 @@ +// Copyright (C) 2020-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/. + +#ifndef D2_STATS_TEST_UTILS_H +#define D2_STATS_TEST_UTILS_H + +#include +#include +#include +#include + +#include + +namespace isc { +namespace d2 { +namespace test { + +/// @brief Import statistic test utils. +using isc::stats::test::StatMap; +using isc::stats::test::checkStat; +using isc::stats::test::checkStats; + +/// @brief Test class with utility functions to test statistics. +class D2StatTest { +public: + /// @brief Constructor. + D2StatTest(); + + /// @brief Destructor. + virtual ~D2StatTest(); +}; + +/// @brief Compares StatsMgr key statistics against expected values. +/// +/// Prepend key part of names before calling checkStats simpler variant. +/// +/// @param key_name Name of the key. +/// @param expected_stats Map of expected static names and values. +void checkStats(const std::string& key_name, const StatMap& expected_stats); + +} +} +} + +#endif // D2_STATS_TEST_UTILS_H -- cgit v1.2.3