diff options
Diffstat (limited to 'src/bin/d2')
69 files changed, 40768 insertions, 0 deletions
diff --git a/src/bin/d2/Makefile.am b/src/bin/d2/Makefile.am new file mode 100644 index 0000000..5328297 --- /dev/null +++ b/src/bin/d2/Makefile.am @@ -0,0 +1,103 @@ +SUBDIRS = . tests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +CLEANFILES = *.gcno *.gcda + +EXTRA_DIST = d2.dox d2_hooks.dox +EXTRA_DIST += d2_parser.yy + +EXTRA_DIST += images/abstract_app_classes.svg images/add_state_model.svg +EXTRA_DIST += images/config_data_classes.svg images/config_from_file_sequence.svg +EXTRA_DIST += images/config_parser_classes.svg images/cpl_signal_classes.svg +EXTRA_DIST += images/cpl_signal_sequence.svg images/d2_app_classes.svg +EXTRA_DIST += images/nc_trans_sequence.svg images/remove_state_model.svg +EXTRA_DIST += images/request_mgt_classes.svg images/state_model_classes.svg +EXTRA_DIST += images/trans_classes.svg images/update_exec_classes.svg + + +# convenience archive + +noinst_LTLIBRARIES = libd2.la + +libd2_la_SOURCES = +libd2_la_SOURCES += d2_process.cc d2_process.h +libd2_la_SOURCES += d2_lexer.ll location.hh +libd2_la_SOURCES += d2_parser.cc d2_parser.h +libd2_la_SOURCES += d2_queue_mgr.cc d2_queue_mgr.h +libd2_la_SOURCES += d2_update_mgr.cc d2_update_mgr.h +libd2_la_SOURCES += nc_add.cc nc_add.h +libd2_la_SOURCES += nc_remove.cc nc_remove.h +libd2_la_SOURCES += d2_controller.cc d2_controller.h +libd2_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h +libd2_la_SOURCES += simple_add.cc simple_add.h +libd2_la_SOURCES += simple_remove.cc simple_remove.h + +sbin_PROGRAMS = kea-dhcp-ddns + +kea_dhcp_ddns_SOURCES = main.cc + +kea_dhcp_ddns_LDADD = libd2.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/d2srv/libkea-d2srv.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/process/libkea-process.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiodns/libkea-asiodns.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/http/libkea-http.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/database/libkea-database.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +kea_dhcp_ddns_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +kea_dhcp_ddns_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS) + +kea_dhcp_ddns_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) + +kea_dhcp_ddnsdir = $(pkgdatadir) + +if GENERATE_PARSER + +# Generate parser first. +all-recursive: d2_lexer.cc location.hh d2_parser.cc d2_parser.h + +parser: d2_lexer.cc location.hh d2_parser.cc d2_parser.h + @echo "Flex/bison files regenerated" + +# --- Flex/Bison stuff below -------------------------------------------------- +# When debugging grammar issues, it's useful to add -v to bison parameters. +# bison will generate parser.output file that explains the whole grammar. +# It can be used to manually follow what's going on in the parser. +# This is especially useful if yydebug_ is set to 1 as that variable +# will cause parser to print out its internal state. +# Call flex with -s to check that the default rule can be suppressed +# Call bison with -W to get warnings like unmarked empty rules +# Note C++11 deprecated register still used by flex < 2.6.0 +location.hh d2_parser.cc d2_parser.h: d2_parser.yy + $(YACC) -Wno-yacc --defines=d2_parser.h --report=all \ + --report-file=d2_parser.report -o d2_parser.cc d2_parser.yy + +d2_lexer.cc: d2_lexer.ll + $(LEX) --prefix d2_parser_ -o d2_lexer.cc d2_lexer.ll + +else + +parser location.hh d2_parser.cc d2_parser.h d2_lexer.cc: + @echo Parser generation disabled. Configure with --enable-generate-parser to enable it. + +endif diff --git a/src/bin/d2/Makefile.in b/src/bin/d2/Makefile.in new file mode 100644 index 0000000..a876038 --- /dev/null +++ b/src/bin/d2/Makefile.in @@ -0,0 +1,1030 @@ +# 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@ +sbin_PROGRAMS = kea-dhcp-ddns$(EXEEXT) +subdir = src/bin/d2 +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 = +am__installdirs = "$(DESTDIR)$(sbindir)" +PROGRAMS = $(sbin_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +libd2_la_LIBADD = +am_libd2_la_OBJECTS = d2_process.lo d2_lexer.lo d2_parser.lo \ + d2_queue_mgr.lo d2_update_mgr.lo nc_add.lo nc_remove.lo \ + d2_controller.lo parser_context.lo simple_add.lo \ + simple_remove.lo +libd2_la_OBJECTS = $(am_libd2_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 = +am_kea_dhcp_ddns_OBJECTS = main.$(OBJEXT) +kea_dhcp_ddns_OBJECTS = $(am_kea_dhcp_ddns_OBJECTS) +am__DEPENDENCIES_1 = +kea_dhcp_ddns_DEPENDENCIES = libd2.la \ + $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \ + $(top_builddir)/src/lib/d2srv/libkea-d2srv.la \ + $(top_builddir)/src/lib/process/libkea-process.la \ + $(top_builddir)/src/lib/eval/libkea-eval.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/database/libkea-database.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/dns/libkea-dns++.la \ + $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ + $(top_builddir)/src/lib/hooks/libkea-hooks.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) +kea_dhcp_ddns_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(kea_dhcp_ddns_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)/d2_controller.Plo \ + ./$(DEPDIR)/d2_lexer.Plo ./$(DEPDIR)/d2_parser.Plo \ + ./$(DEPDIR)/d2_process.Plo ./$(DEPDIR)/d2_queue_mgr.Plo \ + ./$(DEPDIR)/d2_update_mgr.Plo ./$(DEPDIR)/main.Po \ + ./$(DEPDIR)/nc_add.Plo ./$(DEPDIR)/nc_remove.Plo \ + ./$(DEPDIR)/parser_context.Plo ./$(DEPDIR)/simple_add.Plo \ + ./$(DEPDIR)/simple_remove.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 = +LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS) +LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS) +AM_V_LEX = $(am__v_LEX_@AM_V@) +am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@) +am__v_LEX_0 = @echo " LEX " $@; +am__v_LEX_1 = +YLWRAP = $(top_srcdir)/ylwrap +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 = $(libd2_la_SOURCES) $(kea_dhcp_ddns_SOURCES) +DIST_SOURCES = $(libd2_la_SOURCES) $(kea_dhcp_ddns_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 +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 \ + $(top_srcdir)/ylwrap d2_lexer.cc +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 = . tests +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib \ + -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin \ + $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) +@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static +CLEANFILES = *.gcno *.gcda +EXTRA_DIST = d2.dox d2_hooks.dox d2_parser.yy \ + images/abstract_app_classes.svg images/add_state_model.svg \ + images/config_data_classes.svg \ + images/config_from_file_sequence.svg \ + images/config_parser_classes.svg images/cpl_signal_classes.svg \ + images/cpl_signal_sequence.svg images/d2_app_classes.svg \ + images/nc_trans_sequence.svg images/remove_state_model.svg \ + images/request_mgt_classes.svg images/state_model_classes.svg \ + images/trans_classes.svg images/update_exec_classes.svg + +# convenience archive +noinst_LTLIBRARIES = libd2.la +libd2_la_SOURCES = d2_process.cc d2_process.h d2_lexer.ll location.hh \ + d2_parser.cc d2_parser.h d2_queue_mgr.cc d2_queue_mgr.h \ + d2_update_mgr.cc d2_update_mgr.h nc_add.cc nc_add.h \ + nc_remove.cc nc_remove.h d2_controller.cc d2_controller.h \ + parser_context.cc parser_context.h parser_context_decl.h \ + simple_add.cc simple_add.h simple_remove.cc simple_remove.h +kea_dhcp_ddns_SOURCES = main.cc +kea_dhcp_ddns_LDADD = libd2.la \ + $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \ + $(top_builddir)/src/lib/d2srv/libkea-d2srv.la \ + $(top_builddir)/src/lib/process/libkea-process.la \ + $(top_builddir)/src/lib/eval/libkea-eval.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/database/libkea-database.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/dns/libkea-dns++.la \ + $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \ + $(top_builddir)/src/lib/hooks/libkea-hooks.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) +kea_dhcp_ddns_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +kea_dhcp_ddnsdir = $(pkgdatadir) +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .ll .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/bin/d2/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/bin/d2/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-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_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 + +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}; \ + } + +libd2.la: $(libd2_la_OBJECTS) $(libd2_la_DEPENDENCIES) $(EXTRA_libd2_la_DEPENDENCIES) + $(AM_V_CXXLD)$(CXXLINK) $(libd2_la_OBJECTS) $(libd2_la_LIBADD) $(LIBS) + +kea-dhcp-ddns$(EXEEXT): $(kea_dhcp_ddns_OBJECTS) $(kea_dhcp_ddns_DEPENDENCIES) $(EXTRA_kea_dhcp_ddns_DEPENDENCIES) + @rm -f kea-dhcp-ddns$(EXEEXT) + $(AM_V_CXXLD)$(kea_dhcp_ddns_LINK) $(kea_dhcp_ddns_OBJECTS) $(kea_dhcp_ddns_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_controller.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_lexer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_parser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_process.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_queue_mgr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_update_mgr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nc_add.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nc_remove.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser_context.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple_add.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple_remove.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 $@ $< + +.ll.cc: + $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE) + +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 $(PROGRAMS) $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(sbindir)"; 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." + -rm -f d2_lexer.cc +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/d2_controller.Plo + -rm -f ./$(DEPDIR)/d2_lexer.Plo + -rm -f ./$(DEPDIR)/d2_parser.Plo + -rm -f ./$(DEPDIR)/d2_process.Plo + -rm -f ./$(DEPDIR)/d2_queue_mgr.Plo + -rm -f ./$(DEPDIR)/d2_update_mgr.Plo + -rm -f ./$(DEPDIR)/main.Po + -rm -f ./$(DEPDIR)/nc_add.Plo + -rm -f ./$(DEPDIR)/nc_remove.Plo + -rm -f ./$(DEPDIR)/parser_context.Plo + -rm -f ./$(DEPDIR)/simple_add.Plo + -rm -f ./$(DEPDIR)/simple_remove.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-sbinPROGRAMS + +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)/d2_controller.Plo + -rm -f ./$(DEPDIR)/d2_lexer.Plo + -rm -f ./$(DEPDIR)/d2_parser.Plo + -rm -f ./$(DEPDIR)/d2_process.Plo + -rm -f ./$(DEPDIR)/d2_queue_mgr.Plo + -rm -f ./$(DEPDIR)/d2_update_mgr.Plo + -rm -f ./$(DEPDIR)/main.Po + -rm -f ./$(DEPDIR)/nc_add.Plo + -rm -f ./$(DEPDIR)/nc_remove.Plo + -rm -f ./$(DEPDIR)/parser_context.Plo + -rm -f ./$(DEPDIR)/simple_add.Plo + -rm -f ./$(DEPDIR)/simple_remove.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: uninstall-sbinPROGRAMS + +.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 clean-sbinPROGRAMS 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-sbinPROGRAMS 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 \ + uninstall-sbinPROGRAMS + +.PRECIOUS: Makefile + + +# Generate parser first. +@GENERATE_PARSER_TRUE@all-recursive: d2_lexer.cc location.hh d2_parser.cc d2_parser.h + +@GENERATE_PARSER_TRUE@parser: d2_lexer.cc location.hh d2_parser.cc d2_parser.h +@GENERATE_PARSER_TRUE@ @echo "Flex/bison files regenerated" + +# --- Flex/Bison stuff below -------------------------------------------------- +# When debugging grammar issues, it's useful to add -v to bison parameters. +# bison will generate parser.output file that explains the whole grammar. +# It can be used to manually follow what's going on in the parser. +# This is especially useful if yydebug_ is set to 1 as that variable +# will cause parser to print out its internal state. +# Call flex with -s to check that the default rule can be suppressed +# Call bison with -W to get warnings like unmarked empty rules +# Note C++11 deprecated register still used by flex < 2.6.0 +@GENERATE_PARSER_TRUE@location.hh d2_parser.cc d2_parser.h: d2_parser.yy +@GENERATE_PARSER_TRUE@ $(YACC) -Wno-yacc --defines=d2_parser.h --report=all \ +@GENERATE_PARSER_TRUE@ --report-file=d2_parser.report -o d2_parser.cc d2_parser.yy + +@GENERATE_PARSER_TRUE@d2_lexer.cc: d2_lexer.ll +@GENERATE_PARSER_TRUE@ $(LEX) --prefix d2_parser_ -o d2_lexer.cc d2_lexer.ll + +@GENERATE_PARSER_FALSE@parser location.hh d2_parser.cc d2_parser.h d2_lexer.cc: +@GENERATE_PARSER_FALSE@ @echo Parser generation disabled. Configure with --enable-generate-parser 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/bin/d2/d2.dox b/src/bin/d2/d2.dox new file mode 100644 index 0000000..8a41e52 --- /dev/null +++ b/src/bin/d2/d2.dox @@ -0,0 +1,316 @@ +// Copyright (C) 2014-2018 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 d2 DHCP-DDNS Component + +Kea is capable of sending dynamic DNS updates to DNS Servers, based on lease +changes made by Kea's DHCP servers. When DDNS updating is enabled, +the DHCP servers generate requests to update DNS as they make lease changes. +These requests, implemented by isc::dhcp_ddns::NameChangeRequest (NCR), are sent +to a separate process, informally known as D2. D2 processes these requests by +carrying out DDNS conversations with the appropriate DNS servers such that they +update the DNS data. + +The design documentation for D2 can be found here: +<a href="https://gitlab.isc.org/isc-projects/kea/wikis/designs/ddns-design">D2 Design</a>. + +The implementation is split in several libraries which can be used separately +(linked only when required): +- src/lib/dns (libkea-dns++) - contains classes and enumerations which define +DNS update message, message content, EDNS, RData, RR Types, RCODEs, OPCODEs, +DNS Name, TSIG key and TSIG record, exception types, etc. +- src/lib/dhcp_ddns (libkea-dhcp_ddns) - contains classes to send and receive, +encapsulate and decapsulate DNS messages, etc. +- src/lib/d2srv (libkea-d2srv) - contains classes to handle NCR transactions, +the DNS client implementation, statistics, configuration parser and manager, +logger, DNS Zone, etc. +- src/bin/d2 (kea-dhcp-ddns) - contains classes which handle message queues, +D2 process and D2 controller internals, etc. + +This document contains several UML diagrams, and a few conventions used within +these diagrams are worth noting: + +- If a class is appearing on class diagram for the first time (within this +document) its background color will be yellow. If it has been presented +already, its background color will be blue and/or its details may not be shown. + +- Associations between classes in D2 are implemented in most cases via typedefs, +sometimes through a small chain of typedefs. These typedefs are shown for +accuracy but are unimportant to a general discussion. + +@section d2ProcessDerivation D2's CPL Derivations + +D2's core application classes are DDNS-specific derivations of the CPL as show +in the diagram below: + +@image html d2_app_classes.svg "D2's CPL Derivations" + +- isc::d2::D2Controller - entry point for running D2, it processes command line +options, starts and controls the application process, @c D2Process +(see @ref src/bin/d2/d2_controller.h). + +- isc::d2::D2Process - creates and manages D2's primary resources and +implements the main event loop described in @ref d2EventLoop section +(see @ref src/bin/d2/d2_process.h). + +- isc::d2::D2CfgMgr - creates, updates, and provides access to D2's application +configuration which is embodied by @c D2CfgContext +(see @ref src/lib/d2srv/d2_cfg_mgr.h). + +- isc::d2::D2CfgContext - warehouses D2's application configuration +(see @ref src/lib/d2srv/d2_cfg_mgr.h). + +@section d2ConfigMgt Configuration Management + +D2's configuration management uses the same underlying mechanisms as Kea's DHCP +servers. It's configuration information is organized into a hierarchical data +model which is mirrored in the implementation by a hierarchy of parser classes +and the runtime classes they instantiate. + +D2's data model is organized as follows: + +- A set of application level values such as the D2's IP address, port + +- Two lists of "domains": one for Forward DNS and one for Reverse DNS. @n + Each domain is described by a zone name and a list of DNS servers that can + update that zone. + +- A list of TSIG keys for conducting signed DDNS exchanges with DNS servers + +The runtime classes that embody this model are shown in the following diagram: + +@image html config_data_classes.svg "D2's Configuration Data Classes" + +- isc::d2::D2CfgContext - D2-specific derivation of @c DCfgContextBase. It +houses the "global" configuration for an instance of D2 +(see @ref src/lib/d2srv/d2_cfg_mgr.h). + +- isc::d2::DdnsDomainListMgr - manages a list of domains. Currently there is +one manager instance for the list of forward domains, and one for the list of +reverse domains. In addition the domain list, it will may house other values +specific to that list of domains (e.g. enable flag) +(see @ref src/lib/d2srv/d2_config.h). + +- isc::d2::DdnsDomain - represents a DNS domain (really a zone). When requests +are received they are matched to a domain by comparing their FQDN to the +domain's name (see @ref src/lib/d2srv/d2_config.h). + +- isc::d2::DnsServerInfo - describes a DNS server which supports DDNS for a +given domain (see @ref src/lib/d2srv/d2_config.h). + +- isc::d2::TSIGKeyInfo - describes a TSIG key used for authenticated DDNS for a +given domain (see @ref src/lib/d2srv/d2_config.h). + +The parsing classes, as one would expect, parallel the runtime classes quite +closely. The parsers are named for the runtime class they instantiate and are +either designed to parse a single occurrence of that class or list of that +class. The parser classes are shown in the diagram below: + +@image html config_parser_classes.svg "D2's Configuration Parsers" + +- isc::d2::DdnsDomainListMgrParser - parser for a domain list manager, it +creates a domain list parser. + +- isc::d2::DdnsDomainListParser - parser for a list of domains, it creates a +domain parser for domain described in a list domains. + +- isc::d2::DdnsDomainParser - Parser for a domain, it creates a DNS server list +parser. + +- isc::d2::DnsServerInfoListParser - parser for a list of DNS servers, it +creates a DNS server parser for server described in a list of servers. + +- isc::d2::DnsServerInfoParser - parser for DNS server. + +- isc::d2::TSIGKeyInfoListParser - parser for a list of TSIG keys, it creates a +parser for key described in a list of keys. + +- isc::d2::TSIGKeyInfoParser - parser for TSIG key. + +For more details on the parsers see @ref src/lib/d2srv/d2_config.h. + +The underlying common libraries for configuration parsing support configuration +input in JSON format, that complies with a fixed set of generic constructs that +may be described by a spec file (also JSON). + +@section d2NCRReceipt Request Reception and Queuing + +D2 is designed to receive requests from Kea's DHCP servers, asynchronously and +store them in queue to be processed. The classes responsible for this are +shown in the diagram below: + +@image html request_mgt_classes.svg "Request Management Classes" + +- isc::d2::D2QueueMgr - owned by @c D2Process, it listens for +@c NameChangeRequests and queues them for processing. It also provides services +for adding, finding, and removing queue entries. It owns the interface used to +receive requests and thus shields the remainder of D2 from any specific +knowledge or interaction with this interface +(see @ref src/bin/d2/d2_queue_mgr.h). + +- isc::d2::RequestQueue - storage container for the received requests +(see @ref src/bin/d2/d2_queue_mgr.h). + +- isc::dhcp_ddns::NameChangeListener - Abstract asynchronous interface for +receiving requests which uses ASIO to process IO and invoke a callback upon +request receipt (see @ref src/lib/dhcp_ddns/ncr_io.h). + +- isc::dhcp_ddns::NameChangeUDPListener - Derivation of NameChangeListener +which supports receiving request via UDP socket +(see @ref src/lib/dhcp_ddns/ncr_udp.h). + +- isc::dhcp_ddns::NameChangeRequest - embodies a request to update DNS entries +based upon a DHCP lease change (see @ref src/lib/dhcp_ddns/ncr_msg.h). + +D2QueueMgr is state-driven, albeit with a very simple state model. The states +are defined by isc::d2::D2QueueMgr::State (see @ref src/bin/d2/d2_queue_mgr.h). + +@section d2DDNSUpdateExecution Update Execution + +The DDNS protocol can lead to a multiple step conversation between the updater +and the DNS server to update entries for a single client. In addition, +@c NameChangeRequests can request changes be made for both forward and reverse +DNS. In order to carry out the appropriate conversation, D2 wraps each request +in a stateful transaction. + +Working such transactions serially can be inefficient, especially if those +requests involve different DNS servers. Therefore, D2 has been designed to +work on more than one transaction at a time by creating and managing a list of +transactions. + +The classes which are responsible for carrying out this work are shown in the +following diagram: + +@image html update_exec_classes.svg "Update Execution Classes" + +- isc::d2::D2UpdateMgr owned by @c D2Process, orchestrates the fulfillment of +each request by managing the execution of its transaction. Its high level +method @ref isc::d2::D2UpdateMgr::sweep() is meant to be called whenever IO +events occur (see @ref src/bin/d2/d2_update_mgr.h). The following steps are +performed each time the method is called: + - Any transaction which has been completed, is logged and culled from the + transaction list. + - Start a new transaction for the next queued request (if any). + +- isc::d2::NameChangeTransaction - abstract state-driven class which carries +out the steps necessary to fulfill a single request. Fulfilling a request is +achieved as IO events in response it DDNS requests drive the transaction through +its state model (see @ref src/lib/d2srv/nc_trans.h). The type of transaction is +based on the nature of the request, this is discussed further on +@ref d2TransDetail section. + +- isc::d2::DNSClient - an asynchronous worker which atomically performs a +single DDNS packet exchange with a given server, providing the response via a +callback mechanism. Each time a transaction's state model calls for a packet +exchange with a DNS server, it uses an instance of this class to do it +(see @ref src/lib/d2srv/dns_client.h). + +- isc::d2::D2UpdateMessage - container for sending and receiving DDNS packets +(see @ref src/lib/d2srv/d2_update_message.h). + +@section d2EventLoop Main Event Loop + +Now that all of the primary components have been introduced it is worth while +discussing D2's main event loop. As mentioned earlier D2 is constructed around +the CPL which is designed to be driven by asynchronous IO processed by a common +IO service thread (isc::asiolink::io_service). Any IO that needs to be +performed by the application thread uses this service to do so. This organizes +the IO event processing into a single event loop centered around the service. +(This does not preclude spinning off worker threads to conduct other tasks, with +their own io_service instances). D2's main event loop, implemented in +@ref isc::d2::D2Process::run() may be paraphrased as follows: + +@code + As long as we should not shutdown repeat the following steps: + 1. Check on the state of the request queue. Take any actions necessary + regarding it. For example, it may be in the process of shutting down + its listener prior to applying a configuration change. + + 2. Give update manager a "time slice" to cull finished transactions and + start new ones. + + 3. Block until one or more of the following IO events occur: + a. NCR message has been received + b. Transaction IO has completed + c. Interval timer expired + d. A process command has been received + e. Something has stopped the IO service (most likely a fatal error) +@endcode + +@section d2TransDetail Transactions + +There are two types of @c NameChangeRequests: an "Add" that is issued when DNS +entries need to be added for new or updated lease, and a "Remove" that is +issued when DNS entries need to be removed for obsolete or expired lease. The +DDNS protocol dictates the steps that should be followed in both cases. + +D2's design addresses this by calling for two types of transactions: one for +adding entries and one for removing them, each with their own state model. +The transaction classes are shown in the following diagram: + +@image html trans_classes.svg "NameChangeTransaction Derivations" + +- isc::d2::NameAddTransaction - carries out a @c NameChangeRequest to add +entries (see @ref src/bin/d2/nc_add.h). + +- isc::d2::NameRemoveTransaction - carries out a @c NameChangeRequest to remove +entries (see @ref src/bin/d2/nc_remove.h). + +- isc::util::StateModel - abstract state model described in @ref d2StateModel +section (see @ref src/lib/util/state_model.h). + +The state models for these two transactions implement DDNS with conflict +resolution as described in <a href="https://tools.ietf.org/html/rfc4703">RFC 4703</a>. + +The state model for isc::d2::NameAddTransaction is diagrammed below: + +@image html add_state_model.svg "State Model for NameAddTransaction" + +The state model for isc::d2::NameRemoveTransaction is depicted next: + +@image html remove_state_model.svg "State Model for NameRemoveTransaction" + +@subsection d2StateModel State Model Implementation + +D2 implements a abstract state-machine through a light weight set of classes. +At construction, the model's dictionary of events and states is initialized. +This allows, the deriving class the ability to bind a method of its choosing +to each state as that state's handler. Each handler contains the knowledge +of how to respond to the "posted" event and including posting other events and +transitioning to other states. + +Executing the model consists of beginning at the current state with the posted +event and continuing until the model needs to wait for an IO-based event or +it has reached the end of the state model. These classes will likely move to +a common library. + +@image html state_model_classes.svg "State Model Classes" + +- isc::util::StateModel - provides the mechanics for executing a state model +described by a dictionary events and states. It provides methods to: + - initialize the model - constructs the dictionary of events and states. + - start the model - sets the model to its initial state, posts a "start" + event and starts the model "running". + - run the model - process the posted event (if one) until the model reaches + a wait state or reaches the end state. + - transition from one state to another. +- isc::d2::Event - Defines an event used to stimulate the model. +- isc::d2::State - Defines a state with the model, each state has a handler +method that is executed upon transition "into" that state from another state. +- isc::d2::LabeledValue - An abstract that associates a integer value with a +text label. +- isc::d2::LabeledValueSet - A collection of LabeledValues for which the integer +values must be unique. + +@subsection d2TransExecExample Transaction Execution Example + +The following sequence chart depicts the typically sequence of events that occur +when D2UpdateMgr creates and starts executing a transaction: + +@image html nc_trans_sequence.svg "Transaction Execution Sequence" + +*/ diff --git a/src/bin/d2/d2_controller.cc b/src/bin/d2/d2_controller.cc new file mode 100644 index 0000000..af2c6df --- /dev/null +++ b/src/bin/d2/d2_controller.cc @@ -0,0 +1,156 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <cfgrpt/config_report.h> +#include <config/command_mgr.h> +#include <d2/d2_controller.h> +#include <d2/d2_process.h> +#include <d2/parser_context.h> +#include <stats/stats_mgr.h> + +#include <stdlib.h> + +using namespace isc::config; +using namespace isc::process; +using namespace isc::stats; +namespace ph = std::placeholders; + +namespace isc { +namespace d2 { + +/// @brief Defines the application name, this is passed into base class +/// it may be used to locate configuration data and appears in log statement. +const char* D2Controller::d2_app_name_ = "DhcpDdns"; + +/// @brief Defines the executable name. This is passed into the base class +const char* D2Controller::d2_bin_name_ = "kea-dhcp-ddns"; + +DControllerBasePtr& +D2Controller::instance() { + // If the instance hasn't been created yet, create it. Note this method + // must use the base class singleton instance methods. + if (!getController()) { + DControllerBasePtr controller_ptr(new D2Controller()); + setController(controller_ptr); + } + + return (getController()); +} + +DProcessBase* D2Controller::createProcess() { + // Instantiate and return an instance of the D2 application process. Note + // that the process is passed the controller's io_service. + return (new D2Process(getAppName().c_str(), getIOService())); +} + +D2Controller::D2Controller() + : DControllerBase(d2_app_name_, d2_bin_name_) { +} + +void +D2Controller::registerCommands() { + // These are the commands always supported by the D2 server. + // Please keep the list in alphabetic order. + CommandMgr::instance().registerCommand(BUILD_REPORT_COMMAND, + std::bind(&D2Controller::buildReportHandler, this, ph::_1, ph::_2)); + + CommandMgr::instance().registerCommand(CONFIG_GET_COMMAND, + std::bind(&D2Controller::configGetHandler, this, ph::_1, ph::_2)); + + CommandMgr::instance().registerCommand(CONFIG_RELOAD_COMMAND, + std::bind(&D2Controller::configReloadHandler, this, ph::_1, ph::_2)); + + CommandMgr::instance().registerCommand(CONFIG_SET_COMMAND, + std::bind(&D2Controller::configSetHandler, this, ph::_1, ph::_2)); + + CommandMgr::instance().registerCommand(CONFIG_TEST_COMMAND, + std::bind(&D2Controller::configTestHandler, this, ph::_1, ph::_2)); + + CommandMgr::instance().registerCommand(CONFIG_WRITE_COMMAND, + std::bind(&D2Controller::configWriteHandler, this, ph::_1, ph::_2)); + + CommandMgr::instance().registerCommand(SHUT_DOWN_COMMAND, + std::bind(&D2Controller::shutdownHandler, this, ph::_1, ph::_2)); + + CommandMgr::instance().registerCommand(STATUS_GET_COMMAND, + std::bind(&DControllerBase::statusGetHandler, this, ph::_1, ph::_2)); + + CommandMgr::instance().registerCommand(VERSION_GET_COMMAND, + std::bind(&D2Controller::versionGetHandler, this, ph::_1, ph::_2)); + + // Register statistic related commands. + CommandMgr::instance().registerCommand("statistic-get", + std::bind(&StatsMgr::statisticGetHandler, ph::_1, ph::_2)); + + CommandMgr::instance().registerCommand("statistic-get-all", + std::bind(&StatsMgr::statisticGetAllHandler, ph::_1, ph::_2)); + + CommandMgr::instance().registerCommand("statistic-reset", + std::bind(&StatsMgr::statisticResetHandler, ph::_1, ph::_2)); + + CommandMgr::instance().registerCommand("statistic-reset-all", + std::bind(&StatsMgr::statisticResetAllHandler, ph::_1, ph::_2)); +} + +void +D2Controller::deregisterCommands() { + try { + // Close the command socket (if it exists). + CommandMgr::instance().closeCommandSocket(); + + // Deregister any registered commands (please keep in alphabetic order) + CommandMgr::instance().deregisterCommand(BUILD_REPORT_COMMAND); + CommandMgr::instance().deregisterCommand(CONFIG_GET_COMMAND); + CommandMgr::instance().deregisterCommand(CONFIG_RELOAD_COMMAND); + CommandMgr::instance().deregisterCommand(CONFIG_SET_COMMAND); + CommandMgr::instance().deregisterCommand(CONFIG_TEST_COMMAND); + CommandMgr::instance().deregisterCommand(CONFIG_WRITE_COMMAND); + CommandMgr::instance().deregisterCommand(SHUT_DOWN_COMMAND); + CommandMgr::instance().deregisterCommand("statistic-get"); + CommandMgr::instance().deregisterCommand("statistic-get-all"); + CommandMgr::instance().deregisterCommand("statistic-reset"); + CommandMgr::instance().deregisterCommand("statistic-reset-all"); + CommandMgr::instance().deregisterCommand(STATUS_GET_COMMAND); + CommandMgr::instance().deregisterCommand(VERSION_GET_COMMAND); + + } catch (...) { + // What to do? Simply ignore... + } +} + +isc::data::ConstElementPtr +D2Controller::parseFile(const std::string& file_name) { + isc::data::ConstElementPtr elements; + + // Read contents of the file and parse it as JSON + D2ParserContext parser; + elements = parser.parseFile(file_name, D2ParserContext::PARSER_DHCPDDNS); + if (!elements) { + isc_throw(isc::BadValue, "no configuration found in file"); + } + + return (elements); +} + +D2Controller::~D2Controller() { +} + +// Refer to config_report so it will be embedded in the binary. +const char* const* d2_config_report = isc::detail::config_report; + +std::string +D2Controller::getVersionAddendum() { + std::stringstream stream; + // Currently the only dependency D2 adds to base is cryptolink + stream << isc::cryptolink::CryptoLink::getVersion() << std::endl; + return (stream.str()); + +} + +} // end namespace isc::d2 +} // end namespace isc diff --git a/src/bin/d2/d2_controller.h b/src/bin/d2/d2_controller.h new file mode 100644 index 0000000..6f61735 --- /dev/null +++ b/src/bin/d2/d2_controller.h @@ -0,0 +1,93 @@ +// Copyright (C) 2013-2019 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_CONTROLLER_H +#define D2_CONTROLLER_H + +#include <process/d_controller.h> + +namespace isc { +namespace d2 { + +class D2Controller; +/// @brief Pointer to a process controller. +typedef boost::shared_ptr<D2Controller> D2ControllerPtr; + +/// @brief Process Controller for D2 Process +/// This class is the DHCP-DDNS specific derivation of DControllerBase. It +/// creates and manages an instance of the DHCP-DDNS application process, +/// D2Process. +/// @todo Currently, this class provides only the minimum required specialized +/// behavior to run the DHCP-DDNS service. It may very well expand as the +/// service implementation evolves. Some thought was given to making +/// DControllerBase a templated class but the labor savings versus the +/// potential number of virtual methods which may be overridden didn't seem +/// worth the clutter at this point. +class D2Controller : public process::DControllerBase { +public: + /// @brief Static singleton instance method. This method returns the + /// base class singleton instance member. It instantiates the singleton + /// and sets the base class instance member upon first invocation. + /// + /// @return returns the pointer reference to the singleton instance. + static process::DControllerBasePtr& instance(); + + /// @brief Destructor. + virtual ~D2Controller(); + + /// @brief Defines the application name, this is passed into base class + /// and appears in log statements. + static const char* d2_app_name_; + + /// @brief Defines the executable name. This is passed into the base class + /// by convention this should match the executable name. + static const char* d2_bin_name_; + + /// @brief Register commands. + void registerCommands(); + + /// @brief Deregister commands. + /// @note Does not throw. + void deregisterCommands(); + +protected: + /// @brief Returns version info specific to D2 + virtual std::string getVersionAddendum(); + +private: + /// @brief Creates an instance of the DHCP-DDNS specific application + /// process. This method is invoked during the process initialization + /// step of the controller launch. + /// + /// @return returns a DProcessBase* to the application process created. + /// Note the caller is responsible for destructing the process. This + /// is handled by the base class, which wraps this pointer with a smart + /// pointer. + virtual process::DProcessBase* createProcess(); + + ///@brief Parse a given file into Elements + /// + /// Uses bison parsing to parse a JSON configuration file into an + /// a element map. + /// + /// @param file_name pathname of the file to parse + /// + /// @return pointer to the map of elements created + /// @throw BadValue if the file is empty + virtual isc::data::ConstElementPtr parseFile(const std::string& file_name); + + /// @brief Constructor is declared private to maintain the integrity of + /// the singleton instance. + D2Controller(); + + /// To facilitate unit testing. + friend class NakedD2Controller; +}; + +}; // namespace isc::d2 +}; // namespace isc + +#endif diff --git a/src/bin/d2/d2_hooks.dox b/src/bin/d2/d2_hooks.dox new file mode 100644 index 0000000..0c33820 --- /dev/null +++ b/src/bin/d2/d2_hooks.dox @@ -0,0 +1,81 @@ +// 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/. + +/** + @page d2Hooks The Hooks API for the Kea DHCP DDNS (D2) + + @section d2HooksIntroduction Introduction + Kea features an API (the "Hooks" API) that allows user-written code to + be integrated into Kea and called at specific points in its processing. + An overview of the API and a tutorial for writing such code can be found in + the @ref hooksdgDevelopersGuide. It also includes information how hooks + framework can be used to implement additional control commands for + Kea DHCP DDNS. Information for Kea maintainers can be found in the + @ref hooksComponentDeveloperGuide. + + This manual is more specialized and is aimed at developers of hook + code for Kea DHCP DDNS. It describes each hook point, what the callouts + attached to the hook are able to do, and the arguments passed to the + callouts. Each entry in this manual has the following information: + + - Name of the hook point. + - Arguments for the callout. As well as the argument name and data type, the + information includes the direction, which can be one of: + - @b in - the server passes values to the callout but ignored any data + returned. + - @b out - the callout is expected to set this value. + - <b>in/out</b> - the server passes a value to the callout and uses whatever + value the callout sends back. Note that the callout may choose not to + do any modification, in which case the server will use whatever value + it sent to the callout. + - Description of the hook. This explains where in the processing the hook + is located, the possible actions a callout attached to this hook could take, + and a description of the data passed to the callouts. + - Next step status: the action taken by the server when a callout chooses to set + status to specified value. Actions not listed explicitly are not supported. + If a callout sets status to unsupported value, this specific value will be + ignored and treated as if the status was CONTINUE. + +@section d2HooksHookPoints Hooks in Kea DHCP DDNS + +The following list is roughly ordered by appearance of specific hook points during +packet processing, but the exact order depends on the actual processing. Hook points +that are not specific to packet processing (e.g. lease expiration) will be added +to the end of this list. + + @subsection d2HooksD2SrvConfigured d2_srv_configured + - @b Arguments: + - name: @b io_context, type: isc::asiolink::IOServicePtr, direction: <b>in</b> + - name: @b json_config, type: isc::data::ConstElementPtr, direction: <b>in</b> + - name: @b server_config, type: isc::d2::D2CfgContextPtr, direction: <b>in</b> + - name: @b error, type: std::string, direction: <b>out</b> + + - @b Description: this callout is executed when the server has completed + its (re)configuration. The server provides received and parsed configuration + structures to the hook library. It also provides a pointer to the IOService + object which is used by the server to run asynchronous operations. The hooks + libraries can use this IOService object to schedule asynchronous tasks which + are triggered by the Kea DHCP DDNS's main loop. The hook library should hold + the provided pointer until the library is unloaded. The D2CfgContext + object provides access to the D2 running configuration. + + - <b>Next step status</b>: If any callout sets the status to DROP, the server + considers the configuration is incorrect and rejects it using the error + string as an error message. + + @subsection d2HooksSelectKey select_key + - @b Arguments: + - name: @b current_server, type: isc::d2::DnsServerInfoPtr, direction: <b>in</b> + - name: @b tsig_key, type: isc::d2::D2TsigKeyPtr, direction: <b>in/out</b> + + - @b Description: this callout is executed when the D2 is selecting for + a TSIG key to protect the next DNS update to the already chosen DNS + server. Pointers to the current DNS server and TSIG key are provided. + If a hook library wants to use another TSIG key the pointer must be updated. + + - <b>Next step status</b>: If any callout sets the status to a different + value than CONTINUE the current server is skipped. +*/ diff --git a/src/bin/d2/d2_lexer.cc b/src/bin/d2/d2_lexer.cc new file mode 100644 index 0000000..df67bfd --- /dev/null +++ b/src/bin/d2/d2_lexer.cc @@ -0,0 +1,3676 @@ +#line 1 "d2_lexer.cc" + +#line 3 "d2_lexer.cc" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +/* %not-for-header */ +/* %if-c-only */ +/* %if-not-reentrant */ +#define yy_create_buffer d2_parser__create_buffer +#define yy_delete_buffer d2_parser__delete_buffer +#define yy_scan_buffer d2_parser__scan_buffer +#define yy_scan_string d2_parser__scan_string +#define yy_scan_bytes d2_parser__scan_bytes +#define yy_init_buffer d2_parser__init_buffer +#define yy_flush_buffer d2_parser__flush_buffer +#define yy_load_buffer_state d2_parser__load_buffer_state +#define yy_switch_to_buffer d2_parser__switch_to_buffer +#define yypush_buffer_state d2_parser_push_buffer_state +#define yypop_buffer_state d2_parser_pop_buffer_state +#define yyensure_buffer_stack d2_parser_ensure_buffer_stack +#define yy_flex_debug d2_parser__flex_debug +#define yyin d2_parser_in +#define yyleng d2_parser_leng +#define yylex d2_parser_lex +#define yylineno d2_parser_lineno +#define yyout d2_parser_out +#define yyrestart d2_parser_restart +#define yytext d2_parser_text +#define yywrap d2_parser_wrap +#define yyalloc d2_parser_alloc +#define yyrealloc d2_parser_realloc +#define yyfree d2_parser_free + +/* %endif */ +/* %endif */ +/* %ok-for-header */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* %if-c++-only */ +/* %endif */ + +/* %if-c-only */ +#ifdef yy_create_buffer +#define d2_parser__create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer d2_parser__create_buffer +#endif + +#ifdef yy_delete_buffer +#define d2_parser__delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer d2_parser__delete_buffer +#endif + +#ifdef yy_scan_buffer +#define d2_parser__scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer d2_parser__scan_buffer +#endif + +#ifdef yy_scan_string +#define d2_parser__scan_string_ALREADY_DEFINED +#else +#define yy_scan_string d2_parser__scan_string +#endif + +#ifdef yy_scan_bytes +#define d2_parser__scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes d2_parser__scan_bytes +#endif + +#ifdef yy_init_buffer +#define d2_parser__init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer d2_parser__init_buffer +#endif + +#ifdef yy_flush_buffer +#define d2_parser__flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer d2_parser__flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define d2_parser__load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state d2_parser__load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define d2_parser__switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer d2_parser__switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define d2_parser_push_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state d2_parser_push_buffer_state +#endif + +#ifdef yypop_buffer_state +#define d2_parser_pop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state d2_parser_pop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define d2_parser_ensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack d2_parser_ensure_buffer_stack +#endif + +#ifdef yylex +#define d2_parser_lex_ALREADY_DEFINED +#else +#define yylex d2_parser_lex +#endif + +#ifdef yyrestart +#define d2_parser_restart_ALREADY_DEFINED +#else +#define yyrestart d2_parser_restart +#endif + +#ifdef yylex_init +#define d2_parser_lex_init_ALREADY_DEFINED +#else +#define yylex_init d2_parser_lex_init +#endif + +#ifdef yylex_init_extra +#define d2_parser_lex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra d2_parser_lex_init_extra +#endif + +#ifdef yylex_destroy +#define d2_parser_lex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy d2_parser_lex_destroy +#endif + +#ifdef yyget_debug +#define d2_parser_get_debug_ALREADY_DEFINED +#else +#define yyget_debug d2_parser_get_debug +#endif + +#ifdef yyset_debug +#define d2_parser_set_debug_ALREADY_DEFINED +#else +#define yyset_debug d2_parser_set_debug +#endif + +#ifdef yyget_extra +#define d2_parser_get_extra_ALREADY_DEFINED +#else +#define yyget_extra d2_parser_get_extra +#endif + +#ifdef yyset_extra +#define d2_parser_set_extra_ALREADY_DEFINED +#else +#define yyset_extra d2_parser_set_extra +#endif + +#ifdef yyget_in +#define d2_parser_get_in_ALREADY_DEFINED +#else +#define yyget_in d2_parser_get_in +#endif + +#ifdef yyset_in +#define d2_parser_set_in_ALREADY_DEFINED +#else +#define yyset_in d2_parser_set_in +#endif + +#ifdef yyget_out +#define d2_parser_get_out_ALREADY_DEFINED +#else +#define yyget_out d2_parser_get_out +#endif + +#ifdef yyset_out +#define d2_parser_set_out_ALREADY_DEFINED +#else +#define yyset_out d2_parser_set_out +#endif + +#ifdef yyget_leng +#define d2_parser_get_leng_ALREADY_DEFINED +#else +#define yyget_leng d2_parser_get_leng +#endif + +#ifdef yyget_text +#define d2_parser_get_text_ALREADY_DEFINED +#else +#define yyget_text d2_parser_get_text +#endif + +#ifdef yyget_lineno +#define d2_parser_get_lineno_ALREADY_DEFINED +#else +#define yyget_lineno d2_parser_get_lineno +#endif + +#ifdef yyset_lineno +#define d2_parser_set_lineno_ALREADY_DEFINED +#else +#define yyset_lineno d2_parser_set_lineno +#endif + +#ifdef yywrap +#define d2_parser_wrap_ALREADY_DEFINED +#else +#define yywrap d2_parser_wrap +#endif + +/* %endif */ + +#ifdef yyalloc +#define d2_parser_alloc_ALREADY_DEFINED +#else +#define yyalloc d2_parser_alloc +#endif + +#ifdef yyrealloc +#define d2_parser_realloc_ALREADY_DEFINED +#else +#define yyrealloc d2_parser_realloc +#endif + +#ifdef yyfree +#define d2_parser_free_ALREADY_DEFINED +#else +#define yyfree d2_parser_free +#endif + +/* %if-c-only */ + +#ifdef yytext +#define d2_parser_text_ALREADY_DEFINED +#else +#define yytext d2_parser_text +#endif + +#ifdef yyleng +#define d2_parser_leng_ALREADY_DEFINED +#else +#define yyleng d2_parser_leng +#endif + +#ifdef yyin +#define d2_parser_in_ALREADY_DEFINED +#else +#define yyin d2_parser_in +#endif + +#ifdef yyout +#define d2_parser_out_ALREADY_DEFINED +#else +#define yyout d2_parser_out +#endif + +#ifdef yy_flex_debug +#define d2_parser__flex_debug_ALREADY_DEFINED +#else +#define yy_flex_debug d2_parser__flex_debug +#endif + +#ifdef yylineno +#define d2_parser_lineno_ALREADY_DEFINED +#else +#define yylineno d2_parser_lineno +#endif + +/* %endif */ + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +/* %if-c-only */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +/* %endif */ + +/* %if-tables-serialization */ +/* %endif */ +/* end standard C headers. */ + +/* %if-c-or-c++ */ +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* %endif */ + +/* begin standard C++ headers. */ +/* %if-c++-only */ +/* %endif */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* %not-for-header */ +/* Returned upon end-of-file. */ +#define YY_NULL 0 +/* %ok-for-header */ + +/* %not-for-header */ +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) +/* %ok-for-header */ + +/* %if-reentrant */ +/* %endif */ + +/* %if-not-reentrant */ + +/* %endif */ + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +/* %if-not-reentrant */ +extern int yyleng; +/* %endif */ + +/* %if-c-only */ +/* %if-not-reentrant */ +extern FILE *yyin, *yyout; +/* %endif */ +/* %endif */ + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { +/* %if-c-only */ + FILE *yy_input_file; +/* %endif */ + +/* %if-c++-only */ +/* %endif */ + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* %if-c-only Standard (non-C++) definition */ +/* %not-for-header */ +/* %if-not-reentrant */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ +/* %endif */ +/* %ok-for-header */ + +/* %endif */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* %if-c-only Standard (non-C++) definition */ + +/* %if-not-reentrant */ +/* %not-for-header */ +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = NULL; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; +/* %ok-for-header */ + +/* %endif */ + +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_flush_buffer ( YY_BUFFER_STATE b ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state ( void ); + +static void yyensure_buffer_stack ( void ); +static void yy_load_buffer_state ( void ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); + +/* %endif */ + +void *yyalloc ( yy_size_t ); +void *yyrealloc ( void *, yy_size_t ); +void yyfree ( void * ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */ +/* Begin user sect3 */ + +#define d2_parser_wrap() (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define FLEX_DEBUG +typedef flex_uint8_t YY_CHAR; + +FILE *yyin = NULL, *yyout = NULL; + +typedef int yy_state_type; + +extern int yylineno; +int yylineno = 1; + +extern char *yytext; +#ifdef yytext_ptr +#undef yytext_ptr +#endif +#define yytext_ptr yytext + +/* %% [1.5] DFA */ + +/* %if-c-only Standard (non-C++) definition */ + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yynoreturn yy_fatal_error ( const char* msg ); + +/* %endif */ + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ +/* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\ + yyleng = (int) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ +/* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\ + (yy_c_buf_p) = yy_cp; +/* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */ +#define YY_NUM_RULES 68 +#define YY_END_OF_BUFFER 69 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[425] = + { 0, + 61, 61, 0, 0, 0, 0, 0, 0, 0, 0, + 69, 67, 10, 11, 67, 1, 61, 58, 61, 61, + 67, 60, 59, 67, 67, 67, 67, 67, 54, 55, + 67, 67, 67, 56, 57, 5, 5, 5, 67, 67, + 67, 10, 11, 0, 0, 49, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 61, 61, + 0, 60, 61, 3, 2, 6, 0, 61, 0, 0, + 0, 0, 0, 0, 4, 0, 0, 9, 0, 50, + 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 51, 53, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 66, 64, + 0, 63, 62, 0, 0, 0, 19, 18, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 65, 62, + 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 46, 0, 0, 0, 0, 0, 14, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 44, 0, 0, 41, 0, 0, 0, 0, 32, + + 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 38, 39, 43, + 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, + 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, + 28, 0, 26, 0, 0, 0, 0, 0, 48, 0, + 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 0, 0, 0, 47, 0, 0, 0, 0, 0, 13, + 17, 0, 0, 37, 0, 0, 0, 0, 0, 0, + 31, 0, 27, 0, 0, 0, 0, 0, 35, 34, + + 0, 0, 25, 0, 23, 0, 16, 0, 24, 21, + 0, 0, 0, 0, 33, 0, 0, 40, 0, 36, + 0, 0, 15, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 5, 6, 7, 5, 5, 5, 5, 5, + 5, 8, 9, 10, 11, 12, 13, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 15, 5, 16, + 5, 17, 18, 5, 19, 20, 21, 22, 23, 24, + 5, 5, 5, 25, 5, 26, 5, 27, 28, 29, + 5, 30, 31, 32, 33, 5, 5, 5, 5, 5, + 34, 35, 36, 5, 37, 5, 38, 39, 40, 41, + + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 5, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 5, 64, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5 + } ; + +static const YY_CHAR yy_meta[65] = + { 0, + 1, 1, 2, 1, 1, 3, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1 + } ; + +static const flex_int16_t yy_base[433] = + { 0, + 0, 63, 17, 27, 35, 42, 46, 51, 80, 89, + 677, 678, 24, 673, 127, 0, 180, 678, 181, 66, + 9, 184, 678, 657, 101, 21, 14, 31, 678, 678, + 22, 61, 66, 678, 678, 678, 98, 661, 622, 0, + 655, 99, 668, 27, 205, 678, 625, 166, 83, 177, + 228, 620, 616, 159, 64, 615, 613, 623, 65, 626, + 84, 606, 164, 620, 16, 185, 187, 0, 194, 252, + 259, 260, 197, 678, 0, 678, 647, 646, 186, 198, + 232, 243, 250, 244, 678, 619, 652, 678, 209, 678, + 274, 617, 253, 257, 259, 650, 0, 326, 611, 163, + + 603, 614, 608, 596, 593, 595, 177, 637, 586, 607, + 601, 584, 593, 588, 585, 177, 586, 581, 238, 598, + 591, 594, 0, 259, 267, 277, 260, 269, 280, 586, + 678, 581, 270, 627, 626, 625, 678, 678, 356, 578, + 579, 572, 572, 569, 583, 613, 568, 563, 573, 564, + 581, 607, 563, 572, 249, 573, 603, 560, 574, 555, + 554, 567, 554, 565, 558, 561, 550, 285, 678, 678, + 297, 678, 678, 546, 580, 595, 678, 678, 386, 546, + 557, 544, 586, 552, 540, 539, 548, 554, 536, 539, + 548, 537, 549, 544, 539, 542, 577, 280, 525, 531, + + 538, 573, 524, 535, 522, 533, 563, 562, 678, 678, + 531, 530, 678, 416, 524, 518, 516, 526, 517, 509, + 522, 557, 508, 550, 522, 518, 520, 503, 502, 493, + 500, 678, 501, 498, 495, 508, 495, 678, 493, 491, + 500, 489, 496, 503, 500, 490, 484, 483, 489, 485, + 494, 524, 480, 678, 492, 483, 481, 476, 479, 467, + 472, 484, 519, 470, 471, 292, 466, 470, 478, 513, + 462, 506, 474, 463, 678, 459, 468, 506, 500, 460, + 451, 469, 449, 495, 459, 462, 461, 460, 495, 494, + 493, 678, 448, 441, 678, 444, 453, 488, 482, 678, + + 431, 255, 430, 439, 483, 438, 678, 432, 448, 443, + 438, 441, 441, 442, 470, 412, 447, 678, 678, 678, + 414, 399, 397, 395, 678, 407, 441, 408, 384, 389, + 387, 678, 436, 389, 388, 384, 376, 377, 382, 368, + 678, 366, 678, 364, 379, 362, 362, 375, 678, 365, + 361, 407, 370, 678, 371, 353, 397, 347, 326, 350, + 355, 386, 385, 338, 343, 382, 336, 344, 343, 678, + 324, 335, 327, 678, 368, 317, 366, 316, 309, 678, + 678, 313, 309, 678, 305, 353, 352, 301, 314, 349, + 678, 308, 678, 347, 306, 345, 293, 337, 678, 678, + + 336, 285, 678, 288, 678, 294, 678, 280, 678, 678, + 328, 284, 269, 243, 678, 167, 119, 678, 64, 678, + 53, 2, 678, 678, 459, 462, 465, 0, 468, 471, + 474, 477 + } ; + +static const flex_int16_t yy_def[433] = + { 0, + 425, 425, 426, 426, 425, 425, 425, 425, 425, 425, + 424, 424, 424, 424, 424, 427, 424, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 428, + 424, 424, 424, 429, 15, 424, 45, 45, 45, 45, + 430, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 427, 424, 424, + 424, 424, 424, 424, 431, 424, 424, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 428, 424, 429, 424, + 424, 45, 45, 45, 45, 432, 45, 430, 45, 45, + + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 431, 424, 424, 424, 424, 424, 424, 424, + 424, 45, 45, 45, 45, 432, 424, 424, 98, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 424, 424, 424, + 424, 424, 424, 424, 45, 45, 424, 424, 98, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + + 45, 45, 45, 45, 45, 45, 45, 45, 424, 424, + 424, 45, 424, 98, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 424, 45, 45, 45, 45, 45, 424, 45, 45, + 45, 45, 45, 45, 424, 45, 45, 45, 45, 45, + 45, 45, 45, 424, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 424, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 424, 45, 45, 424, 45, 45, 45, 45, 424, + + 45, 45, 45, 45, 45, 45, 424, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 424, 424, 424, + 45, 45, 45, 45, 424, 45, 45, 45, 45, 45, + 45, 424, 45, 45, 45, 45, 45, 45, 45, 45, + 424, 45, 424, 45, 45, 45, 45, 45, 424, 45, + 45, 45, 45, 424, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 424, + 45, 45, 45, 424, 45, 45, 45, 45, 45, 424, + 424, 45, 45, 424, 45, 45, 45, 45, 45, 45, + 424, 45, 424, 45, 45, 45, 45, 45, 424, 424, + + 45, 45, 424, 45, 424, 45, 424, 45, 424, 424, + 45, 45, 45, 45, 424, 45, 45, 424, 45, 424, + 45, 45, 424, 0, 424, 424, 424, 424, 424, 424, + 424, 424 + } ; + +static const flex_int16_t yy_nxt[743] = + { 0, + 87, 13, 14, 13, 424, 15, 16, 423, 17, 18, + 19, 20, 21, 22, 23, 24, 74, 424, 37, 14, + 37, 75, 25, 26, 38, 42, 27, 42, 37, 14, + 37, 28, 90, 29, 38, 30, 13, 14, 13, 79, + 79, 25, 31, 13, 14, 13, 80, 13, 14, 13, + 32, 40, 13, 14, 13, 33, 40, 119, 79, 82, + 81, 91, 34, 35, 13, 14, 13, 120, 15, 16, + 80, 17, 18, 19, 20, 21, 22, 23, 24, 73, + 39, 13, 14, 13, 81, 25, 26, 39, 71, 27, + 13, 14, 13, 80, 28, 81, 29, 41, 30, 42, + + 42, 42, 42, 94, 25, 31, 41, 71, 422, 77, + 110, 77, 105, 32, 78, 106, 111, 83, 33, 84, + 421, 113, 94, 114, 420, 34, 35, 44, 44, 44, + 45, 45, 46, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 47, 45, + 45, 48, 45, 45, 45, 45, 45, 45, 49, 50, + 45, 51, 45, 45, 52, 45, 53, 54, 45, 55, + 45, 56, 57, 48, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 45, 45, 45, 45, 45, 45, + 45, 69, 69, 70, 72, 69, 93, 72, 95, 101, + + 102, 116, 71, 71, 103, 94, 71, 73, 95, 104, + 73, 124, 141, 142, 90, 117, 71, 95, 419, 71, + 93, 71, 71, 125, 94, 71, 45, 95, 149, 45, + 159, 150, 160, 97, 124, 71, 45, 45, 71, 121, + 45, 122, 45, 91, 45, 45, 125, 45, 418, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 69, 126, 70, 45, 77, 124, 77, + 45, 69, 78, 72, 71, 125, 126, 163, 45, 89, + 133, 45, 71, 45, 98, 134, 89, 135, 126, 168, + 168, 127, 169, 71, 169, 164, 176, 295, 128, 170, + + 129, 71, 170, 195, 133, 328, 196, 209, 89, 134, + 329, 135, 89, 168, 171, 169, 89, 172, 170, 209, + 176, 173, 233, 417, 89, 416, 209, 89, 296, 89, + 89, 138, 234, 415, 414, 413, 376, 412, 210, 139, + 411, 410, 409, 408, 139, 139, 139, 139, 139, 139, + 407, 406, 405, 404, 403, 402, 401, 400, 399, 398, + 397, 396, 395, 139, 139, 139, 139, 139, 139, 179, + 394, 393, 392, 391, 179, 179, 179, 179, 179, 179, + 377, 390, 389, 388, 387, 386, 385, 384, 383, 382, + 381, 380, 379, 179, 179, 179, 179, 179, 179, 214, + + 378, 375, 374, 373, 214, 214, 214, 214, 214, 214, + 372, 371, 370, 369, 368, 367, 366, 365, 364, 363, + 362, 361, 360, 214, 214, 214, 214, 214, 214, 45, + 359, 358, 357, 356, 45, 45, 45, 45, 45, 45, + 355, 354, 353, 352, 351, 350, 349, 348, 347, 346, + 345, 344, 343, 45, 45, 45, 45, 45, 45, 12, + 12, 12, 36, 36, 36, 68, 342, 68, 89, 89, + 89, 96, 96, 96, 123, 341, 123, 136, 136, 136, + 340, 339, 338, 337, 336, 335, 334, 333, 332, 331, + 330, 327, 326, 325, 324, 323, 322, 321, 320, 319, + + 318, 317, 316, 315, 314, 313, 312, 311, 310, 309, + 308, 307, 306, 305, 304, 303, 302, 301, 300, 299, + 298, 297, 294, 293, 292, 291, 290, 289, 288, 287, + 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, + 276, 275, 274, 273, 272, 271, 270, 269, 268, 267, + 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, + 256, 255, 254, 253, 252, 251, 250, 249, 248, 247, + 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, + 236, 235, 232, 231, 230, 229, 228, 227, 226, 225, + 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, + + 213, 212, 211, 208, 207, 206, 205, 204, 203, 202, + 201, 200, 199, 198, 197, 194, 193, 192, 191, 190, + 189, 188, 187, 186, 185, 184, 183, 182, 181, 180, + 137, 178, 177, 175, 174, 167, 166, 165, 162, 161, + 158, 157, 156, 155, 154, 153, 152, 151, 148, 147, + 146, 145, 144, 143, 140, 137, 132, 131, 130, 78, + 78, 118, 115, 112, 109, 108, 107, 100, 99, 92, + 43, 88, 86, 85, 76, 43, 424, 11, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + 424, 424 + } ; + +static const flex_int16_t yy_chk[743] = + { 0, + 428, 1, 1, 1, 0, 1, 1, 422, 1, 1, + 1, 1, 1, 1, 1, 1, 21, 0, 3, 3, + 3, 21, 1, 1, 3, 13, 1, 13, 4, 4, + 4, 1, 44, 1, 4, 1, 5, 5, 5, 26, + 31, 1, 1, 6, 6, 6, 27, 7, 7, 7, + 1, 7, 8, 8, 8, 1, 8, 65, 26, 31, + 28, 44, 1, 1, 2, 2, 2, 65, 2, 2, + 27, 2, 2, 2, 2, 2, 2, 2, 2, 20, + 5, 9, 9, 9, 28, 2, 2, 6, 20, 2, + 10, 10, 10, 32, 2, 33, 2, 9, 2, 37, + + 42, 37, 42, 49, 2, 2, 10, 20, 421, 25, + 59, 25, 55, 2, 25, 55, 59, 32, 2, 33, + 419, 61, 49, 61, 417, 2, 2, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 17, 19, 17, 19, 22, 48, 22, 50, 54, + + 54, 63, 17, 19, 54, 66, 22, 69, 67, 54, + 73, 79, 100, 100, 89, 63, 69, 50, 416, 73, + 48, 17, 19, 80, 66, 22, 45, 67, 107, 45, + 116, 107, 116, 51, 79, 69, 45, 45, 73, 66, + 51, 67, 45, 89, 45, 45, 80, 45, 414, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 51, 70, 81, 70, 51, 71, 82, 71, + 51, 72, 71, 72, 70, 83, 84, 119, 51, 91, + 93, 51, 72, 51, 51, 94, 91, 95, 81, 124, + 127, 82, 125, 70, 128, 119, 133, 266, 83, 126, + + 84, 72, 129, 155, 93, 302, 155, 168, 91, 94, + 302, 95, 91, 124, 127, 125, 91, 128, 126, 171, + 133, 129, 198, 413, 91, 412, 168, 91, 266, 91, + 91, 98, 198, 411, 408, 406, 359, 404, 171, 98, + 402, 401, 398, 397, 98, 98, 98, 98, 98, 98, + 396, 395, 394, 392, 390, 389, 388, 387, 386, 385, + 383, 382, 379, 98, 98, 98, 98, 98, 98, 139, + 378, 377, 376, 375, 139, 139, 139, 139, 139, 139, + 359, 373, 372, 371, 369, 368, 367, 366, 365, 364, + 363, 362, 361, 139, 139, 139, 139, 139, 139, 179, + + 360, 358, 357, 356, 179, 179, 179, 179, 179, 179, + 355, 353, 352, 351, 350, 348, 347, 346, 345, 344, + 342, 340, 339, 179, 179, 179, 179, 179, 179, 214, + 338, 337, 336, 335, 214, 214, 214, 214, 214, 214, + 334, 333, 331, 330, 329, 328, 327, 326, 324, 323, + 322, 321, 317, 214, 214, 214, 214, 214, 214, 425, + 425, 425, 426, 426, 426, 427, 316, 427, 429, 429, + 429, 430, 430, 430, 431, 315, 431, 432, 432, 432, + 314, 313, 312, 311, 310, 309, 308, 306, 305, 304, + 303, 301, 299, 298, 297, 296, 294, 293, 291, 290, + + 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, + 279, 278, 277, 276, 274, 273, 272, 271, 270, 269, + 268, 267, 265, 264, 263, 262, 261, 260, 259, 258, + 257, 256, 255, 253, 252, 251, 250, 249, 248, 247, + 246, 245, 244, 243, 242, 241, 240, 239, 237, 236, + 235, 234, 233, 231, 230, 229, 228, 227, 226, 225, + 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, + 212, 211, 208, 207, 206, 205, 204, 203, 202, 201, + 200, 199, 197, 196, 195, 194, 193, 192, 191, 190, + 189, 188, 187, 186, 185, 184, 183, 182, 181, 180, + + 176, 175, 174, 167, 166, 165, 164, 163, 162, 161, + 160, 159, 158, 157, 156, 154, 153, 152, 151, 150, + 149, 148, 147, 146, 145, 144, 143, 142, 141, 140, + 136, 135, 134, 132, 130, 122, 121, 120, 118, 117, + 115, 114, 113, 112, 111, 110, 109, 108, 106, 105, + 104, 103, 102, 101, 99, 96, 92, 87, 86, 78, + 77, 64, 62, 60, 58, 57, 56, 53, 52, 47, + 43, 41, 39, 38, 24, 14, 11, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 424, 424, + 424, 424 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 1; + +static const flex_int16_t yy_rule_linenum[68] = + { 0, + 136, 138, 140, 145, 146, 151, 152, 153, 165, 168, + 173, 179, 188, 199, 210, 219, 228, 237, 247, 257, + 267, 284, 301, 310, 319, 329, 341, 351, 362, 371, + 381, 391, 401, 410, 419, 428, 437, 446, 455, 464, + 473, 482, 491, 500, 509, 518, 531, 540, 549, 650, + 666, 715, 723, 738, 739, 740, 741, 742, 743, 745, + 763, 776, 781, 785, 787, 789, 791 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "d2_lexer.ll" +/* 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/. */ +#line 8 "d2_lexer.ll" + +/* Generated files do not make clang static analyser so happy */ +#ifndef __clang_analyzer__ + +#include <cctype> +#include <cerrno> +#include <climits> +#include <cstdlib> +#include <string> +#include <d2/parser_context.h> +#include <asiolink/io_address.h> +#include <boost/lexical_cast.hpp> +#include <exceptions/exceptions.h> + +/* Please avoid C++ style comments (// ... eol) as they break flex 2.6.2 */ + +/* Work around an incompatibility in flex (at least versions + 2.5.31 through 2.5.33): it generates code that does + not conform to C89. See Debian bug 333231 + <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>. */ +# undef yywrap +# define yywrap() 1 + +namespace { + +bool start_token_flag = false; + +isc::d2::D2ParserContext::ParserType start_token_value; +unsigned int comment_start_line = 0; + +}; + +/* To avoid the call to exit... oops! */ +#define YY_FATAL_ERROR(msg) isc::d2::D2ParserContext::fatal(msg) +#line 1139 "d2_lexer.cc" +/* noyywrap disables automatic rewinding for the next file to parse. Since we + always parse only a single string, there's no need to do any wraps. And + using yywrap requires linking with -lfl, which provides the default yywrap + implementation that always returns 1 anyway. */ +/* nounput simplifies the lexer, by removing support for putting a character + back into the input stream. We never use such capability anyway. */ +/* batch means that we'll never use the generated lexer interactively. */ +/* avoid to get static global variables to remain with C++. */ +/* in last resort %option reentrant */ +/* Enables debug mode. To see the debug messages, one needs to also set + yy_flex_debug to 1, then the debug messages will be printed on stderr. */ +/* I have no idea what this option does, except it was specified in the bison + examples and Postgres folks added it to remove gcc 4.3 warnings. Let's + be on the safe side and keep it. */ +#define YY_NO_INPUT 1 + +/* These are not token expressions yet, just convenience expressions that + can be used during actual token definitions. Note some can match + incorrect inputs (e.g., IP addresses) which must be checked. */ +/* for errors */ +#line 93 "d2_lexer.ll" +/* This code run each time a pattern is matched. It updates the location + by moving it ahead by yyleng bytes. yyleng specifies the length of the + currently matched token. */ +#define YY_USER_ACTION driver.loc_.columns(yyleng); +#line 1165 "d2_lexer.cc" +#line 1166 "d2_lexer.cc" + +#define INITIAL 0 +#define COMMENT 1 +#define DIR_ENTER 2 +#define DIR_INCLUDE 3 +#define DIR_EXIT 4 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +/* %if-c-only */ +#include <unistd.h> +/* %endif */ +/* %if-c++-only */ +/* %endif */ +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* %if-c-only Reentrant structure and macros (non-C++). */ +/* %if-reentrant */ +/* %if-c-only */ + +static int yy_init_globals ( void ); + +/* %endif */ +/* %if-reentrant */ +/* %endif */ +/* %endif End reentrant structures and macros. */ + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( void ); + +int yyget_debug ( void ); + +void yyset_debug ( int debug_flag ); + +YY_EXTRA_TYPE yyget_extra ( void ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in ( void ); + +void yyset_in ( FILE * _in_str ); + +FILE *yyget_out ( void ); + +void yyset_out ( FILE * _out_str ); + + int yyget_leng ( void ); + +char *yyget_text ( void ); + +int yyget_lineno ( void ); + +void yyset_lineno ( int _line_number ); + +/* %if-bison-bridge */ +/* %endif */ + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( void ); +#else +extern int yywrap ( void ); +#endif +#endif + +/* %not-for-header */ +#ifndef YY_NO_UNPUT + +#endif +/* %ok-for-header */ + +/* %endif */ + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * ); +#endif + +#ifndef YY_NO_INPUT +/* %if-c-only Standard (non-C++) definition */ +/* %not-for-header */ +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif +/* %ok-for-header */ + +/* %endif */ +#endif + +/* %if-c-only */ + +/* %endif */ + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* %if-c-only Standard (non-C++) definition */ +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +/* %endif */ +/* %if-c++-only C++ definition */ +/* %endif */ +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ +/* %% [5.0] fread()/read() definition of YY_INPUT goes here unless we're doing C++ \ */\ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ +/* %if-c++-only C++ definition \ */\ +/* %endif */ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +/* %if-c-only */ +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +#endif + +/* %if-tables-serialization structures and prototypes */ +/* %not-for-header */ +/* %ok-for-header */ + +/* %not-for-header */ +/* %tables-yydmap generated elements */ +/* %endif */ +/* end tables serialization structures and prototypes */ + +/* %ok-for-header */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 +/* %if-c-only Standard (non-C++) definition */ + +extern int yylex (void); + +#define YY_DECL int yylex (void) +/* %endif */ +/* %if-c++-only C++ definition */ +/* %endif */ +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +/* %% [6.0] YY_RULE_SETUP definition goes here */ +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/* %not-for-header */ +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) +/* %if-c-only */ + yyin = stdin; +/* %endif */ +/* %if-c++-only */ +/* %endif */ + + if ( ! yyout ) +/* %if-c-only */ + yyout = stdout; +/* %endif */ +/* %if-c++-only */ +/* %endif */ + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + { +/* %% [7.0] user's declarations go here */ +#line 99 "d2_lexer.ll" + + + +#line 103 "d2_lexer.ll" + /* This part of the code is copied over to the verbatim to the top + of the generated yylex function. Explanation: + http://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html */ + + /* Code run each time yylex is called. */ + driver.loc_.step(); + + if (start_token_flag) { + start_token_flag = false; + switch (start_token_value) { + case D2ParserContext::PARSER_JSON: + default: + return isc::d2::D2Parser::make_TOPLEVEL_JSON(driver.loc_); + case D2ParserContext::PARSER_DHCPDDNS: + return isc::d2::D2Parser::make_TOPLEVEL_DHCPDDNS(driver.loc_); + case D2ParserContext::PARSER_SUB_DHCPDDNS: + return isc::d2::D2Parser::make_SUB_DHCPDDNS(driver.loc_); + case D2ParserContext::PARSER_TSIG_KEY: + return isc::d2::D2Parser::make_SUB_TSIG_KEY(driver.loc_); + case D2ParserContext::PARSER_TSIG_KEYS: + return isc::d2::D2Parser::make_SUB_TSIG_KEYS(driver.loc_); + case D2ParserContext::PARSER_DDNS_DOMAIN: + return isc::d2::D2Parser::make_SUB_DDNS_DOMAIN(driver.loc_); + case D2ParserContext::PARSER_DDNS_DOMAINS: + return isc::d2::D2Parser::make_SUB_DDNS_DOMAINS(driver.loc_); + case D2ParserContext::PARSER_DNS_SERVER: + return isc::d2::D2Parser::make_SUB_DNS_SERVER(driver.loc_); + case D2ParserContext::PARSER_HOOKS_LIBRARY: + return isc::d2::D2Parser::make_SUB_HOOKS_LIBRARY(driver.loc_); + } + } + + +#line 1486 "d2_lexer.cc" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { +/* %% [8.0] yymore()-related code goes here */ + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + +/* %% [9.0] code to set up and find next match goes here */ + yy_current_state = (yy_start); +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 425 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_current_state != 424 ); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + +yy_find_action: +/* %% [10.0] code to find the action number goes here */ + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +/* %% [11.0] code for yylineno update goes here */ + +do_action: /* This label is used only to access EOF actions. */ + +/* %% [12.0] debug code goes here */ + if ( yy_flex_debug ) + { + if ( yy_act == 0 ) + fprintf( stderr, "--scanner backing up\n" ); + else if ( yy_act < 68 ) + fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n", + (long)yy_rule_linenum[yy_act], yytext ); + else if ( yy_act == 68 ) + fprintf( stderr, "--accepting default rule (\"%s\")\n", + yytext ); + else if ( yy_act == 69 ) + fprintf( stderr, "--(end of buffer or a NUL)\n" ); + else + fprintf( stderr, "--EOF (start condition %d)\n", YY_START ); + } + + switch ( yy_act ) + { /* beginning of action switch */ +/* %% [13.0] actions go here */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 136 "d2_lexer.ll" +; + YY_BREAK +case 2: +YY_RULE_SETUP +#line 138 "d2_lexer.ll" +; + YY_BREAK +case 3: +YY_RULE_SETUP +#line 140 "d2_lexer.ll" +{ + BEGIN(COMMENT); + comment_start_line = driver.loc_.end.line;; +} + YY_BREAK +case 4: +YY_RULE_SETUP +#line 145 "d2_lexer.ll" +BEGIN(INITIAL); + YY_BREAK +case 5: +YY_RULE_SETUP +#line 146 "d2_lexer.ll" +; + YY_BREAK +case YY_STATE_EOF(COMMENT): +#line 147 "d2_lexer.ll" +{ + isc_throw(D2ParseError, "Comment not closed. (/* in line " << comment_start_line); +} + YY_BREAK +case 6: +YY_RULE_SETUP +#line 151 "d2_lexer.ll" +BEGIN(DIR_ENTER); + YY_BREAK +case 7: +YY_RULE_SETUP +#line 152 "d2_lexer.ll" +BEGIN(DIR_INCLUDE); + YY_BREAK +case 8: +YY_RULE_SETUP +#line 153 "d2_lexer.ll" +{ + /* Include directive. */ + + /* Extract the filename. */ + std::string tmp(yytext+1); + tmp.resize(tmp.size() - 1); + + driver.includeFile(tmp); +} + YY_BREAK +case YY_STATE_EOF(DIR_ENTER): +case YY_STATE_EOF(DIR_INCLUDE): +case YY_STATE_EOF(DIR_EXIT): +#line 162 "d2_lexer.ll" +{ + isc_throw(D2ParseError, "Directive not closed."); +} + YY_BREAK +case 9: +YY_RULE_SETUP +#line 165 "d2_lexer.ll" +BEGIN(INITIAL); + YY_BREAK +case 10: +YY_RULE_SETUP +#line 168 "d2_lexer.ll" +{ + /* Ok, we found a with space. Let's ignore it and update loc variable. */ + driver.loc_.step(); +} + YY_BREAK +case 11: +/* rule 11 can match eol */ +YY_RULE_SETUP +#line 173 "d2_lexer.ll" +{ + /* Newline found. Let's update the location and continue. */ + driver.loc_.lines(yyleng); + driver.loc_.step(); +} + YY_BREAK +case 12: +YY_RULE_SETUP +#line 179 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::CONFIG: + return isc::d2::D2Parser::make_DHCPDDNS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("DhcpDdns", driver.loc_); + } +} + YY_BREAK +case 13: +YY_RULE_SETUP +#line 188 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + return isc::d2::D2Parser::make_IP_ADDRESS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("ip-address", driver.loc_); + } +} + YY_BREAK +case 14: +YY_RULE_SETUP +#line 199 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + return isc::d2::D2Parser::make_PORT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("port", driver.loc_); + } +} + YY_BREAK +case 15: +YY_RULE_SETUP +#line 210 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_DNS_SERVER_TIMEOUT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("dns-server-timeout", driver.loc_); + } +} + YY_BREAK +case 16: +YY_RULE_SETUP +#line 219 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_NCR_PROTOCOL(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("ncr-protocol", driver.loc_); + } +} + YY_BREAK +case 17: +YY_RULE_SETUP +#line 228 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_NCR_FORMAT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("ncr-format", driver.loc_); + } +} + YY_BREAK +case 18: +YY_RULE_SETUP +#line 237 "d2_lexer.ll" +{ + /* dhcp-ddns value keywords are case insensitive */ + if (driver.ctx_ == isc::d2::D2ParserContext::NCR_PROTOCOL) { + return isc::d2::D2Parser::make_UDP(driver.loc_); + } + std::string tmp(yytext+1); + tmp.resize(tmp.size() - 1); + return isc::d2::D2Parser::make_STRING(tmp, driver.loc_); +} + YY_BREAK +case 19: +YY_RULE_SETUP +#line 247 "d2_lexer.ll" +{ + /* dhcp-ddns value keywords are case insensitive */ + if (driver.ctx_ == isc::d2::D2ParserContext::NCR_PROTOCOL) { + return isc::d2::D2Parser::make_TCP(driver.loc_); + } + std::string tmp(yytext+1); + tmp.resize(tmp.size() - 1); + return isc::d2::D2Parser::make_STRING(tmp, driver.loc_); +} + YY_BREAK +case 20: +YY_RULE_SETUP +#line 257 "d2_lexer.ll" +{ + /* dhcp-ddns value keywords are case insensitive */ + if (driver.ctx_ == isc::d2::D2ParserContext::NCR_FORMAT) { + return isc::d2::D2Parser::make_JSON(driver.loc_); + } + std::string tmp(yytext+1); + tmp.resize(tmp.size() - 1); + return isc::d2::D2Parser::make_STRING(tmp, driver.loc_); +} + YY_BREAK +case 21: +YY_RULE_SETUP +#line 267 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + case isc::d2::D2ParserContext::DDNS_DOMAIN: + case isc::d2::D2ParserContext::DDNS_DOMAINS: + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + case isc::d2::D2ParserContext::CONTROL_SOCKET: + case isc::d2::D2ParserContext::LOGGERS: + return isc::d2::D2Parser::make_USER_CONTEXT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("user-context", driver.loc_); + } +} + YY_BREAK +case 22: +YY_RULE_SETUP +#line 284 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + case isc::d2::D2ParserContext::DDNS_DOMAIN: + case isc::d2::D2ParserContext::DDNS_DOMAINS: + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + case isc::d2::D2ParserContext::CONTROL_SOCKET: + case isc::d2::D2ParserContext::LOGGERS: + return isc::d2::D2Parser::make_COMMENT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("comment", driver.loc_); + } +} + YY_BREAK +case 23: +YY_RULE_SETUP +#line 301 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_FORWARD_DDNS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("forward-ddns", driver.loc_); + } +} + YY_BREAK +case 24: +YY_RULE_SETUP +#line 310 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_REVERSE_DDNS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("reverse-ddns", driver.loc_); + } +} + YY_BREAK +case 25: +YY_RULE_SETUP +#line 319 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::FORWARD_DDNS: + case isc::d2::D2ParserContext::REVERSE_DDNS: + return isc::d2::D2Parser::make_DDNS_DOMAINS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("ddns-domains", driver.loc_); + } +} + YY_BREAK +case 26: +YY_RULE_SETUP +#line 329 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DDNS_DOMAIN: + case isc::d2::D2ParserContext::DDNS_DOMAINS: + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + return isc::d2::D2Parser::make_KEY_NAME(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("key-name", driver.loc_); + } +} + YY_BREAK +case 27: +YY_RULE_SETUP +#line 341 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DDNS_DOMAIN: + case isc::d2::D2ParserContext::DDNS_DOMAINS: + return isc::d2::D2Parser::make_DNS_SERVERS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("dns-servers", driver.loc_); + } +} + YY_BREAK +case 28: +YY_RULE_SETUP +#line 351 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + return isc::d2::D2Parser::make_HOSTNAME(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("hostname", driver.loc_); + } +} + YY_BREAK +case 29: +YY_RULE_SETUP +#line 362 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_TSIG_KEYS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("tsig-keys", driver.loc_); + } +} + YY_BREAK +case 30: +YY_RULE_SETUP +#line 371 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + return isc::d2::D2Parser::make_ALGORITHM(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("algorithm", driver.loc_); + } +} + YY_BREAK +case 31: +YY_RULE_SETUP +#line 381 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + return isc::d2::D2Parser::make_DIGEST_BITS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("digest-bits", driver.loc_); + } +} + YY_BREAK +case 32: +YY_RULE_SETUP +#line 391 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + return isc::d2::D2Parser::make_SECRET(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("secret", driver.loc_); + } +} + YY_BREAK +case 33: +YY_RULE_SETUP +#line 401 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_CONTROL_SOCKET(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("control-socket", driver.loc_); + } +} + YY_BREAK +case 34: +YY_RULE_SETUP +#line 410 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::CONTROL_SOCKET: + return isc::d2::D2Parser::make_SOCKET_TYPE(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("socket-type", driver.loc_); + } +} + YY_BREAK +case 35: +YY_RULE_SETUP +#line 419 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::CONTROL_SOCKET: + return isc::d2::D2Parser::make_SOCKET_NAME(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("socket-name", driver.loc_); + } +} + YY_BREAK +case 36: +YY_RULE_SETUP +#line 428 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_HOOKS_LIBRARIES(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("hooks-libraries", driver.loc_); + } +} + YY_BREAK +case 37: +YY_RULE_SETUP +#line 437 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::HOOKS_LIBRARIES: + return isc::d2::D2Parser::make_PARAMETERS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("parameters", driver.loc_); + } +} + YY_BREAK +case 38: +YY_RULE_SETUP +#line 446 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::HOOKS_LIBRARIES: + return isc::d2::D2Parser::make_LIBRARY(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("library", driver.loc_); + } +} + YY_BREAK +case 39: +YY_RULE_SETUP +#line 455 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_LOGGERS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("loggers", driver.loc_); + } +} + YY_BREAK +case 40: +YY_RULE_SETUP +#line 464 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::LOGGERS: + return isc::d2::D2Parser::make_OUTPUT_OPTIONS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("output_options", driver.loc_); + } +} + YY_BREAK +case 41: +YY_RULE_SETUP +#line 473 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::OUTPUT_OPTIONS: + return isc::d2::D2Parser::make_OUTPUT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("output", driver.loc_); + } +} + YY_BREAK +case 42: +YY_RULE_SETUP +#line 482 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::OUTPUT_OPTIONS: + return isc::d2::D2Parser::make_FLUSH(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("flush", driver.loc_); + } +} + YY_BREAK +case 43: +YY_RULE_SETUP +#line 491 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::OUTPUT_OPTIONS: + return isc::d2::D2Parser::make_MAXSIZE(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("maxsize", driver.loc_); + } +} + YY_BREAK +case 44: +YY_RULE_SETUP +#line 500 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::OUTPUT_OPTIONS: + return isc::d2::D2Parser::make_MAXVER(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("maxver", driver.loc_); + } +} + YY_BREAK +case 45: +YY_RULE_SETUP +#line 509 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::OUTPUT_OPTIONS: + return isc::d2::D2Parser::make_PATTERN(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("pattern", driver.loc_); + } +} + YY_BREAK +case 46: +YY_RULE_SETUP +#line 518 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::LOGGERS: + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + case isc::d2::D2ParserContext::DDNS_DOMAIN: + case isc::d2::D2ParserContext::DDNS_DOMAINS: + return isc::d2::D2Parser::make_NAME(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("name", driver.loc_); + } +} + YY_BREAK +case 47: +YY_RULE_SETUP +#line 531 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::LOGGERS: + return isc::d2::D2Parser::make_DEBUGLEVEL(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("debuglevel", driver.loc_); + } +} + YY_BREAK +case 48: +YY_RULE_SETUP +#line 540 "d2_lexer.ll" +{ + switch(driver.ctx_) { + case isc::d2::D2ParserContext::LOGGERS: + return isc::d2::D2Parser::make_SEVERITY(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("severity", driver.loc_); + } +} + YY_BREAK +case 49: +YY_RULE_SETUP +#line 549 "d2_lexer.ll" +{ + /* A string has been matched. It contains the actual string and single quotes. + We need to get those quotes out of the way and just use its content, e.g. + for 'foo' we should get foo */ + std::string raw(yytext+1); + size_t len = raw.size() - 1; + raw.resize(len); + std::string decoded; + decoded.reserve(len); + for (size_t pos = 0; pos < len; ++pos) { + int b = 0; + char c = raw[pos]; + switch (c) { + case '"': + /* impossible condition */ + driver.error(driver.loc_, "Bad quote in \"" + raw + "\""); + break; + case '\\': + ++pos; + if (pos >= len) { + /* impossible condition */ + driver.error(driver.loc_, "Overflow escape in \"" + raw + "\""); + } + c = raw[pos]; + switch (c) { + case '"': + case '\\': + case '/': + decoded.push_back(c); + break; + case 'b': + decoded.push_back('\b'); + break; + case 'f': + decoded.push_back('\f'); + break; + case 'n': + decoded.push_back('\n'); + break; + case 'r': + decoded.push_back('\r'); + break; + case 't': + decoded.push_back('\t'); + break; + case 'u': + /* support only \u0000 to \u00ff */ + ++pos; + if (pos + 4 > len) { + /* impossible condition */ + driver.error(driver.loc_, + "Overflow unicode escape in \"" + raw + "\""); + } + if ((raw[pos] != '0') || (raw[pos + 1] != '0')) { + driver.error(driver.loc_, + "Unsupported unicode escape in \"" + raw + "\"", + pos + 1); + } + pos += 2; + c = raw[pos]; + if ((c >= '0') && (c <= '9')) { + b = (c - '0') << 4; + } else if ((c >= 'A') && (c <= 'F')) { + b = (c - 'A' + 10) << 4; + } else if ((c >= 'a') && (c <= 'f')) { + b = (c - 'a' + 10) << 4; + } else { + /* impossible condition */ + driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\""); + } + pos++; + c = raw[pos]; + if ((c >= '0') && (c <= '9')) { + b |= c - '0'; + } else if ((c >= 'A') && (c <= 'F')) { + b |= c - 'A' + 10; + } else if ((c >= 'a') && (c <= 'f')) { + b |= c - 'a' + 10; + } else { + /* impossible condition */ + driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\""); + } + decoded.push_back(static_cast<char>(b & 0xff)); + break; + default: + /* impossible condition */ + driver.error(driver.loc_, "Bad escape in \"" + raw + "\""); + } + break; + default: + if ((c >= 0) && (c < 0x20)) { + /* impossible condition */ + driver.error(driver.loc_, "Invalid control in \"" + raw + "\""); + } + decoded.push_back(c); + } + } + + return isc::d2::D2Parser::make_STRING(decoded, driver.loc_); +} + YY_BREAK +case 50: +/* rule 50 can match eol */ +YY_RULE_SETUP +#line 650 "d2_lexer.ll" +{ + /* Bad string with a forbidden control character inside */ + std::string raw(yytext+1); + size_t len = raw.size() - 1; + size_t pos = 0; + for (; pos < len; ++pos) { + char c = raw[pos]; + if ((c >= 0) && (c < 0x20)) { + break; + } + } + driver.error(driver.loc_, + "Invalid control in " + std::string(yytext), + pos + 1); +} + YY_BREAK +case 51: +/* rule 51 can match eol */ +YY_RULE_SETUP +#line 666 "d2_lexer.ll" +{ + /* Bad string with a bad escape inside */ + std::string raw(yytext+1); + size_t len = raw.size() - 1; + size_t pos = 0; + bool found = false; + for (; pos < len; ++pos) { + if (found) { + break; + } + char c = raw[pos]; + if (c == '\\') { + ++pos; + c = raw[pos]; + switch (c) { + case '"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + break; + case 'u': + if ((pos + 4 > len) || + !std::isxdigit(raw[pos + 1]) || + !std::isxdigit(raw[pos + 2]) || + !std::isxdigit(raw[pos + 3]) || + !std::isxdigit(raw[pos + 4])) { + found = true; + } + break; + default: + found = true; + break; + } + } + } + /* The rule stops on the first " including on \" so add ... in this case */ + std::string trailer = ""; + if (raw[len - 1] == '\\') { + trailer = "..."; + } + driver.error(driver.loc_, + "Bad escape in " + std::string(yytext) + trailer, + pos); +} + YY_BREAK +case 52: +YY_RULE_SETUP +#line 715 "d2_lexer.ll" +{ + /* Bad string with an open escape at the end */ + std::string raw(yytext+1); + driver.error(driver.loc_, + "Overflow escape in " + std::string(yytext), + raw.size() + 1); +} + YY_BREAK +case 53: +YY_RULE_SETUP +#line 723 "d2_lexer.ll" +{ + /* Bad string with an open unicode escape at the end */ + std::string raw(yytext+1); + size_t pos = raw.size() - 1; + for (; pos > 0; --pos) { + char c = raw[pos]; + if (c == 'u') { + break; + } + } + driver.error(driver.loc_, + "Overflow unicode escape in " + std::string(yytext), + pos + 1); +} + YY_BREAK +case 54: +YY_RULE_SETUP +#line 738 "d2_lexer.ll" +{ return isc::d2::D2Parser::make_LSQUARE_BRACKET(driver.loc_); } + YY_BREAK +case 55: +YY_RULE_SETUP +#line 739 "d2_lexer.ll" +{ return isc::d2::D2Parser::make_RSQUARE_BRACKET(driver.loc_); } + YY_BREAK +case 56: +YY_RULE_SETUP +#line 740 "d2_lexer.ll" +{ return isc::d2::D2Parser::make_LCURLY_BRACKET(driver.loc_); } + YY_BREAK +case 57: +YY_RULE_SETUP +#line 741 "d2_lexer.ll" +{ return isc::d2::D2Parser::make_RCURLY_BRACKET(driver.loc_); } + YY_BREAK +case 58: +YY_RULE_SETUP +#line 742 "d2_lexer.ll" +{ return isc::d2::D2Parser::make_COMMA(driver.loc_); } + YY_BREAK +case 59: +YY_RULE_SETUP +#line 743 "d2_lexer.ll" +{ return isc::d2::D2Parser::make_COLON(driver.loc_); } + YY_BREAK +case 60: +YY_RULE_SETUP +#line 745 "d2_lexer.ll" +{ + /* An integer was found. */ + std::string tmp(yytext); + int64_t integer = 0; + try { + /* In substring we want to use negative values (e.g. -1). + In enterprise-id we need to use values up to 0xffffffff. + To cover both of those use cases, we need at least + int64_t. */ + integer = boost::lexical_cast<int64_t>(tmp); + } catch (const boost::bad_lexical_cast &) { + driver.error(driver.loc_, "Failed to convert " + tmp + " to an integer."); + } + + /* The parser needs the string form as double conversion is no lossless */ + return isc::d2::D2Parser::make_INTEGER(integer, driver.loc_); +} + YY_BREAK +case 61: +YY_RULE_SETUP +#line 763 "d2_lexer.ll" +{ + /* A floating point was found. */ + std::string tmp(yytext); + double fp = 0.0; + try { + fp = boost::lexical_cast<double>(tmp); + } catch (const boost::bad_lexical_cast &) { + driver.error(driver.loc_, "Failed to convert " + tmp + " to a floating point."); + } + + return isc::d2::D2Parser::make_FLOAT(fp, driver.loc_); +} + YY_BREAK +case 62: +YY_RULE_SETUP +#line 776 "d2_lexer.ll" +{ + string tmp(yytext); + return isc::d2::D2Parser::make_BOOLEAN(tmp == "true", driver.loc_); +} + YY_BREAK +case 63: +YY_RULE_SETUP +#line 781 "d2_lexer.ll" +{ + return isc::d2::D2Parser::make_NULL_TYPE(driver.loc_); +} + YY_BREAK +case 64: +YY_RULE_SETUP +#line 785 "d2_lexer.ll" +driver.error (driver.loc_, "JSON true reserved keyword is lower case only"); + YY_BREAK +case 65: +YY_RULE_SETUP +#line 787 "d2_lexer.ll" +driver.error (driver.loc_, "JSON false reserved keyword is lower case only"); + YY_BREAK +case 66: +YY_RULE_SETUP +#line 789 "d2_lexer.ll" +driver.error (driver.loc_, "JSON null reserved keyword is lower case only"); + YY_BREAK +case 67: +YY_RULE_SETUP +#line 791 "d2_lexer.ll" +driver.error (driver.loc_, "Invalid character: " + std::string(yytext)); + YY_BREAK +case YY_STATE_EOF(INITIAL): +#line 793 "d2_lexer.ll" +{ + if (driver.states_.empty()) { + return isc::d2::D2Parser::make_END(driver.loc_); + } + driver.loc_ = driver.locs_.back(); + driver.locs_.pop_back(); + driver.file_ = driver.files_.back(); + driver.files_.pop_back(); + if (driver.sfile_) { + fclose(driver.sfile_); + driver.sfile_ = 0; + } + if (!driver.sfiles_.empty()) { + driver.sfile_ = driver.sfiles_.back(); + driver.sfiles_.pop_back(); + } + d2_parser__delete_buffer(YY_CURRENT_BUFFER); + d2_parser__switch_to_buffer(driver.states_.back()); + driver.states_.pop_back(); + + BEGIN(DIR_EXIT); +} + YY_BREAK +case 68: +YY_RULE_SETUP +#line 816 "d2_lexer.ll" +ECHO; + YY_BREAK +#line 2468 "d2_lexer.cc" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; +/* %if-c-only */ + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; +/* %endif */ +/* %if-c++-only */ +/* %endif */ + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { +/* %% [14.0] code to do back-up for compressed tables and set up yy_cp goes here */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ +/* %ok-for-header */ + +/* %if-c++-only */ +/* %not-for-header */ +/* %ok-for-header */ + +/* %endif */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +/* %if-c-only */ +static int yy_get_next_buffer (void) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = (yytext_ptr); + int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +/* %if-c-only */ +/* %not-for-header */ + static yy_state_type yy_get_previous_state (void) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + yy_state_type yy_current_state; + char *yy_cp; + +/* %% [15.0] code to get the start state into yy_current_state goes here */ + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { +/* %% [16.0] code to find the next state goes here */ + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 425 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ +/* %if-c-only */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + int yy_is_jam; + /* %% [17.0] code to find the next state, and perhaps do backing up, goes here */ + char *yy_cp = (yy_c_buf_p); + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 425 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 424); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT +/* %if-c-only */ + +/* %endif */ +#endif + +/* %if-c-only */ +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return 0; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + +/* %% [19.0] update BOL and yylineno */ + + return c; +} +/* %if-c-only */ +#endif /* ifndef YY_NO_INPUT */ +/* %endif */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ +/* %if-c-only */ + void yyrestart (FILE * input_file ) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file ); + yy_load_buffer_state( ); +} + +/* %if-c++-only */ +/* %endif */ + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ +/* %if-c-only */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +/* %if-c-only */ +static void yy_load_buffer_state (void) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; +/* %if-c-only */ + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; +/* %endif */ +/* %if-c++-only */ +/* %endif */ + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ +/* %if-c-only */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; +} + +/* %if-c++-only */ +/* %endif */ + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ +/* %if-c-only */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf ); + + yyfree( (void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ +/* %if-c-only */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) +/* %endif */ +/* %if-c++-only */ +/* %endif */ + +{ + int oerrno = errno; + + yy_flush_buffer( b ); + +/* %if-c-only */ + b->yy_input_file = file; +/* %endif */ +/* %if-c++-only */ +/* %endif */ + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + +/* %if-c-only */ + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + +/* %endif */ +/* %if-c++-only */ +/* %endif */ + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ +/* %if-c-only */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/* %if-c-or-c++ */ +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +/* %if-c-only */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} +/* %endif */ + +/* %if-c-or-c++ */ +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +/* %if-c-only */ +void yypop_buffer_state (void) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} +/* %endif */ + +/* %if-c-or-c++ */ +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +/* %if-c-only */ +static void yyensure_buffer_stack (void) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + yy_size_t num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} +/* %endif */ + +/* %if-c-only */ +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; +} +/* %endif */ + +/* %if-c-only */ +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr ) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) ); +} +/* %endif */ + +/* %if-c-only */ +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} +/* %endif */ + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +/* %if-c-only */ +static void yynoreturn yy_fatal_error (const char* msg ) +{ + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} +/* %endif */ +/* %if-c++-only */ +/* %endif */ + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/* %if-c-only */ +/* %if-reentrant */ +/* %endif */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/* %if-reentrant */ +/* %endif */ + +/** Set the current line number. + * @param _line_number line number + * + */ +void yyset_lineno (int _line_number ) +{ + + yylineno = _line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str ) +{ + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str ) +{ + yyout = _out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int _bdebug ) +{ + yy_flex_debug = _bdebug ; +} + +/* %endif */ + +/* %if-reentrant */ +/* %if-bison-bridge */ +/* %endif */ +/* %endif if-c-only */ + +/* %if-c-only */ +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = NULL; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = NULL; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} +/* %endif */ + +/* %if-c-only SNIP! this currently causes conflicts with the c++ scanner */ +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + +/* %if-reentrant */ +/* %endif */ + return 0; +} +/* %endif */ + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n ) +{ + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s ) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +/* %if-tables-serialization definitions */ +/* %define-yytables The name for this specific scanner's tables. */ +#define YYTABLES_NAME "yytables" +/* %endif */ + +/* %ok-for-header */ + +#line 816 "d2_lexer.ll" + + +using namespace isc::dhcp; + +void +D2ParserContext::scanStringBegin(const std::string& str, ParserType parser_type) +{ + start_token_flag = true; + start_token_value = parser_type; + + file_ = "<string>"; + sfile_ = 0; + loc_.initialize(&file_); + yy_flex_debug = trace_scanning_; + YY_BUFFER_STATE buffer; + buffer = d2_parser__scan_bytes(str.c_str(), str.size()); + if (!buffer) { + fatal("cannot scan string"); + /* fatal() throws an exception so this can't be reached */ + } +} + +void +D2ParserContext::scanFileBegin(FILE * f, + const std::string& filename, + ParserType parser_type) +{ + start_token_flag = true; + start_token_value = parser_type; + + file_ = filename; + sfile_ = f; + loc_.initialize(&file_); + yy_flex_debug = trace_scanning_; + YY_BUFFER_STATE buffer; + + /* See d2_lexer.cc header for available definitions */ + buffer = d2_parser__create_buffer(f, 65536 /*buffer size*/); + if (!buffer) { + fatal("cannot scan file " + filename); + } + d2_parser__switch_to_buffer(buffer); +} + +void +D2ParserContext::scanEnd() { + if (sfile_) + fclose(sfile_); + sfile_ = 0; + static_cast<void>(d2_parser_lex_destroy()); + /* Close files */ + while (!sfiles_.empty()) { + FILE* f = sfiles_.back(); + if (f) { + fclose(f); + } + sfiles_.pop_back(); + } + /* Delete states */ + while (!states_.empty()) { + d2_parser__delete_buffer(states_.back()); + states_.pop_back(); + } +} + +void +D2ParserContext::includeFile(const std::string& filename) { + if (states_.size() > 10) { + fatal("Too many nested include."); + } + + FILE* f = fopen(filename.c_str(), "r"); + if (!f) { + fatal("Can't open include file " + filename); + } + if (sfile_) { + sfiles_.push_back(sfile_); + } + sfile_ = f; + states_.push_back(YY_CURRENT_BUFFER); + YY_BUFFER_STATE buffer; + buffer = d2_parser__create_buffer(f, 65536 /*buffer size*/); + if (!buffer) { + fatal( "Can't scan include file " + filename); + } + d2_parser__switch_to_buffer(buffer); + files_.push_back(file_); + file_ = filename; + locs_.push_back(loc_); + loc_.initialize(&file_); + + BEGIN(INITIAL); +} + +namespace { +/** To avoid unused function error */ +class Dummy { + /* cppcheck-suppress unusedPrivateFunction */ + void dummy() { yy_fatal_error("Fix me: how to disable its definition?"); } +}; +} +#endif /* !__clang_analyzer__ */ + diff --git a/src/bin/d2/d2_lexer.ll b/src/bin/d2/d2_lexer.ll new file mode 100644 index 0000000..14c3411 --- /dev/null +++ b/src/bin/d2/d2_lexer.ll @@ -0,0 +1,916 @@ +/* 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/. */ + +%{ /* -*- C++ -*- */ + +/* Generated files do not make clang static analyser so happy */ +#ifndef __clang_analyzer__ + +#include <cctype> +#include <cerrno> +#include <climits> +#include <cstdlib> +#include <string> +#include <d2/parser_context.h> +#include <asiolink/io_address.h> +#include <boost/lexical_cast.hpp> +#include <exceptions/exceptions.h> + +/* Please avoid C++ style comments (// ... eol) as they break flex 2.6.2 */ + +/* Work around an incompatibility in flex (at least versions + 2.5.31 through 2.5.33): it generates code that does + not conform to C89. See Debian bug 333231 + <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>. */ +# undef yywrap +# define yywrap() 1 + +namespace { + +bool start_token_flag = false; + +isc::d2::D2ParserContext::ParserType start_token_value; +unsigned int comment_start_line = 0; + +}; + +/* To avoid the call to exit... oops! */ +#define YY_FATAL_ERROR(msg) isc::d2::D2ParserContext::fatal(msg) +%} + +/* noyywrap disables automatic rewinding for the next file to parse. Since we + always parse only a single string, there's no need to do any wraps. And + using yywrap requires linking with -lfl, which provides the default yywrap + implementation that always returns 1 anyway. */ +%option noyywrap + +/* nounput simplifies the lexer, by removing support for putting a character + back into the input stream. We never use such capability anyway. */ +%option nounput + +/* batch means that we'll never use the generated lexer interactively. */ +%option batch + +/* avoid to get static global variables to remain with C++. */ +/* in last resort %option reentrant */ + +/* Enables debug mode. To see the debug messages, one needs to also set + yy_flex_debug to 1, then the debug messages will be printed on stderr. */ +%option debug + +/* I have no idea what this option does, except it was specified in the bison + examples and Postgres folks added it to remove gcc 4.3 warnings. Let's + be on the safe side and keep it. */ +%option noinput + +%x COMMENT +%x DIR_ENTER DIR_INCLUDE DIR_EXIT + +/* These are not token expressions yet, just convenience expressions that + can be used during actual token definitions. Note some can match + incorrect inputs (e.g., IP addresses) which must be checked. */ +int \-?[0-9]+ +blank [ \t\r] + +UnicodeEscapeSequence u[0-9A-Fa-f]{4} +JSONEscapeCharacter ["\\/bfnrt] +JSONEscapeSequence {JSONEscapeCharacter}|{UnicodeEscapeSequence} +JSONStandardCharacter [^\x00-\x1f"\\] +JSONStringCharacter {JSONStandardCharacter}|\\{JSONEscapeSequence} +JSONString \"{JSONStringCharacter}*\" + +/* for errors */ + +BadUnicodeEscapeSequence u[0-9A-Fa-f]{0,3}[^0-9A-Fa-f"] +BadJSONEscapeSequence [^"\\/bfnrtu]|{BadUnicodeEscapeSequence} +ControlCharacter [\x00-\x1f] +ControlCharacterFill [^"\\]|\\["\\/bfnrtu] + +%{ +/* This code run each time a pattern is matched. It updates the location + by moving it ahead by yyleng bytes. yyleng specifies the length of the + currently matched token. */ +#define YY_USER_ACTION driver.loc_.columns(yyleng); +%} + +%% + +%{ + /* This part of the code is copied over to the verbatim to the top + of the generated yylex function. Explanation: + http://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html */ + + /* Code run each time yylex is called. */ + driver.loc_.step(); + + if (start_token_flag) { + start_token_flag = false; + switch (start_token_value) { + case D2ParserContext::PARSER_JSON: + default: + return isc::d2::D2Parser::make_TOPLEVEL_JSON(driver.loc_); + case D2ParserContext::PARSER_DHCPDDNS: + return isc::d2::D2Parser::make_TOPLEVEL_DHCPDDNS(driver.loc_); + case D2ParserContext::PARSER_SUB_DHCPDDNS: + return isc::d2::D2Parser::make_SUB_DHCPDDNS(driver.loc_); + case D2ParserContext::PARSER_TSIG_KEY: + return isc::d2::D2Parser::make_SUB_TSIG_KEY(driver.loc_); + case D2ParserContext::PARSER_TSIG_KEYS: + return isc::d2::D2Parser::make_SUB_TSIG_KEYS(driver.loc_); + case D2ParserContext::PARSER_DDNS_DOMAIN: + return isc::d2::D2Parser::make_SUB_DDNS_DOMAIN(driver.loc_); + case D2ParserContext::PARSER_DDNS_DOMAINS: + return isc::d2::D2Parser::make_SUB_DDNS_DOMAINS(driver.loc_); + case D2ParserContext::PARSER_DNS_SERVER: + return isc::d2::D2Parser::make_SUB_DNS_SERVER(driver.loc_); + case D2ParserContext::PARSER_HOOKS_LIBRARY: + return isc::d2::D2Parser::make_SUB_HOOKS_LIBRARY(driver.loc_); + } + } +%} + +#.* ; + +"//"(.*) ; + +"/*" { + BEGIN(COMMENT); + comment_start_line = driver.loc_.end.line;; +} + +<COMMENT>"*/" BEGIN(INITIAL); +<COMMENT>. ; +<COMMENT><<EOF>> { + isc_throw(D2ParseError, "Comment not closed. (/* in line " << comment_start_line); +} + +"<?" BEGIN(DIR_ENTER); +<DIR_ENTER>"include" BEGIN(DIR_INCLUDE); +<DIR_INCLUDE>\"([^\"\n])+\" { + /* Include directive. */ + + /* Extract the filename. */ + std::string tmp(yytext+1); + tmp.resize(tmp.size() - 1); + + driver.includeFile(tmp); +} +<DIR_ENTER,DIR_INCLUDE,DIR_EXIT><<EOF>> { + isc_throw(D2ParseError, "Directive not closed."); +} +<DIR_EXIT>"?>" BEGIN(INITIAL); + + +<*>{blank}+ { + /* Ok, we found a with space. Let's ignore it and update loc variable. */ + driver.loc_.step(); +} + +<*>[\n]+ { + /* Newline found. Let's update the location and continue. */ + driver.loc_.lines(yyleng); + driver.loc_.step(); +} + +\"DhcpDdns\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::CONFIG: + return isc::d2::D2Parser::make_DHCPDDNS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("DhcpDdns", driver.loc_); + } +} + +\"ip-address\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + return isc::d2::D2Parser::make_IP_ADDRESS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("ip-address", driver.loc_); + } +} + +\"port\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + return isc::d2::D2Parser::make_PORT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("port", driver.loc_); + } +} + +\"dns-server-timeout\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_DNS_SERVER_TIMEOUT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("dns-server-timeout", driver.loc_); + } +} + +\"ncr-protocol\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_NCR_PROTOCOL(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("ncr-protocol", driver.loc_); + } +} + +\"ncr-format\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_NCR_FORMAT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("ncr-format", driver.loc_); + } +} + +(?i:\"UDP\") { + /* dhcp-ddns value keywords are case insensitive */ + if (driver.ctx_ == isc::d2::D2ParserContext::NCR_PROTOCOL) { + return isc::d2::D2Parser::make_UDP(driver.loc_); + } + std::string tmp(yytext+1); + tmp.resize(tmp.size() - 1); + return isc::d2::D2Parser::make_STRING(tmp, driver.loc_); +} + +(?i:\"TCP\") { + /* dhcp-ddns value keywords are case insensitive */ + if (driver.ctx_ == isc::d2::D2ParserContext::NCR_PROTOCOL) { + return isc::d2::D2Parser::make_TCP(driver.loc_); + } + std::string tmp(yytext+1); + tmp.resize(tmp.size() - 1); + return isc::d2::D2Parser::make_STRING(tmp, driver.loc_); +} + +(?i:\"JSON\") { + /* dhcp-ddns value keywords are case insensitive */ + if (driver.ctx_ == isc::d2::D2ParserContext::NCR_FORMAT) { + return isc::d2::D2Parser::make_JSON(driver.loc_); + } + std::string tmp(yytext+1); + tmp.resize(tmp.size() - 1); + return isc::d2::D2Parser::make_STRING(tmp, driver.loc_); +} + +\"user-context\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + case isc::d2::D2ParserContext::DDNS_DOMAIN: + case isc::d2::D2ParserContext::DDNS_DOMAINS: + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + case isc::d2::D2ParserContext::CONTROL_SOCKET: + case isc::d2::D2ParserContext::LOGGERS: + return isc::d2::D2Parser::make_USER_CONTEXT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("user-context", driver.loc_); + } +} + +\"comment\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + case isc::d2::D2ParserContext::DDNS_DOMAIN: + case isc::d2::D2ParserContext::DDNS_DOMAINS: + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + case isc::d2::D2ParserContext::CONTROL_SOCKET: + case isc::d2::D2ParserContext::LOGGERS: + return isc::d2::D2Parser::make_COMMENT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("comment", driver.loc_); + } +} + +\"forward-ddns\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_FORWARD_DDNS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("forward-ddns", driver.loc_); + } +} + +\"reverse-ddns\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_REVERSE_DDNS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("reverse-ddns", driver.loc_); + } +} + +\"ddns-domains\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::FORWARD_DDNS: + case isc::d2::D2ParserContext::REVERSE_DDNS: + return isc::d2::D2Parser::make_DDNS_DOMAINS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("ddns-domains", driver.loc_); + } +} + +\"key-name\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DDNS_DOMAIN: + case isc::d2::D2ParserContext::DDNS_DOMAINS: + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + return isc::d2::D2Parser::make_KEY_NAME(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("key-name", driver.loc_); + } +} + +\"dns-servers\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DDNS_DOMAIN: + case isc::d2::D2ParserContext::DDNS_DOMAINS: + return isc::d2::D2Parser::make_DNS_SERVERS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("dns-servers", driver.loc_); + } +} + +\"hostname\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DNS_SERVER: + case isc::d2::D2ParserContext::DNS_SERVERS: + return isc::d2::D2Parser::make_HOSTNAME(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("hostname", driver.loc_); + } +} + + +\"tsig-keys\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_TSIG_KEYS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("tsig-keys", driver.loc_); + } +} + +\"algorithm\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + return isc::d2::D2Parser::make_ALGORITHM(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("algorithm", driver.loc_); + } +} + +\"digest-bits\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + return isc::d2::D2Parser::make_DIGEST_BITS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("digest-bits", driver.loc_); + } +} + +\"secret\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + return isc::d2::D2Parser::make_SECRET(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("secret", driver.loc_); + } +} + +\"control-socket\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_CONTROL_SOCKET(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("control-socket", driver.loc_); + } +} + +\"socket-type\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::CONTROL_SOCKET: + return isc::d2::D2Parser::make_SOCKET_TYPE(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("socket-type", driver.loc_); + } +} + +\"socket-name\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::CONTROL_SOCKET: + return isc::d2::D2Parser::make_SOCKET_NAME(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("socket-name", driver.loc_); + } +} + +\"hooks-libraries\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_HOOKS_LIBRARIES(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("hooks-libraries", driver.loc_); + } +} + +\"parameters\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::HOOKS_LIBRARIES: + return isc::d2::D2Parser::make_PARAMETERS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("parameters", driver.loc_); + } +} + +\"library\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::HOOKS_LIBRARIES: + return isc::d2::D2Parser::make_LIBRARY(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("library", driver.loc_); + } +} + +\"loggers\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::DHCPDDNS: + return isc::d2::D2Parser::make_LOGGERS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("loggers", driver.loc_); + } +} + +\"output_options\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::LOGGERS: + return isc::d2::D2Parser::make_OUTPUT_OPTIONS(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("output_options", driver.loc_); + } +} + +\"output\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::OUTPUT_OPTIONS: + return isc::d2::D2Parser::make_OUTPUT(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("output", driver.loc_); + } +} + +\"flush\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::OUTPUT_OPTIONS: + return isc::d2::D2Parser::make_FLUSH(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("flush", driver.loc_); + } +} + +\"maxsize\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::OUTPUT_OPTIONS: + return isc::d2::D2Parser::make_MAXSIZE(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("maxsize", driver.loc_); + } +} + +\"maxver\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::OUTPUT_OPTIONS: + return isc::d2::D2Parser::make_MAXVER(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("maxver", driver.loc_); + } +} + +\"pattern\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::OUTPUT_OPTIONS: + return isc::d2::D2Parser::make_PATTERN(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("pattern", driver.loc_); + } +} + +\"name\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::LOGGERS: + case isc::d2::D2ParserContext::TSIG_KEY: + case isc::d2::D2ParserContext::TSIG_KEYS: + case isc::d2::D2ParserContext::DDNS_DOMAIN: + case isc::d2::D2ParserContext::DDNS_DOMAINS: + return isc::d2::D2Parser::make_NAME(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("name", driver.loc_); + } +} + +\"debuglevel\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::LOGGERS: + return isc::d2::D2Parser::make_DEBUGLEVEL(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("debuglevel", driver.loc_); + } +} + +\"severity\" { + switch(driver.ctx_) { + case isc::d2::D2ParserContext::LOGGERS: + return isc::d2::D2Parser::make_SEVERITY(driver.loc_); + default: + return isc::d2::D2Parser::make_STRING("severity", driver.loc_); + } +} + +{JSONString} { + /* A string has been matched. It contains the actual string and single quotes. + We need to get those quotes out of the way and just use its content, e.g. + for 'foo' we should get foo */ + std::string raw(yytext+1); + size_t len = raw.size() - 1; + raw.resize(len); + std::string decoded; + decoded.reserve(len); + for (size_t pos = 0; pos < len; ++pos) { + int b = 0; + char c = raw[pos]; + switch (c) { + case '"': + /* impossible condition */ + driver.error(driver.loc_, "Bad quote in \"" + raw + "\""); + break; + case '\\': + ++pos; + if (pos >= len) { + /* impossible condition */ + driver.error(driver.loc_, "Overflow escape in \"" + raw + "\""); + } + c = raw[pos]; + switch (c) { + case '"': + case '\\': + case '/': + decoded.push_back(c); + break; + case 'b': + decoded.push_back('\b'); + break; + case 'f': + decoded.push_back('\f'); + break; + case 'n': + decoded.push_back('\n'); + break; + case 'r': + decoded.push_back('\r'); + break; + case 't': + decoded.push_back('\t'); + break; + case 'u': + /* support only \u0000 to \u00ff */ + ++pos; + if (pos + 4 > len) { + /* impossible condition */ + driver.error(driver.loc_, + "Overflow unicode escape in \"" + raw + "\""); + } + if ((raw[pos] != '0') || (raw[pos + 1] != '0')) { + driver.error(driver.loc_, + "Unsupported unicode escape in \"" + raw + "\"", + pos + 1); + } + pos += 2; + c = raw[pos]; + if ((c >= '0') && (c <= '9')) { + b = (c - '0') << 4; + } else if ((c >= 'A') && (c <= 'F')) { + b = (c - 'A' + 10) << 4; + } else if ((c >= 'a') && (c <= 'f')) { + b = (c - 'a' + 10) << 4; + } else { + /* impossible condition */ + driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\""); + } + pos++; + c = raw[pos]; + if ((c >= '0') && (c <= '9')) { + b |= c - '0'; + } else if ((c >= 'A') && (c <= 'F')) { + b |= c - 'A' + 10; + } else if ((c >= 'a') && (c <= 'f')) { + b |= c - 'a' + 10; + } else { + /* impossible condition */ + driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\""); + } + decoded.push_back(static_cast<char>(b & 0xff)); + break; + default: + /* impossible condition */ + driver.error(driver.loc_, "Bad escape in \"" + raw + "\""); + } + break; + default: + if ((c >= 0) && (c < 0x20)) { + /* impossible condition */ + driver.error(driver.loc_, "Invalid control in \"" + raw + "\""); + } + decoded.push_back(c); + } + } + + return isc::d2::D2Parser::make_STRING(decoded, driver.loc_); +} + +\"{JSONStringCharacter}*{ControlCharacter}{ControlCharacterFill}*\" { + /* Bad string with a forbidden control character inside */ + std::string raw(yytext+1); + size_t len = raw.size() - 1; + size_t pos = 0; + for (; pos < len; ++pos) { + char c = raw[pos]; + if ((c >= 0) && (c < 0x20)) { + break; + } + } + driver.error(driver.loc_, + "Invalid control in " + std::string(yytext), + pos + 1); +} + +\"{JSONStringCharacter}*\\{BadJSONEscapeSequence}[^"]*\" { + /* Bad string with a bad escape inside */ + std::string raw(yytext+1); + size_t len = raw.size() - 1; + size_t pos = 0; + bool found = false; + for (; pos < len; ++pos) { + if (found) { + break; + } + char c = raw[pos]; + if (c == '\\') { + ++pos; + c = raw[pos]; + switch (c) { + case '"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + break; + case 'u': + if ((pos + 4 > len) || + !std::isxdigit(raw[pos + 1]) || + !std::isxdigit(raw[pos + 2]) || + !std::isxdigit(raw[pos + 3]) || + !std::isxdigit(raw[pos + 4])) { + found = true; + } + break; + default: + found = true; + break; + } + } + } + /* The rule stops on the first " including on \" so add ... in this case */ + std::string trailer = ""; + if (raw[len - 1] == '\\') { + trailer = "..."; + } + driver.error(driver.loc_, + "Bad escape in " + std::string(yytext) + trailer, + pos); +} + +\"{JSONStringCharacter}*\\\" { + /* Bad string with an open escape at the end */ + std::string raw(yytext+1); + driver.error(driver.loc_, + "Overflow escape in " + std::string(yytext), + raw.size() + 1); +} + +\"{JSONStringCharacter}*\\u[0-9A-Fa-f]{0,3}\" { + /* Bad string with an open unicode escape at the end */ + std::string raw(yytext+1); + size_t pos = raw.size() - 1; + for (; pos > 0; --pos) { + char c = raw[pos]; + if (c == 'u') { + break; + } + } + driver.error(driver.loc_, + "Overflow unicode escape in " + std::string(yytext), + pos + 1); +} + +"[" { return isc::d2::D2Parser::make_LSQUARE_BRACKET(driver.loc_); } +"]" { return isc::d2::D2Parser::make_RSQUARE_BRACKET(driver.loc_); } +"{" { return isc::d2::D2Parser::make_LCURLY_BRACKET(driver.loc_); } +"}" { return isc::d2::D2Parser::make_RCURLY_BRACKET(driver.loc_); } +"," { return isc::d2::D2Parser::make_COMMA(driver.loc_); } +":" { return isc::d2::D2Parser::make_COLON(driver.loc_); } + +{int} { + /* An integer was found. */ + std::string tmp(yytext); + int64_t integer = 0; + try { + /* In substring we want to use negative values (e.g. -1). + In enterprise-id we need to use values up to 0xffffffff. + To cover both of those use cases, we need at least + int64_t. */ + integer = boost::lexical_cast<int64_t>(tmp); + } catch (const boost::bad_lexical_cast &) { + driver.error(driver.loc_, "Failed to convert " + tmp + " to an integer."); + } + + /* The parser needs the string form as double conversion is no lossless */ + return isc::d2::D2Parser::make_INTEGER(integer, driver.loc_); +} + +[-+]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)? { + /* A floating point was found. */ + std::string tmp(yytext); + double fp = 0.0; + try { + fp = boost::lexical_cast<double>(tmp); + } catch (const boost::bad_lexical_cast &) { + driver.error(driver.loc_, "Failed to convert " + tmp + " to a floating point."); + } + + return isc::d2::D2Parser::make_FLOAT(fp, driver.loc_); +} + +true|false { + string tmp(yytext); + return isc::d2::D2Parser::make_BOOLEAN(tmp == "true", driver.loc_); +} + +null { + return isc::d2::D2Parser::make_NULL_TYPE(driver.loc_); +} + +(?i:true) driver.error (driver.loc_, "JSON true reserved keyword is lower case only"); + +(?i:false) driver.error (driver.loc_, "JSON false reserved keyword is lower case only"); + +(?i:null) driver.error (driver.loc_, "JSON null reserved keyword is lower case only"); + +<*>. driver.error (driver.loc_, "Invalid character: " + std::string(yytext)); + +<<EOF>> { + if (driver.states_.empty()) { + return isc::d2::D2Parser::make_END(driver.loc_); + } + driver.loc_ = driver.locs_.back(); + driver.locs_.pop_back(); + driver.file_ = driver.files_.back(); + driver.files_.pop_back(); + if (driver.sfile_) { + fclose(driver.sfile_); + driver.sfile_ = 0; + } + if (!driver.sfiles_.empty()) { + driver.sfile_ = driver.sfiles_.back(); + driver.sfiles_.pop_back(); + } + d2_parser__delete_buffer(YY_CURRENT_BUFFER); + d2_parser__switch_to_buffer(driver.states_.back()); + driver.states_.pop_back(); + + BEGIN(DIR_EXIT); +} + +%% + +using namespace isc::dhcp; + +void +D2ParserContext::scanStringBegin(const std::string& str, ParserType parser_type) +{ + start_token_flag = true; + start_token_value = parser_type; + + file_ = "<string>"; + sfile_ = 0; + loc_.initialize(&file_); + yy_flex_debug = trace_scanning_; + YY_BUFFER_STATE buffer; + buffer = d2_parser__scan_bytes(str.c_str(), str.size()); + if (!buffer) { + fatal("cannot scan string"); + /* fatal() throws an exception so this can't be reached */ + } +} + +void +D2ParserContext::scanFileBegin(FILE * f, + const std::string& filename, + ParserType parser_type) +{ + start_token_flag = true; + start_token_value = parser_type; + + file_ = filename; + sfile_ = f; + loc_.initialize(&file_); + yy_flex_debug = trace_scanning_; + YY_BUFFER_STATE buffer; + + /* See d2_lexer.cc header for available definitions */ + buffer = d2_parser__create_buffer(f, 65536 /*buffer size*/); + if (!buffer) { + fatal("cannot scan file " + filename); + } + d2_parser__switch_to_buffer(buffer); +} + +void +D2ParserContext::scanEnd() { + if (sfile_) + fclose(sfile_); + sfile_ = 0; + static_cast<void>(d2_parser_lex_destroy()); + /* Close files */ + while (!sfiles_.empty()) { + FILE* f = sfiles_.back(); + if (f) { + fclose(f); + } + sfiles_.pop_back(); + } + /* Delete states */ + while (!states_.empty()) { + d2_parser__delete_buffer(states_.back()); + states_.pop_back(); + } +} + +void +D2ParserContext::includeFile(const std::string& filename) { + if (states_.size() > 10) { + fatal("Too many nested include."); + } + + FILE* f = fopen(filename.c_str(), "r"); + if (!f) { + fatal("Can't open include file " + filename); + } + if (sfile_) { + sfiles_.push_back(sfile_); + } + sfile_ = f; + states_.push_back(YY_CURRENT_BUFFER); + YY_BUFFER_STATE buffer; + buffer = d2_parser__create_buffer(f, 65536 /*buffer size*/); + if (!buffer) { + fatal( "Can't scan include file " + filename); + } + d2_parser__switch_to_buffer(buffer); + files_.push_back(file_); + file_ = filename; + locs_.push_back(loc_); + loc_.initialize(&file_); + + BEGIN(INITIAL); +} + +namespace { +/** To avoid unused function error */ +class Dummy { + /* cppcheck-suppress unusedPrivateFunction */ + void dummy() { yy_fatal_error("Fix me: how to disable its definition?"); } +}; +} +#endif /* !__clang_analyzer__ */ diff --git a/src/bin/d2/d2_parser.cc b/src/bin/d2/d2_parser.cc new file mode 100644 index 0000000..ba6a862 --- /dev/null +++ b/src/bin/d2/d2_parser.cc @@ -0,0 +1,2858 @@ +// A Bison parser, made by GNU Bison 3.8.2. + +// Skeleton implementation for Bison LALR(1) parsers in C++ + +// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +// As a special exception, you may create a larger work that contains +// part or all of the Bison parser skeleton and distribute that work +// under terms of your choice, so long as that work isn't itself a +// parser generator using the skeleton or a modified version thereof +// as a parser skeleton. Alternatively, if you modify or redistribute +// the parser skeleton itself, you may (at your option) remove this +// special exception, which will cause the skeleton and the resulting +// Bison output files to be licensed under the GNU General Public +// License without this special exception. + +// This special exception was added by the Free Software Foundation in +// version 2.2 of Bison. + +// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, +// especially those whose name start with YY_ or yy_. They are +// private implementation details that can be changed or removed. + + +// Take the name prefix into account. +#define yylex d2_parser_lex + + + +#include "d2_parser.h" + + +// Unqualified %code blocks. +#line 34 "d2_parser.yy" + +#include <d2/parser_context.h> + +#line 52 "d2_parser.cc" + + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> // FIXME: INFRINGES ON USER NAME SPACE. +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + + +// Whether we are compiled with exception support. +#ifndef YY_EXCEPTIONS +# if defined __GNUC__ && !defined __EXCEPTIONS +# define YY_EXCEPTIONS 0 +# else +# define YY_EXCEPTIONS 1 +# endif +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K].location) +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +# ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).begin = YYRHSLOC (Rhs, 1).begin; \ + (Current).end = YYRHSLOC (Rhs, N).end; \ + } \ + else \ + { \ + (Current).begin = (Current).end = YYRHSLOC (Rhs, 0).end; \ + } \ + while (false) +# endif + + +// Enable debugging if requested. +#if D2_PARSER_DEBUG + +// A pseudo ostream that takes yydebug_ into account. +# define YYCDEBUG if (yydebug_) (*yycdebug_) + +# define YY_SYMBOL_PRINT(Title, Symbol) \ + do { \ + if (yydebug_) \ + { \ + *yycdebug_ << Title << ' '; \ + yy_print_ (*yycdebug_, Symbol); \ + *yycdebug_ << '\n'; \ + } \ + } while (false) + +# define YY_REDUCE_PRINT(Rule) \ + do { \ + if (yydebug_) \ + yy_reduce_print_ (Rule); \ + } while (false) + +# define YY_STACK_PRINT() \ + do { \ + if (yydebug_) \ + yy_stack_print_ (); \ + } while (false) + +#else // !D2_PARSER_DEBUG + +# define YYCDEBUG if (false) std::cerr +# define YY_SYMBOL_PRINT(Title, Symbol) YY_USE (Symbol) +# define YY_REDUCE_PRINT(Rule) static_cast<void> (0) +# define YY_STACK_PRINT() static_cast<void> (0) + +#endif // !D2_PARSER_DEBUG + +#define yyerrok (yyerrstatus_ = 0) +#define yyclearin (yyla.clear ()) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab +#define YYRECOVERING() (!!yyerrstatus_) + +#line 14 "d2_parser.yy" +namespace isc { namespace d2 { +#line 145 "d2_parser.cc" + + /// Build a parser object. + D2Parser::D2Parser (isc::d2::D2ParserContext& ctx_yyarg) +#if D2_PARSER_DEBUG + : yydebug_ (false), + yycdebug_ (&std::cerr), +#else + : +#endif + ctx (ctx_yyarg) + {} + + D2Parser::~D2Parser () + {} + + D2Parser::syntax_error::~syntax_error () YY_NOEXCEPT YY_NOTHROW + {} + + /*---------. + | symbol. | + `---------*/ + + + + // by_state. + D2Parser::by_state::by_state () YY_NOEXCEPT + : state (empty_state) + {} + + D2Parser::by_state::by_state (const by_state& that) YY_NOEXCEPT + : state (that.state) + {} + + void + D2Parser::by_state::clear () YY_NOEXCEPT + { + state = empty_state; + } + + void + D2Parser::by_state::move (by_state& that) + { + state = that.state; + that.clear (); + } + + D2Parser::by_state::by_state (state_type s) YY_NOEXCEPT + : state (s) + {} + + D2Parser::symbol_kind_type + D2Parser::by_state::kind () const YY_NOEXCEPT + { + if (state == empty_state) + return symbol_kind::S_YYEMPTY; + else + return YY_CAST (symbol_kind_type, yystos_[+state]); + } + + D2Parser::stack_symbol_type::stack_symbol_type () + {} + + D2Parser::stack_symbol_type::stack_symbol_type (YY_RVREF (stack_symbol_type) that) + : super_type (YY_MOVE (that.state), YY_MOVE (that.location)) + { + switch (that.kind ()) + { + case symbol_kind::S_value: // value + case symbol_kind::S_map_value: // map_value + case symbol_kind::S_ncr_protocol_value: // ncr_protocol_value + value.YY_MOVE_OR_COPY< ElementPtr > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_BOOLEAN: // "boolean" + value.YY_MOVE_OR_COPY< bool > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_FLOAT: // "floating point" + value.YY_MOVE_OR_COPY< double > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_INTEGER: // "integer" + value.YY_MOVE_OR_COPY< int64_t > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_STRING: // "constant string" + value.YY_MOVE_OR_COPY< std::string > (YY_MOVE (that.value)); + break; + + default: + break; + } + +#if 201103L <= YY_CPLUSPLUS + // that is emptied. + that.state = empty_state; +#endif + } + + D2Parser::stack_symbol_type::stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) that) + : super_type (s, YY_MOVE (that.location)) + { + switch (that.kind ()) + { + case symbol_kind::S_value: // value + case symbol_kind::S_map_value: // map_value + case symbol_kind::S_ncr_protocol_value: // ncr_protocol_value + value.move< ElementPtr > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_BOOLEAN: // "boolean" + value.move< bool > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_FLOAT: // "floating point" + value.move< double > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_INTEGER: // "integer" + value.move< int64_t > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_STRING: // "constant string" + value.move< std::string > (YY_MOVE (that.value)); + break; + + default: + break; + } + + // that is emptied. + that.kind_ = symbol_kind::S_YYEMPTY; + } + +#if YY_CPLUSPLUS < 201103L + D2Parser::stack_symbol_type& + D2Parser::stack_symbol_type::operator= (const stack_symbol_type& that) + { + state = that.state; + switch (that.kind ()) + { + case symbol_kind::S_value: // value + case symbol_kind::S_map_value: // map_value + case symbol_kind::S_ncr_protocol_value: // ncr_protocol_value + value.copy< ElementPtr > (that.value); + break; + + case symbol_kind::S_BOOLEAN: // "boolean" + value.copy< bool > (that.value); + break; + + case symbol_kind::S_FLOAT: // "floating point" + value.copy< double > (that.value); + break; + + case symbol_kind::S_INTEGER: // "integer" + value.copy< int64_t > (that.value); + break; + + case symbol_kind::S_STRING: // "constant string" + value.copy< std::string > (that.value); + break; + + default: + break; + } + + location = that.location; + return *this; + } + + D2Parser::stack_symbol_type& + D2Parser::stack_symbol_type::operator= (stack_symbol_type& that) + { + state = that.state; + switch (that.kind ()) + { + case symbol_kind::S_value: // value + case symbol_kind::S_map_value: // map_value + case symbol_kind::S_ncr_protocol_value: // ncr_protocol_value + value.move< ElementPtr > (that.value); + break; + + case symbol_kind::S_BOOLEAN: // "boolean" + value.move< bool > (that.value); + break; + + case symbol_kind::S_FLOAT: // "floating point" + value.move< double > (that.value); + break; + + case symbol_kind::S_INTEGER: // "integer" + value.move< int64_t > (that.value); + break; + + case symbol_kind::S_STRING: // "constant string" + value.move< std::string > (that.value); + break; + + default: + break; + } + + location = that.location; + // that is emptied. + that.state = empty_state; + return *this; + } +#endif + + template <typename Base> + void + D2Parser::yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const + { + if (yymsg) + YY_SYMBOL_PRINT (yymsg, yysym); + } + +#if D2_PARSER_DEBUG + template <typename Base> + void + D2Parser::yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const + { + std::ostream& yyoutput = yyo; + YY_USE (yyoutput); + if (yysym.empty ()) + yyo << "empty symbol"; + else + { + symbol_kind_type yykind = yysym.kind (); + yyo << (yykind < YYNTOKENS ? "token" : "nterm") + << ' ' << yysym.name () << " (" + << yysym.location << ": "; + switch (yykind) + { + case symbol_kind::S_STRING: // "constant string" +#line 116 "d2_parser.yy" + { yyoutput << yysym.value.template as < std::string > (); } +#line 384 "d2_parser.cc" + break; + + case symbol_kind::S_INTEGER: // "integer" +#line 116 "d2_parser.yy" + { yyoutput << yysym.value.template as < int64_t > (); } +#line 390 "d2_parser.cc" + break; + + case symbol_kind::S_FLOAT: // "floating point" +#line 116 "d2_parser.yy" + { yyoutput << yysym.value.template as < double > (); } +#line 396 "d2_parser.cc" + break; + + case symbol_kind::S_BOOLEAN: // "boolean" +#line 116 "d2_parser.yy" + { yyoutput << yysym.value.template as < bool > (); } +#line 402 "d2_parser.cc" + break; + + case symbol_kind::S_value: // value +#line 116 "d2_parser.yy" + { yyoutput << yysym.value.template as < ElementPtr > (); } +#line 408 "d2_parser.cc" + break; + + case symbol_kind::S_map_value: // map_value +#line 116 "d2_parser.yy" + { yyoutput << yysym.value.template as < ElementPtr > (); } +#line 414 "d2_parser.cc" + break; + + case symbol_kind::S_ncr_protocol_value: // ncr_protocol_value +#line 116 "d2_parser.yy" + { yyoutput << yysym.value.template as < ElementPtr > (); } +#line 420 "d2_parser.cc" + break; + + default: + break; + } + yyo << ')'; + } + } +#endif + + void + D2Parser::yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym) + { + if (m) + YY_SYMBOL_PRINT (m, sym); + yystack_.push (YY_MOVE (sym)); + } + + void + D2Parser::yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym) + { +#if 201103L <= YY_CPLUSPLUS + yypush_ (m, stack_symbol_type (s, std::move (sym))); +#else + stack_symbol_type ss (s, sym); + yypush_ (m, ss); +#endif + } + + void + D2Parser::yypop_ (int n) YY_NOEXCEPT + { + yystack_.pop (n); + } + +#if D2_PARSER_DEBUG + std::ostream& + D2Parser::debug_stream () const + { + return *yycdebug_; + } + + void + D2Parser::set_debug_stream (std::ostream& o) + { + yycdebug_ = &o; + } + + + D2Parser::debug_level_type + D2Parser::debug_level () const + { + return yydebug_; + } + + void + D2Parser::set_debug_level (debug_level_type l) + { + yydebug_ = l; + } +#endif // D2_PARSER_DEBUG + + D2Parser::state_type + D2Parser::yy_lr_goto_state_ (state_type yystate, int yysym) + { + int yyr = yypgoto_[yysym - YYNTOKENS] + yystate; + if (0 <= yyr && yyr <= yylast_ && yycheck_[yyr] == yystate) + return yytable_[yyr]; + else + return yydefgoto_[yysym - YYNTOKENS]; + } + + bool + D2Parser::yy_pact_value_is_default_ (int yyvalue) YY_NOEXCEPT + { + return yyvalue == yypact_ninf_; + } + + bool + D2Parser::yy_table_value_is_error_ (int yyvalue) YY_NOEXCEPT + { + return yyvalue == yytable_ninf_; + } + + int + D2Parser::operator() () + { + return parse (); + } + + int + D2Parser::parse () + { + int yyn; + /// Length of the RHS of the rule being reduced. + int yylen = 0; + + // Error handling. + int yynerrs_ = 0; + int yyerrstatus_ = 0; + + /// The lookahead symbol. + symbol_type yyla; + + /// The locations where the error started and ended. + stack_symbol_type yyerror_range[3]; + + /// The return value of parse (). + int yyresult; + +#if YY_EXCEPTIONS + try +#endif // YY_EXCEPTIONS + { + YYCDEBUG << "Starting parse\n"; + + + /* Initialize the stack. The initial state will be set in + yynewstate, since the latter expects the semantical and the + location values to have been already stored, initialize these + stacks with a primary value. */ + yystack_.clear (); + yypush_ (YY_NULLPTR, 0, YY_MOVE (yyla)); + + /*-----------------------------------------------. + | yynewstate -- push a new symbol on the stack. | + `-----------------------------------------------*/ + yynewstate: + YYCDEBUG << "Entering state " << int (yystack_[0].state) << '\n'; + YY_STACK_PRINT (); + + // Accept? + if (yystack_[0].state == yyfinal_) + YYACCEPT; + + goto yybackup; + + + /*-----------. + | yybackup. | + `-----------*/ + yybackup: + // Try to take a decision without lookahead. + yyn = yypact_[+yystack_[0].state]; + if (yy_pact_value_is_default_ (yyn)) + goto yydefault; + + // Read a lookahead token. + if (yyla.empty ()) + { + YYCDEBUG << "Reading a token\n"; +#if YY_EXCEPTIONS + try +#endif // YY_EXCEPTIONS + { + symbol_type yylookahead (yylex (ctx)); + yyla.move (yylookahead); + } +#if YY_EXCEPTIONS + catch (const syntax_error& yyexc) + { + YYCDEBUG << "Caught exception: " << yyexc.what() << '\n'; + error (yyexc); + goto yyerrlab1; + } +#endif // YY_EXCEPTIONS + } + YY_SYMBOL_PRINT ("Next token is", yyla); + + if (yyla.kind () == symbol_kind::S_YYerror) + { + // The scanner already issued an error message, process directly + // to error recovery. But do not keep the error token as + // lookahead, it is too special and may lead us to an endless + // loop in error recovery. */ + yyla.kind_ = symbol_kind::S_YYUNDEF; + goto yyerrlab1; + } + + /* If the proper action on seeing token YYLA.TYPE is to reduce or + to detect an error, take that action. */ + yyn += yyla.kind (); + if (yyn < 0 || yylast_ < yyn || yycheck_[yyn] != yyla.kind ()) + { + goto yydefault; + } + + // Reduce or error. + yyn = yytable_[yyn]; + if (yyn <= 0) + { + if (yy_table_value_is_error_ (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + // Count tokens shifted since error; after three, turn off error status. + if (yyerrstatus_) + --yyerrstatus_; + + // Shift the lookahead token. + yypush_ ("Shifting", state_type (yyn), YY_MOVE (yyla)); + goto yynewstate; + + + /*-----------------------------------------------------------. + | yydefault -- do the default action for the current state. | + `-----------------------------------------------------------*/ + yydefault: + yyn = yydefact_[+yystack_[0].state]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + + /*-----------------------------. + | yyreduce -- do a reduction. | + `-----------------------------*/ + yyreduce: + yylen = yyr2_[yyn]; + { + stack_symbol_type yylhs; + yylhs.state = yy_lr_goto_state_ (yystack_[yylen].state, yyr1_[yyn]); + /* Variants are always initialized to an empty instance of the + correct type. The default '$$ = $1' action is NOT applied + when using variants. */ + switch (yyr1_[yyn]) + { + case symbol_kind::S_value: // value + case symbol_kind::S_map_value: // map_value + case symbol_kind::S_ncr_protocol_value: // ncr_protocol_value + yylhs.value.emplace< ElementPtr > (); + break; + + case symbol_kind::S_BOOLEAN: // "boolean" + yylhs.value.emplace< bool > (); + break; + + case symbol_kind::S_FLOAT: // "floating point" + yylhs.value.emplace< double > (); + break; + + case symbol_kind::S_INTEGER: // "integer" + yylhs.value.emplace< int64_t > (); + break; + + case symbol_kind::S_STRING: // "constant string" + yylhs.value.emplace< std::string > (); + break; + + default: + break; + } + + + // Default location. + { + stack_type::slice range (yystack_, yylen); + YYLLOC_DEFAULT (yylhs.location, range, yylen); + yyerror_range[1].location = yylhs.location; + } + + // Perform the reduction. + YY_REDUCE_PRINT (yyn); +#if YY_EXCEPTIONS + try +#endif // YY_EXCEPTIONS + { + switch (yyn) + { + case 2: // $@1: %empty +#line 125 "d2_parser.yy" + { ctx.ctx_ = ctx.NO_KEYWORD; } +#line 695 "d2_parser.cc" + break; + + case 4: // $@2: %empty +#line 126 "d2_parser.yy" + { ctx.ctx_ = ctx.CONFIG; } +#line 701 "d2_parser.cc" + break; + + case 6: // $@3: %empty +#line 127 "d2_parser.yy" + { ctx.ctx_ = ctx.DHCPDDNS; } +#line 707 "d2_parser.cc" + break; + + case 8: // $@4: %empty +#line 128 "d2_parser.yy" + { ctx.ctx_ = ctx.TSIG_KEY; } +#line 713 "d2_parser.cc" + break; + + case 10: // $@5: %empty +#line 129 "d2_parser.yy" + { ctx.ctx_ = ctx.TSIG_KEYS; } +#line 719 "d2_parser.cc" + break; + + case 12: // $@6: %empty +#line 130 "d2_parser.yy" + { ctx.ctx_ = ctx.DDNS_DOMAIN; } +#line 725 "d2_parser.cc" + break; + + case 14: // $@7: %empty +#line 131 "d2_parser.yy" + { ctx.ctx_ = ctx.DDNS_DOMAINS; } +#line 731 "d2_parser.cc" + break; + + case 16: // $@8: %empty +#line 132 "d2_parser.yy" + { ctx.ctx_ = ctx.DNS_SERVERS; } +#line 737 "d2_parser.cc" + break; + + case 18: // $@9: %empty +#line 133 "d2_parser.yy" + { ctx.ctx_ = ctx.DNS_SERVERS; } +#line 743 "d2_parser.cc" + break; + + case 20: // $@10: %empty +#line 134 "d2_parser.yy" + { ctx.ctx_ = ctx.HOOKS_LIBRARIES; } +#line 749 "d2_parser.cc" + break; + + case 22: // value: "integer" +#line 142 "d2_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location))); } +#line 755 "d2_parser.cc" + break; + + case 23: // value: "floating point" +#line 143 "d2_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new DoubleElement(yystack_[0].value.as < double > (), ctx.loc2pos(yystack_[0].location))); } +#line 761 "d2_parser.cc" + break; + + case 24: // value: "boolean" +#line 144 "d2_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new BoolElement(yystack_[0].value.as < bool > (), ctx.loc2pos(yystack_[0].location))); } +#line 767 "d2_parser.cc" + break; + + case 25: // value: "constant string" +#line 145 "d2_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); } +#line 773 "d2_parser.cc" + break; + + case 26: // value: "null" +#line 146 "d2_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new NullElement(ctx.loc2pos(yystack_[0].location))); } +#line 779 "d2_parser.cc" + break; + + case 27: // value: map2 +#line 147 "d2_parser.yy" + { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); } +#line 785 "d2_parser.cc" + break; + + case 28: // value: list_generic +#line 148 "d2_parser.yy" + { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); } +#line 791 "d2_parser.cc" + break; + + case 29: // sub_json: value +#line 151 "d2_parser.yy" + { + // Push back the JSON value on the stack + ctx.stack_.push_back(yystack_[0].value.as < ElementPtr > ()); +} +#line 800 "d2_parser.cc" + break; + + case 30: // $@11: %empty +#line 156 "d2_parser.yy" + { + // This code is executed when we're about to start parsing + // the content of the map + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(m); +} +#line 811 "d2_parser.cc" + break; + + case 31: // map2: "{" $@11 map_content "}" +#line 161 "d2_parser.yy" + { + // map parsing completed. If we ever want to do any wrap up + // (maybe some sanity checking), this would be the best place + // for it. +} +#line 821 "d2_parser.cc" + break; + + case 32: // map_value: map2 +#line 167 "d2_parser.yy" + { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); } +#line 827 "d2_parser.cc" + break; + + case 35: // not_empty_map: "constant string" ":" value +#line 174 "d2_parser.yy" + { + // map containing a single entry + ctx.unique(yystack_[2].value.as < std::string > (), ctx.loc2pos(yystack_[2].location)); + ctx.stack_.back()->set(yystack_[2].value.as < std::string > (), yystack_[0].value.as < ElementPtr > ()); + } +#line 837 "d2_parser.cc" + break; + + case 36: // not_empty_map: not_empty_map "," "constant string" ":" value +#line 179 "d2_parser.yy" + { + // map consisting of a shorter map followed by + // comma and string:value + ctx.unique(yystack_[2].value.as < std::string > (), ctx.loc2pos(yystack_[2].location)); + ctx.stack_.back()->set(yystack_[2].value.as < std::string > (), yystack_[0].value.as < ElementPtr > ()); + } +#line 848 "d2_parser.cc" + break; + + case 37: // not_empty_map: not_empty_map "," +#line 185 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 856 "d2_parser.cc" + break; + + case 38: // $@12: %empty +#line 190 "d2_parser.yy" + { + ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(l); +} +#line 865 "d2_parser.cc" + break; + + case 39: // list_generic: "[" $@12 list_content "]" +#line 193 "d2_parser.yy" + { + // list parsing complete. Put any sanity checking here +} +#line 873 "d2_parser.cc" + break; + + case 42: // not_empty_list: value +#line 201 "d2_parser.yy" + { + // List consisting of a single element. + ctx.stack_.back()->add(yystack_[0].value.as < ElementPtr > ()); + } +#line 882 "d2_parser.cc" + break; + + case 43: // not_empty_list: not_empty_list "," value +#line 205 "d2_parser.yy" + { + // List ending with , and a value. + ctx.stack_.back()->add(yystack_[0].value.as < ElementPtr > ()); + } +#line 891 "d2_parser.cc" + break; + + case 44: // not_empty_list: not_empty_list "," +#line 209 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 899 "d2_parser.cc" + break; + + case 45: // unknown_map_entry: "constant string" ":" +#line 219 "d2_parser.yy" + { + const std::string& where = ctx.contextName(); + const std::string& keyword = yystack_[1].value.as < std::string > (); + error(yystack_[1].location, + "got unexpected keyword \"" + keyword + "\" in " + where + " map."); +} +#line 910 "d2_parser.cc" + break; + + case 46: // $@13: %empty +#line 228 "d2_parser.yy" + { + // This code is executed when we're about to start parsing + // the content of the map + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(m); +} +#line 921 "d2_parser.cc" + break; + + case 47: // syntax_map: "{" $@13 global_object "}" +#line 233 "d2_parser.yy" + { + // map parsing completed. If we ever want to do any wrap up + // (maybe some sanity checking), this would be the best place + // for it. +} +#line 931 "d2_parser.cc" + break; + + case 48: // $@14: %empty +#line 241 "d2_parser.yy" + { + ctx.unique("DhcpDdns", ctx.loc2pos(yystack_[0].location)); + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("DhcpDdns", m); + ctx.stack_.push_back(m); + ctx.enter(ctx.DHCPDDNS); +} +#line 943 "d2_parser.cc" + break; + + case 49: // global_object: "DhcpDdns" $@14 ":" "{" dhcpddns_params "}" +#line 247 "d2_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 952 "d2_parser.cc" + break; + + case 51: // global_object_comma: global_object "," +#line 254 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); +} +#line 960 "d2_parser.cc" + break; + + case 52: // $@15: %empty +#line 258 "d2_parser.yy" + { + // Parse the dhcpddns map + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(m); +} +#line 970 "d2_parser.cc" + break; + + case 53: // sub_dhcpddns: "{" $@15 dhcpddns_params "}" +#line 262 "d2_parser.yy" + { + // parsing completed +} +#line 978 "d2_parser.cc" + break; + + case 56: // dhcpddns_params: dhcpddns_params "," +#line 268 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 986 "d2_parser.cc" + break; + + case 71: // $@16: %empty +#line 290 "d2_parser.yy" + { + ctx.unique("ip-address", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 995 "d2_parser.cc" + break; + + case 72: // ip_address: "ip-address" $@16 ":" "constant string" +#line 293 "d2_parser.yy" + { + ElementPtr s(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("ip-address", s); + ctx.leave(); +} +#line 1005 "d2_parser.cc" + break; + + case 73: // port: "port" ":" "integer" +#line 299 "d2_parser.yy" + { + ctx.unique("port", ctx.loc2pos(yystack_[2].location)); + if (yystack_[0].value.as < int64_t > () <= 0 || yystack_[0].value.as < int64_t > () >= 65536 ) { + error(yystack_[0].location, "port must be greater than zero but less than 65536"); + } + ElementPtr i(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("port", i); +} +#line 1018 "d2_parser.cc" + break; + + case 74: // dns_server_timeout: "dns-server-timeout" ":" "integer" +#line 308 "d2_parser.yy" + { + ctx.unique("dns-server-timeout", ctx.loc2pos(yystack_[2].location)); + if (yystack_[0].value.as < int64_t > () <= 0) { + error(yystack_[0].location, "dns-server-timeout must be greater than zero"); + } else { + ElementPtr i(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("dns-server-timeout", i); + } +} +#line 1032 "d2_parser.cc" + break; + + case 75: // $@17: %empty +#line 318 "d2_parser.yy" + { + ctx.unique("ncr-protocol", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NCR_PROTOCOL); +} +#line 1041 "d2_parser.cc" + break; + + case 76: // ncr_protocol: "ncr-protocol" $@17 ":" ncr_protocol_value +#line 321 "d2_parser.yy" + { + ctx.stack_.back()->set("ncr-protocol", yystack_[0].value.as < ElementPtr > ()); + ctx.leave(); +} +#line 1050 "d2_parser.cc" + break; + + case 77: // ncr_protocol_value: "UDP" +#line 327 "d2_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement("UDP", ctx.loc2pos(yystack_[0].location))); } +#line 1056 "d2_parser.cc" + break; + + case 78: // ncr_protocol_value: "TCP" +#line 328 "d2_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement("TCP", ctx.loc2pos(yystack_[0].location))); } +#line 1062 "d2_parser.cc" + break; + + case 79: // $@18: %empty +#line 331 "d2_parser.yy" + { + ctx.unique("ncr-format", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NCR_FORMAT); +} +#line 1071 "d2_parser.cc" + break; + + case 80: // ncr_format: "ncr-format" $@18 ":" "JSON" +#line 334 "d2_parser.yy" + { + ElementPtr json(new StringElement("JSON", ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("ncr-format", json); + ctx.leave(); +} +#line 1081 "d2_parser.cc" + break; + + case 81: // $@19: %empty +#line 340 "d2_parser.yy" + { + ctx.enter(ctx.NO_KEYWORD); +} +#line 1089 "d2_parser.cc" + break; + + case 82: // user_context: "user-context" $@19 ":" map_value +#line 342 "d2_parser.yy" + { + ElementPtr parent = ctx.stack_.back(); + ElementPtr user_context = yystack_[0].value.as < ElementPtr > (); + ConstElementPtr old = parent->get("user-context"); + + // Handle already existing user context + if (old) { + // Check if it was a comment or a duplicate + if ((old->size() != 1) || !old->contains("comment")) { + std::stringstream msg; + msg << "duplicate user-context entries (previous at " + << old->getPosition().str() << ")"; + error(yystack_[3].location, msg.str()); + } + // Merge the comment + user_context->set("comment", old->get("comment")); + } + + // Set the user context + parent->set("user-context", user_context); + ctx.leave(); +} +#line 1116 "d2_parser.cc" + break; + + case 83: // $@20: %empty +#line 365 "d2_parser.yy" + { + ctx.enter(ctx.NO_KEYWORD); +} +#line 1124 "d2_parser.cc" + break; + + case 84: // comment: "comment" $@20 ":" "constant string" +#line 367 "d2_parser.yy" + { + ElementPtr parent = ctx.stack_.back(); + ElementPtr user_context(new MapElement(ctx.loc2pos(yystack_[3].location))); + ElementPtr comment(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + user_context->set("comment", comment); + + // Handle already existing user context + ConstElementPtr old = parent->get("user-context"); + if (old) { + // Check for duplicate comment + if (old->contains("comment")) { + std::stringstream msg; + msg << "duplicate user-context/comment entries (previous at " + << old->getPosition().str() << ")"; + error(yystack_[3].location, msg.str()); + } + // Merge the user context in the comment + merge(user_context, old); + } + + // Set the user context + parent->set("user-context", user_context); + ctx.leave(); +} +#line 1153 "d2_parser.cc" + break; + + case 85: // $@21: %empty +#line 392 "d2_parser.yy" + { + ctx.unique("forward-ddns", ctx.loc2pos(yystack_[0].location)); + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("forward-ddns", m); + ctx.stack_.push_back(m); + ctx.enter(ctx.FORWARD_DDNS); +} +#line 1165 "d2_parser.cc" + break; + + case 86: // forward_ddns: "forward-ddns" $@21 ":" "{" ddns_mgr_params "}" +#line 398 "d2_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1174 "d2_parser.cc" + break; + + case 87: // $@22: %empty +#line 403 "d2_parser.yy" + { + ctx.unique("reverse-ddns", ctx.loc2pos(yystack_[0].location)); + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("reverse-ddns", m); + ctx.stack_.push_back(m); + ctx.enter(ctx.REVERSE_DDNS); +} +#line 1186 "d2_parser.cc" + break; + + case 88: // reverse_ddns: "reverse-ddns" $@22 ":" "{" ddns_mgr_params "}" +#line 409 "d2_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1195 "d2_parser.cc" + break; + + case 93: // not_empty_ddns_mgr_params: ddns_mgr_params "," +#line 420 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1203 "d2_parser.cc" + break; + + case 96: // $@23: %empty +#line 431 "d2_parser.yy" + { + ctx.unique("ddns-domains", ctx.loc2pos(yystack_[0].location)); + ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("ddns-domains", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.DDNS_DOMAINS); +} +#line 1215 "d2_parser.cc" + break; + + case 97: // ddns_domains: "ddns-domains" $@23 ":" "[" ddns_domain_list "]" +#line 437 "d2_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1224 "d2_parser.cc" + break; + + case 98: // $@24: %empty +#line 442 "d2_parser.yy" + { + ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(l); +} +#line 1233 "d2_parser.cc" + break; + + case 99: // sub_ddns_domains: "[" $@24 ddns_domain_list "]" +#line 445 "d2_parser.yy" + { + // parsing completed +} +#line 1241 "d2_parser.cc" + break; + + case 104: // not_empty_ddns_domain_list: not_empty_ddns_domain_list "," +#line 455 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1249 "d2_parser.cc" + break; + + case 105: // $@25: %empty +#line 460 "d2_parser.yy" + { + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} +#line 1259 "d2_parser.cc" + break; + + case 106: // ddns_domain: "{" $@25 ddns_domain_params "}" +#line 464 "d2_parser.yy" + { + ctx.stack_.pop_back(); +} +#line 1267 "d2_parser.cc" + break; + + case 107: // $@26: %empty +#line 468 "d2_parser.yy" + { + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(m); +} +#line 1276 "d2_parser.cc" + break; + + case 108: // sub_ddns_domain: "{" $@26 ddns_domain_params "}" +#line 471 "d2_parser.yy" + { + // parsing completed +} +#line 1284 "d2_parser.cc" + break; + + case 111: // ddns_domain_params: ddns_domain_params "," +#line 477 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1292 "d2_parser.cc" + break; + + case 118: // $@27: %empty +#line 491 "d2_parser.yy" + { + ctx.unique("name", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1301 "d2_parser.cc" + break; + + case 119: // ddns_domain_name: "name" $@27 ":" "constant string" +#line 494 "d2_parser.yy" + { + if (yystack_[0].value.as < std::string > () == "") { + error(yystack_[1].location, "Ddns domain name cannot be blank"); + } + ElementPtr elem(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ElementPtr name(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("name", name); + ctx.leave(); +} +#line 1315 "d2_parser.cc" + break; + + case 120: // $@28: %empty +#line 504 "d2_parser.yy" + { + ctx.unique("key-name", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1324 "d2_parser.cc" + break; + + case 121: // ddns_key_name: "key-name" $@28 ":" "constant string" +#line 507 "d2_parser.yy" + { + ElementPtr elem(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ElementPtr name(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("key-name", name); + ctx.leave(); +} +#line 1335 "d2_parser.cc" + break; + + case 122: // $@29: %empty +#line 517 "d2_parser.yy" + { + ctx.unique("dns-servers", ctx.loc2pos(yystack_[0].location)); + ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("dns-servers", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.DNS_SERVERS); +} +#line 1347 "d2_parser.cc" + break; + + case 123: // dns_servers: "dns-servers" $@29 ":" "[" dns_server_list "]" +#line 523 "d2_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1356 "d2_parser.cc" + break; + + case 124: // $@30: %empty +#line 528 "d2_parser.yy" + { + ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(l); +} +#line 1365 "d2_parser.cc" + break; + + case 125: // sub_dns_servers: "[" $@30 dns_server_list "]" +#line 531 "d2_parser.yy" + { + // parsing completed +} +#line 1373 "d2_parser.cc" + break; + + case 128: // dns_server_list: dns_server_list "," +#line 537 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1381 "d2_parser.cc" + break; + + case 129: // $@31: %empty +#line 542 "d2_parser.yy" + { + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} +#line 1391 "d2_parser.cc" + break; + + case 130: // dns_server: "{" $@31 dns_server_params "}" +#line 546 "d2_parser.yy" + { + ctx.stack_.pop_back(); +} +#line 1399 "d2_parser.cc" + break; + + case 131: // $@32: %empty +#line 550 "d2_parser.yy" + { + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(m); +} +#line 1408 "d2_parser.cc" + break; + + case 132: // sub_dns_server: "{" $@32 dns_server_params "}" +#line 553 "d2_parser.yy" + { + // parsing completed +} +#line 1416 "d2_parser.cc" + break; + + case 135: // dns_server_params: dns_server_params "," +#line 559 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1424 "d2_parser.cc" + break; + + case 143: // $@33: %empty +#line 573 "d2_parser.yy" + { + ctx.unique("hostname", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1433 "d2_parser.cc" + break; + + case 144: // dns_server_hostname: "hostname" $@33 ":" "constant string" +#line 576 "d2_parser.yy" + { + if (yystack_[0].value.as < std::string > () != "") { + error(yystack_[1].location, "hostname is not yet supported"); + } + ElementPtr elem(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ElementPtr name(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("hostname", name); + ctx.leave(); +} +#line 1447 "d2_parser.cc" + break; + + case 145: // $@34: %empty +#line 586 "d2_parser.yy" + { + ctx.unique("ip-address", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1456 "d2_parser.cc" + break; + + case 146: // dns_server_ip_address: "ip-address" $@34 ":" "constant string" +#line 589 "d2_parser.yy" + { + ElementPtr s(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("ip-address", s); + ctx.leave(); +} +#line 1466 "d2_parser.cc" + break; + + case 147: // dns_server_port: "port" ":" "integer" +#line 595 "d2_parser.yy" + { + ctx.unique("port", ctx.loc2pos(yystack_[2].location)); + if (yystack_[0].value.as < int64_t > () <= 0 || yystack_[0].value.as < int64_t > () >= 65536 ) { + error(yystack_[0].location, "port must be greater than zero but less than 65536"); + } + ElementPtr i(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("port", i); +} +#line 1479 "d2_parser.cc" + break; + + case 148: // $@35: %empty +#line 610 "d2_parser.yy" + { + ctx.unique("tsig-keys", ctx.loc2pos(yystack_[0].location)); + ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("tsig-keys", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.TSIG_KEYS); +} +#line 1491 "d2_parser.cc" + break; + + case 149: // tsig_keys: "tsig-keys" $@35 ":" "[" tsig_keys_list "]" +#line 616 "d2_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1500 "d2_parser.cc" + break; + + case 150: // $@36: %empty +#line 621 "d2_parser.yy" + { + ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(l); +} +#line 1509 "d2_parser.cc" + break; + + case 151: // sub_tsig_keys: "[" $@36 tsig_keys_list "]" +#line 624 "d2_parser.yy" + { + // parsing completed +} +#line 1517 "d2_parser.cc" + break; + + case 156: // not_empty_tsig_keys_list: not_empty_tsig_keys_list "," +#line 634 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1525 "d2_parser.cc" + break; + + case 157: // $@37: %empty +#line 639 "d2_parser.yy" + { + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} +#line 1535 "d2_parser.cc" + break; + + case 158: // tsig_key: "{" $@37 tsig_key_params "}" +#line 643 "d2_parser.yy" + { + ctx.stack_.pop_back(); +} +#line 1543 "d2_parser.cc" + break; + + case 159: // $@38: %empty +#line 647 "d2_parser.yy" + { + // Parse tsig key list entry map + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(m); +} +#line 1553 "d2_parser.cc" + break; + + case 160: // sub_tsig_key: "{" $@38 tsig_key_params "}" +#line 651 "d2_parser.yy" + { + // parsing completed +} +#line 1561 "d2_parser.cc" + break; + + case 163: // tsig_key_params: tsig_key_params "," +#line 658 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1569 "d2_parser.cc" + break; + + case 171: // $@39: %empty +#line 672 "d2_parser.yy" + { + ctx.unique("name", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1578 "d2_parser.cc" + break; + + case 172: // tsig_key_name: "name" $@39 ":" "constant string" +#line 675 "d2_parser.yy" + { + if (yystack_[0].value.as < std::string > () == "") { + error(yystack_[1].location, "TSIG key name cannot be blank"); + } + ElementPtr elem(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ElementPtr name(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("name", name); + ctx.leave(); +} +#line 1592 "d2_parser.cc" + break; + + case 173: // $@40: %empty +#line 685 "d2_parser.yy" + { + ctx.unique("algorithm", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1601 "d2_parser.cc" + break; + + case 174: // tsig_key_algorithm: "algorithm" $@40 ":" "constant string" +#line 688 "d2_parser.yy" + { + if (yystack_[0].value.as < std::string > () == "") { + error(yystack_[1].location, "TSIG key algorithm cannot be blank"); + } + ElementPtr elem(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("algorithm", elem); + ctx.leave(); +} +#line 1614 "d2_parser.cc" + break; + + case 175: // tsig_key_digest_bits: "digest-bits" ":" "integer" +#line 697 "d2_parser.yy" + { + ctx.unique("digest-bits", ctx.loc2pos(yystack_[2].location)); + if (yystack_[0].value.as < int64_t > () < 0 || (yystack_[0].value.as < int64_t > () > 0 && (yystack_[0].value.as < int64_t > () % 8 != 0))) { + error(yystack_[0].location, "TSIG key digest-bits must either be zero or a positive, multiple of eight"); + } + ElementPtr elem(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("digest-bits", elem); +} +#line 1627 "d2_parser.cc" + break; + + case 176: // $@41: %empty +#line 706 "d2_parser.yy" + { + ctx.unique("secret", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1636 "d2_parser.cc" + break; + + case 177: // tsig_key_secret: "secret" $@41 ":" "constant string" +#line 709 "d2_parser.yy" + { + if (yystack_[0].value.as < std::string > () == "") { + error(yystack_[1].location, "TSIG key secret cannot be blank"); + } + ElementPtr elem(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("secret", elem); + ctx.leave(); +} +#line 1649 "d2_parser.cc" + break; + + case 178: // $@42: %empty +#line 723 "d2_parser.yy" + { + ctx.unique("control-socket", ctx.loc2pos(yystack_[0].location)); + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("control-socket", m); + ctx.stack_.push_back(m); + ctx.enter(ctx.CONTROL_SOCKET); +} +#line 1661 "d2_parser.cc" + break; + + case 179: // control_socket: "control-socket" $@42 ":" "{" control_socket_params "}" +#line 729 "d2_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1670 "d2_parser.cc" + break; + + case 182: // control_socket_params: control_socket_params "," +#line 736 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1678 "d2_parser.cc" + break; + + case 188: // $@43: %empty +#line 748 "d2_parser.yy" + { + ctx.unique("socket-type", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1687 "d2_parser.cc" + break; + + case 189: // control_socket_type: "socket-type" $@43 ":" "constant string" +#line 751 "d2_parser.yy" + { + ElementPtr stype(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("socket-type", stype); + ctx.leave(); +} +#line 1697 "d2_parser.cc" + break; + + case 190: // $@44: %empty +#line 757 "d2_parser.yy" + { + ctx.unique("socket-name", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1706 "d2_parser.cc" + break; + + case 191: // control_socket_name: "socket-name" $@44 ":" "constant string" +#line 760 "d2_parser.yy" + { + ElementPtr name(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("socket-name", name); + ctx.leave(); +} +#line 1716 "d2_parser.cc" + break; + + case 192: // $@45: %empty +#line 768 "d2_parser.yy" + { + ctx.unique("hooks-libraries", ctx.loc2pos(yystack_[0].location)); + ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("hooks-libraries", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.HOOKS_LIBRARIES); +} +#line 1728 "d2_parser.cc" + break; + + case 193: // hooks_libraries: "hooks-libraries" $@45 ":" "[" hooks_libraries_list "]" +#line 774 "d2_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1737 "d2_parser.cc" + break; + + case 198: // not_empty_hooks_libraries_list: not_empty_hooks_libraries_list "," +#line 785 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1745 "d2_parser.cc" + break; + + case 199: // $@46: %empty +#line 790 "d2_parser.yy" + { + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} +#line 1755 "d2_parser.cc" + break; + + case 200: // hooks_library: "{" $@46 hooks_params "}" +#line 794 "d2_parser.yy" + { + // The library hooks parameter is required + ctx.require("library", ctx.loc2pos(yystack_[3].location), ctx.loc2pos(yystack_[0].location)); + ctx.stack_.pop_back(); +} +#line 1765 "d2_parser.cc" + break; + + case 201: // $@47: %empty +#line 800 "d2_parser.yy" + { + // Parse the hooks-libraries list entry map + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(m); +} +#line 1775 "d2_parser.cc" + break; + + case 202: // sub_hooks_library: "{" $@47 hooks_params "}" +#line 804 "d2_parser.yy" + { + // The library hooks parameter is required + ctx.require("library", ctx.loc2pos(yystack_[3].location), ctx.loc2pos(yystack_[0].location)); + // parsing completed +} +#line 1785 "d2_parser.cc" + break; + + case 205: // hooks_params: hooks_params "," +#line 812 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1793 "d2_parser.cc" + break; + + case 209: // $@48: %empty +#line 822 "d2_parser.yy" + { + ctx.unique("library", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1802 "d2_parser.cc" + break; + + case 210: // library: "library" $@48 ":" "constant string" +#line 825 "d2_parser.yy" + { + ElementPtr lib(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("library", lib); + ctx.leave(); +} +#line 1812 "d2_parser.cc" + break; + + case 211: // $@49: %empty +#line 831 "d2_parser.yy" + { + ctx.unique("parameters", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1821 "d2_parser.cc" + break; + + case 212: // parameters: "parameters" $@49 ":" map_value +#line 834 "d2_parser.yy" + { + ctx.stack_.back()->set("parameters", yystack_[0].value.as < ElementPtr > ()); + ctx.leave(); +} +#line 1830 "d2_parser.cc" + break; + + case 213: // $@50: %empty +#line 841 "d2_parser.yy" + { + ctx.unique("loggers", ctx.loc2pos(yystack_[0].location)); + ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("loggers", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.LOGGERS); +} +#line 1842 "d2_parser.cc" + break; + + case 214: // loggers: "loggers" $@50 ":" "[" loggers_entries "]" +#line 847 "d2_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1851 "d2_parser.cc" + break; + + case 217: // loggers_entries: loggers_entries "," +#line 856 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1859 "d2_parser.cc" + break; + + case 218: // $@51: %empty +#line 862 "d2_parser.yy" + { + ElementPtr l(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->add(l); + ctx.stack_.push_back(l); +} +#line 1869 "d2_parser.cc" + break; + + case 219: // logger_entry: "{" $@51 logger_params "}" +#line 866 "d2_parser.yy" + { + ctx.stack_.pop_back(); +} +#line 1877 "d2_parser.cc" + break; + + case 222: // logger_params: logger_params "," +#line 872 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1885 "d2_parser.cc" + break; + + case 230: // $@52: %empty +#line 886 "d2_parser.yy" + { + ctx.unique("name", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1894 "d2_parser.cc" + break; + + case 231: // name: "name" $@52 ":" "constant string" +#line 889 "d2_parser.yy" + { + ElementPtr name(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("name", name); + ctx.leave(); +} +#line 1904 "d2_parser.cc" + break; + + case 232: // debuglevel: "debuglevel" ":" "integer" +#line 895 "d2_parser.yy" + { + ctx.unique("debuglevel", ctx.loc2pos(yystack_[2].location)); + ElementPtr dl(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("debuglevel", dl); +} +#line 1914 "d2_parser.cc" + break; + + case 233: // $@53: %empty +#line 901 "d2_parser.yy" + { + ctx.unique("severity", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1923 "d2_parser.cc" + break; + + case 234: // severity: "severity" $@53 ":" "constant string" +#line 904 "d2_parser.yy" + { + ElementPtr sev(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("severity", sev); + ctx.leave(); +} +#line 1933 "d2_parser.cc" + break; + + case 235: // $@54: %empty +#line 910 "d2_parser.yy" + { + ctx.unique("output_options", ctx.loc2pos(yystack_[0].location)); + ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("output_options", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.OUTPUT_OPTIONS); +} +#line 1945 "d2_parser.cc" + break; + + case 236: // output_options_list: "output_options" $@54 ":" "[" output_options_list_content "]" +#line 916 "d2_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1954 "d2_parser.cc" + break; + + case 239: // output_options_list_content: output_options_list_content "," +#line 923 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1962 "d2_parser.cc" + break; + + case 240: // $@55: %empty +#line 928 "d2_parser.yy" + { + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} +#line 1972 "d2_parser.cc" + break; + + case 241: // output_entry: "{" $@55 output_params_list "}" +#line 932 "d2_parser.yy" + { + ctx.stack_.pop_back(); +} +#line 1980 "d2_parser.cc" + break; + + case 244: // output_params_list: output_params_list "," +#line 938 "d2_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1988 "d2_parser.cc" + break; + + case 250: // $@56: %empty +#line 950 "d2_parser.yy" + { + ctx.unique("output", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 1997 "d2_parser.cc" + break; + + case 251: // output: "output" $@56 ":" "constant string" +#line 953 "d2_parser.yy" + { + ElementPtr sev(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("output", sev); + ctx.leave(); +} +#line 2007 "d2_parser.cc" + break; + + case 252: // flush: "flush" ":" "boolean" +#line 959 "d2_parser.yy" + { + ctx.unique("flush", ctx.loc2pos(yystack_[2].location)); + ElementPtr flush(new BoolElement(yystack_[0].value.as < bool > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("flush", flush); +} +#line 2017 "d2_parser.cc" + break; + + case 253: // maxsize: "maxsize" ":" "integer" +#line 965 "d2_parser.yy" + { + ctx.unique("maxsize", ctx.loc2pos(yystack_[2].location)); + ElementPtr maxsize(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("maxsize", maxsize); +} +#line 2027 "d2_parser.cc" + break; + + case 254: // maxver: "maxver" ":" "integer" +#line 971 "d2_parser.yy" + { + ctx.unique("maxver", ctx.loc2pos(yystack_[2].location)); + ElementPtr maxver(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("maxver", maxver); +} +#line 2037 "d2_parser.cc" + break; + + case 255: // $@57: %empty +#line 977 "d2_parser.yy" + { + ctx.unique("pattern", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORD); +} +#line 2046 "d2_parser.cc" + break; + + case 256: // pattern: "pattern" $@57 ":" "constant string" +#line 980 "d2_parser.yy" + { + ElementPtr sev(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("pattern", sev); + ctx.leave(); +} +#line 2056 "d2_parser.cc" + break; + + +#line 2060 "d2_parser.cc" + + default: + break; + } + } +#if YY_EXCEPTIONS + catch (const syntax_error& yyexc) + { + YYCDEBUG << "Caught exception: " << yyexc.what() << '\n'; + error (yyexc); + YYERROR; + } +#endif // YY_EXCEPTIONS + YY_SYMBOL_PRINT ("-> $$ =", yylhs); + yypop_ (yylen); + yylen = 0; + + // Shift the result of the reduction. + yypush_ (YY_NULLPTR, YY_MOVE (yylhs)); + } + goto yynewstate; + + + /*--------------------------------------. + | yyerrlab -- here on detecting error. | + `--------------------------------------*/ + yyerrlab: + // If not already recovering from an error, report this error. + if (!yyerrstatus_) + { + ++yynerrs_; + context yyctx (*this, yyla); + std::string msg = yysyntax_error_ (yyctx); + error (yyla.location, YY_MOVE (msg)); + } + + + yyerror_range[1].location = yyla.location; + if (yyerrstatus_ == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + // Return failure if at end of input. + if (yyla.kind () == symbol_kind::S_YYEOF) + YYABORT; + else if (!yyla.empty ()) + { + yy_destroy_ ("Error: discarding", yyla); + yyla.clear (); + } + } + + // Else will try to reuse lookahead token after shifting the error token. + goto yyerrlab1; + + + /*---------------------------------------------------. + | yyerrorlab -- error raised explicitly by YYERROR. | + `---------------------------------------------------*/ + yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and + the label yyerrorlab therefore never appears in user code. */ + if (false) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + yypop_ (yylen); + yylen = 0; + YY_STACK_PRINT (); + goto yyerrlab1; + + + /*-------------------------------------------------------------. + | yyerrlab1 -- common code for both syntax error and YYERROR. | + `-------------------------------------------------------------*/ + yyerrlab1: + yyerrstatus_ = 3; // Each real token shifted decrements this. + // Pop stack until we find a state that shifts the error token. + for (;;) + { + yyn = yypact_[+yystack_[0].state]; + if (!yy_pact_value_is_default_ (yyn)) + { + yyn += symbol_kind::S_YYerror; + if (0 <= yyn && yyn <= yylast_ + && yycheck_[yyn] == symbol_kind::S_YYerror) + { + yyn = yytable_[yyn]; + if (0 < yyn) + break; + } + } + + // Pop the current state because it cannot handle the error token. + if (yystack_.size () == 1) + YYABORT; + + yyerror_range[1].location = yystack_[0].location; + yy_destroy_ ("Error: popping", yystack_[0]); + yypop_ (); + YY_STACK_PRINT (); + } + { + stack_symbol_type error_token; + + yyerror_range[2].location = yyla.location; + YYLLOC_DEFAULT (error_token.location, yyerror_range, 2); + + // Shift the error token. + error_token.state = state_type (yyn); + yypush_ ("Shifting", YY_MOVE (error_token)); + } + goto yynewstate; + + + /*-------------------------------------. + | yyacceptlab -- YYACCEPT comes here. | + `-------------------------------------*/ + yyacceptlab: + yyresult = 0; + goto yyreturn; + + + /*-----------------------------------. + | yyabortlab -- YYABORT comes here. | + `-----------------------------------*/ + yyabortlab: + yyresult = 1; + goto yyreturn; + + + /*-----------------------------------------------------. + | yyreturn -- parsing is finished, return the result. | + `-----------------------------------------------------*/ + yyreturn: + if (!yyla.empty ()) + yy_destroy_ ("Cleanup: discarding lookahead", yyla); + + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + yypop_ (yylen); + YY_STACK_PRINT (); + while (1 < yystack_.size ()) + { + yy_destroy_ ("Cleanup: popping", yystack_[0]); + yypop_ (); + } + + return yyresult; + } +#if YY_EXCEPTIONS + catch (...) + { + YYCDEBUG << "Exception caught: cleaning lookahead and stack\n"; + // Do not try to display the values of the reclaimed symbols, + // as their printers might throw an exception. + if (!yyla.empty ()) + yy_destroy_ (YY_NULLPTR, yyla); + + while (1 < yystack_.size ()) + { + yy_destroy_ (YY_NULLPTR, yystack_[0]); + yypop_ (); + } + throw; + } +#endif // YY_EXCEPTIONS + } + + void + D2Parser::error (const syntax_error& yyexc) + { + error (yyexc.location, yyexc.what ()); + } + + /* Return YYSTR after stripping away unnecessary quotes and + backslashes, so that it's suitable for yyerror. The heuristic is + that double-quoting is unnecessary unless the string contains an + apostrophe, a comma, or backslash (other than backslash-backslash). + YYSTR is taken from yytname. */ + std::string + D2Parser::yytnamerr_ (const char *yystr) + { + if (*yystr == '"') + { + std::string yyr; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + yyr += *yyp; + break; + + case '"': + return yyr; + } + do_not_strip_quotes: ; + } + + return yystr; + } + + std::string + D2Parser::symbol_name (symbol_kind_type yysymbol) + { + return yytnamerr_ (yytname_[yysymbol]); + } + + + + // D2Parser::context. + D2Parser::context::context (const D2Parser& yyparser, const symbol_type& yyla) + : yyparser_ (yyparser) + , yyla_ (yyla) + {} + + int + D2Parser::context::expected_tokens (symbol_kind_type yyarg[], int yyargn) const + { + // Actual number of expected tokens + int yycount = 0; + + const int yyn = yypact_[+yyparser_.yystack_[0].state]; + if (!yy_pact_value_is_default_ (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + const int yyxbegin = yyn < 0 ? -yyn : 0; + // Stay within bounds of both yycheck and yytname. + const int yychecklim = yylast_ - yyn + 1; + const int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + for (int yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck_[yyx + yyn] == yyx && yyx != symbol_kind::S_YYerror + && !yy_table_value_is_error_ (yytable_[yyx + yyn])) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + return 0; + else + yyarg[yycount++] = YY_CAST (symbol_kind_type, yyx); + } + } + + if (yyarg && yycount == 0 && 0 < yyargn) + yyarg[0] = symbol_kind::S_YYEMPTY; + return yycount; + } + + + + + + + int + D2Parser::yy_syntax_error_arguments_ (const context& yyctx, + symbol_kind_type yyarg[], int yyargn) const + { + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yyla) is + if this state is a consistent state with a default action. + Thus, detecting the absence of a lookahead is sufficient to + determine that there is no unexpected or expected token to + report. In that case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is + a consistent state with a default action. There might have + been a previous inconsistent state, consistent state with a + non-default action, or user semantic action that manipulated + yyla. (However, yyla is currently not documented for users.) + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + + if (!yyctx.lookahead ().empty ()) + { + if (yyarg) + yyarg[0] = yyctx.token (); + int yyn = yyctx.expected_tokens (yyarg ? yyarg + 1 : yyarg, yyargn - 1); + return yyn + 1; + } + return 0; + } + + // Generate an error message. + std::string + D2Parser::yysyntax_error_ (const context& yyctx) const + { + // Its maximum. + enum { YYARGS_MAX = 5 }; + // Arguments of yyformat. + symbol_kind_type yyarg[YYARGS_MAX]; + int yycount = yy_syntax_error_arguments_ (yyctx, yyarg, YYARGS_MAX); + + char const* yyformat = YY_NULLPTR; + switch (yycount) + { +#define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: // Avoid compiler warnings. + YYCASE_ (0, YY_("syntax error")); + YYCASE_ (1, YY_("syntax error, unexpected %s")); + YYCASE_ (2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_ (3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_ (4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_ (5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +#undef YYCASE_ + } + + std::string yyres; + // Argument number. + std::ptrdiff_t yyi = 0; + for (char const* yyp = yyformat; *yyp; ++yyp) + if (yyp[0] == '%' && yyp[1] == 's' && yyi < yycount) + { + yyres += symbol_name (yyarg[yyi++]); + ++yyp; + } + else + yyres += *yyp; + return yyres; + } + + + const short D2Parser::yypact_ninf_ = -212; + + const signed char D2Parser::yytable_ninf_ = -1; + + const short + D2Parser::yypact_[] = + { + 49, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, 10, 8, 24, 30, 42, 48, 64, 128, 74, + 136, 127, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, 8, -22, 33, 7, 31, 146, 38, 156, + 28, 160, 37, -212, 137, 163, 166, 164, 168, -212, + 22, -212, -212, 169, 170, -212, -212, -212, -212, -212, + -212, -212, -212, -212, -212, 171, -212, 76, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -212, 172, -212, -212, -212, -212, -212, 79, + -212, -212, -212, -212, -212, -212, 173, 174, -212, -212, + -212, -212, -212, -212, -212, 103, -212, -212, -212, -212, + -212, 175, 177, -212, -212, 178, -212, -212, -212, -212, + -212, 104, -212, -212, -212, -212, -212, 77, -212, -212, + -212, -212, 105, -212, -212, -212, -212, 8, 8, -212, + 121, 179, -212, -212, 180, 130, 131, 181, 182, 183, + 186, 187, 188, 189, 190, 191, 192, -212, 7, -212, + 193, 140, 195, 196, 31, -212, 31, -212, 146, 197, + 198, 201, 38, -212, 38, -212, 156, 205, 154, 206, + 28, -212, 28, 160, -212, 207, 209, -13, -212, -212, + -212, 210, 208, 162, -212, -212, 153, 199, 213, 165, + 214, 216, 211, 217, 220, 221, -212, 176, -212, 184, + 185, -212, 106, -212, 203, 222, 204, -212, 107, -212, + 215, -212, 218, -212, 115, -212, 219, 213, -212, 8, + 7, -212, -212, -212, -212, -212, -212, -212, -212, -15, + -15, 146, 13, 223, 224, -212, -212, -212, -212, -212, + 160, -212, -212, -212, -212, -212, -212, -212, -212, 116, + -212, -212, 117, -212, -212, -212, 118, 226, -212, -212, + -212, -212, -212, 119, -212, -212, -212, -212, 228, 225, + -212, -212, 129, -212, 158, -212, 231, -15, -212, -212, + -212, 232, 233, 13, -212, 37, -212, 223, 36, 224, + -212, -212, 234, -212, 227, 229, -212, 149, -212, -212, + -212, 236, -212, -212, -212, -212, 151, -212, -212, -212, + -212, -212, -212, 156, -212, -212, -212, 239, 240, 194, + 241, 36, -212, 242, 230, 244, -212, 235, -212, -212, + -212, 243, -212, -212, 159, -212, 46, 243, -212, -212, + 249, 250, 251, -212, 152, -212, -212, -212, -212, -212, + -212, -212, 252, 237, 212, 245, 260, 46, -212, 247, + -212, -212, -212, 248, -212, -212, -212 + }; + + const short + D2Parser::yydefact_[] = + { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, + 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 38, 30, 26, 25, 22, 23, 24, + 29, 3, 27, 28, 46, 5, 52, 7, 159, 9, + 150, 11, 107, 13, 98, 15, 131, 17, 124, 19, + 201, 21, 40, 33, 0, 0, 0, 152, 0, 100, + 0, 0, 0, 42, 0, 41, 0, 0, 34, 48, + 0, 50, 71, 0, 0, 75, 79, 81, 83, 85, + 87, 148, 178, 192, 213, 0, 70, 0, 54, 57, + 58, 59, 60, 61, 68, 69, 62, 63, 64, 65, + 66, 67, 173, 0, 176, 171, 170, 168, 169, 0, + 161, 164, 165, 166, 167, 157, 0, 153, 154, 120, + 122, 118, 117, 115, 116, 0, 109, 112, 113, 114, + 105, 0, 101, 102, 145, 0, 143, 142, 140, 141, + 139, 0, 133, 136, 137, 138, 129, 0, 126, 209, + 211, 206, 0, 203, 207, 208, 39, 44, 0, 31, + 37, 0, 51, 47, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 45, 56, 53, + 0, 0, 0, 0, 163, 160, 0, 151, 156, 0, + 0, 0, 111, 108, 0, 99, 104, 0, 0, 0, + 135, 132, 0, 128, 125, 0, 0, 205, 202, 43, + 35, 0, 0, 0, 73, 74, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 55, 0, 175, 0, + 0, 162, 0, 155, 0, 0, 0, 110, 0, 103, + 0, 147, 0, 134, 0, 127, 0, 0, 204, 0, + 0, 72, 77, 78, 76, 80, 32, 82, 84, 89, + 89, 152, 0, 194, 0, 174, 177, 172, 158, 121, + 0, 119, 106, 146, 144, 130, 210, 212, 36, 0, + 96, 95, 0, 90, 91, 94, 0, 0, 188, 190, + 187, 185, 186, 0, 180, 183, 184, 199, 0, 195, + 196, 218, 0, 215, 0, 49, 0, 93, 86, 88, + 149, 0, 0, 182, 179, 0, 193, 198, 0, 217, + 214, 123, 0, 92, 0, 0, 181, 0, 197, 230, + 235, 0, 233, 229, 227, 228, 0, 220, 223, 225, + 226, 224, 216, 100, 189, 191, 200, 0, 0, 0, + 0, 222, 219, 0, 0, 0, 232, 0, 221, 97, + 231, 0, 234, 240, 0, 237, 0, 239, 236, 250, + 0, 0, 0, 255, 0, 242, 245, 246, 247, 248, + 249, 238, 0, 0, 0, 0, 0, 244, 241, 0, + 252, 253, 254, 0, 243, 251, 256 + }; + + const short + D2Parser::yypgoto_[] = + { + -212, -212, -212, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -41, -212, -211, -212, -18, -212, -212, -212, + -212, -212, -212, -56, -212, -212, -212, -212, -212, -212, + -212, -12, 68, -212, -212, -212, -212, -212, -212, -212, + -212, -212, -55, -212, -44, -212, -212, -212, -212, -212, + 5, -212, -60, -212, -212, -212, -212, -77, -212, 71, + -212, -212, -212, 83, 81, -212, -212, -51, -212, -212, + -212, -212, -212, -2, 75, -212, -212, -212, 69, 80, + -212, -212, -212, -212, -212, -212, -212, -212, -212, 18, + -212, 93, -212, -212, -212, 96, 99, -212, -212, -212, + -212, -212, -212, -212, -212, -212, -212, -28, -212, -212, + -212, -212, -212, -212, -212, -212, -29, -212, -212, -212, + -26, 84, -212, -212, -212, -212, -212, -212, -212, -25, + -212, -212, -61, -212, -212, -212, -212, -212, -212, -212, + -212, -74, -212, -212, -89, -212, -212, -212, -212, -212, + -212, -212 + }; + + const short + D2Parser::yydefgoto_[] = + { + 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 30, 31, 32, 53, 257, 67, 68, 33, + 52, 64, 65, 86, 35, 54, 70, 161, 71, 37, + 55, 87, 88, 89, 164, 90, 91, 92, 167, 254, + 93, 168, 94, 169, 95, 170, 96, 171, 97, 172, + 282, 283, 284, 285, 306, 45, 59, 131, 132, 133, + 194, 43, 58, 125, 126, 127, 191, 128, 189, 129, + 190, 49, 61, 147, 148, 202, 47, 60, 141, 142, + 143, 199, 144, 197, 145, 98, 173, 41, 57, 116, + 117, 118, 186, 39, 56, 109, 110, 111, 183, 112, + 180, 113, 114, 182, 99, 174, 293, 294, 295, 311, + 296, 312, 100, 175, 298, 299, 300, 315, 51, 62, + 152, 153, 154, 205, 155, 206, 101, 176, 302, 303, + 318, 336, 337, 338, 347, 339, 340, 350, 341, 348, + 364, 365, 366, 374, 375, 376, 382, 377, 378, 379, + 380, 386 + }; + + const short + D2Parser::yytable_[] = + { + 106, 107, 122, 123, 137, 138, 151, 256, 280, 140, + 22, 63, 108, 23, 124, 24, 139, 25, 72, 73, + 74, 75, 149, 150, 76, 162, 77, 78, 79, 80, + 163, 34, 77, 78, 81, 66, 256, 36, 82, 134, + 135, 83, 85, 69, 84, 288, 289, 77, 78, 38, + 77, 78, 119, 40, 136, 77, 78, 77, 78, 102, + 103, 104, 119, 120, 85, 26, 27, 28, 29, 105, + 85, 42, 149, 150, 329, 330, 121, 331, 332, 178, + 203, 46, 184, 204, 179, 85, 369, 185, 85, 370, + 371, 372, 373, 85, 85, 85, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 192, 200, 207, 184, + 192, 193, 201, 208, 268, 272, 209, 210, 200, 178, + 307, 307, 313, 275, 305, 308, 309, 314, 106, 107, + 106, 107, 319, 44, 50, 320, 122, 123, 122, 123, + 108, 48, 108, 156, 137, 138, 137, 138, 124, 140, + 124, 140, 207, 115, 351, 387, 139, 346, 139, 352, + 388, 203, 367, 130, 321, 368, 157, 146, 252, 253, + 158, 160, 159, 165, 166, 177, 181, 188, 211, 187, + 196, 195, 198, 212, 213, 216, 217, 218, 214, 215, + 219, 220, 221, 222, 223, 224, 225, 227, 228, 229, + 230, 234, 235, 281, 281, 236, 290, 291, 278, 240, + 242, 246, 241, 247, 249, 250, 261, 255, 292, 251, + 24, 259, 258, 260, 262, 263, 264, 270, 317, 277, + 297, 301, 310, 265, 316, 322, 324, 325, 279, 343, + 349, 266, 267, 354, 355, 357, 226, 323, 359, 361, + 363, 281, 356, 383, 384, 385, 389, 290, 291, 151, + 269, 271, 333, 334, 393, 286, 353, 239, 304, 292, + 391, 244, 273, 237, 335, 274, 276, 238, 245, 287, + 243, 233, 232, 231, 344, 326, 345, 360, 328, 327, + 358, 248, 362, 381, 342, 333, 334, 390, 394, 0, + 0, 0, 0, 392, 395, 396, 0, 335 + }; + + const short + D2Parser::yycheck_[] = + { + 56, 56, 58, 58, 60, 60, 62, 218, 23, 60, + 0, 52, 56, 5, 58, 7, 60, 9, 11, 12, + 13, 14, 35, 36, 17, 3, 19, 20, 21, 22, + 8, 7, 19, 20, 27, 57, 247, 7, 31, 11, + 12, 34, 57, 10, 37, 32, 33, 19, 20, 7, + 19, 20, 24, 5, 26, 19, 20, 19, 20, 28, + 29, 30, 24, 25, 57, 57, 58, 59, 60, 38, + 57, 7, 35, 36, 38, 39, 38, 41, 42, 3, + 3, 7, 3, 6, 8, 57, 40, 8, 57, 43, + 44, 45, 46, 57, 57, 57, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 3, 3, 3, 3, + 3, 8, 8, 8, 8, 8, 157, 158, 3, 3, + 3, 3, 3, 8, 8, 8, 8, 8, 184, 184, + 186, 186, 3, 5, 7, 6, 192, 192, 194, 194, + 184, 5, 186, 6, 200, 200, 202, 202, 192, 200, + 194, 202, 3, 7, 3, 3, 200, 8, 202, 8, + 8, 3, 3, 7, 6, 6, 3, 7, 15, 16, + 4, 3, 8, 4, 4, 4, 4, 3, 57, 6, + 3, 6, 4, 4, 4, 4, 4, 4, 58, 58, + 4, 4, 4, 4, 4, 4, 4, 4, 58, 4, + 4, 4, 4, 259, 260, 4, 262, 262, 249, 4, + 4, 4, 58, 4, 4, 7, 5, 18, 262, 57, + 7, 7, 57, 7, 7, 5, 5, 5, 3, 247, + 7, 7, 6, 57, 6, 4, 4, 4, 250, 5, + 4, 57, 57, 4, 4, 4, 178, 307, 6, 5, + 7, 307, 58, 4, 4, 4, 4, 313, 313, 315, + 57, 57, 318, 318, 4, 260, 343, 196, 270, 313, + 58, 202, 57, 192, 318, 57, 57, 194, 203, 261, + 200, 188, 186, 184, 57, 313, 57, 57, 317, 315, + 351, 207, 57, 367, 319, 351, 351, 60, 387, -1, + -1, -1, -1, 58, 57, 57, -1, 351 + }; + + const unsigned char + D2Parser::yystos_[] = + { + 0, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 0, 5, 7, 9, 57, 58, 59, 60, + 73, 74, 75, 80, 7, 85, 7, 90, 7, 154, + 5, 148, 7, 122, 5, 116, 7, 137, 5, 132, + 7, 179, 81, 76, 86, 91, 155, 149, 123, 117, + 138, 133, 180, 73, 82, 83, 57, 78, 79, 10, + 87, 89, 11, 12, 13, 14, 17, 19, 20, 21, + 22, 27, 31, 34, 37, 57, 84, 92, 93, 94, + 96, 97, 98, 101, 103, 105, 107, 109, 146, 165, + 173, 187, 28, 29, 30, 38, 84, 103, 105, 156, + 157, 158, 160, 162, 163, 7, 150, 151, 152, 24, + 25, 38, 84, 103, 105, 124, 125, 126, 128, 130, + 7, 118, 119, 120, 11, 12, 26, 84, 103, 105, + 128, 139, 140, 141, 143, 145, 7, 134, 135, 35, + 36, 84, 181, 182, 183, 185, 6, 3, 4, 8, + 3, 88, 3, 8, 95, 4, 4, 99, 102, 104, + 106, 108, 110, 147, 166, 174, 188, 4, 3, 8, + 161, 4, 164, 159, 3, 8, 153, 6, 3, 129, + 131, 127, 3, 8, 121, 6, 3, 144, 4, 142, + 3, 8, 136, 3, 6, 184, 186, 3, 8, 73, + 73, 57, 4, 4, 58, 58, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 93, 4, 58, 4, + 4, 157, 156, 152, 4, 4, 4, 125, 124, 120, + 4, 58, 4, 140, 139, 135, 4, 4, 182, 4, + 7, 57, 15, 16, 100, 18, 75, 77, 57, 7, + 7, 5, 7, 5, 5, 57, 57, 57, 8, 57, + 5, 57, 8, 57, 57, 8, 57, 77, 73, 92, + 23, 84, 111, 112, 113, 114, 111, 150, 32, 33, + 84, 103, 105, 167, 168, 169, 171, 7, 175, 176, + 177, 7, 189, 190, 134, 8, 115, 3, 8, 8, + 6, 170, 172, 3, 8, 178, 6, 3, 191, 3, + 6, 6, 4, 113, 4, 4, 168, 181, 177, 38, + 39, 41, 42, 84, 103, 105, 192, 193, 194, 196, + 197, 199, 190, 5, 57, 57, 8, 195, 200, 4, + 198, 3, 8, 118, 4, 4, 58, 4, 193, 6, + 57, 5, 57, 7, 201, 202, 203, 3, 6, 40, + 43, 44, 45, 46, 204, 205, 206, 208, 209, 210, + 211, 202, 207, 4, 4, 4, 212, 3, 8, 4, + 60, 58, 58, 4, 205, 57, 57 + }; + + const unsigned char + D2Parser::yyr1_[] = + { + 0, 61, 63, 62, 64, 62, 65, 62, 66, 62, + 67, 62, 68, 62, 69, 62, 70, 62, 71, 62, + 72, 62, 73, 73, 73, 73, 73, 73, 73, 74, + 76, 75, 77, 78, 78, 79, 79, 79, 81, 80, + 82, 82, 83, 83, 83, 84, 86, 85, 88, 87, + 87, 89, 91, 90, 92, 92, 92, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 95, 94, 96, 97, 99, 98, 100, 100, 102, + 101, 104, 103, 106, 105, 108, 107, 110, 109, 111, + 111, 112, 112, 112, 113, 113, 115, 114, 117, 116, + 118, 118, 119, 119, 119, 121, 120, 123, 122, 124, + 124, 124, 125, 125, 125, 125, 125, 125, 127, 126, + 129, 128, 131, 130, 133, 132, 134, 134, 134, 136, + 135, 138, 137, 139, 139, 139, 140, 140, 140, 140, + 140, 140, 140, 142, 141, 144, 143, 145, 147, 146, + 149, 148, 150, 150, 151, 151, 151, 153, 152, 155, + 154, 156, 156, 156, 157, 157, 157, 157, 157, 157, + 157, 159, 158, 161, 160, 162, 164, 163, 166, 165, + 167, 167, 167, 168, 168, 168, 168, 168, 170, 169, + 172, 171, 174, 173, 175, 175, 176, 176, 176, 178, + 177, 180, 179, 181, 181, 181, 181, 182, 182, 184, + 183, 186, 185, 188, 187, 189, 189, 189, 191, 190, + 192, 192, 192, 193, 193, 193, 193, 193, 193, 193, + 195, 194, 196, 198, 197, 200, 199, 201, 201, 201, + 203, 202, 204, 204, 204, 205, 205, 205, 205, 205, + 207, 206, 208, 209, 210, 212, 211 + }; + + const signed char + D2Parser::yyr2_[] = + { + 0, 2, 0, 3, 0, 3, 0, 3, 0, 3, + 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, + 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 4, 1, 0, 1, 3, 5, 2, 0, 4, + 0, 1, 1, 3, 2, 2, 0, 4, 0, 6, + 1, 2, 0, 4, 1, 3, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 4, 3, 3, 0, 4, 1, 1, 0, + 4, 0, 4, 0, 4, 0, 6, 0, 6, 0, + 1, 1, 3, 2, 1, 1, 0, 6, 0, 4, + 0, 1, 1, 3, 2, 0, 4, 0, 4, 1, + 3, 2, 1, 1, 1, 1, 1, 1, 0, 4, + 0, 4, 0, 6, 0, 4, 1, 3, 2, 0, + 4, 0, 4, 1, 3, 2, 1, 1, 1, 1, + 1, 1, 1, 0, 4, 0, 4, 3, 0, 6, + 0, 4, 0, 1, 1, 3, 2, 0, 4, 0, + 4, 1, 3, 2, 1, 1, 1, 1, 1, 1, + 1, 0, 4, 0, 4, 3, 0, 4, 0, 6, + 1, 3, 2, 1, 1, 1, 1, 1, 0, 4, + 0, 4, 0, 6, 0, 1, 1, 3, 2, 0, + 4, 0, 4, 1, 3, 2, 1, 1, 1, 0, + 4, 0, 4, 0, 6, 1, 3, 2, 0, 4, + 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, + 0, 4, 3, 0, 4, 0, 6, 1, 3, 2, + 0, 4, 1, 3, 2, 1, 1, 1, 1, 1, + 0, 4, 3, 3, 3, 0, 4 + }; + + +#if D2_PARSER_DEBUG || 1 + // YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + // First, the terminals, then, starting at \a YYNTOKENS, nonterminals. + const char* + const D2Parser::yytname_[] = + { + "\"end of file\"", "error", "\"invalid token\"", "\",\"", "\":\"", + "\"[\"", "\"]\"", "\"{\"", "\"}\"", "\"null\"", "\"DhcpDdns\"", + "\"ip-address\"", "\"port\"", "\"dns-server-timeout\"", + "\"ncr-protocol\"", "\"UDP\"", "\"TCP\"", "\"ncr-format\"", "\"JSON\"", + "\"user-context\"", "\"comment\"", "\"forward-ddns\"", + "\"reverse-ddns\"", "\"ddns-domains\"", "\"key-name\"", + "\"dns-servers\"", "\"hostname\"", "\"tsig-keys\"", "\"algorithm\"", + "\"digest-bits\"", "\"secret\"", "\"control-socket\"", "\"socket-type\"", + "\"socket-name\"", "\"hooks-libraries\"", "\"library\"", + "\"parameters\"", "\"loggers\"", "\"name\"", "\"output_options\"", + "\"output\"", "\"debuglevel\"", "\"severity\"", "\"flush\"", + "\"maxsize\"", "\"maxver\"", "\"pattern\"", "TOPLEVEL_JSON", + "TOPLEVEL_DHCPDDNS", "SUB_DHCPDDNS", "SUB_TSIG_KEY", "SUB_TSIG_KEYS", + "SUB_DDNS_DOMAIN", "SUB_DDNS_DOMAINS", "SUB_DNS_SERVER", + "SUB_DNS_SERVERS", "SUB_HOOKS_LIBRARY", "\"constant string\"", + "\"integer\"", "\"floating point\"", "\"boolean\"", "$accept", "start", + "$@1", "$@2", "$@3", "$@4", "$@5", "$@6", "$@7", "$@8", "$@9", "$@10", + "value", "sub_json", "map2", "$@11", "map_value", "map_content", + "not_empty_map", "list_generic", "$@12", "list_content", + "not_empty_list", "unknown_map_entry", "syntax_map", "$@13", + "global_object", "$@14", "global_object_comma", "sub_dhcpddns", "$@15", + "dhcpddns_params", "dhcpddns_param", "ip_address", "$@16", "port", + "dns_server_timeout", "ncr_protocol", "$@17", "ncr_protocol_value", + "ncr_format", "$@18", "user_context", "$@19", "comment", "$@20", + "forward_ddns", "$@21", "reverse_ddns", "$@22", "ddns_mgr_params", + "not_empty_ddns_mgr_params", "ddns_mgr_param", "ddns_domains", "$@23", + "sub_ddns_domains", "$@24", "ddns_domain_list", + "not_empty_ddns_domain_list", "ddns_domain", "$@25", "sub_ddns_domain", + "$@26", "ddns_domain_params", "ddns_domain_param", "ddns_domain_name", + "$@27", "ddns_key_name", "$@28", "dns_servers", "$@29", + "sub_dns_servers", "$@30", "dns_server_list", "dns_server", "$@31", + "sub_dns_server", "$@32", "dns_server_params", "dns_server_param", + "dns_server_hostname", "$@33", "dns_server_ip_address", "$@34", + "dns_server_port", "tsig_keys", "$@35", "sub_tsig_keys", "$@36", + "tsig_keys_list", "not_empty_tsig_keys_list", "tsig_key", "$@37", + "sub_tsig_key", "$@38", "tsig_key_params", "tsig_key_param", + "tsig_key_name", "$@39", "tsig_key_algorithm", "$@40", + "tsig_key_digest_bits", "tsig_key_secret", "$@41", "control_socket", + "$@42", "control_socket_params", "control_socket_param", + "control_socket_type", "$@43", "control_socket_name", "$@44", + "hooks_libraries", "$@45", "hooks_libraries_list", + "not_empty_hooks_libraries_list", "hooks_library", "$@46", + "sub_hooks_library", "$@47", "hooks_params", "hooks_param", "library", + "$@48", "parameters", "$@49", "loggers", "$@50", "loggers_entries", + "logger_entry", "$@51", "logger_params", "logger_param", "name", "$@52", + "debuglevel", "severity", "$@53", "output_options_list", "$@54", + "output_options_list_content", "output_entry", "$@55", + "output_params_list", "output_params", "output", "$@56", "flush", + "maxsize", "maxver", "pattern", "$@57", YY_NULLPTR + }; +#endif + + +#if D2_PARSER_DEBUG + const short + D2Parser::yyrline_[] = + { + 0, 125, 125, 125, 126, 126, 127, 127, 128, 128, + 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, + 134, 134, 142, 143, 144, 145, 146, 147, 148, 151, + 156, 156, 167, 170, 171, 174, 179, 185, 190, 190, + 197, 198, 201, 205, 209, 219, 228, 228, 241, 241, + 251, 254, 258, 258, 266, 267, 268, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 290, 290, 299, 308, 318, 318, 327, 328, 331, + 331, 340, 340, 365, 365, 392, 392, 403, 403, 414, + 415, 418, 419, 420, 425, 426, 431, 431, 442, 442, + 449, 450, 453, 454, 455, 460, 460, 468, 468, 475, + 476, 477, 482, 483, 484, 485, 486, 487, 491, 491, + 504, 504, 517, 517, 528, 528, 535, 536, 537, 542, + 542, 550, 550, 557, 558, 559, 564, 565, 566, 567, + 568, 569, 570, 573, 573, 586, 586, 595, 610, 610, + 621, 621, 628, 629, 632, 633, 634, 639, 639, 647, + 647, 656, 657, 658, 663, 664, 665, 666, 667, 668, + 669, 672, 672, 685, 685, 697, 706, 706, 723, 723, + 734, 735, 736, 741, 742, 743, 744, 745, 748, 748, + 757, 757, 768, 768, 779, 780, 783, 784, 785, 790, + 790, 800, 800, 810, 811, 812, 815, 818, 819, 822, + 822, 831, 831, 841, 841, 854, 855, 856, 862, 862, + 870, 871, 872, 877, 878, 879, 880, 881, 882, 883, + 886, 886, 895, 901, 901, 910, 910, 921, 922, 923, + 928, 928, 936, 937, 938, 943, 944, 945, 946, 947, + 950, 950, 959, 965, 971, 977, 977 + }; + + void + D2Parser::yy_stack_print_ () const + { + *yycdebug_ << "Stack now"; + for (stack_type::const_iterator + i = yystack_.begin (), + i_end = yystack_.end (); + i != i_end; ++i) + *yycdebug_ << ' ' << int (i->state); + *yycdebug_ << '\n'; + } + + void + D2Parser::yy_reduce_print_ (int yyrule) const + { + int yylno = yyrline_[yyrule]; + int yynrhs = yyr2_[yyrule]; + // Print the symbols being reduced, and their result. + *yycdebug_ << "Reducing stack by rule " << yyrule - 1 + << " (line " << yylno << "):\n"; + // The symbols being reduced. + for (int yyi = 0; yyi < yynrhs; yyi++) + YY_SYMBOL_PRINT (" $" << yyi + 1 << " =", + yystack_[(yynrhs) - (yyi + 1)]); + } +#endif // D2_PARSER_DEBUG + + +#line 14 "d2_parser.yy" +} } // isc::d2 +#line 2849 "d2_parser.cc" + +#line 986 "d2_parser.yy" + + +void +isc::d2::D2Parser::error(const location_type& loc, + const std::string& what) +{ + ctx.error(loc, what); +} diff --git a/src/bin/d2/d2_parser.h b/src/bin/d2/d2_parser.h new file mode 100644 index 0000000..352e62f --- /dev/null +++ b/src/bin/d2/d2_parser.h @@ -0,0 +1,2645 @@ +// A Bison parser, made by GNU Bison 3.8.2. + +// Skeleton interface for Bison LALR(1) parsers in C++ + +// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +// As a special exception, you may create a larger work that contains +// part or all of the Bison parser skeleton and distribute that work +// under terms of your choice, so long as that work isn't itself a +// parser generator using the skeleton or a modified version thereof +// as a parser skeleton. Alternatively, if you modify or redistribute +// the parser skeleton itself, you may (at your option) remove this +// special exception, which will cause the skeleton and the resulting +// Bison output files to be licensed under the GNU General Public +// License without this special exception. + +// This special exception was added by the Free Software Foundation in +// version 2.2 of Bison. + + +/** + ** \file d2_parser.h + ** Define the isc::d2::parser class. + */ + +// C++ LALR(1) parser skeleton written by Akim Demaille. + +// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, +// especially those whose name start with YY_ or yy_. They are +// private implementation details that can be changed or removed. + +#ifndef YY_D2_PARSER_D2_PARSER_H_INCLUDED +# define YY_D2_PARSER_D2_PARSER_H_INCLUDED +// "%code requires" blocks. +#line 17 "d2_parser.yy" + +#include <string> +#include <cc/data.h> +#include <d2srv/d2_config.h> +#include <boost/lexical_cast.hpp> +#include <d2/parser_context_decl.h> + +using namespace isc::d2; +using namespace isc::data; +using namespace std; + +#line 61 "d2_parser.h" + +# include <cassert> +# include <cstdlib> // std::abort +# include <iostream> +# include <stdexcept> +# include <string> +# include <vector> + +#if defined __cplusplus +# define YY_CPLUSPLUS __cplusplus +#else +# define YY_CPLUSPLUS 199711L +#endif + +// Support move semantics when possible. +#if 201103L <= YY_CPLUSPLUS +# define YY_MOVE std::move +# define YY_MOVE_OR_COPY move +# define YY_MOVE_REF(Type) Type&& +# define YY_RVREF(Type) Type&& +# define YY_COPY(Type) Type +#else +# define YY_MOVE +# define YY_MOVE_OR_COPY copy +# define YY_MOVE_REF(Type) Type& +# define YY_RVREF(Type) const Type& +# define YY_COPY(Type) const Type& +#endif + +// Support noexcept when possible. +#if 201103L <= YY_CPLUSPLUS +# define YY_NOEXCEPT noexcept +# define YY_NOTHROW +#else +# define YY_NOEXCEPT +# define YY_NOTHROW throw () +#endif + +// Support constexpr when possible. +#if 201703 <= YY_CPLUSPLUS +# define YY_CONSTEXPR constexpr +#else +# define YY_CONSTEXPR +#endif +# include "location.hh" +#include <typeinfo> +#ifndef D2_PARSER__ASSERT +# include <cassert> +# define D2_PARSER__ASSERT assert +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast<Type> (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Debug traces. */ +#ifndef D2_PARSER_DEBUG +# if defined YYDEBUG +#if YYDEBUG +# define D2_PARSER_DEBUG 1 +# else +# define D2_PARSER_DEBUG 0 +# endif +# else /* ! defined YYDEBUG */ +# define D2_PARSER_DEBUG 1 +# endif /* ! defined YYDEBUG */ +#endif /* ! defined D2_PARSER_DEBUG */ + +#line 14 "d2_parser.yy" +namespace isc { namespace d2 { +#line 210 "d2_parser.h" + + + + + /// A Bison parser. + class D2Parser + { + public: +#ifdef D2_PARSER_STYPE +# ifdef __GNUC__ +# pragma GCC message "bison: do not #define D2_PARSER_STYPE in C++, use %define api.value.type" +# endif + typedef D2_PARSER_STYPE value_type; +#else + /// A buffer to store and retrieve objects. + /// + /// Sort of a variant, but does not keep track of the nature + /// of the stored data, since that knowledge is available + /// via the current parser state. + class value_type + { + public: + /// Type of *this. + typedef value_type self_type; + + /// Empty construction. + value_type () YY_NOEXCEPT + : yyraw_ () + , yytypeid_ (YY_NULLPTR) + {} + + /// Construct and fill. + template <typename T> + value_type (YY_RVREF (T) t) + : yytypeid_ (&typeid (T)) + { + D2_PARSER__ASSERT (sizeof (T) <= size); + new (yyas_<T> ()) T (YY_MOVE (t)); + } + +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + value_type (const self_type&) = delete; + /// Non copyable. + self_type& operator= (const self_type&) = delete; +#endif + + /// Destruction, allowed only if empty. + ~value_type () YY_NOEXCEPT + { + D2_PARSER__ASSERT (!yytypeid_); + } + +# if 201103L <= YY_CPLUSPLUS + /// Instantiate a \a T in here from \a t. + template <typename T, typename... U> + T& + emplace (U&&... u) + { + D2_PARSER__ASSERT (!yytypeid_); + D2_PARSER__ASSERT (sizeof (T) <= size); + yytypeid_ = & typeid (T); + return *new (yyas_<T> ()) T (std::forward <U>(u)...); + } +# else + /// Instantiate an empty \a T in here. + template <typename T> + T& + emplace () + { + D2_PARSER__ASSERT (!yytypeid_); + D2_PARSER__ASSERT (sizeof (T) <= size); + yytypeid_ = & typeid (T); + return *new (yyas_<T> ()) T (); + } + + /// Instantiate a \a T in here from \a t. + template <typename T> + T& + emplace (const T& t) + { + D2_PARSER__ASSERT (!yytypeid_); + D2_PARSER__ASSERT (sizeof (T) <= size); + yytypeid_ = & typeid (T); + return *new (yyas_<T> ()) T (t); + } +# endif + + /// Instantiate an empty \a T in here. + /// Obsolete, use emplace. + template <typename T> + T& + build () + { + return emplace<T> (); + } + + /// Instantiate a \a T in here from \a t. + /// Obsolete, use emplace. + template <typename T> + T& + build (const T& t) + { + return emplace<T> (t); + } + + /// Accessor to a built \a T. + template <typename T> + T& + as () YY_NOEXCEPT + { + D2_PARSER__ASSERT (yytypeid_); + D2_PARSER__ASSERT (*yytypeid_ == typeid (T)); + D2_PARSER__ASSERT (sizeof (T) <= size); + return *yyas_<T> (); + } + + /// Const accessor to a built \a T (for %printer). + template <typename T> + const T& + as () const YY_NOEXCEPT + { + D2_PARSER__ASSERT (yytypeid_); + D2_PARSER__ASSERT (*yytypeid_ == typeid (T)); + D2_PARSER__ASSERT (sizeof (T) <= size); + return *yyas_<T> (); + } + + /// Swap the content with \a that, of same type. + /// + /// Both variants must be built beforehand, because swapping the actual + /// data requires reading it (with as()), and this is not possible on + /// unconstructed variants: it would require some dynamic testing, which + /// should not be the variant's responsibility. + /// Swapping between built and (possibly) non-built is done with + /// self_type::move (). + template <typename T> + void + swap (self_type& that) YY_NOEXCEPT + { + D2_PARSER__ASSERT (yytypeid_); + D2_PARSER__ASSERT (*yytypeid_ == *that.yytypeid_); + std::swap (as<T> (), that.as<T> ()); + } + + /// Move the content of \a that to this. + /// + /// Destroys \a that. + template <typename T> + void + move (self_type& that) + { +# if 201103L <= YY_CPLUSPLUS + emplace<T> (std::move (that.as<T> ())); +# else + emplace<T> (); + swap<T> (that); +# endif + that.destroy<T> (); + } + +# if 201103L <= YY_CPLUSPLUS + /// Move the content of \a that to this. + template <typename T> + void + move (self_type&& that) + { + emplace<T> (std::move (that.as<T> ())); + that.destroy<T> (); + } +#endif + + /// Copy the content of \a that to this. + template <typename T> + void + copy (const self_type& that) + { + emplace<T> (that.as<T> ()); + } + + /// Destroy the stored \a T. + template <typename T> + void + destroy () + { + as<T> ().~T (); + yytypeid_ = YY_NULLPTR; + } + + private: +#if YY_CPLUSPLUS < 201103L + /// Non copyable. + value_type (const self_type&); + /// Non copyable. + self_type& operator= (const self_type&); +#endif + + /// Accessor to raw memory as \a T. + template <typename T> + T* + yyas_ () YY_NOEXCEPT + { + void *yyp = yyraw_; + return static_cast<T*> (yyp); + } + + /// Const accessor to raw memory as \a T. + template <typename T> + const T* + yyas_ () const YY_NOEXCEPT + { + const void *yyp = yyraw_; + return static_cast<const T*> (yyp); + } + + /// An auxiliary type to compute the largest semantic type. + union union_type + { + // value + // map_value + // ncr_protocol_value + char dummy1[sizeof (ElementPtr)]; + + // "boolean" + char dummy2[sizeof (bool)]; + + // "floating point" + char dummy3[sizeof (double)]; + + // "integer" + char dummy4[sizeof (int64_t)]; + + // "constant string" + char dummy5[sizeof (std::string)]; + }; + + /// The size of the largest semantic type. + enum { size = sizeof (union_type) }; + + /// A buffer to store semantic values. + union + { + /// Strongest alignment constraints. + long double yyalign_me_; + /// A buffer large enough to store any of the semantic values. + char yyraw_[size]; + }; + + /// Whether the content is built: if defined, the name of the stored type. + const std::type_info *yytypeid_; + }; + +#endif + /// Backward compatibility (Bison 3.8). + typedef value_type semantic_type; + + /// Symbol locations. + typedef location location_type; + + /// Syntax errors thrown from user actions. + struct syntax_error : std::runtime_error + { + syntax_error (const location_type& l, const std::string& m) + : std::runtime_error (m) + , location (l) + {} + + syntax_error (const syntax_error& s) + : std::runtime_error (s.what ()) + , location (s.location) + {} + + ~syntax_error () YY_NOEXCEPT YY_NOTHROW; + + location_type location; + }; + + /// Token kinds. + struct token + { + enum token_kind_type + { + TOKEN_D2_PARSER_EMPTY = -2, + TOKEN_END = 0, // "end of file" + TOKEN_D2_PARSER_error = 256, // error + TOKEN_D2_PARSER_UNDEF = 257, // "invalid token" + TOKEN_COMMA = 258, // "," + TOKEN_COLON = 259, // ":" + TOKEN_LSQUARE_BRACKET = 260, // "[" + TOKEN_RSQUARE_BRACKET = 261, // "]" + TOKEN_LCURLY_BRACKET = 262, // "{" + TOKEN_RCURLY_BRACKET = 263, // "}" + TOKEN_NULL_TYPE = 264, // "null" + TOKEN_DHCPDDNS = 265, // "DhcpDdns" + TOKEN_IP_ADDRESS = 266, // "ip-address" + TOKEN_PORT = 267, // "port" + TOKEN_DNS_SERVER_TIMEOUT = 268, // "dns-server-timeout" + TOKEN_NCR_PROTOCOL = 269, // "ncr-protocol" + TOKEN_UDP = 270, // "UDP" + TOKEN_TCP = 271, // "TCP" + TOKEN_NCR_FORMAT = 272, // "ncr-format" + TOKEN_JSON = 273, // "JSON" + TOKEN_USER_CONTEXT = 274, // "user-context" + TOKEN_COMMENT = 275, // "comment" + TOKEN_FORWARD_DDNS = 276, // "forward-ddns" + TOKEN_REVERSE_DDNS = 277, // "reverse-ddns" + TOKEN_DDNS_DOMAINS = 278, // "ddns-domains" + TOKEN_KEY_NAME = 279, // "key-name" + TOKEN_DNS_SERVERS = 280, // "dns-servers" + TOKEN_HOSTNAME = 281, // "hostname" + TOKEN_TSIG_KEYS = 282, // "tsig-keys" + TOKEN_ALGORITHM = 283, // "algorithm" + TOKEN_DIGEST_BITS = 284, // "digest-bits" + TOKEN_SECRET = 285, // "secret" + TOKEN_CONTROL_SOCKET = 286, // "control-socket" + TOKEN_SOCKET_TYPE = 287, // "socket-type" + TOKEN_SOCKET_NAME = 288, // "socket-name" + TOKEN_HOOKS_LIBRARIES = 289, // "hooks-libraries" + TOKEN_LIBRARY = 290, // "library" + TOKEN_PARAMETERS = 291, // "parameters" + TOKEN_LOGGERS = 292, // "loggers" + TOKEN_NAME = 293, // "name" + TOKEN_OUTPUT_OPTIONS = 294, // "output_options" + TOKEN_OUTPUT = 295, // "output" + TOKEN_DEBUGLEVEL = 296, // "debuglevel" + TOKEN_SEVERITY = 297, // "severity" + TOKEN_FLUSH = 298, // "flush" + TOKEN_MAXSIZE = 299, // "maxsize" + TOKEN_MAXVER = 300, // "maxver" + TOKEN_PATTERN = 301, // "pattern" + TOKEN_TOPLEVEL_JSON = 302, // TOPLEVEL_JSON + TOKEN_TOPLEVEL_DHCPDDNS = 303, // TOPLEVEL_DHCPDDNS + TOKEN_SUB_DHCPDDNS = 304, // SUB_DHCPDDNS + TOKEN_SUB_TSIG_KEY = 305, // SUB_TSIG_KEY + TOKEN_SUB_TSIG_KEYS = 306, // SUB_TSIG_KEYS + TOKEN_SUB_DDNS_DOMAIN = 307, // SUB_DDNS_DOMAIN + TOKEN_SUB_DDNS_DOMAINS = 308, // SUB_DDNS_DOMAINS + TOKEN_SUB_DNS_SERVER = 309, // SUB_DNS_SERVER + TOKEN_SUB_DNS_SERVERS = 310, // SUB_DNS_SERVERS + TOKEN_SUB_HOOKS_LIBRARY = 311, // SUB_HOOKS_LIBRARY + TOKEN_STRING = 312, // "constant string" + TOKEN_INTEGER = 313, // "integer" + TOKEN_FLOAT = 314, // "floating point" + TOKEN_BOOLEAN = 315 // "boolean" + }; + /// Backward compatibility alias (Bison 3.6). + typedef token_kind_type yytokentype; + }; + + /// Token kind, as returned by yylex. + typedef token::token_kind_type token_kind_type; + + /// Backward compatibility alias (Bison 3.6). + typedef token_kind_type token_type; + + /// Symbol kinds. + struct symbol_kind + { + enum symbol_kind_type + { + YYNTOKENS = 61, ///< Number of tokens. + S_YYEMPTY = -2, + S_YYEOF = 0, // "end of file" + S_YYerror = 1, // error + S_YYUNDEF = 2, // "invalid token" + S_COMMA = 3, // "," + S_COLON = 4, // ":" + S_LSQUARE_BRACKET = 5, // "[" + S_RSQUARE_BRACKET = 6, // "]" + S_LCURLY_BRACKET = 7, // "{" + S_RCURLY_BRACKET = 8, // "}" + S_NULL_TYPE = 9, // "null" + S_DHCPDDNS = 10, // "DhcpDdns" + S_IP_ADDRESS = 11, // "ip-address" + S_PORT = 12, // "port" + S_DNS_SERVER_TIMEOUT = 13, // "dns-server-timeout" + S_NCR_PROTOCOL = 14, // "ncr-protocol" + S_UDP = 15, // "UDP" + S_TCP = 16, // "TCP" + S_NCR_FORMAT = 17, // "ncr-format" + S_JSON = 18, // "JSON" + S_USER_CONTEXT = 19, // "user-context" + S_COMMENT = 20, // "comment" + S_FORWARD_DDNS = 21, // "forward-ddns" + S_REVERSE_DDNS = 22, // "reverse-ddns" + S_DDNS_DOMAINS = 23, // "ddns-domains" + S_KEY_NAME = 24, // "key-name" + S_DNS_SERVERS = 25, // "dns-servers" + S_HOSTNAME = 26, // "hostname" + S_TSIG_KEYS = 27, // "tsig-keys" + S_ALGORITHM = 28, // "algorithm" + S_DIGEST_BITS = 29, // "digest-bits" + S_SECRET = 30, // "secret" + S_CONTROL_SOCKET = 31, // "control-socket" + S_SOCKET_TYPE = 32, // "socket-type" + S_SOCKET_NAME = 33, // "socket-name" + S_HOOKS_LIBRARIES = 34, // "hooks-libraries" + S_LIBRARY = 35, // "library" + S_PARAMETERS = 36, // "parameters" + S_LOGGERS = 37, // "loggers" + S_NAME = 38, // "name" + S_OUTPUT_OPTIONS = 39, // "output_options" + S_OUTPUT = 40, // "output" + S_DEBUGLEVEL = 41, // "debuglevel" + S_SEVERITY = 42, // "severity" + S_FLUSH = 43, // "flush" + S_MAXSIZE = 44, // "maxsize" + S_MAXVER = 45, // "maxver" + S_PATTERN = 46, // "pattern" + S_TOPLEVEL_JSON = 47, // TOPLEVEL_JSON + S_TOPLEVEL_DHCPDDNS = 48, // TOPLEVEL_DHCPDDNS + S_SUB_DHCPDDNS = 49, // SUB_DHCPDDNS + S_SUB_TSIG_KEY = 50, // SUB_TSIG_KEY + S_SUB_TSIG_KEYS = 51, // SUB_TSIG_KEYS + S_SUB_DDNS_DOMAIN = 52, // SUB_DDNS_DOMAIN + S_SUB_DDNS_DOMAINS = 53, // SUB_DDNS_DOMAINS + S_SUB_DNS_SERVER = 54, // SUB_DNS_SERVER + S_SUB_DNS_SERVERS = 55, // SUB_DNS_SERVERS + S_SUB_HOOKS_LIBRARY = 56, // SUB_HOOKS_LIBRARY + S_STRING = 57, // "constant string" + S_INTEGER = 58, // "integer" + S_FLOAT = 59, // "floating point" + S_BOOLEAN = 60, // "boolean" + S_YYACCEPT = 61, // $accept + S_start = 62, // start + S_63_1 = 63, // $@1 + S_64_2 = 64, // $@2 + S_65_3 = 65, // $@3 + S_66_4 = 66, // $@4 + S_67_5 = 67, // $@5 + S_68_6 = 68, // $@6 + S_69_7 = 69, // $@7 + S_70_8 = 70, // $@8 + S_71_9 = 71, // $@9 + S_72_10 = 72, // $@10 + S_value = 73, // value + S_sub_json = 74, // sub_json + S_map2 = 75, // map2 + S_76_11 = 76, // $@11 + S_map_value = 77, // map_value + S_map_content = 78, // map_content + S_not_empty_map = 79, // not_empty_map + S_list_generic = 80, // list_generic + S_81_12 = 81, // $@12 + S_list_content = 82, // list_content + S_not_empty_list = 83, // not_empty_list + S_unknown_map_entry = 84, // unknown_map_entry + S_syntax_map = 85, // syntax_map + S_86_13 = 86, // $@13 + S_global_object = 87, // global_object + S_88_14 = 88, // $@14 + S_global_object_comma = 89, // global_object_comma + S_sub_dhcpddns = 90, // sub_dhcpddns + S_91_15 = 91, // $@15 + S_dhcpddns_params = 92, // dhcpddns_params + S_dhcpddns_param = 93, // dhcpddns_param + S_ip_address = 94, // ip_address + S_95_16 = 95, // $@16 + S_port = 96, // port + S_dns_server_timeout = 97, // dns_server_timeout + S_ncr_protocol = 98, // ncr_protocol + S_99_17 = 99, // $@17 + S_ncr_protocol_value = 100, // ncr_protocol_value + S_ncr_format = 101, // ncr_format + S_102_18 = 102, // $@18 + S_user_context = 103, // user_context + S_104_19 = 104, // $@19 + S_comment = 105, // comment + S_106_20 = 106, // $@20 + S_forward_ddns = 107, // forward_ddns + S_108_21 = 108, // $@21 + S_reverse_ddns = 109, // reverse_ddns + S_110_22 = 110, // $@22 + S_ddns_mgr_params = 111, // ddns_mgr_params + S_not_empty_ddns_mgr_params = 112, // not_empty_ddns_mgr_params + S_ddns_mgr_param = 113, // ddns_mgr_param + S_ddns_domains = 114, // ddns_domains + S_115_23 = 115, // $@23 + S_sub_ddns_domains = 116, // sub_ddns_domains + S_117_24 = 117, // $@24 + S_ddns_domain_list = 118, // ddns_domain_list + S_not_empty_ddns_domain_list = 119, // not_empty_ddns_domain_list + S_ddns_domain = 120, // ddns_domain + S_121_25 = 121, // $@25 + S_sub_ddns_domain = 122, // sub_ddns_domain + S_123_26 = 123, // $@26 + S_ddns_domain_params = 124, // ddns_domain_params + S_ddns_domain_param = 125, // ddns_domain_param + S_ddns_domain_name = 126, // ddns_domain_name + S_127_27 = 127, // $@27 + S_ddns_key_name = 128, // ddns_key_name + S_129_28 = 129, // $@28 + S_dns_servers = 130, // dns_servers + S_131_29 = 131, // $@29 + S_sub_dns_servers = 132, // sub_dns_servers + S_133_30 = 133, // $@30 + S_dns_server_list = 134, // dns_server_list + S_dns_server = 135, // dns_server + S_136_31 = 136, // $@31 + S_sub_dns_server = 137, // sub_dns_server + S_138_32 = 138, // $@32 + S_dns_server_params = 139, // dns_server_params + S_dns_server_param = 140, // dns_server_param + S_dns_server_hostname = 141, // dns_server_hostname + S_142_33 = 142, // $@33 + S_dns_server_ip_address = 143, // dns_server_ip_address + S_144_34 = 144, // $@34 + S_dns_server_port = 145, // dns_server_port + S_tsig_keys = 146, // tsig_keys + S_147_35 = 147, // $@35 + S_sub_tsig_keys = 148, // sub_tsig_keys + S_149_36 = 149, // $@36 + S_tsig_keys_list = 150, // tsig_keys_list + S_not_empty_tsig_keys_list = 151, // not_empty_tsig_keys_list + S_tsig_key = 152, // tsig_key + S_153_37 = 153, // $@37 + S_sub_tsig_key = 154, // sub_tsig_key + S_155_38 = 155, // $@38 + S_tsig_key_params = 156, // tsig_key_params + S_tsig_key_param = 157, // tsig_key_param + S_tsig_key_name = 158, // tsig_key_name + S_159_39 = 159, // $@39 + S_tsig_key_algorithm = 160, // tsig_key_algorithm + S_161_40 = 161, // $@40 + S_tsig_key_digest_bits = 162, // tsig_key_digest_bits + S_tsig_key_secret = 163, // tsig_key_secret + S_164_41 = 164, // $@41 + S_control_socket = 165, // control_socket + S_166_42 = 166, // $@42 + S_control_socket_params = 167, // control_socket_params + S_control_socket_param = 168, // control_socket_param + S_control_socket_type = 169, // control_socket_type + S_170_43 = 170, // $@43 + S_control_socket_name = 171, // control_socket_name + S_172_44 = 172, // $@44 + S_hooks_libraries = 173, // hooks_libraries + S_174_45 = 174, // $@45 + S_hooks_libraries_list = 175, // hooks_libraries_list + S_not_empty_hooks_libraries_list = 176, // not_empty_hooks_libraries_list + S_hooks_library = 177, // hooks_library + S_178_46 = 178, // $@46 + S_sub_hooks_library = 179, // sub_hooks_library + S_180_47 = 180, // $@47 + S_hooks_params = 181, // hooks_params + S_hooks_param = 182, // hooks_param + S_library = 183, // library + S_184_48 = 184, // $@48 + S_parameters = 185, // parameters + S_186_49 = 186, // $@49 + S_loggers = 187, // loggers + S_188_50 = 188, // $@50 + S_loggers_entries = 189, // loggers_entries + S_logger_entry = 190, // logger_entry + S_191_51 = 191, // $@51 + S_logger_params = 192, // logger_params + S_logger_param = 193, // logger_param + S_name = 194, // name + S_195_52 = 195, // $@52 + S_debuglevel = 196, // debuglevel + S_severity = 197, // severity + S_198_53 = 198, // $@53 + S_output_options_list = 199, // output_options_list + S_200_54 = 200, // $@54 + S_output_options_list_content = 201, // output_options_list_content + S_output_entry = 202, // output_entry + S_203_55 = 203, // $@55 + S_output_params_list = 204, // output_params_list + S_output_params = 205, // output_params + S_output = 206, // output + S_207_56 = 207, // $@56 + S_flush = 208, // flush + S_maxsize = 209, // maxsize + S_maxver = 210, // maxver + S_pattern = 211, // pattern + S_212_57 = 212 // $@57 + }; + }; + + /// (Internal) symbol kind. + typedef symbol_kind::symbol_kind_type symbol_kind_type; + + /// The number of tokens. + static const symbol_kind_type YYNTOKENS = symbol_kind::YYNTOKENS; + + /// A complete symbol. + /// + /// Expects its Base type to provide access to the symbol kind + /// via kind (). + /// + /// Provide access to semantic value and location. + template <typename Base> + struct basic_symbol : Base + { + /// Alias to Base. + typedef Base super_type; + + /// Default constructor. + basic_symbol () YY_NOEXCEPT + : value () + , location () + {} + +#if 201103L <= YY_CPLUSPLUS + /// Move constructor. + basic_symbol (basic_symbol&& that) + : Base (std::move (that)) + , value () + , location (std::move (that.location)) + { + switch (this->kind ()) + { + case symbol_kind::S_value: // value + case symbol_kind::S_map_value: // map_value + case symbol_kind::S_ncr_protocol_value: // ncr_protocol_value + value.move< ElementPtr > (std::move (that.value)); + break; + + case symbol_kind::S_BOOLEAN: // "boolean" + value.move< bool > (std::move (that.value)); + break; + + case symbol_kind::S_FLOAT: // "floating point" + value.move< double > (std::move (that.value)); + break; + + case symbol_kind::S_INTEGER: // "integer" + value.move< int64_t > (std::move (that.value)); + break; + + case symbol_kind::S_STRING: // "constant string" + value.move< std::string > (std::move (that.value)); + break; + + default: + break; + } + + } +#endif + + /// Copy constructor. + basic_symbol (const basic_symbol& that); + + /// Constructors for typed symbols. +#if 201103L <= YY_CPLUSPLUS + basic_symbol (typename Base::kind_type t, location_type&& l) + : Base (t) + , location (std::move (l)) + {} +#else + basic_symbol (typename Base::kind_type t, const location_type& l) + : Base (t) + , location (l) + {} +#endif + +#if 201103L <= YY_CPLUSPLUS + basic_symbol (typename Base::kind_type t, ElementPtr&& v, location_type&& l) + : Base (t) + , value (std::move (v)) + , location (std::move (l)) + {} +#else + basic_symbol (typename Base::kind_type t, const ElementPtr& v, const location_type& l) + : Base (t) + , value (v) + , location (l) + {} +#endif + +#if 201103L <= YY_CPLUSPLUS + basic_symbol (typename Base::kind_type t, bool&& v, location_type&& l) + : Base (t) + , value (std::move (v)) + , location (std::move (l)) + {} +#else + basic_symbol (typename Base::kind_type t, const bool& v, const location_type& l) + : Base (t) + , value (v) + , location (l) + {} +#endif + +#if 201103L <= YY_CPLUSPLUS + basic_symbol (typename Base::kind_type t, double&& v, location_type&& l) + : Base (t) + , value (std::move (v)) + , location (std::move (l)) + {} +#else + basic_symbol (typename Base::kind_type t, const double& v, const location_type& l) + : Base (t) + , value (v) + , location (l) + {} +#endif + +#if 201103L <= YY_CPLUSPLUS + basic_symbol (typename Base::kind_type t, int64_t&& v, location_type&& l) + : Base (t) + , value (std::move (v)) + , location (std::move (l)) + {} +#else + basic_symbol (typename Base::kind_type t, const int64_t& v, const location_type& l) + : Base (t) + , value (v) + , location (l) + {} +#endif + +#if 201103L <= YY_CPLUSPLUS + basic_symbol (typename Base::kind_type t, std::string&& v, location_type&& l) + : Base (t) + , value (std::move (v)) + , location (std::move (l)) + {} +#else + basic_symbol (typename Base::kind_type t, const std::string& v, const location_type& l) + : Base (t) + , value (v) + , location (l) + {} +#endif + + /// Destroy the symbol. + ~basic_symbol () + { + clear (); + } + + + + /// Destroy contents, and record that is empty. + void clear () YY_NOEXCEPT + { + // User destructor. + symbol_kind_type yykind = this->kind (); + basic_symbol<Base>& yysym = *this; + (void) yysym; + switch (yykind) + { + default: + break; + } + + // Value type destructor. +switch (yykind) + { + case symbol_kind::S_value: // value + case symbol_kind::S_map_value: // map_value + case symbol_kind::S_ncr_protocol_value: // ncr_protocol_value + value.template destroy< ElementPtr > (); + break; + + case symbol_kind::S_BOOLEAN: // "boolean" + value.template destroy< bool > (); + break; + + case symbol_kind::S_FLOAT: // "floating point" + value.template destroy< double > (); + break; + + case symbol_kind::S_INTEGER: // "integer" + value.template destroy< int64_t > (); + break; + + case symbol_kind::S_STRING: // "constant string" + value.template destroy< std::string > (); + break; + + default: + break; + } + + Base::clear (); + } + + /// The user-facing name of this symbol. + std::string name () const YY_NOEXCEPT + { + return D2Parser::symbol_name (this->kind ()); + } + + /// Backward compatibility (Bison 3.6). + symbol_kind_type type_get () const YY_NOEXCEPT; + + /// Whether empty. + bool empty () const YY_NOEXCEPT; + + /// Destructive move, \a s is emptied into this. + void move (basic_symbol& s); + + /// The semantic value. + value_type value; + + /// The location. + location_type location; + + private: +#if YY_CPLUSPLUS < 201103L + /// Assignment operator. + basic_symbol& operator= (const basic_symbol& that); +#endif + }; + + /// Type access provider for token (enum) based symbols. + struct by_kind + { + /// The symbol kind as needed by the constructor. + typedef token_kind_type kind_type; + + /// Default constructor. + by_kind () YY_NOEXCEPT; + +#if 201103L <= YY_CPLUSPLUS + /// Move constructor. + by_kind (by_kind&& that) YY_NOEXCEPT; +#endif + + /// Copy constructor. + by_kind (const by_kind& that) YY_NOEXCEPT; + + /// Constructor from (external) token numbers. + by_kind (kind_type t) YY_NOEXCEPT; + + + + /// Record that this symbol is empty. + void clear () YY_NOEXCEPT; + + /// Steal the symbol kind from \a that. + void move (by_kind& that); + + /// The (internal) type number (corresponding to \a type). + /// \a empty when empty. + symbol_kind_type kind () const YY_NOEXCEPT; + + /// Backward compatibility (Bison 3.6). + symbol_kind_type type_get () const YY_NOEXCEPT; + + /// The symbol kind. + /// \a S_YYEMPTY when empty. + symbol_kind_type kind_; + }; + + /// Backward compatibility for a private implementation detail (Bison 3.6). + typedef by_kind by_type; + + /// "External" symbols: returned by the scanner. + struct symbol_type : basic_symbol<by_kind> + { + /// Superclass. + typedef basic_symbol<by_kind> super_type; + + /// Empty symbol. + symbol_type () YY_NOEXCEPT {} + + /// Constructor for valueless symbols, and symbols from each type. +#if 201103L <= YY_CPLUSPLUS + symbol_type (int tok, location_type l) + : super_type (token_kind_type (tok), std::move (l)) +#else + symbol_type (int tok, const location_type& l) + : super_type (token_kind_type (tok), l) +#endif + { +#if !defined _MSC_VER || defined __clang__ + D2_PARSER__ASSERT (tok == token::TOKEN_END + || (token::TOKEN_D2_PARSER_error <= tok && tok <= token::TOKEN_SUB_HOOKS_LIBRARY)); +#endif + } +#if 201103L <= YY_CPLUSPLUS + symbol_type (int tok, bool v, location_type l) + : super_type (token_kind_type (tok), std::move (v), std::move (l)) +#else + symbol_type (int tok, const bool& v, const location_type& l) + : super_type (token_kind_type (tok), v, l) +#endif + { +#if !defined _MSC_VER || defined __clang__ + D2_PARSER__ASSERT (tok == token::TOKEN_BOOLEAN); +#endif + } +#if 201103L <= YY_CPLUSPLUS + symbol_type (int tok, double v, location_type l) + : super_type (token_kind_type (tok), std::move (v), std::move (l)) +#else + symbol_type (int tok, const double& v, const location_type& l) + : super_type (token_kind_type (tok), v, l) +#endif + { +#if !defined _MSC_VER || defined __clang__ + D2_PARSER__ASSERT (tok == token::TOKEN_FLOAT); +#endif + } +#if 201103L <= YY_CPLUSPLUS + symbol_type (int tok, int64_t v, location_type l) + : super_type (token_kind_type (tok), std::move (v), std::move (l)) +#else + symbol_type (int tok, const int64_t& v, const location_type& l) + : super_type (token_kind_type (tok), v, l) +#endif + { +#if !defined _MSC_VER || defined __clang__ + D2_PARSER__ASSERT (tok == token::TOKEN_INTEGER); +#endif + } +#if 201103L <= YY_CPLUSPLUS + symbol_type (int tok, std::string v, location_type l) + : super_type (token_kind_type (tok), std::move (v), std::move (l)) +#else + symbol_type (int tok, const std::string& v, const location_type& l) + : super_type (token_kind_type (tok), v, l) +#endif + { +#if !defined _MSC_VER || defined __clang__ + D2_PARSER__ASSERT (tok == token::TOKEN_STRING); +#endif + } + }; + + /// Build a parser object. + D2Parser (isc::d2::D2ParserContext& ctx_yyarg); + virtual ~D2Parser (); + +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + D2Parser (const D2Parser&) = delete; + /// Non copyable. + D2Parser& operator= (const D2Parser&) = delete; +#endif + + /// Parse. An alias for parse (). + /// \returns 0 iff parsing succeeded. + int operator() (); + + /// Parse. + /// \returns 0 iff parsing succeeded. + virtual int parse (); + +#if D2_PARSER_DEBUG + /// The current debugging stream. + std::ostream& debug_stream () const YY_ATTRIBUTE_PURE; + /// Set the current debugging stream. + void set_debug_stream (std::ostream &); + + /// Type for debugging levels. + typedef int debug_level_type; + /// The current debugging level. + debug_level_type debug_level () const YY_ATTRIBUTE_PURE; + /// Set the current debugging level. + void set_debug_level (debug_level_type l); +#endif + + /// Report a syntax error. + /// \param loc where the syntax error is found. + /// \param msg a description of the syntax error. + virtual void error (const location_type& loc, const std::string& msg); + + /// Report a syntax error. + void error (const syntax_error& err); + + /// The user-facing name of the symbol whose (internal) number is + /// YYSYMBOL. No bounds checking. + static std::string symbol_name (symbol_kind_type yysymbol); + + // Implementation of make_symbol for each token kind. +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_END (location_type l) + { + return symbol_type (token::TOKEN_END, std::move (l)); + } +#else + static + symbol_type + make_END (const location_type& l) + { + return symbol_type (token::TOKEN_END, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_D2_PARSER_error (location_type l) + { + return symbol_type (token::TOKEN_D2_PARSER_error, std::move (l)); + } +#else + static + symbol_type + make_D2_PARSER_error (const location_type& l) + { + return symbol_type (token::TOKEN_D2_PARSER_error, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_D2_PARSER_UNDEF (location_type l) + { + return symbol_type (token::TOKEN_D2_PARSER_UNDEF, std::move (l)); + } +#else + static + symbol_type + make_D2_PARSER_UNDEF (const location_type& l) + { + return symbol_type (token::TOKEN_D2_PARSER_UNDEF, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_COMMA (location_type l) + { + return symbol_type (token::TOKEN_COMMA, std::move (l)); + } +#else + static + symbol_type + make_COMMA (const location_type& l) + { + return symbol_type (token::TOKEN_COMMA, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_COLON (location_type l) + { + return symbol_type (token::TOKEN_COLON, std::move (l)); + } +#else + static + symbol_type + make_COLON (const location_type& l) + { + return symbol_type (token::TOKEN_COLON, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LSQUARE_BRACKET (location_type l) + { + return symbol_type (token::TOKEN_LSQUARE_BRACKET, std::move (l)); + } +#else + static + symbol_type + make_LSQUARE_BRACKET (const location_type& l) + { + return symbol_type (token::TOKEN_LSQUARE_BRACKET, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_RSQUARE_BRACKET (location_type l) + { + return symbol_type (token::TOKEN_RSQUARE_BRACKET, std::move (l)); + } +#else + static + symbol_type + make_RSQUARE_BRACKET (const location_type& l) + { + return symbol_type (token::TOKEN_RSQUARE_BRACKET, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LCURLY_BRACKET (location_type l) + { + return symbol_type (token::TOKEN_LCURLY_BRACKET, std::move (l)); + } +#else + static + symbol_type + make_LCURLY_BRACKET (const location_type& l) + { + return symbol_type (token::TOKEN_LCURLY_BRACKET, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_RCURLY_BRACKET (location_type l) + { + return symbol_type (token::TOKEN_RCURLY_BRACKET, std::move (l)); + } +#else + static + symbol_type + make_RCURLY_BRACKET (const location_type& l) + { + return symbol_type (token::TOKEN_RCURLY_BRACKET, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NULL_TYPE (location_type l) + { + return symbol_type (token::TOKEN_NULL_TYPE, std::move (l)); + } +#else + static + symbol_type + make_NULL_TYPE (const location_type& l) + { + return symbol_type (token::TOKEN_NULL_TYPE, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_DHCPDDNS (location_type l) + { + return symbol_type (token::TOKEN_DHCPDDNS, std::move (l)); + } +#else + static + symbol_type + make_DHCPDDNS (const location_type& l) + { + return symbol_type (token::TOKEN_DHCPDDNS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_IP_ADDRESS (location_type l) + { + return symbol_type (token::TOKEN_IP_ADDRESS, std::move (l)); + } +#else + static + symbol_type + make_IP_ADDRESS (const location_type& l) + { + return symbol_type (token::TOKEN_IP_ADDRESS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_PORT (location_type l) + { + return symbol_type (token::TOKEN_PORT, std::move (l)); + } +#else + static + symbol_type + make_PORT (const location_type& l) + { + return symbol_type (token::TOKEN_PORT, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_DNS_SERVER_TIMEOUT (location_type l) + { + return symbol_type (token::TOKEN_DNS_SERVER_TIMEOUT, std::move (l)); + } +#else + static + symbol_type + make_DNS_SERVER_TIMEOUT (const location_type& l) + { + return symbol_type (token::TOKEN_DNS_SERVER_TIMEOUT, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NCR_PROTOCOL (location_type l) + { + return symbol_type (token::TOKEN_NCR_PROTOCOL, std::move (l)); + } +#else + static + symbol_type + make_NCR_PROTOCOL (const location_type& l) + { + return symbol_type (token::TOKEN_NCR_PROTOCOL, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_UDP (location_type l) + { + return symbol_type (token::TOKEN_UDP, std::move (l)); + } +#else + static + symbol_type + make_UDP (const location_type& l) + { + return symbol_type (token::TOKEN_UDP, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_TCP (location_type l) + { + return symbol_type (token::TOKEN_TCP, std::move (l)); + } +#else + static + symbol_type + make_TCP (const location_type& l) + { + return symbol_type (token::TOKEN_TCP, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NCR_FORMAT (location_type l) + { + return symbol_type (token::TOKEN_NCR_FORMAT, std::move (l)); + } +#else + static + symbol_type + make_NCR_FORMAT (const location_type& l) + { + return symbol_type (token::TOKEN_NCR_FORMAT, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_JSON (location_type l) + { + return symbol_type (token::TOKEN_JSON, std::move (l)); + } +#else + static + symbol_type + make_JSON (const location_type& l) + { + return symbol_type (token::TOKEN_JSON, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_USER_CONTEXT (location_type l) + { + return symbol_type (token::TOKEN_USER_CONTEXT, std::move (l)); + } +#else + static + symbol_type + make_USER_CONTEXT (const location_type& l) + { + return symbol_type (token::TOKEN_USER_CONTEXT, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_COMMENT (location_type l) + { + return symbol_type (token::TOKEN_COMMENT, std::move (l)); + } +#else + static + symbol_type + make_COMMENT (const location_type& l) + { + return symbol_type (token::TOKEN_COMMENT, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_FORWARD_DDNS (location_type l) + { + return symbol_type (token::TOKEN_FORWARD_DDNS, std::move (l)); + } +#else + static + symbol_type + make_FORWARD_DDNS (const location_type& l) + { + return symbol_type (token::TOKEN_FORWARD_DDNS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_REVERSE_DDNS (location_type l) + { + return symbol_type (token::TOKEN_REVERSE_DDNS, std::move (l)); + } +#else + static + symbol_type + make_REVERSE_DDNS (const location_type& l) + { + return symbol_type (token::TOKEN_REVERSE_DDNS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_DDNS_DOMAINS (location_type l) + { + return symbol_type (token::TOKEN_DDNS_DOMAINS, std::move (l)); + } +#else + static + symbol_type + make_DDNS_DOMAINS (const location_type& l) + { + return symbol_type (token::TOKEN_DDNS_DOMAINS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_KEY_NAME (location_type l) + { + return symbol_type (token::TOKEN_KEY_NAME, std::move (l)); + } +#else + static + symbol_type + make_KEY_NAME (const location_type& l) + { + return symbol_type (token::TOKEN_KEY_NAME, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_DNS_SERVERS (location_type l) + { + return symbol_type (token::TOKEN_DNS_SERVERS, std::move (l)); + } +#else + static + symbol_type + make_DNS_SERVERS (const location_type& l) + { + return symbol_type (token::TOKEN_DNS_SERVERS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_HOSTNAME (location_type l) + { + return symbol_type (token::TOKEN_HOSTNAME, std::move (l)); + } +#else + static + symbol_type + make_HOSTNAME (const location_type& l) + { + return symbol_type (token::TOKEN_HOSTNAME, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_TSIG_KEYS (location_type l) + { + return symbol_type (token::TOKEN_TSIG_KEYS, std::move (l)); + } +#else + static + symbol_type + make_TSIG_KEYS (const location_type& l) + { + return symbol_type (token::TOKEN_TSIG_KEYS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_ALGORITHM (location_type l) + { + return symbol_type (token::TOKEN_ALGORITHM, std::move (l)); + } +#else + static + symbol_type + make_ALGORITHM (const location_type& l) + { + return symbol_type (token::TOKEN_ALGORITHM, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_DIGEST_BITS (location_type l) + { + return symbol_type (token::TOKEN_DIGEST_BITS, std::move (l)); + } +#else + static + symbol_type + make_DIGEST_BITS (const location_type& l) + { + return symbol_type (token::TOKEN_DIGEST_BITS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SECRET (location_type l) + { + return symbol_type (token::TOKEN_SECRET, std::move (l)); + } +#else + static + symbol_type + make_SECRET (const location_type& l) + { + return symbol_type (token::TOKEN_SECRET, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_CONTROL_SOCKET (location_type l) + { + return symbol_type (token::TOKEN_CONTROL_SOCKET, std::move (l)); + } +#else + static + symbol_type + make_CONTROL_SOCKET (const location_type& l) + { + return symbol_type (token::TOKEN_CONTROL_SOCKET, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SOCKET_TYPE (location_type l) + { + return symbol_type (token::TOKEN_SOCKET_TYPE, std::move (l)); + } +#else + static + symbol_type + make_SOCKET_TYPE (const location_type& l) + { + return symbol_type (token::TOKEN_SOCKET_TYPE, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SOCKET_NAME (location_type l) + { + return symbol_type (token::TOKEN_SOCKET_NAME, std::move (l)); + } +#else + static + symbol_type + make_SOCKET_NAME (const location_type& l) + { + return symbol_type (token::TOKEN_SOCKET_NAME, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_HOOKS_LIBRARIES (location_type l) + { + return symbol_type (token::TOKEN_HOOKS_LIBRARIES, std::move (l)); + } +#else + static + symbol_type + make_HOOKS_LIBRARIES (const location_type& l) + { + return symbol_type (token::TOKEN_HOOKS_LIBRARIES, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LIBRARY (location_type l) + { + return symbol_type (token::TOKEN_LIBRARY, std::move (l)); + } +#else + static + symbol_type + make_LIBRARY (const location_type& l) + { + return symbol_type (token::TOKEN_LIBRARY, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_PARAMETERS (location_type l) + { + return symbol_type (token::TOKEN_PARAMETERS, std::move (l)); + } +#else + static + symbol_type + make_PARAMETERS (const location_type& l) + { + return symbol_type (token::TOKEN_PARAMETERS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_LOGGERS (location_type l) + { + return symbol_type (token::TOKEN_LOGGERS, std::move (l)); + } +#else + static + symbol_type + make_LOGGERS (const location_type& l) + { + return symbol_type (token::TOKEN_LOGGERS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_NAME (location_type l) + { + return symbol_type (token::TOKEN_NAME, std::move (l)); + } +#else + static + symbol_type + make_NAME (const location_type& l) + { + return symbol_type (token::TOKEN_NAME, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_OUTPUT_OPTIONS (location_type l) + { + return symbol_type (token::TOKEN_OUTPUT_OPTIONS, std::move (l)); + } +#else + static + symbol_type + make_OUTPUT_OPTIONS (const location_type& l) + { + return symbol_type (token::TOKEN_OUTPUT_OPTIONS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_OUTPUT (location_type l) + { + return symbol_type (token::TOKEN_OUTPUT, std::move (l)); + } +#else + static + symbol_type + make_OUTPUT (const location_type& l) + { + return symbol_type (token::TOKEN_OUTPUT, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_DEBUGLEVEL (location_type l) + { + return symbol_type (token::TOKEN_DEBUGLEVEL, std::move (l)); + } +#else + static + symbol_type + make_DEBUGLEVEL (const location_type& l) + { + return symbol_type (token::TOKEN_DEBUGLEVEL, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SEVERITY (location_type l) + { + return symbol_type (token::TOKEN_SEVERITY, std::move (l)); + } +#else + static + symbol_type + make_SEVERITY (const location_type& l) + { + return symbol_type (token::TOKEN_SEVERITY, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_FLUSH (location_type l) + { + return symbol_type (token::TOKEN_FLUSH, std::move (l)); + } +#else + static + symbol_type + make_FLUSH (const location_type& l) + { + return symbol_type (token::TOKEN_FLUSH, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_MAXSIZE (location_type l) + { + return symbol_type (token::TOKEN_MAXSIZE, std::move (l)); + } +#else + static + symbol_type + make_MAXSIZE (const location_type& l) + { + return symbol_type (token::TOKEN_MAXSIZE, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_MAXVER (location_type l) + { + return symbol_type (token::TOKEN_MAXVER, std::move (l)); + } +#else + static + symbol_type + make_MAXVER (const location_type& l) + { + return symbol_type (token::TOKEN_MAXVER, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_PATTERN (location_type l) + { + return symbol_type (token::TOKEN_PATTERN, std::move (l)); + } +#else + static + symbol_type + make_PATTERN (const location_type& l) + { + return symbol_type (token::TOKEN_PATTERN, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_TOPLEVEL_JSON (location_type l) + { + return symbol_type (token::TOKEN_TOPLEVEL_JSON, std::move (l)); + } +#else + static + symbol_type + make_TOPLEVEL_JSON (const location_type& l) + { + return symbol_type (token::TOKEN_TOPLEVEL_JSON, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_TOPLEVEL_DHCPDDNS (location_type l) + { + return symbol_type (token::TOKEN_TOPLEVEL_DHCPDDNS, std::move (l)); + } +#else + static + symbol_type + make_TOPLEVEL_DHCPDDNS (const location_type& l) + { + return symbol_type (token::TOKEN_TOPLEVEL_DHCPDDNS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SUB_DHCPDDNS (location_type l) + { + return symbol_type (token::TOKEN_SUB_DHCPDDNS, std::move (l)); + } +#else + static + symbol_type + make_SUB_DHCPDDNS (const location_type& l) + { + return symbol_type (token::TOKEN_SUB_DHCPDDNS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SUB_TSIG_KEY (location_type l) + { + return symbol_type (token::TOKEN_SUB_TSIG_KEY, std::move (l)); + } +#else + static + symbol_type + make_SUB_TSIG_KEY (const location_type& l) + { + return symbol_type (token::TOKEN_SUB_TSIG_KEY, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SUB_TSIG_KEYS (location_type l) + { + return symbol_type (token::TOKEN_SUB_TSIG_KEYS, std::move (l)); + } +#else + static + symbol_type + make_SUB_TSIG_KEYS (const location_type& l) + { + return symbol_type (token::TOKEN_SUB_TSIG_KEYS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SUB_DDNS_DOMAIN (location_type l) + { + return symbol_type (token::TOKEN_SUB_DDNS_DOMAIN, std::move (l)); + } +#else + static + symbol_type + make_SUB_DDNS_DOMAIN (const location_type& l) + { + return symbol_type (token::TOKEN_SUB_DDNS_DOMAIN, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SUB_DDNS_DOMAINS (location_type l) + { + return symbol_type (token::TOKEN_SUB_DDNS_DOMAINS, std::move (l)); + } +#else + static + symbol_type + make_SUB_DDNS_DOMAINS (const location_type& l) + { + return symbol_type (token::TOKEN_SUB_DDNS_DOMAINS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SUB_DNS_SERVER (location_type l) + { + return symbol_type (token::TOKEN_SUB_DNS_SERVER, std::move (l)); + } +#else + static + symbol_type + make_SUB_DNS_SERVER (const location_type& l) + { + return symbol_type (token::TOKEN_SUB_DNS_SERVER, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SUB_DNS_SERVERS (location_type l) + { + return symbol_type (token::TOKEN_SUB_DNS_SERVERS, std::move (l)); + } +#else + static + symbol_type + make_SUB_DNS_SERVERS (const location_type& l) + { + return symbol_type (token::TOKEN_SUB_DNS_SERVERS, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_SUB_HOOKS_LIBRARY (location_type l) + { + return symbol_type (token::TOKEN_SUB_HOOKS_LIBRARY, std::move (l)); + } +#else + static + symbol_type + make_SUB_HOOKS_LIBRARY (const location_type& l) + { + return symbol_type (token::TOKEN_SUB_HOOKS_LIBRARY, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_STRING (std::string v, location_type l) + { + return symbol_type (token::TOKEN_STRING, std::move (v), std::move (l)); + } +#else + static + symbol_type + make_STRING (const std::string& v, const location_type& l) + { + return symbol_type (token::TOKEN_STRING, v, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_INTEGER (int64_t v, location_type l) + { + return symbol_type (token::TOKEN_INTEGER, std::move (v), std::move (l)); + } +#else + static + symbol_type + make_INTEGER (const int64_t& v, const location_type& l) + { + return symbol_type (token::TOKEN_INTEGER, v, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_FLOAT (double v, location_type l) + { + return symbol_type (token::TOKEN_FLOAT, std::move (v), std::move (l)); + } +#else + static + symbol_type + make_FLOAT (const double& v, const location_type& l) + { + return symbol_type (token::TOKEN_FLOAT, v, l); + } +#endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_BOOLEAN (bool v, location_type l) + { + return symbol_type (token::TOKEN_BOOLEAN, std::move (v), std::move (l)); + } +#else + static + symbol_type + make_BOOLEAN (const bool& v, const location_type& l) + { + return symbol_type (token::TOKEN_BOOLEAN, v, l); + } +#endif + + + class context + { + public: + context (const D2Parser& yyparser, const symbol_type& yyla); + const symbol_type& lookahead () const YY_NOEXCEPT { return yyla_; } + symbol_kind_type token () const YY_NOEXCEPT { return yyla_.kind (); } + const location_type& location () const YY_NOEXCEPT { return yyla_.location; } + + /// Put in YYARG at most YYARGN of the expected tokens, and return the + /// number of tokens stored in YYARG. If YYARG is null, return the + /// number of expected tokens (guaranteed to be less than YYNTOKENS). + int expected_tokens (symbol_kind_type yyarg[], int yyargn) const; + + private: + const D2Parser& yyparser_; + const symbol_type& yyla_; + }; + + private: +#if YY_CPLUSPLUS < 201103L + /// Non copyable. + D2Parser (const D2Parser&); + /// Non copyable. + D2Parser& operator= (const D2Parser&); +#endif + + + /// Stored state numbers (used for stacks). + typedef short state_type; + + /// The arguments of the error message. + int yy_syntax_error_arguments_ (const context& yyctx, + symbol_kind_type yyarg[], int yyargn) const; + + /// Generate an error message. + /// \param yyctx the context in which the error occurred. + virtual std::string yysyntax_error_ (const context& yyctx) const; + /// Compute post-reduction state. + /// \param yystate the current state + /// \param yysym the nonterminal to push on the stack + static state_type yy_lr_goto_state_ (state_type yystate, int yysym); + + /// Whether the given \c yypact_ value indicates a defaulted state. + /// \param yyvalue the value to check + static bool yy_pact_value_is_default_ (int yyvalue) YY_NOEXCEPT; + + /// Whether the given \c yytable_ value indicates a syntax error. + /// \param yyvalue the value to check + static bool yy_table_value_is_error_ (int yyvalue) YY_NOEXCEPT; + + static const short yypact_ninf_; + static const signed char yytable_ninf_; + + /// Convert a scanner token kind \a t to a symbol kind. + /// In theory \a t should be a token_kind_type, but character literals + /// are valid, yet not members of the token_kind_type enum. + static symbol_kind_type yytranslate_ (int t) YY_NOEXCEPT; + + /// Convert the symbol name \a n to a form suitable for a diagnostic. + static std::string yytnamerr_ (const char *yystr); + + /// For a symbol, its name in clear. + static const char* const yytname_[]; + + + // Tables. + // YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + // STATE-NUM. + static const short yypact_[]; + + // YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + // Performed when YYTABLE does not specify something else to do. Zero + // means the default is an error. + static const short yydefact_[]; + + // YYPGOTO[NTERM-NUM]. + static const short yypgoto_[]; + + // YYDEFGOTO[NTERM-NUM]. + static const short yydefgoto_[]; + + // YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + // positive, shift that token. If negative, reduce the rule whose + // number is the opposite. If YYTABLE_NINF, syntax error. + static const short yytable_[]; + + static const short yycheck_[]; + + // YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of + // state STATE-NUM. + static const unsigned char yystos_[]; + + // YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. + static const unsigned char yyr1_[]; + + // YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. + static const signed char yyr2_[]; + + +#if D2_PARSER_DEBUG + // YYRLINE[YYN] -- Source line where rule number YYN was defined. + static const short yyrline_[]; + /// Report on the debug stream that the rule \a r is going to be reduced. + virtual void yy_reduce_print_ (int r) const; + /// Print the state stack on the debug stream. + virtual void yy_stack_print_ () const; + + /// Debugging level. + int yydebug_; + /// Debug stream. + std::ostream* yycdebug_; + + /// \brief Display a symbol kind, value and location. + /// \param yyo The output stream. + /// \param yysym The symbol. + template <typename Base> + void yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const; +#endif + + /// \brief Reclaim the memory associated to a symbol. + /// \param yymsg Why this token is reclaimed. + /// If null, print nothing. + /// \param yysym The symbol. + template <typename Base> + void yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const; + + private: + /// Type access provider for state based symbols. + struct by_state + { + /// Default constructor. + by_state () YY_NOEXCEPT; + + /// The symbol kind as needed by the constructor. + typedef state_type kind_type; + + /// Constructor. + by_state (kind_type s) YY_NOEXCEPT; + + /// Copy constructor. + by_state (const by_state& that) YY_NOEXCEPT; + + /// Record that this symbol is empty. + void clear () YY_NOEXCEPT; + + /// Steal the symbol kind from \a that. + void move (by_state& that); + + /// The symbol kind (corresponding to \a state). + /// \a symbol_kind::S_YYEMPTY when empty. + symbol_kind_type kind () const YY_NOEXCEPT; + + /// The state number used to denote an empty symbol. + /// We use the initial state, as it does not have a value. + enum { empty_state = 0 }; + + /// The state. + /// \a empty when empty. + state_type state; + }; + + /// "Internal" symbol: element of the stack. + struct stack_symbol_type : basic_symbol<by_state> + { + /// Superclass. + typedef basic_symbol<by_state> super_type; + /// Construct an empty symbol. + stack_symbol_type (); + /// Move or copy construction. + stack_symbol_type (YY_RVREF (stack_symbol_type) that); + /// Steal the contents from \a sym to build this. + stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) sym); +#if YY_CPLUSPLUS < 201103L + /// Assignment, needed by push_back by some old implementations. + /// Moves the contents of that. + stack_symbol_type& operator= (stack_symbol_type& that); + + /// Assignment, needed by push_back by other implementations. + /// Needed by some other old implementations. + stack_symbol_type& operator= (const stack_symbol_type& that); +#endif + }; + + /// A stack with random access from its top. + template <typename T, typename S = std::vector<T> > + class stack + { + public: + // Hide our reversed order. + typedef typename S::iterator iterator; + typedef typename S::const_iterator const_iterator; + typedef typename S::size_type size_type; + typedef typename std::ptrdiff_t index_type; + + stack (size_type n = 200) YY_NOEXCEPT + : seq_ (n) + {} + +#if 201103L <= YY_CPLUSPLUS + /// Non copyable. + stack (const stack&) = delete; + /// Non copyable. + stack& operator= (const stack&) = delete; +#endif + + /// Random access. + /// + /// Index 0 returns the topmost element. + const T& + operator[] (index_type i) const + { + return seq_[size_type (size () - 1 - i)]; + } + + /// Random access. + /// + /// Index 0 returns the topmost element. + T& + operator[] (index_type i) + { + return seq_[size_type (size () - 1 - i)]; + } + + /// Steal the contents of \a t. + /// + /// Close to move-semantics. + void + push (YY_MOVE_REF (T) t) + { + seq_.push_back (T ()); + operator[] (0).move (t); + } + + /// Pop elements from the stack. + void + pop (std::ptrdiff_t n = 1) YY_NOEXCEPT + { + for (; 0 < n; --n) + seq_.pop_back (); + } + + /// Pop all elements from the stack. + void + clear () YY_NOEXCEPT + { + seq_.clear (); + } + + /// Number of elements on the stack. + index_type + size () const YY_NOEXCEPT + { + return index_type (seq_.size ()); + } + + /// Iterator on top of the stack (going downwards). + const_iterator + begin () const YY_NOEXCEPT + { + return seq_.begin (); + } + + /// Bottom of the stack. + const_iterator + end () const YY_NOEXCEPT + { + return seq_.end (); + } + + /// Present a slice of the top of a stack. + class slice + { + public: + slice (const stack& stack, index_type range) YY_NOEXCEPT + : stack_ (stack) + , range_ (range) + {} + + const T& + operator[] (index_type i) const + { + return stack_[range_ - i]; + } + + private: + const stack& stack_; + index_type range_; + }; + + private: +#if YY_CPLUSPLUS < 201103L + /// Non copyable. + stack (const stack&); + /// Non copyable. + stack& operator= (const stack&); +#endif + /// The wrapped container. + S seq_; + }; + + + /// Stack type. + typedef stack<stack_symbol_type> stack_type; + + /// The stack. + stack_type yystack_; + + /// Push a new state on the stack. + /// \param m a debug message to display + /// if null, no trace is output. + /// \param sym the symbol + /// \warning the contents of \a s.value is stolen. + void yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym); + + /// Push a new look ahead token on the state on the stack. + /// \param m a debug message to display + /// if null, no trace is output. + /// \param s the state + /// \param sym the symbol (for its value and location). + /// \warning the contents of \a sym.value is stolen. + void yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym); + + /// Pop \a n symbols from the stack. + void yypop_ (int n = 1) YY_NOEXCEPT; + + /// Constants. + enum + { + yylast_ = 307, ///< Last index in yytable_. + yynnts_ = 152, ///< Number of nonterminal symbols. + yyfinal_ = 22 ///< Termination state number. + }; + + + // User arguments. + isc::d2::D2ParserContext& ctx; + + }; + + inline + D2Parser::symbol_kind_type + D2Parser::yytranslate_ (int t) YY_NOEXCEPT + { + // YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to + // TOKEN-NUM as returned by yylex. + static + const signed char + translate_table[] = + { + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60 + }; + // Last valid token kind. + const int code_max = 315; + + if (t <= 0) + return symbol_kind::S_YYEOF; + else if (t <= code_max) + return static_cast <symbol_kind_type> (translate_table[t]); + else + return symbol_kind::S_YYUNDEF; + } + + // basic_symbol. + template <typename Base> + D2Parser::basic_symbol<Base>::basic_symbol (const basic_symbol& that) + : Base (that) + , value () + , location (that.location) + { + switch (this->kind ()) + { + case symbol_kind::S_value: // value + case symbol_kind::S_map_value: // map_value + case symbol_kind::S_ncr_protocol_value: // ncr_protocol_value + value.copy< ElementPtr > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_BOOLEAN: // "boolean" + value.copy< bool > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_FLOAT: // "floating point" + value.copy< double > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_INTEGER: // "integer" + value.copy< int64_t > (YY_MOVE (that.value)); + break; + + case symbol_kind::S_STRING: // "constant string" + value.copy< std::string > (YY_MOVE (that.value)); + break; + + default: + break; + } + + } + + + + + template <typename Base> + D2Parser::symbol_kind_type + D2Parser::basic_symbol<Base>::type_get () const YY_NOEXCEPT + { + return this->kind (); + } + + + template <typename Base> + bool + D2Parser::basic_symbol<Base>::empty () const YY_NOEXCEPT + { + return this->kind () == symbol_kind::S_YYEMPTY; + } + + template <typename Base> + void + D2Parser::basic_symbol<Base>::move (basic_symbol& s) + { + super_type::move (s); + switch (this->kind ()) + { + case symbol_kind::S_value: // value + case symbol_kind::S_map_value: // map_value + case symbol_kind::S_ncr_protocol_value: // ncr_protocol_value + value.move< ElementPtr > (YY_MOVE (s.value)); + break; + + case symbol_kind::S_BOOLEAN: // "boolean" + value.move< bool > (YY_MOVE (s.value)); + break; + + case symbol_kind::S_FLOAT: // "floating point" + value.move< double > (YY_MOVE (s.value)); + break; + + case symbol_kind::S_INTEGER: // "integer" + value.move< int64_t > (YY_MOVE (s.value)); + break; + + case symbol_kind::S_STRING: // "constant string" + value.move< std::string > (YY_MOVE (s.value)); + break; + + default: + break; + } + + location = YY_MOVE (s.location); + } + + // by_kind. + inline + D2Parser::by_kind::by_kind () YY_NOEXCEPT + : kind_ (symbol_kind::S_YYEMPTY) + {} + +#if 201103L <= YY_CPLUSPLUS + inline + D2Parser::by_kind::by_kind (by_kind&& that) YY_NOEXCEPT + : kind_ (that.kind_) + { + that.clear (); + } +#endif + + inline + D2Parser::by_kind::by_kind (const by_kind& that) YY_NOEXCEPT + : kind_ (that.kind_) + {} + + inline + D2Parser::by_kind::by_kind (token_kind_type t) YY_NOEXCEPT + : kind_ (yytranslate_ (t)) + {} + + + + inline + void + D2Parser::by_kind::clear () YY_NOEXCEPT + { + kind_ = symbol_kind::S_YYEMPTY; + } + + inline + void + D2Parser::by_kind::move (by_kind& that) + { + kind_ = that.kind_; + that.clear (); + } + + inline + D2Parser::symbol_kind_type + D2Parser::by_kind::kind () const YY_NOEXCEPT + { + return kind_; + } + + + inline + D2Parser::symbol_kind_type + D2Parser::by_kind::type_get () const YY_NOEXCEPT + { + return this->kind (); + } + + +#line 14 "d2_parser.yy" +} } // isc::d2 +#line 2641 "d2_parser.h" + + + + +#endif // !YY_D2_PARSER_D2_PARSER_H_INCLUDED diff --git a/src/bin/d2/d2_parser.yy b/src/bin/d2/d2_parser.yy new file mode 100644 index 0000000..ddee8c1 --- /dev/null +++ b/src/bin/d2/d2_parser.yy @@ -0,0 +1,993 @@ +/* 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/. */ + +%skeleton "lalr1.cc" /* -*- C++ -*- */ +%require "3.3.0" +%defines +%define api.parser.class {D2Parser} +%define api.prefix {d2_parser_} +%define api.token.constructor +%define api.value.type variant +%define api.namespace {isc::d2} +%define parse.assert +%code requires +{ +#include <string> +#include <cc/data.h> +#include <d2srv/d2_config.h> +#include <boost/lexical_cast.hpp> +#include <d2/parser_context_decl.h> + +using namespace isc::d2; +using namespace isc::data; +using namespace std; +} +// The parsing context. +%param { isc::d2::D2ParserContext& ctx } +%locations +%define parse.trace +%define parse.error verbose +%code +{ +#include <d2/parser_context.h> +} + + +%define api.token.prefix {TOKEN_} +// Tokens in an order which makes sense and related to the intended use. +// Actual regexps for tokens are defined in d2_lexer.ll. +%token + END 0 "end of file" + COMMA "," + COLON ":" + LSQUARE_BRACKET "[" + RSQUARE_BRACKET "]" + LCURLY_BRACKET "{" + RCURLY_BRACKET "}" + NULL_TYPE "null" + + DHCPDDNS "DhcpDdns" + IP_ADDRESS "ip-address" + PORT "port" + DNS_SERVER_TIMEOUT "dns-server-timeout" + NCR_PROTOCOL "ncr-protocol" + UDP "UDP" + TCP "TCP" + NCR_FORMAT "ncr-format" + JSON "JSON" + USER_CONTEXT "user-context" + COMMENT "comment" + FORWARD_DDNS "forward-ddns" + REVERSE_DDNS "reverse-ddns" + DDNS_DOMAINS "ddns-domains" + KEY_NAME "key-name" + DNS_SERVERS "dns-servers" + HOSTNAME "hostname" + TSIG_KEYS "tsig-keys" + ALGORITHM "algorithm" + DIGEST_BITS "digest-bits" + SECRET "secret" + + CONTROL_SOCKET "control-socket" + SOCKET_TYPE "socket-type" + SOCKET_NAME "socket-name" + + HOOKS_LIBRARIES "hooks-libraries" + LIBRARY "library" + PARAMETERS "parameters" + + LOGGERS "loggers" + NAME "name" + OUTPUT_OPTIONS "output_options" + OUTPUT "output" + DEBUGLEVEL "debuglevel" + SEVERITY "severity" + FLUSH "flush" + MAXSIZE "maxsize" + MAXVER "maxver" + PATTERN "pattern" + + // Not real tokens, just a way to signal what the parser is expected to + // parse. + TOPLEVEL_JSON + TOPLEVEL_DHCPDDNS + SUB_DHCPDDNS + SUB_TSIG_KEY + SUB_TSIG_KEYS + SUB_DDNS_DOMAIN + SUB_DDNS_DOMAINS + SUB_DNS_SERVER + SUB_DNS_SERVERS + SUB_HOOKS_LIBRARY +; + +%token <std::string> STRING "constant string" +%token <int64_t> INTEGER "integer" +%token <double> FLOAT "floating point" +%token <bool> BOOLEAN "boolean" + +%type <ElementPtr> value +%type <ElementPtr> map_value +%type <ElementPtr> ncr_protocol_value + +%printer { yyoutput << $$; } <*>; + +%% + +// The whole grammar starts with a map, because the config file +// consists of Dhcp, Logger and DhcpDdns entries in one big { }. +// We made the same for subparsers at the exception of the JSON value. +%start start; + +start: TOPLEVEL_JSON { ctx.ctx_ = ctx.NO_KEYWORD; } sub_json + | TOPLEVEL_DHCPDDNS { ctx.ctx_ = ctx.CONFIG; } syntax_map + | SUB_DHCPDDNS { ctx.ctx_ = ctx.DHCPDDNS; } sub_dhcpddns + | SUB_TSIG_KEY { ctx.ctx_ = ctx.TSIG_KEY; } sub_tsig_key + | SUB_TSIG_KEYS { ctx.ctx_ = ctx.TSIG_KEYS; } sub_tsig_keys + | SUB_DDNS_DOMAIN { ctx.ctx_ = ctx.DDNS_DOMAIN; } sub_ddns_domain + | SUB_DDNS_DOMAINS { ctx.ctx_ = ctx.DDNS_DOMAINS; } sub_ddns_domains + | SUB_DNS_SERVER { ctx.ctx_ = ctx.DNS_SERVERS; } sub_dns_server + | SUB_DNS_SERVERS { ctx.ctx_ = ctx.DNS_SERVERS; } sub_dns_servers + | SUB_HOOKS_LIBRARY { ctx.ctx_ = ctx.HOOKS_LIBRARIES; } sub_hooks_library + ; + +// ---- generic JSON parser --------------------------------- + +// Note that ctx_ is NO_KEYWORD here + +// Values rule +value: INTEGER { $$ = ElementPtr(new IntElement($1, ctx.loc2pos(@1))); } + | FLOAT { $$ = ElementPtr(new DoubleElement($1, ctx.loc2pos(@1))); } + | BOOLEAN { $$ = ElementPtr(new BoolElement($1, ctx.loc2pos(@1))); } + | STRING { $$ = ElementPtr(new StringElement($1, ctx.loc2pos(@1))); } + | NULL_TYPE { $$ = ElementPtr(new NullElement(ctx.loc2pos(@1))); } + | map2 { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); } + | list_generic { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); } + ; + +sub_json: value { + // Push back the JSON value on the stack + ctx.stack_.push_back($1); +}; + +map2: LCURLY_BRACKET { + // This code is executed when we're about to start parsing + // the content of the map + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.push_back(m); +} map_content RCURLY_BRACKET { + // map parsing completed. If we ever want to do any wrap up + // (maybe some sanity checking), this would be the best place + // for it. +}; + +map_value: map2 { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }; + +// Assignments rule +map_content: %empty // empty map + | not_empty_map + ; + +not_empty_map: STRING COLON value { + // map containing a single entry + ctx.unique($1, ctx.loc2pos(@1)); + ctx.stack_.back()->set($1, $3); + } + | not_empty_map COMMA STRING COLON value { + // map consisting of a shorter map followed by + // comma and string:value + ctx.unique($3, ctx.loc2pos(@3)); + ctx.stack_.back()->set($3, $5); + } + | not_empty_map COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +list_generic: LSQUARE_BRACKET { + ElementPtr l(new ListElement(ctx.loc2pos(@1))); + ctx.stack_.push_back(l); +} list_content RSQUARE_BRACKET { + // list parsing complete. Put any sanity checking here +}; + +list_content: %empty // Empty list + | not_empty_list + ; + +not_empty_list: value { + // List consisting of a single element. + ctx.stack_.back()->add($1); + } + | not_empty_list COMMA value { + // List ending with , and a value. + ctx.stack_.back()->add($3); + } + | not_empty_list COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +// ---- generic JSON parser ends here ---------------------------------- + +// ---- syntax checking parser starts here ----------------------------- + +// Unknown keyword in a map +unknown_map_entry: STRING COLON { + const std::string& where = ctx.contextName(); + const std::string& keyword = $1; + error(@1, + "got unexpected keyword \"" + keyword + "\" in " + where + " map."); +}; + + +// This defines the top-level { } that holds only DhcpDdns object. +syntax_map: LCURLY_BRACKET { + // This code is executed when we're about to start parsing + // the content of the map + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.push_back(m); +} global_object RCURLY_BRACKET { + // map parsing completed. If we ever want to do any wrap up + // (maybe some sanity checking), this would be the best place + // for it. +}; + +// --- dhcp ddns --------------------------------------------- +// This represents the single top level entry, e.g. DhcpDdns. +global_object: DHCPDDNS { + ctx.unique("DhcpDdns", ctx.loc2pos(@1)); + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("DhcpDdns", m); + ctx.stack_.push_back(m); + ctx.enter(ctx.DHCPDDNS); +} COLON LCURLY_BRACKET dhcpddns_params RCURLY_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +} + | global_object_comma + ; + +global_object_comma: global_object COMMA { + ctx.warnAboutExtraCommas(@2); +}; + +sub_dhcpddns: LCURLY_BRACKET { + // Parse the dhcpddns map + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.push_back(m); +} dhcpddns_params RCURLY_BRACKET { + // parsing completed +}; + +dhcpddns_params: dhcpddns_param + | dhcpddns_params COMMA dhcpddns_param + | dhcpddns_params COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +// These are the top-level parameters allowed for DhcpDdns +dhcpddns_param: ip_address + | port + | dns_server_timeout + | ncr_protocol + | ncr_format + | forward_ddns + | reverse_ddns + | tsig_keys + | control_socket + | hooks_libraries + | loggers + | user_context + | comment + | unknown_map_entry + ; + +ip_address: IP_ADDRESS { + ctx.unique("ip-address", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr s(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("ip-address", s); + ctx.leave(); +}; + +port: PORT COLON INTEGER { + ctx.unique("port", ctx.loc2pos(@1)); + if ($3 <= 0 || $3 >= 65536 ) { + error(@3, "port must be greater than zero but less than 65536"); + } + ElementPtr i(new IntElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("port", i); +}; + +dns_server_timeout: DNS_SERVER_TIMEOUT COLON INTEGER { + ctx.unique("dns-server-timeout", ctx.loc2pos(@1)); + if ($3 <= 0) { + error(@3, "dns-server-timeout must be greater than zero"); + } else { + ElementPtr i(new IntElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("dns-server-timeout", i); + } +}; + +ncr_protocol: NCR_PROTOCOL { + ctx.unique("ncr-protocol", ctx.loc2pos(@1)); + ctx.enter(ctx.NCR_PROTOCOL); +} COLON ncr_protocol_value { + ctx.stack_.back()->set("ncr-protocol", $4); + ctx.leave(); +}; + +ncr_protocol_value: + UDP { $$ = ElementPtr(new StringElement("UDP", ctx.loc2pos(@1))); } + | TCP { $$ = ElementPtr(new StringElement("TCP", ctx.loc2pos(@1))); } + ; + +ncr_format: NCR_FORMAT { + ctx.unique("ncr-format", ctx.loc2pos(@1)); + ctx.enter(ctx.NCR_FORMAT); +} COLON JSON { + ElementPtr json(new StringElement("JSON", ctx.loc2pos(@4))); + ctx.stack_.back()->set("ncr-format", json); + ctx.leave(); +}; + +user_context: USER_CONTEXT { + ctx.enter(ctx.NO_KEYWORD); +} COLON map_value { + ElementPtr parent = ctx.stack_.back(); + ElementPtr user_context = $4; + ConstElementPtr old = parent->get("user-context"); + + // Handle already existing user context + if (old) { + // Check if it was a comment or a duplicate + if ((old->size() != 1) || !old->contains("comment")) { + std::stringstream msg; + msg << "duplicate user-context entries (previous at " + << old->getPosition().str() << ")"; + error(@1, msg.str()); + } + // Merge the comment + user_context->set("comment", old->get("comment")); + } + + // Set the user context + parent->set("user-context", user_context); + ctx.leave(); +}; + +comment: COMMENT { + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr parent = ctx.stack_.back(); + ElementPtr user_context(new MapElement(ctx.loc2pos(@1))); + ElementPtr comment(new StringElement($4, ctx.loc2pos(@4))); + user_context->set("comment", comment); + + // Handle already existing user context + ConstElementPtr old = parent->get("user-context"); + if (old) { + // Check for duplicate comment + if (old->contains("comment")) { + std::stringstream msg; + msg << "duplicate user-context/comment entries (previous at " + << old->getPosition().str() << ")"; + error(@1, msg.str()); + } + // Merge the user context in the comment + merge(user_context, old); + } + + // Set the user context + parent->set("user-context", user_context); + ctx.leave(); +}; + +forward_ddns : FORWARD_DDNS { + ctx.unique("forward-ddns", ctx.loc2pos(@1)); + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("forward-ddns", m); + ctx.stack_.push_back(m); + ctx.enter(ctx.FORWARD_DDNS); +} COLON LCURLY_BRACKET ddns_mgr_params RCURLY_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + +reverse_ddns : REVERSE_DDNS { + ctx.unique("reverse-ddns", ctx.loc2pos(@1)); + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("reverse-ddns", m); + ctx.stack_.push_back(m); + ctx.enter(ctx.REVERSE_DDNS); +} COLON LCURLY_BRACKET ddns_mgr_params RCURLY_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + +ddns_mgr_params: %empty + | not_empty_ddns_mgr_params + ; + +not_empty_ddns_mgr_params: ddns_mgr_param + | ddns_mgr_params COMMA ddns_mgr_param + | ddns_mgr_params COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +ddns_mgr_param: ddns_domains + | unknown_map_entry + ; + + +// --- ddns-domains ---------------------------------------- +ddns_domains: DDNS_DOMAINS { + ctx.unique("ddns-domains", ctx.loc2pos(@1)); + ElementPtr l(new ListElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("ddns-domains", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.DDNS_DOMAINS); +} COLON LSQUARE_BRACKET ddns_domain_list RSQUARE_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + +sub_ddns_domains: LSQUARE_BRACKET { + ElementPtr l(new ListElement(ctx.loc2pos(@1))); + ctx.stack_.push_back(l); +} ddns_domain_list RSQUARE_BRACKET { + // parsing completed +} + +ddns_domain_list: %empty + | not_empty_ddns_domain_list + ; + +not_empty_ddns_domain_list: ddns_domain + | not_empty_ddns_domain_list COMMA ddns_domain + | not_empty_ddns_domain_list COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +ddns_domain: LCURLY_BRACKET { + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} ddns_domain_params RCURLY_BRACKET { + ctx.stack_.pop_back(); +}; + +sub_ddns_domain: LCURLY_BRACKET { + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.push_back(m); +} ddns_domain_params RCURLY_BRACKET { + // parsing completed +}; + +ddns_domain_params: ddns_domain_param + | ddns_domain_params COMMA ddns_domain_param + | ddns_domain_params COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +ddns_domain_param: ddns_domain_name + | ddns_key_name + | dns_servers + | user_context + | comment + | unknown_map_entry + ; + +// @todo NAME needs to be an FQDN sort of thing +ddns_domain_name: NAME { + ctx.unique("name", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + if ($4 == "") { + error(@3, "Ddns domain name cannot be blank"); + } + ElementPtr elem(new StringElement($4, ctx.loc2pos(@4))); + ElementPtr name(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("name", name); + ctx.leave(); +}; + +ddns_key_name: KEY_NAME { + ctx.unique("key-name", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr elem(new StringElement($4, ctx.loc2pos(@4))); + ElementPtr name(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("key-name", name); + ctx.leave(); +}; + +// --- end ddns-domains ---------------------------------------- + +// --- dns-servers ---------------------------------------- +dns_servers: DNS_SERVERS { + ctx.unique("dns-servers", ctx.loc2pos(@1)); + ElementPtr l(new ListElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("dns-servers", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.DNS_SERVERS); +} COLON LSQUARE_BRACKET dns_server_list RSQUARE_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + +sub_dns_servers: LSQUARE_BRACKET { + ElementPtr l(new ListElement(ctx.loc2pos(@1))); + ctx.stack_.push_back(l); +} dns_server_list RSQUARE_BRACKET { + // parsing completed +} + +dns_server_list: dns_server + | dns_server_list COMMA dns_server + | dns_server_list COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +dns_server: LCURLY_BRACKET { + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} dns_server_params RCURLY_BRACKET { + ctx.stack_.pop_back(); +}; + +sub_dns_server: LCURLY_BRACKET { + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.push_back(m); +} dns_server_params RCURLY_BRACKET { + // parsing completed +}; + +dns_server_params: dns_server_param + | dns_server_params COMMA dns_server_param + | dns_server_params COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +dns_server_param: dns_server_hostname + | dns_server_ip_address + | dns_server_port + | ddns_key_name + | user_context + | comment + | unknown_map_entry + ; + +dns_server_hostname: HOSTNAME { + ctx.unique("hostname", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + if ($4 != "") { + error(@3, "hostname is not yet supported"); + } + ElementPtr elem(new StringElement($4, ctx.loc2pos(@4))); + ElementPtr name(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("hostname", name); + ctx.leave(); +}; + +dns_server_ip_address: IP_ADDRESS { + ctx.unique("ip-address", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr s(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("ip-address", s); + ctx.leave(); +}; + +dns_server_port: PORT COLON INTEGER { + ctx.unique("port", ctx.loc2pos(@1)); + if ($3 <= 0 || $3 >= 65536 ) { + error(@3, "port must be greater than zero but less than 65536"); + } + ElementPtr i(new IntElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("port", i); +}; + +// --- end of dns-servers --------------------------------- + + + +// --- tsig-keys ---------------------------------------- +// "tsig-keys" : [ ... ] +tsig_keys: TSIG_KEYS { + ctx.unique("tsig-keys", ctx.loc2pos(@1)); + ElementPtr l(new ListElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("tsig-keys", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.TSIG_KEYS); +} COLON LSQUARE_BRACKET tsig_keys_list RSQUARE_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + +sub_tsig_keys: LSQUARE_BRACKET { + ElementPtr l(new ListElement(ctx.loc2pos(@1))); + ctx.stack_.push_back(l); +} tsig_keys_list RSQUARE_BRACKET { + // parsing completed +} + +tsig_keys_list: %empty + | not_empty_tsig_keys_list + ; + +not_empty_tsig_keys_list: tsig_key + | not_empty_tsig_keys_list COMMA tsig_key + | not_empty_tsig_keys_list COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +tsig_key: LCURLY_BRACKET { + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} tsig_key_params RCURLY_BRACKET { + ctx.stack_.pop_back(); +}; + +sub_tsig_key: LCURLY_BRACKET { + // Parse tsig key list entry map + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.push_back(m); +} tsig_key_params RCURLY_BRACKET { + // parsing completed +}; + + +tsig_key_params: tsig_key_param + | tsig_key_params COMMA tsig_key_param + | tsig_key_params COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +tsig_key_param: tsig_key_name + | tsig_key_algorithm + | tsig_key_digest_bits + | tsig_key_secret + | user_context + | comment + | unknown_map_entry + ; + +tsig_key_name: NAME { + ctx.unique("name", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + if ($4 == "") { + error(@3, "TSIG key name cannot be blank"); + } + ElementPtr elem(new StringElement($4, ctx.loc2pos(@4))); + ElementPtr name(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("name", name); + ctx.leave(); +}; + +tsig_key_algorithm: ALGORITHM { + ctx.unique("algorithm", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + if ($4 == "") { + error(@3, "TSIG key algorithm cannot be blank"); + } + ElementPtr elem(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("algorithm", elem); + ctx.leave(); +}; + +tsig_key_digest_bits: DIGEST_BITS COLON INTEGER { + ctx.unique("digest-bits", ctx.loc2pos(@1)); + if ($3 < 0 || ($3 > 0 && ($3 % 8 != 0))) { + error(@3, "TSIG key digest-bits must either be zero or a positive, multiple of eight"); + } + ElementPtr elem(new IntElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("digest-bits", elem); +}; + +tsig_key_secret: SECRET { + ctx.unique("secret", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + if ($4 == "") { + error(@3, "TSIG key secret cannot be blank"); + } + ElementPtr elem(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("secret", elem); + ctx.leave(); +}; + + +// --- end of tsig-keys --------------------------------- + +// --- control socket ---------------------------------------- + +control_socket: CONTROL_SOCKET { + ctx.unique("control-socket", ctx.loc2pos(@1)); + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("control-socket", m); + ctx.stack_.push_back(m); + ctx.enter(ctx.CONTROL_SOCKET); +} COLON LCURLY_BRACKET control_socket_params RCURLY_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + +control_socket_params: control_socket_param + | control_socket_params COMMA control_socket_param + | control_socket_params COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +control_socket_param: control_socket_type + | control_socket_name + | user_context + | comment + | unknown_map_entry + ; + +control_socket_type: SOCKET_TYPE { + ctx.unique("socket-type", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr stype(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("socket-type", stype); + ctx.leave(); +}; + +control_socket_name: SOCKET_NAME { + ctx.unique("socket-name", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr name(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("socket-name", name); + ctx.leave(); +}; + +// --- hooks libraries ----------------------------------------- + +hooks_libraries: HOOKS_LIBRARIES { + ctx.unique("hooks-libraries", ctx.loc2pos(@1)); + ElementPtr l(new ListElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("hooks-libraries", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.HOOKS_LIBRARIES); +} COLON LSQUARE_BRACKET hooks_libraries_list RSQUARE_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + +hooks_libraries_list: %empty + | not_empty_hooks_libraries_list + ; + +not_empty_hooks_libraries_list: hooks_library + | not_empty_hooks_libraries_list COMMA hooks_library + | not_empty_hooks_libraries_list COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +hooks_library: LCURLY_BRACKET { + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} hooks_params RCURLY_BRACKET { + // The library hooks parameter is required + ctx.require("library", ctx.loc2pos(@1), ctx.loc2pos(@4)); + ctx.stack_.pop_back(); +}; + +sub_hooks_library: LCURLY_BRACKET { + // Parse the hooks-libraries list entry map + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.push_back(m); +} hooks_params RCURLY_BRACKET { + // The library hooks parameter is required + ctx.require("library", ctx.loc2pos(@1), ctx.loc2pos(@4)); + // parsing completed +}; + +hooks_params: hooks_param + | hooks_params COMMA hooks_param + | hooks_params COMMA { + ctx.warnAboutExtraCommas(@2); + } + | unknown_map_entry + ; + +hooks_param: library + | parameters + ; + +library: LIBRARY { + ctx.unique("library", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr lib(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("library", lib); + ctx.leave(); +}; + +parameters: PARAMETERS { + ctx.unique("parameters", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON map_value { + ctx.stack_.back()->set("parameters", $4); + ctx.leave(); +}; + +// --- loggers entry ----------------------------------------- + +loggers: LOGGERS { + ctx.unique("loggers", ctx.loc2pos(@1)); + ElementPtr l(new ListElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("loggers", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.LOGGERS); +} COLON LSQUARE_BRACKET loggers_entries RSQUARE_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + +// These are the parameters allowed in loggers: either one logger +// entry or multiple entries separate by commas. +loggers_entries: logger_entry + | loggers_entries COMMA logger_entry + | loggers_entries COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +// This defines a single entry defined in loggers. +logger_entry: LCURLY_BRACKET { + ElementPtr l(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->add(l); + ctx.stack_.push_back(l); +} logger_params RCURLY_BRACKET { + ctx.stack_.pop_back(); +}; + +logger_params: logger_param + | logger_params COMMA logger_param + | logger_params COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +logger_param: name + | output_options_list + | debuglevel + | severity + | user_context + | comment + | unknown_map_entry + ; + +name: NAME { + ctx.unique("name", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr name(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("name", name); + ctx.leave(); +}; + +debuglevel: DEBUGLEVEL COLON INTEGER { + ctx.unique("debuglevel", ctx.loc2pos(@1)); + ElementPtr dl(new IntElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("debuglevel", dl); +}; + +severity: SEVERITY { + ctx.unique("severity", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr sev(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("severity", sev); + ctx.leave(); +}; + +output_options_list: OUTPUT_OPTIONS { + ctx.unique("output_options", ctx.loc2pos(@1)); + ElementPtr l(new ListElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("output_options", l); + ctx.stack_.push_back(l); + ctx.enter(ctx.OUTPUT_OPTIONS); +} COLON LSQUARE_BRACKET output_options_list_content RSQUARE_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + +output_options_list_content: output_entry + | output_options_list_content COMMA output_entry + | output_options_list_content COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +output_entry: LCURLY_BRACKET { + ElementPtr m(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} output_params_list RCURLY_BRACKET { + ctx.stack_.pop_back(); +}; + +output_params_list: output_params + | output_params_list COMMA output_params + | output_params_list COMMA { + ctx.warnAboutExtraCommas(@2); + } + ; + +output_params: output + | flush + | maxsize + | maxver + | pattern + ; + +output: OUTPUT { + ctx.unique("output", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr sev(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("output", sev); + ctx.leave(); +}; + +flush: FLUSH COLON BOOLEAN { + ctx.unique("flush", ctx.loc2pos(@1)); + ElementPtr flush(new BoolElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("flush", flush); +} + +maxsize: MAXSIZE COLON INTEGER { + ctx.unique("maxsize", ctx.loc2pos(@1)); + ElementPtr maxsize(new IntElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("maxsize", maxsize); +} + +maxver: MAXVER COLON INTEGER { + ctx.unique("maxver", ctx.loc2pos(@1)); + ElementPtr maxver(new IntElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("maxver", maxver); +} + +pattern: PATTERN { + ctx.unique("pattern", ctx.loc2pos(@1)); + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr sev(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("pattern", sev); + ctx.leave(); +}; + +%% + +void +isc::d2::D2Parser::error(const location_type& loc, + const std::string& what) +{ + ctx.error(loc, what); +} diff --git a/src/bin/d2/d2_process.cc b/src/bin/d2/d2_process.cc new file mode 100644 index 0000000..c06e0be --- /dev/null +++ b/src/bin/d2/d2_process.cc @@ -0,0 +1,502 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <asiolink/asio_wrapper.h> +#include <cc/command_interpreter.h> +#include <config/command_mgr.h> +#include <d2/d2_controller.h> +#include <d2/d2_process.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/d2_log.h> +#include <d2srv/d2_stats.h> +#include <d2srv/d2_tsig_key.h> +#include <hooks/hooks.h> +#include <hooks/hooks_manager.h> + +using namespace isc::hooks; +using namespace isc::process; + +namespace { + +/// Structure that holds registered hook indexes. +struct D2ProcessHooks { + int hooks_index_d2_srv_configured_; + + /// Constructor that registers hook points for the D2 server. + D2ProcessHooks() { + hooks_index_d2_srv_configured_ = HooksManager::registerHook("d2_srv_configured"); + } + +}; + +// 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. +D2ProcessHooks Hooks; + +} + +namespace isc { +namespace d2 { + +// Setting to 80% for now. This is an arbitrary choice and should probably +// be configurable. +const unsigned int D2Process::QUEUE_RESTART_PERCENT = 80; + +D2Process::D2Process(const char* name, const asiolink::IOServicePtr& io_service) + : DProcessBase(name, io_service, DCfgMgrBasePtr(new D2CfgMgr())), + reconf_queue_flag_(false), shutdown_type_(SD_NORMAL) { + + // Instantiate queue manager. Note that queue manager does not start + // listening at this point. That can only occur after configuration has + // been received. This means that until we receive the configuration, + // D2 will neither receive nor process NameChangeRequests. + // Pass in IOService for NCR IO event processing. + queue_mgr_.reset(new D2QueueMgr(getIoService())); + + // Instantiate update manager. + // Pass in both queue manager and configuration manager. + // Pass in IOService for DNS update transaction IO event processing. + D2CfgMgrPtr tmp = getD2CfgMgr(); + update_mgr_.reset(new D2UpdateMgr(queue_mgr_, tmp, getIoService())); + + // Initialize stats manager. + D2Stats::init(); +}; + +void +D2Process::init() { + // CommandMgr uses IO service to run asynchronous socket operations. + isc::config::CommandMgr::instance().setIOService(getIoService()); +}; + +void +D2Process::run() { + LOG_INFO(d2_logger, DHCP_DDNS_STARTED).arg(VERSION); + D2ControllerPtr controller = + boost::dynamic_pointer_cast<D2Controller>(D2Controller::instance()); + try { + // Now logging was initialized so commands can be registered. + controller->registerCommands(); + + // Loop forever until we are allowed to shutdown. + while (!canShutdown()) { + // Check on the state of the request queue. Take any + // actions necessary regarding it. + checkQueueStatus(); + + // Give update manager a time slice to queue new jobs and + // process finished ones. + update_mgr_->sweep(); + + // Wait on IO event(s) - block until one or more of the following + // has occurred: + // a. NCR message has been received + // b. Transaction IO has completed + // c. Interval timer expired + // d. Control channel event + // e. Something stopped IO service (runIO returns 0) + if (runIO() == 0) { + // Pretty sure this amounts to an unexpected stop and we + // should bail out now. Normal shutdowns do not utilize + // stopping the IOService. + isc_throw(DProcessBaseError, + "Primary IO service stopped unexpectedly"); + } + } + } catch (const std::exception& ex) { + LOG_FATAL(d2_logger, DHCP_DDNS_FAILED).arg(ex.what()); + controller->deregisterCommands(); + isc_throw (DProcessBaseError, + "Process run method failed: " << ex.what()); + } + + /// @todo - if queue isn't empty, we may need to persist its contents + /// this might be the place to do it, once there is a persistence mgr. + /// This may also be better in checkQueueStatus. + + controller->deregisterCommands(); + + LOG_DEBUG(d2_logger, isc::log::DBGLVL_START_SHUT, DHCP_DDNS_RUN_EXIT); + +}; + +size_t +D2Process::runIO() { + // We want to block until at least one handler is called. We'll use + // boost::asio::io_service directly for two reasons. First off + // asiolink::IOService::run_one is a void and boost::asio::io_service::stopped + // is not present in older versions of boost. We need to know if any + // handlers ran or if the io_service was stopped. That latter represents + // some form of error and the application cannot proceed with a stopped + // service. Secondly, asiolink::IOService does not provide the poll + // method. This is a handy method which runs all ready handlers without + // blocking. + asiolink::IOServicePtr& io = getIoService(); + boost::asio::io_service& asio_io_service = io->get_io_service(); + + // Poll runs all that are ready. If none are ready it returns immediately + // with a count of zero. + size_t cnt = asio_io_service.poll(); + if (!cnt) { + // Poll ran no handlers either none are ready or the service has been + // stopped. Either way, call run_one to wait for a IO event. If the + // service is stopped it will return immediately with a cnt of zero. + cnt = asio_io_service.run_one(); + } + + return (cnt); +} + +bool +D2Process::canShutdown() const { + bool all_clear = false; + + // If we have been told to shutdown, find out if we are ready to do so. + if (shouldShutdown()) { + switch (shutdown_type_) { + case SD_NORMAL: + // For a normal shutdown we need to stop the queue manager but + // wait until we have finished all the transactions in progress. + all_clear = (((queue_mgr_->getMgrState() != D2QueueMgr::RUNNING) && + (queue_mgr_->getMgrState() != D2QueueMgr::STOPPING)) + && (update_mgr_->getTransactionCount() == 0)); + break; + + case SD_DRAIN_FIRST: + // For a drain first shutdown we need to stop the queue manager but + // process all of the requests in the receive queue first. + all_clear = (((queue_mgr_->getMgrState() != D2QueueMgr::RUNNING) && + (queue_mgr_->getMgrState() != D2QueueMgr::STOPPING)) + && (queue_mgr_->getQueueSize() == 0) + && (update_mgr_->getTransactionCount() == 0)); + break; + + case SD_NOW: + // Get out right now, no niceties. + all_clear = true; + break; + + default: + // shutdown_type_ is an enum and should only be one of the above. + // if its getting through to this, something is whacked. + break; + } + + if (all_clear) { + LOG_DEBUG(d2_logger, isc::log::DBGLVL_START_SHUT, + DHCP_DDNS_CLEARED_FOR_SHUTDOWN) + .arg(getShutdownTypeStr(shutdown_type_)); + } + } + + return (all_clear); +} + +isc::data::ConstElementPtr +D2Process::shutdown(isc::data::ConstElementPtr args) { + LOG_DEBUG(d2_logger, isc::log::DBGLVL_START_SHUT, + DHCP_DDNS_SHUTDOWN_COMMAND) + .arg(args ? args->str() : "(no arguments)"); + + // Default shutdown type is normal. + std::string type_str(getShutdownTypeStr(SD_NORMAL)); + shutdown_type_ = SD_NORMAL; + + if (args) { + if ((args->getType() == isc::data::Element::map) && + args->contains("type")) { + type_str = args->get("type")->stringValue(); + + if (type_str == getShutdownTypeStr(SD_NORMAL)) { + shutdown_type_ = SD_NORMAL; + } else if (type_str == getShutdownTypeStr(SD_DRAIN_FIRST)) { + shutdown_type_ = SD_DRAIN_FIRST; + } else if (type_str == getShutdownTypeStr(SD_NOW)) { + shutdown_type_ = SD_NOW; + } else { + setShutdownFlag(false); + return (isc::config::createAnswer(1, "Invalid Shutdown type: " + + type_str)); + } + } + } + + // Set the base class's shutdown flag. + setShutdownFlag(true); + return (isc::config::createAnswer(0, "Shutdown initiated, type is: " + + type_str)); +} + +isc::data::ConstElementPtr +D2Process::configure(isc::data::ConstElementPtr config_set, bool check_only) { + LOG_DEBUG(d2_logger, isc::log::DBGLVL_TRACE_BASIC, DHCP_DDNS_CONFIGURE) + .arg(check_only ? "check" : "update") + .arg(getD2CfgMgr()->redactConfig(config_set)->str()); + + isc::data::ConstElementPtr answer; + answer = getCfgMgr()->simpleParseConfig(config_set, check_only, + std::bind(&D2Process::reconfigureCommandChannel, this)); + if (check_only) { + return (answer); + } + + int rcode = 0; + isc::data::ConstElementPtr comment; + comment = isc::config::parseAnswer(rcode, answer); + + if (rcode) { + // Non-zero means we got an invalid configuration, take no further + // action. In integrated mode, this will send a failed response back + // to the configuration backend. + reconf_queue_flag_ = false; + return (answer); + } + + // Set the reconf_queue_flag to indicate that we need to reconfigure + // the queue manager. Reconfiguring the queue manager may be asynchronous + // and require one or more events to occur, therefore we set a flag + // indicating it needs to be done but we cannot do it here. It must + // be done over time, while events are being processed. Remember that + // the method we are in now is invoked as part of the configuration event + // callback. This means you can't wait for events here, you are already + // in one. + /// (@todo NOTE This could be turned into a bitmask of flags if we find other + /// things that need reconfiguration. It might also be useful if we + /// did some analysis to decide what if anything we need to do.) + reconf_queue_flag_ = true; + + // This hook point notifies hooks libraries that the configuration of the + // D2 server has completed. It provides the hook library with the pointer + // to the common IO service object, new server configuration in the JSON + // format and with the pointer to the configuration storage where the + // parsed configuration is stored. + std::string error(""); + if (HooksManager::calloutsPresent(Hooks.hooks_index_d2_srv_configured_)) { + CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle(); + + callout_handle->setArgument("io_context", getIoService()); + callout_handle->setArgument("json_config", config_set); + callout_handle->setArgument("server_config", + getD2CfgMgr()->getD2CfgContext()); + callout_handle->setArgument("error", error); + + HooksManager::callCallouts(Hooks.hooks_index_d2_srv_configured_, + *callout_handle); + + // The config can be rejected by a hook. + if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) { + callout_handle->getArgument("error", error); + LOG_ERROR(d2_logger, DHCP_DDNS_CONFIGURED_CALLOUT_DROP) + .arg(error); + reconf_queue_flag_ = false; + answer = isc::config::createAnswer(1, error); + return (answer); + } + } + + // If we are here, configuration was valid, at least it parsed correctly + // and therefore contained no invalid values. + // Return the success answer from above. + return (answer); +} + +void +D2Process::checkQueueStatus() { + switch (queue_mgr_->getMgrState()){ + case D2QueueMgr::RUNNING: + if (reconf_queue_flag_ || shouldShutdown()) { + /// If we need to reconfigure the queue manager or we have been + /// told to shutdown, then stop listening first. Stopping entails + /// canceling active listening which may generate an IO event, so + /// instigate the stop and get out. + try { + LOG_DEBUG(d2_logger, isc::log::DBGLVL_START_SHUT, + DHCP_DDNS_QUEUE_MGR_STOPPING) + .arg(reconf_queue_flag_ ? "reconfiguration" : "shutdown"); + queue_mgr_->stopListening(); + } catch (const isc::Exception& ex) { + // It is very unlikely that we would experience an error + // here, but theoretically possible. + LOG_ERROR(d2_logger, DHCP_DDNS_QUEUE_MGR_STOP_ERROR) + .arg(ex.what()); + } + } + break; + + case D2QueueMgr::STOPPED_QUEUE_FULL: { + /// Resume receiving once the queue has decreased by twenty + /// percent. This is an arbitrary choice. + /// @todo this value should probably be configurable. + size_t threshold = (((queue_mgr_->getMaxQueueSize() + * QUEUE_RESTART_PERCENT)) / 100); + if (queue_mgr_->getQueueSize() <= threshold) { + LOG_INFO (d2_logger, DHCP_DDNS_QUEUE_MGR_RESUMING) + .arg(threshold).arg(queue_mgr_->getMaxQueueSize()); + try { + queue_mgr_->startListening(); + } catch (const isc::Exception& ex) { + LOG_ERROR(d2_logger, DHCP_DDNS_QUEUE_MGR_RESUME_ERROR) + .arg(ex.what()); + } + } + + break; + } + + case D2QueueMgr::STOPPED_RECV_ERROR: + /// If the receive error is not due to some fallout from shutting + /// down then we will attempt to recover by reconfiguring the listener. + /// This will close and destruct the current listener and make a new + /// one with new resources. + /// @todo This may need a safety valve such as retry count or a timer + /// to keep from endlessly retrying over and over, with little time + /// in between. + if (!shouldShutdown()) { + LOG_INFO (d2_logger, DHCP_DDNS_QUEUE_MGR_RECOVERING); + reconfigureQueueMgr(); + } + break; + + case D2QueueMgr::STOPPING: + /// We are waiting for IO to cancel, so this is a NOP. + /// @todo Possible timer for self-defense? We could conceivably + /// get into a condition where we never get the event, which would + /// leave us stuck in stopping. This is hugely unlikely but possible? + break; + + default: + // If the reconfigure flag is set, then we are in a state now where + // we can do the reconfigure. In other words, we aren't RUNNING or + // STOPPING. + if (reconf_queue_flag_) { + LOG_DEBUG(d2_logger, isc::log::DBGLVL_TRACE_BASIC, + DHCP_DDNS_QUEUE_MGR_RECONFIGURING); + reconfigureQueueMgr(); + } + break; + } +} + +void +D2Process::reconfigureQueueMgr() { + // Set reconfigure flag to false. We are only here because we have + // a valid configuration to work with so if we fail below, it will be + // an operational issue, such as a busy IP address. That will leave + // queue manager in INITTED state, which is fine. + // What we don't want is to continually attempt to reconfigure so set + // the flag false now. + /// @todo This method assumes only 1 type of listener. This will change + /// to support at least a TCP version, possibly some form of RDBMS listener + /// as well. + reconf_queue_flag_ = false; + try { + // Wipe out the current listener. + queue_mgr_->removeListener(); + + // Get the configuration parameters that affect Queue Manager. + const D2ParamsPtr& d2_params = getD2CfgMgr()->getD2Params(); + + /// Warn the user if the server address is not the loopback. + /// @todo Remove this once we provide a secure mechanism. + std::string ip_address = d2_params->getIpAddress().toText(); + if (ip_address != "127.0.0.1" && ip_address != "::1") { + LOG_WARN(d2_logger, DHCP_DDNS_NOT_ON_LOOPBACK).arg(ip_address); + } + + // Instantiate the listener. + if (d2_params->getNcrProtocol() == dhcp_ddns::NCR_UDP) { + queue_mgr_->initUDPListener(d2_params->getIpAddress(), + d2_params->getPort(), + d2_params->getNcrFormat(), true); + } else { + /// @todo Add TCP/IP once it's supported + // We should never get this far but if we do deal with it. + isc_throw(DProcessBaseError, "Unsupported NCR listener protocol:" + << dhcp_ddns::ncrProtocolToString(d2_params-> + getNcrProtocol())); + } + + // Now start it. This assumes that starting is a synchronous, + // blocking call that executes quickly. + /// @todo Should that change then we will have to expand the state model + /// to accommodate this. + queue_mgr_->startListening(); + } catch (const isc::Exception& ex) { + // Queue manager failed to initialize and therefore not listening. + // This is most likely due to an unavailable IP address or port, + // which is a configuration issue. + LOG_ERROR(d2_logger, DHCP_DDNS_QUEUE_MGR_START_ERROR).arg(ex.what()); + } +} + +D2Process::~D2Process() { +} + +D2CfgMgrPtr +D2Process::getD2CfgMgr() { + // The base class gives a base class pointer to our configuration manager. + // Since we are D2, and we need D2 specific extensions, we need a pointer + // to D2CfgMgr for some things. + return (boost::dynamic_pointer_cast<D2CfgMgr>(getCfgMgr())); +} + +const char* D2Process::getShutdownTypeStr(const ShutdownType& type) { + const char* str = "invalid"; + switch (type) { + case SD_NORMAL: + str = "normal"; + break; + case SD_DRAIN_FIRST: + str = "drain_first"; + break; + case SD_NOW: + str = "now"; + break; + default: + break; + } + + return (str); +} + +void +D2Process::reconfigureCommandChannel() { + // Get new socket configuration. + isc::data::ConstElementPtr sock_cfg = getD2CfgMgr()->getControlSocketInfo(); + + // Determine if the socket configuration has changed. It has if + // both old and new configuration is specified but respective + // data elements aren't equal. + bool sock_changed = (sock_cfg && current_control_socket_ && + !sock_cfg->equals(*current_control_socket_)); + + // If the previous or new socket configuration doesn't exist or + // the new configuration differs from the old configuration we + // close the existing socket and open a new socket as appropriate. + // Note that closing an existing socket means the client will not + // receive the configuration result. + if (!sock_cfg || !current_control_socket_ || sock_changed) { + // Close the existing socket. + if (current_control_socket_) { + isc::config::CommandMgr::instance().closeCommandSocket(); + current_control_socket_.reset(); + } + + // Open the new socket. + if (sock_cfg) { + isc::config::CommandMgr::instance().openCommandSocket(sock_cfg); + } + } + + // Commit the new socket configuration. + current_control_socket_ = sock_cfg; +} + +} // namespace isc::d2 +} // namespace isc diff --git a/src/bin/d2/d2_process.h b/src/bin/d2/d2_process.h new file mode 100644 index 0000000..5ca3422 --- /dev/null +++ b/src/bin/d2/d2_process.h @@ -0,0 +1,324 @@ +// Copyright (C) 2013-2019 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_PROCESS_H +#define D2_PROCESS_H + +#include <d2/d2_queue_mgr.h> +#include <d2/d2_update_mgr.h> +#include <process/d_process.h> + +namespace isc { +namespace d2 { + +/// @brief DHCP-DDNS Application Process +/// +/// D2Process provides the top level application logic for DHCP-driven DDNS +/// update processing. It provides the asynchronous event processing required +/// to receive DNS mapping change requests and carry them out. +/// It implements the DProcessBase interface, which structures it such that it +/// is a managed "application", controlled by a management layer. +class D2Process : public process::DProcessBase { +public: + + /// @brief Defines the shutdown types supported by D2Process + /// + /// * SD_NORMAL - Stops the queue manager and finishes all current + /// transactions before exiting. This is the default. + /// + /// * SD_DRAIN_FIRST - Stops the queue manager but continues processing + /// requests from the queue until it is empty. + /// + /// * SD_NOW - Exits immediately. + enum ShutdownType { + SD_NORMAL, + SD_DRAIN_FIRST, + SD_NOW + }; + + /// @brief Defines the point at which to resume receiving requests. + /// If the receive queue has become full, D2Process will "pause" the + /// reception of requests by putting the queue manager in the stopped + /// state. Once the number of entries has decreased to this percentage + /// of the maximum allowed, D2Process will "resume" receiving requests + /// by restarting the queue manager. + static const unsigned int QUEUE_RESTART_PERCENT; + + /// @brief Constructor + /// + /// Construction creates the configuration manager, the queue + /// manager, and the update manager. + /// + /// @param name name is a text label for the process. Generally used + /// in log statements, but otherwise arbitrary. + /// @param io_service is the io_service used by the caller for + /// asynchronous event handling. + /// + /// @throw DProcessBaseError if io_service is NULL. + D2Process(const char* name, const asiolink::IOServicePtr& io_service); + + /// @brief Called after instantiation to perform initialization unique to + /// D2. + /// + /// This is invoked by the controller after command line arguments but + /// PRIOR to configuration reception. The base class provides this method + /// as a place to perform any derivation-specific initialization steps + /// that are inappropriate for the constructor but necessary prior to + /// configure. + /// For D2 it is used to initialize the command manager. + virtual void init(); + + /// @brief Implements the process's event loop. + /// + /// Once entered, the main control thread remains inside this method + /// until shutdown. The event loop logic is as follows: + /// @code + /// while should not shutdown { + /// process queue manager state change + /// process completed jobs + /// dequeue new jobs + /// wait for IO event(s) + /// + /// ON an exception, exit with fatal error + /// } + /// @endcode + /// + /// To summarize, each pass through the event loop first checks the state + /// of the received queue and takes any steps required to ensure it is + /// operating in the manner necessary. Next the update manager is given + /// a chance to clean up any completed transactions and start new + /// transactions by dequeuing jobs from the request queue. Lastly, it + /// allows IOService to process until one or more event handlers are + /// called. Note that this last step will block until at least one + /// ready handler is invoked. In other words, if no IO events have occurred + /// since it was last called, the event loop will block at this step until + /// an IO event occurs. At that time we return to the top of the loop. + /// + /// @throw DProcessBaseError if an error is encountered. Note that + /// exceptions thrown at this point are assumed to be FATAL exceptions. + /// This includes exceptions generated but not caught by IO callbacks. + /// Services which rely on callbacks are expected to be well behaved and + /// any errors they encounter handled internally. + virtual void run(); + + /// @brief Initiates the D2Process shutdown process. + /// + /// This is last step in the shutdown event callback chain. It is invoked + /// to notify D2Process that it needs to begin its shutdown procedure. + /// Note that shutting down may be neither instantaneous nor synchronous, + /// This method records the request for and the type of shutdown desired. + /// Generally it will require one or more subsequent events to complete, + /// dependent on the type of shutdown requested. The type of shutdown is + /// specified as an optional argument of the shutdown command. The types + /// of shutdown supported are: + /// + /// * "normal" - Stops the queue manager and finishes all current + /// transactions before exiting. This is the default. + /// + /// * "drain_first" - Stops the queue manager but continues processing + /// requests from the queue until it is empty. + /// + /// * "now" - Exits immediately. + /// + /// @param args Specifies the shutdown "type" as "normal", "drain_first", + /// or "now" + /// + /// @return an Element that contains the results of argument processing, + /// consisting of an integer status value (0 means successful, + /// non-zero means failure), and a string explanation of the outcome. + virtual isc::data::ConstElementPtr + shutdown(isc::data::ConstElementPtr args); + + /// @brief Processes the given configuration. + /// + /// This method may be called multiple times during the process lifetime. + /// Certainly once during process startup, and possibly later if the user + /// alters the configuration. This method must not throw, it should catch any + /// processing errors and return a success or failure answer as described + /// below. + /// + /// This method passes the newly received configuration to the configuration + /// manager instance for parsing. The configuration manager parses the + /// configuration and updates the necessary values within the context, + /// assuming it parses correctly. If that's the case this method sets the + /// flag to reconfigure the queue manager and returns a successful response + /// as described below. + /// + /// If the new configuration fails to parse, then the current configuration + /// is retained and a failure response is returned as described below. + /// + /// @param config_set a new configuration (JSON) for the process + /// @param check_only true if configuration is to be verified only, not applied + /// @return an Element that contains the results of configuration composed + /// of an integer status value (0 means successful, non-zero means failure), + /// and a string explanation of the outcome. + virtual isc::data::ConstElementPtr + configure(isc::data::ConstElementPtr config_set, + bool check_only = false); + + /// @brief Destructor + virtual ~D2Process(); + +protected: + /// @brief Monitors current queue manager state, takes action accordingly + /// + /// This method ensures that the queue manager transitions to the state + /// most appropriate to the operational state of the D2Process and any + /// events that may have occurred since it was last called. It is called + /// once for each iteration of the event loop. It is essentially a + /// switch statement based on the D2QueueMgr's current state. The logic + /// is as follows: + /// + /// If the state is D2QueueMgr::RUNNING, and the queue manager needs to be + /// reconfigured or we have been told to shutdown, then instruct the queue + /// manager to stop listening. Exit the method. + /// + /// If the state is D2QueueMgr::STOPPED_QUEUE_FULL, then check if the + /// number of entries in the queue has fallen below the "resume threshold". + /// If it has, then instruct the queue manager to start listening. Exit + /// the method. + /// + /// If the state is D2QueueMgr::STOPPED_RECV_ERROR, then attempt to recover + /// by calling reconfigureQueueMgr(). Exit the method. + /// + /// If the state is D2QueueMgr::STOPPING, simply exit the method. This is + /// a NOP condition as we are waiting for the IO cancel event + /// + /// For any other state, (NOT_INITTED,INITTED,STOPPED), if the reconfigure + /// queue flag is set, call reconfigureQueueMgr(). Exit the method. + /// + /// This method is exception safe. + virtual void checkQueueStatus(); + + /// @brief Initializes then starts the queue manager. + /// + /// This method initializes the queue manager with the current + /// configuration parameters and instructs it to start listening. + /// Note the existing listener instance (if it exists) is destroyed, + /// and that a new listener is created during initialization. + /// + /// This method is exception safe. + virtual void reconfigureQueueMgr(); + + /// @brief Allows IO processing to run until at least callback is invoked. + /// + /// This method is called from within the D2Process main event loop and is + /// the point at which the D2Process blocks, waiting for IO events to + /// cause IO event callbacks to be invoked. + /// + /// If callbacks are ready to be executed upon entry, the method will + /// return as soon as these callbacks have completed. If no callbacks + /// are ready, then it will wait (indefinitely) until at least one callback + /// is executed. + /// + /// @note: Should become desirable to periodically force an + /// event, an interval timer could be used to do so. + /// + /// @return The number of callback handlers executed, or 0 if the IO + /// service has been stopped. + /// + /// @throw This method does not throw directly, but the execution of + /// callbacks invoked in response to IO events might. If so, these + /// will propagate upward out of this method. + virtual size_t runIO(); + + /// @brief Indicates whether or not the process can perform a shutdown. + /// + /// Determines if the process has been instructed to shutdown and if + /// the criteria for performing the type of shutdown requested has been + /// met. + /// + /// @return Returns true if the criteria has been met, false otherwise. + virtual bool canShutdown() const; + + /// @brief Sets queue reconfigure indicator to the given value. + /// + /// @param value is the new value to assign to the indicator + /// + /// @note this method is really only intended for testing purposes. + void setReconfQueueFlag(const bool value) { + reconf_queue_flag_ = value; + } + + /// @brief Sets the shutdown type to the given value. + /// + /// @param value is the new value to assign to shutdown type. + /// + /// @note this method is really only intended for testing purposes. + void setShutdownType(const ShutdownType& value) { + shutdown_type_ = value; + } + + /// @brief (Re-)Configure the command channel. + /// + /// Only close the current channel, if the new channel configuration is + /// different. This avoids disconnecting a client and hence not sending + /// them a command result, unless they specifically alter the channel + /// configuration. In that case the user simply has to accept they'll + /// be disconnected. + void reconfigureCommandChannel(); + +public: + /// @brief Returns a pointer to the configuration manager. + /// Note, this method cannot return a reference as it uses dynamic + /// pointer casting of the base class configuration manager. + D2CfgMgrPtr getD2CfgMgr(); + + /// @brief Returns a reference to the queue manager. + const D2QueueMgrPtr& getD2QueueMgr() const { + return (queue_mgr_); + } + + /// @brief Returns a reference to the update manager. + const D2UpdateMgrPtr& getD2UpdateMgr() const { + return (update_mgr_); + } + + /// @brief Returns true if the queue manager should be reconfigured. + bool getReconfQueueFlag() const { + return (reconf_queue_flag_); + } + + /// @brief Returns the type of shutdown requested. + /// + /// Note, this value is meaningless unless shouldShutdown() returns true. + ShutdownType getShutdownType() const { + return (shutdown_type_); + } + + /// @brief Returns a text label for the given shutdown type. + /// + /// @param type the numerical shutdown type for which the label is desired. + /// + /// @return A text label corresponding the value or "invalid" if the + /// value is not a valid value. + static const char* getShutdownTypeStr(const ShutdownType& type); + +private: + /// @brief Pointer to our queue manager instance. + D2QueueMgrPtr queue_mgr_; + + /// @brief Pointer to our update manager instance. + D2UpdateMgrPtr update_mgr_; + + /// @brief Indicates if the queue manager should be reconfigured. + bool reconf_queue_flag_; + + /// @brief Indicates the type of shutdown requested. + ShutdownType shutdown_type_; + + /// @brief Current socket control configuration. + isc::data::ConstElementPtr current_control_socket_; + +}; + +/// @brief Defines a shared pointer to D2Process. +typedef boost::shared_ptr<D2Process> D2ProcessPtr; + +}; // namespace isc::d2 +}; // namespace isc + +#endif diff --git a/src/bin/d2/d2_queue_mgr.cc b/src/bin/d2/d2_queue_mgr.cc new file mode 100644 index 0000000..71949b3 --- /dev/null +++ b/src/bin/d2/d2_queue_mgr.cc @@ -0,0 +1,264 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <d2/d2_queue_mgr.h> +#include <d2srv/d2_log.h> +#include <dhcp_ddns/ncr_udp.h> + +namespace isc { +namespace d2 { + +// Makes constant visible to Google test macros. +const size_t D2QueueMgr::MAX_QUEUE_DEFAULT; + +D2QueueMgr::D2QueueMgr(asiolink::IOServicePtr& io_service, const size_t max_queue_size) + : io_service_(io_service), max_queue_size_(max_queue_size), + mgr_state_(NOT_INITTED), target_stop_state_(NOT_INITTED) { + if (!io_service_) { + isc_throw(D2QueueMgrError, "IOServicePtr cannot be null"); + } + + // Use setter to do validation. + setMaxQueueSize(max_queue_size); +} + +D2QueueMgr::~D2QueueMgr() { +} + +void +D2QueueMgr::operator()(const dhcp_ddns::NameChangeListener::Result result, + dhcp_ddns::NameChangeRequestPtr& ncr) { + try { + // Note that error conditions must be handled here without throwing + // exceptions. Remember this is the application level "link" in the + // callback chain. Throwing an exception here will "break" the + // io_service "run" we are operating under. With that in mind, + // if we hit a problem, we will stop the listener transition to + // the appropriate stopped state. Upper layer(s) must monitor our + // state as well as our queue size. + switch (result) { + case dhcp_ddns::NameChangeListener::SUCCESS: + // Receive was successful, attempt to queue the request. + if (getQueueSize() < getMaxQueueSize()) { + // There's room on the queue, add to the end + enqueue(ncr); + + // Log that we got the request + LOG_DEBUG(dhcp_to_d2_logger, + isc::log::DBGLVL_TRACE_DETAIL_DATA, + DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE) + .arg(ncr->getRequestId()); + return; + } + + // Queue is full, stop the listener. + // Note that we can move straight to a STOPPED state as there + // is no receive in progress. + LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_QUEUE_MGR_QUEUE_FULL) + .arg(max_queue_size_); + stopListening(STOPPED_QUEUE_FULL); + break; + + case dhcp_ddns::NameChangeListener::STOPPED: + if (mgr_state_ == STOPPING) { + // This is confirmation that the listener has stopped and its + // callback will not be called again, unless its restarted. + updateStopState(); + } else { + // We should not get a receive complete status of stopped + // unless we canceled the read as part of stopping. Therefore + // this is unexpected so we will treat it as a receive error. + // This is most likely an unforeseen programmatic issue. + LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP) + .arg(mgr_state_); + stopListening(STOPPED_RECV_ERROR); + } + + break; + + default: + // Receive failed, stop the listener. + // Note that we can move straight to a STOPPED state as there + // is no receive in progress. + LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_QUEUE_MGR_RECV_ERROR); + stopListening(STOPPED_RECV_ERROR); + break; + } + } catch (const std::exception& ex) { + // On the outside chance a throw occurs, let's log it and swallow it. + LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR) + .arg(ex.what()); + } +} + +void +D2QueueMgr::initUDPListener(const isc::asiolink::IOAddress& ip_address, + const uint32_t port, + const dhcp_ddns::NameChangeFormat format, + const bool reuse_address) { + + if (listener_) { + isc_throw(D2QueueMgrError, + "D2QueueMgr listener is already initialized"); + } + + // Instantiate a UDP listener and set state to INITTED. + // Note UDP listener constructor does not throw. + listener_.reset(new dhcp_ddns:: + NameChangeUDPListener(ip_address, port, format, *this, + reuse_address)); + mgr_state_ = INITTED; +} + +void +D2QueueMgr::startListening() { + // We can't listen if we haven't initialized the listener yet. + if (!listener_) { + isc_throw(D2QueueMgrError, "D2QueueMgr " + "listener is not initialized, cannot start listening"); + } + + // If we are already listening, we do not want to "reopen" the listener + // and really we shouldn't be trying. + if (mgr_state_ == RUNNING) { + isc_throw(D2QueueMgrError, "D2QueueMgr " + "cannot call startListening from the RUNNING state"); + } + + // Instruct the listener to start listening and set state accordingly. + try { + listener_->startListening(*io_service_); + mgr_state_ = RUNNING; + } catch (const isc::Exception& ex) { + isc_throw(D2QueueMgrError, "D2QueueMgr listener start failed: " + << ex.what()); + } + + LOG_DEBUG(d2_logger, isc::log::DBGLVL_START_SHUT, + DHCP_DDNS_QUEUE_MGR_STARTED); +} + +void +D2QueueMgr::stopListening(const State target_stop_state) { + if (listener_) { + // Enforce only valid "stop" states. + // This is purely a programmatic error and should never happen. + if (target_stop_state != STOPPED && + target_stop_state != STOPPED_QUEUE_FULL && + target_stop_state != STOPPED_RECV_ERROR) { + isc_throw(D2QueueMgrError, + "D2QueueMgr invalid value for stop state: " + << target_stop_state); + } + + // Remember the state we want to achieve. + target_stop_state_ = target_stop_state; + + // Instruct the listener to stop. If the listener reports that it + // has IO pending, then we transition to STOPPING to wait for the + // cancellation event. Otherwise, we can move directly to the targeted + // state. + listener_->stopListening(); + if (listener_->isIoPending()) { + mgr_state_ = STOPPING; + } else { + updateStopState(); + } + } +} + +void +D2QueueMgr::updateStopState() { + mgr_state_ = target_stop_state_; + LOG_DEBUG(d2_logger, isc::log::DBGLVL_TRACE_BASIC, + DHCP_DDNS_QUEUE_MGR_STOPPED); +} + + +void +D2QueueMgr::removeListener() { + // Force our managing layer(s) to stop us properly first. + if (mgr_state_ == RUNNING) { + isc_throw(D2QueueMgrError, + "D2QueueMgr cannot delete listener while state is RUNNING"); + } + + listener_.reset(); + mgr_state_ = NOT_INITTED; +} + +const dhcp_ddns::NameChangeRequestPtr& +D2QueueMgr::peek() const { + if (getQueueSize() == 0) { + isc_throw(D2QueueMgrQueueEmpty, + "D2QueueMgr peek attempted on an empty queue"); + } + + return (ncr_queue_.front()); +} + +const dhcp_ddns::NameChangeRequestPtr& +D2QueueMgr::peekAt(const size_t index) const { + if (index >= getQueueSize()) { + isc_throw(D2QueueMgrInvalidIndex, + "D2QueueMgr peek beyond end of queue attempted" + << " index: " << index << " queue size: " << getQueueSize()); + } + + return (ncr_queue_.at(index)); +} + +void +D2QueueMgr::dequeueAt(const size_t index) { + if (index >= getQueueSize()) { + isc_throw(D2QueueMgrInvalidIndex, + "D2QueueMgr dequeue beyond end of queue attempted" + << " index: " << index << " queue size: " << getQueueSize()); + } + + RequestQueue::iterator pos = ncr_queue_.begin() + index; + ncr_queue_.erase(pos); +} + + +void +D2QueueMgr::dequeue() { + if (getQueueSize() == 0) { + isc_throw(D2QueueMgrQueueEmpty, + "D2QueueMgr dequeue attempted on an empty queue"); + } + + ncr_queue_.pop_front(); +} + +void +D2QueueMgr::enqueue(dhcp_ddns::NameChangeRequestPtr& ncr) { + ncr_queue_.push_back(ncr); +} + +void +D2QueueMgr::clearQueue() { + ncr_queue_.clear(); +} + +void +D2QueueMgr::setMaxQueueSize(const size_t new_queue_max) { + if (new_queue_max < 1) { + isc_throw(D2QueueMgrError, + "D2QueueMgr maximum queue size must be greater than zero"); + } + + if (new_queue_max < getQueueSize()) { + isc_throw(D2QueueMgrError, "D2QueueMgr maximum queue size value cannot" + " be less than the current queue size :" << getQueueSize()); + } + + max_queue_size_ = new_queue_max; +} + +} // namespace isc::d2 +} // namespace isc diff --git a/src/bin/d2/d2_queue_mgr.h b/src/bin/d2/d2_queue_mgr.h new file mode 100644 index 0000000..bbd95ea --- /dev/null +++ b/src/bin/d2/d2_queue_mgr.h @@ -0,0 +1,348 @@ +// Copyright (C) 2013-2015,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_QUEUE_MGR_H +#define D2_QUEUE_MGR_H + +/// @file d2_queue_mgr.h This file defines the class D2QueueMgr. + +#include <asiolink/io_service.h> +#include <exceptions/exceptions.h> +#include <dhcp_ddns/ncr_msg.h> +#include <dhcp_ddns/ncr_io.h> + +#include <boost/noncopyable.hpp> +#include <deque> + +namespace isc { +namespace d2 { + +/// @brief Defines a queue of requests. +/// @todo This may be replaced with an actual class in the future. +typedef std::deque<dhcp_ddns::NameChangeRequestPtr> RequestQueue; + +/// @brief Thrown if the queue manager encounters a general error. +class D2QueueMgrError : public isc::Exception { +public: + D2QueueMgrError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Thrown if the queue manager's receive handler is passed +/// a failure result. +/// @todo use or remove it. +class D2QueueMgrReceiveError : public isc::Exception { +public: + D2QueueMgrReceiveError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + + +/// @brief Thrown if the request queue is full when an enqueue is attempted. +/// @todo use or remove it. +class D2QueueMgrQueueFull : public isc::Exception { +public: + D2QueueMgrQueueFull(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Thrown if the request queue empty and a read is attempted. +class D2QueueMgrQueueEmpty : public isc::Exception { +public: + D2QueueMgrQueueEmpty(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Thrown if a queue index is beyond the end of the queue +class D2QueueMgrInvalidIndex : public isc::Exception { +public: + D2QueueMgrInvalidIndex(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + + +/// @brief D2QueueMgr creates and manages a queue of DNS update requests. +/// +/// D2QueueMgr is a class specifically designed as an integral part of DHCP-DDNS. +/// Its primary responsibility is to listen for NameChangeRequests from +/// DHCP-DDNS clients (e.g. DHCP servers) and queue them for processing. In +/// addition it may provide a number of services to locate entries in the queue +/// such as by FQDN or DHCID. These services may eventually be used +/// for processing optimization. The initial implementation will support +/// simple FIFO access. +/// +/// D2QueueMgr uses a NameChangeListener to asynchronously receive requests. +/// It derives from NameChangeListener::RequestReceiveHandler and supplies an +/// implementation of the operator()(Result, NameChangeRequestPtr). It is +/// through this operator() that D2QueueMgr is passed inbound NCRs. D2QueueMgr +/// will add each newly received request onto the back of the request queue +/// +/// D2QueueMgr defines a simple state model constructed around the status of +/// its NameChangeListener, consisting of the following states: +/// +/// * NOT_INITTED - D2QueueMgr has been constructed, but its listener has +/// not been initialized. +/// +/// * INITTED - The listener has been initialized, but it is not open for +/// listening. To move from NOT_INITTED to INITTED, one of the D2QueueMgr +/// listener initialization methods must be invoked. Currently there is +/// only one type of listener, NameChangeUDPListener, hence there is only +/// one listener initialization method, initUDPListener. As more listener +/// types are created, listener initialization methods will need to be +/// added. +/// +/// * RUNNING - The listener is open and listening for requests. +/// Once initialized, in order to begin listening for requests, the +/// startListener() method must be invoked. Upon successful completion of +/// of this call, D2QueueMgr will begin receiving requests as they arrive +/// without any further steps. This method may be called from the INITTED +/// or one of the STOPPED states. +/// +/// * STOPPING - The listener is in the process of stopping active +/// listening. This is transitory state between RUNNING and STOPPED, which +/// is completed by IO cancellation event. +/// +/// * STOPPED - The listener has been listening but has been stopped +/// without error. To return to listening, startListener() must be invoked. +/// +/// * STOPPED_QUEUE_FULL - Request queue is full, the listener has been +/// stopped. D2QueueMgr will enter this state when the request queue +/// reaches the maximum queue size. Once this limit is reached, the +/// listener will be closed and no further requests will be received. +/// To return to listening, startListener() must be invoked. Note that so +/// long as the queue is full, any attempt to queue a request will fail. +/// +/// * STOPPED_RECV_ERROR - The listener has experienced a receive error +/// and has been stopped. D2QueueMgr will enter this state when it is +/// passed a failed status into the request completion handler. To return +/// to listening, startListener() must be invoked. +/// +/// D2QueueMgr does not attempt to recover from stopped conditions, this is left +/// to upper layers. +/// +/// It is important to note that the queue contents are preserved between +/// state transitions. In other words entries in the queue remain there +/// until they are removed explicitly via the deque() or implicitly by +/// via the clearQueue() method. +/// +class D2QueueMgr : public dhcp_ddns::NameChangeListener::RequestReceiveHandler, + boost::noncopyable { +public: + /// @brief Maximum number of entries allowed in the request queue. + /// NOTE that 1024 is an arbitrary choice picked for the initial + /// implementation. + static const size_t MAX_QUEUE_DEFAULT = 1024; + + /// @brief Defines the list of possible states for D2QueueMgr. + enum State { + NOT_INITTED, + INITTED, + RUNNING, + STOPPING, + STOPPED_QUEUE_FULL, + STOPPED_RECV_ERROR, + STOPPED, + }; + + /// @brief Constructor + /// + /// Creates a D2QueueMgr instance. Note that the listener is not created + /// in the constructor. The initial state will be NOT_INITTED. + /// + /// @param io_service IOService instance to be passed into the listener for + /// IO management. + /// @param max_queue_size the maximum number of entries allowed in the + /// queue. + /// This value must be greater than zero. It defaults to MAX_QUEUE_DEFAULT. + /// + /// @throw D2QueueMgrError if max_queue_size is zero. + D2QueueMgr(asiolink::IOServicePtr& io_service, + const size_t max_queue_size = MAX_QUEUE_DEFAULT); + + /// @brief Destructor + virtual ~D2QueueMgr(); + + /// @brief Initializes the listener as a UDP listener. + /// + /// Instantiates the listener_ member as NameChangeUDPListener passing + /// the given parameters. Upon successful completion, the D2QueueMgr state + /// will be INITTED. + /// + /// @param ip_address is the network address on which to listen + /// @param port is the IP port on which to listen + /// @param format is the wire format of the inbound requests. + /// @param reuse_address enables IP address sharing when true + /// It defaults to false. + void initUDPListener(const isc::asiolink::IOAddress& ip_address, + const uint32_t port, + const dhcp_ddns::NameChangeFormat format, + const bool reuse_address = false); + + /// @brief Starts actively listening for requests. + /// + /// Invokes the listener's startListening method passing in our + /// IOService instance. + /// + /// @throw D2QueueMgrError if the listener has not been initialized, + /// state is already RUNNING, or the listener fails to actually start. + void startListening(); + + /// @brief Function operator implementing the NCR receive callback. + /// + /// This method is invoked by the listener as part of its receive + /// completion callback and is how the inbound NameChangeRequests are + /// passed up to the D2QueueMgr for queuing. + /// If the given result indicates a successful receive completion and + /// there is room left in the queue, the given request is queued. + /// + /// If the queue is at maximum capacity, stopListening() is invoked and + /// the state is set to STOPPED_QUEUE_FULL. + /// + /// If the result indicates IO stopped, then the state is set to STOPPED. + /// Note this is not an error, it results from a deliberate cancellation + /// of listener IO as part of a normal stopListener call. + /// + /// If the result indicates a failed receive, stopListening() is invoked + /// and the state is set to STOPPED_RECV_ERROR. + /// + /// This method specifically avoids throwing on an error as any such throw + /// would surface at the io_service::run (or run variant) method invocation + /// site. The upper layers are expected to monitor D2QueueMgr's state and + /// act accordingly. + /// + /// @param result contains that receive outcome status. + /// @param ncr is a pointer to the newly received NameChangeRequest if + /// result is NameChangeListener::SUCCESS. It is indeterminate other + /// wise. + virtual void operator ()(const dhcp_ddns::NameChangeListener::Result result, + dhcp_ddns::NameChangeRequestPtr& ncr); + + /// @brief Stops listening for requests. + /// + /// Invokes the listener's stopListening method which will cause it to + /// cancel any pending IO and close its IO source. It the sets target + /// stop state to the given value. + /// + /// If there is no IO pending, the manager state is immediately set to the + /// target stop state, otherwise the manager state is set to STOPPING. + /// + /// @param target_stop_state is one of the three stopped state values. + /// + /// @throw D2QueueMgrError if stop_state is a valid stop state. + void stopListening(const State target_stop_state = STOPPED); + + + /// @brief Deletes the current listener + /// + /// This method will delete the current listener and returns the manager + /// to the NOT_INITTED state. This is provided to support reconfiguring + /// a new listener without losing queued requests. + /// + /// @throw D2QueueMgrError if called when the manager state is RUNNING. + void removeListener(); + + /// @brief Returns the number of entries in the queue. + size_t getQueueSize() const { + return (ncr_queue_.size()); + }; + + /// @brief Returns the maximum number of entries allowed in the queue. + size_t getMaxQueueSize() const { + return (max_queue_size_); + } + + /// @brief Sets the maximum number of entries allowed in the queue. + /// + /// @param max_queue_size is the new maximum size of the queue. + /// + /// @throw D2QueueMgrError if the new value is less than one or if + /// the new value is less than the number of entries currently in the + /// queue. + void setMaxQueueSize(const size_t max_queue_size); + + /// @brief Returns the current state. + State getMgrState() const { + return (mgr_state_); + } + + /// @brief Returns the entry at the front of the queue. + /// + /// The entry returned is next in line to be processed, assuming a FIFO + /// approach to task selection. Note, the entry is not removed from the + /// queue. + /// + /// @return Pointer reference to the queue entry. + /// + /// @throw D2QueueMgrQueueEmpty if there are no entries in the queue. + const dhcp_ddns::NameChangeRequestPtr& peek() const; + + /// @brief Returns the entry at a given position in the queue. + /// + /// Note that the entry is not removed from the queue. + /// @param index the index of the entry in the queue to fetch. + /// Valid values are 0 (front of the queue) to (queue size - 1). + /// + /// @return Pointer reference to the queue entry. + /// + /// @throw D2QueueMgrInvalidIndex if the given index is beyond the + /// end of the queue. + const dhcp_ddns::NameChangeRequestPtr& peekAt(const size_t index) const; + + /// @brief Removes the entry at a given position in the queue. + /// + /// @param index the index of the entry in the queue to remove. + /// Valid values are 0 (front of the queue) to (queue size - 1). + /// + /// @throw D2QueueMgrInvalidIndex if the given index is beyond the + /// end of the queue. + void dequeueAt(const size_t index); + + /// @brief Removes the entry at the front of the queue. + /// + /// @throw D2QueueMgrQueueEmpty if there are no entries in the queue. + void dequeue(); + + /// @brief Adds a request to the end of the queue. + /// + /// @param ncr pointer to the NameChangeRequest to add to the queue. + void enqueue(dhcp_ddns::NameChangeRequestPtr& ncr); + + /// @brief Removes all entries from the queue. + void clearQueue(); + + private: + /// @brief Sets the manager state to the target stop state. + /// + /// Convenience method which sets the manager state to the target stop + /// state and logs that the manager is stopped. + void updateStopState(); + + /// @brief IOService that our listener should use for IO management. + asiolink::IOServicePtr io_service_; + + /// @brief Dictates the maximum number of entries allowed in the queue. + size_t max_queue_size_; + + /// @brief Queue of received NameChangeRequests. + RequestQueue ncr_queue_; + + /// @brief Listener instance from which requests are received. + boost::shared_ptr<dhcp_ddns::NameChangeListener> listener_; + + /// @brief Current state of the manager. + State mgr_state_; + + /// @brief Tracks the state the manager should be in once stopped. + State target_stop_state_; +}; + +/// @brief Defines a pointer for manager instances. +typedef boost::shared_ptr<D2QueueMgr> D2QueueMgrPtr; + +} // namespace isc::d2 +} // namespace isc + +#endif diff --git a/src/bin/d2/d2_update_mgr.cc b/src/bin/d2/d2_update_mgr.cc new file mode 100644 index 0000000..223430e --- /dev/null +++ b/src/bin/d2/d2_update_mgr.cc @@ -0,0 +1,295 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <d2/d2_update_mgr.h> +#include <d2/nc_add.h> +#include <d2/nc_remove.h> +#include <d2/simple_add.h> +#include <d2/simple_remove.h> + +#include <sstream> +#include <iostream> +#include <vector> + +namespace isc { +namespace d2 { + +const size_t D2UpdateMgr::MAX_TRANSACTIONS_DEFAULT; + +D2UpdateMgr::D2UpdateMgr(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr, + asiolink::IOServicePtr& io_service, + const size_t max_transactions) + :queue_mgr_(queue_mgr), cfg_mgr_(cfg_mgr), io_service_(io_service) { + if (!queue_mgr_) { + isc_throw(D2UpdateMgrError, "D2UpdateMgr queue manager cannot be null"); + } + + if (!cfg_mgr_) { + isc_throw(D2UpdateMgrError, + "D2UpdateMgr configuration manager cannot be null"); + } + + if (!io_service_) { + isc_throw(D2UpdateMgrError, "IOServicePtr cannot be null"); + } + + // Use setter to do validation. + setMaxTransactions(max_transactions); +} + +D2UpdateMgr::~D2UpdateMgr() { + transaction_list_.clear(); +} + +void D2UpdateMgr::sweep() { + // cleanup finished transactions; + checkFinishedTransactions(); + + // if the queue isn't empty, find the next suitable job and + // start a transaction for it. + // @todo - Do we want to queue max transactions? The logic here will only + // start one new transaction per invocation. On the other hand a busy + // system will generate many IO events and this method will be called + // frequently. It will likely achieve max transactions quickly on its own. + if (getQueueCount() > 0) { + if (getTransactionCount() >= max_transactions_) { + LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA, + DHCP_DDNS_AT_MAX_TRANSACTIONS).arg(getQueueCount()) + .arg(getMaxTransactions()); + + return; + } + + // We are not at maximum transactions, so pick and start the next job. + pickNextJob(); + } +} + +void +D2UpdateMgr::checkFinishedTransactions() { + // Cycle through transaction list and do whatever needs to be done + // for finished transactions. + // At the moment all we do is remove them from the list. This is likely + // to expand as DHCP_DDNS matures. + // NOTE: One must use postfix increments of the iterator on the calls + // to erase. This replaces the old iterator which becomes invalid by the + // erase with the next valid iterator. Prefix incrementing will not + // work. + TransactionList::iterator it = transaction_list_.begin(); + while (it != transaction_list_.end()) { + NameChangeTransactionPtr trans = (*it).second; + if (trans->isModelDone()) { + // @todo Additional actions based on NCR status could be + // performed here. + transaction_list_.erase(it++); + } else { + ++it; + } + } +} + +void D2UpdateMgr::pickNextJob() { + // Start at the front of the queue, looking for the first entry for + // which no transaction is in progress. If we find an eligible entry + // remove it from the queue and make a transaction for it. + // Requests and transactions are associated by DHCID. If a request has + // the same DHCID as a transaction, they are presumed to be for the same + // "end user". + size_t queue_count = getQueueCount(); + for (size_t index = 0; index < queue_count; ++index) { + dhcp_ddns::NameChangeRequestPtr found_ncr = queue_mgr_->peekAt(index); + if (!hasTransaction(found_ncr->getDhcid())) { + queue_mgr_->dequeueAt(index); + makeTransaction(found_ncr); + return; + } + } + + // There were no eligible jobs. All of the current DHCIDs already have + // transactions pending. + LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA, + DHCP_DDNS_NO_ELIGIBLE_JOBS) + .arg(getQueueCount()).arg(getTransactionCount()); +} + +void +D2UpdateMgr::makeTransaction(dhcp_ddns::NameChangeRequestPtr& next_ncr) { + // First lets ensure there is not a transaction in progress for this + // DHCID. (pickNextJob should ensure this, as it is the only real caller + // but for safety's sake we'll check). + const TransactionKey& key = next_ncr->getDhcid(); + if (findTransaction(key) != transactionListEnd()) { + // This is programmatic error. Caller(s) should be checking this. + isc_throw(D2UpdateMgrError, "Transaction already in progress for: " + << key.toStr()); + } + + int direction_count = 0; + // If forward change is enabled, match to forward servers. + DdnsDomainPtr forward_domain; + if (next_ncr->isForwardChange()) { + if (!cfg_mgr_->forwardUpdatesEnabled()) { + next_ncr->setForwardChange(false); + LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA, + DHCP_DDNS_FWD_REQUEST_IGNORED) + .arg(next_ncr->getRequestId()) + .arg(next_ncr->toText()); + } else { + bool matched = cfg_mgr_->matchForward(next_ncr->getFqdn(), + forward_domain); + // Could not find a match for forward DNS server. Log it and get + // out. This has the net affect of dropping the request on the + // floor. + if (!matched) { + LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_NO_FWD_MATCH_ERROR) + .arg(next_ncr->getRequestId()) + .arg(next_ncr->toText()); + return; + } + + ++direction_count; + } + } + + // If reverse change is enabled, match to reverse servers. + DdnsDomainPtr reverse_domain; + if (next_ncr->isReverseChange()) { + if (!cfg_mgr_->reverseUpdatesEnabled()) { + next_ncr->setReverseChange(false); + LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA, + DHCP_DDNS_REV_REQUEST_IGNORED) + .arg(next_ncr->getRequestId()) + .arg(next_ncr->toText()); + } else { + bool matched = cfg_mgr_->matchReverse(next_ncr->getIpAddress(), + reverse_domain); + // Could not find a match for reverse DNS server. Log it and get + // out. This has the net affect of dropping the request on the + // floor. + if (!matched) { + LOG_ERROR(dhcp_to_d2_logger, DHCP_DDNS_NO_REV_MATCH_ERROR) + .arg(next_ncr->getRequestId()) + .arg(next_ncr->toText()); + return; + } + + ++direction_count; + } + } + + // If there is nothing to actually do, then the request falls on the floor. + // Should we log this? + if (!direction_count) { + LOG_DEBUG(dhcp_to_d2_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA, + DHCP_DDNS_REQUEST_DROPPED) + .arg(next_ncr->getRequestId()) + .arg(next_ncr->toText()); + return; + } + + // We matched to the required servers, so construct the transaction. + // @todo If multi-threading is implemented, one would pass in an + // empty IOServicePtr, rather than our instance value. This would cause + // the transaction to instantiate its own, separate IOService to handle + // the transaction's IO. + NameChangeTransactionPtr trans; + if (next_ncr->getChangeType() == dhcp_ddns::CHG_ADD) { + if (next_ncr->useConflictResolution()) { + trans.reset(new NameAddTransaction(io_service_, next_ncr, + forward_domain, reverse_domain, + cfg_mgr_)); + } else { + trans.reset(new SimpleAddTransaction(io_service_, next_ncr, + forward_domain, reverse_domain, + cfg_mgr_)); + } + } else { + if (next_ncr->useConflictResolution()) { + trans.reset(new NameRemoveTransaction(io_service_, next_ncr, + forward_domain, reverse_domain, + cfg_mgr_)); + } else { + trans.reset(new SimpleRemoveTransaction(io_service_, next_ncr, + forward_domain, reverse_domain, + cfg_mgr_)); + } + } + + // Add the new transaction to the list. + transaction_list_[key] = trans; + + // Start it. + trans->startTransaction(); +} + +TransactionList::iterator +D2UpdateMgr::findTransaction(const TransactionKey& key) { + return (transaction_list_.find(key)); +} + +bool +D2UpdateMgr::hasTransaction(const TransactionKey& key) { + return (findTransaction(key) != transactionListEnd()); +} + +void +D2UpdateMgr::removeTransaction(const TransactionKey& key) { + TransactionList::iterator pos = findTransaction(key); + if (pos != transactionListEnd()) { + transaction_list_.erase(pos); + } +} + +TransactionList::iterator +D2UpdateMgr::transactionListBegin() { + return (transaction_list_.begin()); +} + +TransactionList::iterator +D2UpdateMgr::transactionListEnd() { + return (transaction_list_.end()); +} + +void +D2UpdateMgr::clearTransactionList() { + // @todo for now this just wipes them out. We might need something + // more elegant, that allows a cancel first. + transaction_list_.clear(); +} + +void +D2UpdateMgr::setMaxTransactions(const size_t new_trans_max) { + // Obviously we need at room for at least one transaction. + if (new_trans_max < 1) { + isc_throw(D2UpdateMgrError, "D2UpdateMgr" + " maximum transactions limit must be greater than zero"); + } + + // Do not allow the list maximum to be set to less then current list size. + if (new_trans_max < getTransactionCount()) { + isc_throw(D2UpdateMgrError, "D2UpdateMgr maximum transaction limit " + "cannot be less than the current transaction count :" + << getTransactionCount()); + } + + max_transactions_ = new_trans_max; +} + +size_t +D2UpdateMgr::getQueueCount() const { + return (queue_mgr_->getQueueSize()); +} + +size_t +D2UpdateMgr::getTransactionCount() const { + return (transaction_list_.size()); +} + + +} // namespace isc::d2 +} // namespace isc diff --git a/src/bin/d2/d2_update_mgr.h b/src/bin/d2/d2_update_mgr.h new file mode 100644 index 0000000..593cfd1 --- /dev/null +++ b/src/bin/d2/d2_update_mgr.h @@ -0,0 +1,248 @@ +// 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_MGR_H +#define D2_UPDATE_MGR_H + +/// @file d2_update_mgr.h This file defines the class D2UpdateMgr. + +#include <asiolink/io_service.h> +#include <d2/d2_queue_mgr.h> +#include <d2srv/nc_trans.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/d2_log.h> +#include <exceptions/exceptions.h> + +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <map> + +namespace isc { +namespace d2 { + +/// @brief Thrown if the update manager encounters a general error. +class D2UpdateMgrError : public isc::Exception { +public: + D2UpdateMgrError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Defines a list of transactions. +typedef std::map<TransactionKey, NameChangeTransactionPtr> TransactionList; + +/// @brief D2UpdateMgr creates and manages update transactions. +/// +/// D2UpdateMgr is the DHCP_DDNS task master, instantiating and then supervising +/// transactions that execute the DNS updates needed to fulfill the requests +/// (NameChangeRequests) received from DHCP_DDNS clients (e.g. DHCP servers). +/// +/// D2UpdateMgr uses the services of D2QueueMgr to monitor the queue of +/// NameChangeRequests and select and dequeue requests for processing. +/// When a request is dequeued for processing it is removed from the queue and +/// wrapped in NameChangeTransaction and added to the D2UpdateMgr's list of +/// transactions. +/// +/// As part of the process of forming transactions, D2UpdateMgr matches each +/// request with the appropriate list of DNS servers. This matching is based +/// upon request attributes, primarily the FQDN and update direction (forward +/// or reverse). D2UpdateMgr uses the services of D2CfgMgr to match requests +/// to DNS server lists. +/// +/// Once created, each transaction is responsible for carrying out the steps +/// required to fulfill its specific request. These steps typically consist of +/// one or more DNS packet exchanges with the appropriate DNS server. As +/// transactions complete, D2UpdateMgr removes them from the transaction list, +/// replacing them with new transactions. +/// +/// D2UpdateMgr carries out each of the above steps, with a method called +/// sweep(). This method is intended to be called as IO events complete. +/// The upper layer(s) are responsible for calling sweep in a timely and cyclic +/// manner. +/// +class D2UpdateMgr : public boost::noncopyable { +public: + /// @brief Maximum number of concurrent transactions + /// NOTE that 32 is an arbitrary choice picked for the initial + /// implementation. + static const size_t MAX_TRANSACTIONS_DEFAULT = 32; + + /// @brief Constructor + /// + /// @param queue_mgr reference to the queue manager receiving requests + /// @param cfg_mgr reference to the configuration manager + /// @param io_service IO service used by the upper layer(s) to manage + /// IO events + /// @param max_transactions the maximum number of concurrent transactions + /// + /// @throw D2UpdateMgrError if either the queue manager or configuration + /// managers are NULL, or max transactions is less than one. + D2UpdateMgr(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr, + asiolink::IOServicePtr& io_service, + const size_t max_transactions = MAX_TRANSACTIONS_DEFAULT); + + /// @brief Destructor + virtual ~D2UpdateMgr(); + + /// @brief Check current transactions; start transactions for new requests. + /// + /// This method is the primary public interface used by the upper layer. It + /// should be called as IO events complete. During each invocation it does + /// the following: + /// + /// - Removes all completed transactions from the transaction list. + /// + /// - If the request queue is not empty and the number of transactions + /// in the transaction list has not reached maximum allowed, then select + /// a request from the queue. + /// + /// - If a request was selected, start a new transaction for it and + /// add the transaction to the list of transactions. + void sweep(); + +protected: + /// @brief Performs post-completion cleanup on completed transactions. + /// + /// Iterates through the list of transactions and removes any that have + /// reached completion. This method may expand in complexity or even + /// disappear altogether as the implementation matures. + void checkFinishedTransactions(); + + /// @brief Starts a transaction for the next eligible request in the queue. + /// + /// This method will scan the request queue for the next request to + /// dequeue. The current implementation starts at the front of the queue + /// and looks for the first request for whose DHCID there is no current + /// transaction in progress. + /// + /// If a request is selected, it is removed from the queue and transaction + /// is constructed for it. + /// + /// It is possible that no such request exists, though this is likely to be + /// rather rare unless a system is frequently seeing requests for the same + /// clients in quick succession. + void pickNextJob(); + + /// @brief Create a new transaction for the given request. + /// + /// This method will attempt to match the request to suitable DNS servers. + /// If matching servers are found, it will instantiate a transaction for + /// the requests, add the transaction to the transaction list, and start + /// the transaction. + /// + /// If updates in a given direction are disabled requests for updates in + /// that direction will be ignored. For example: If a request is received + /// which asks for updates in both directions but only forward updates are + /// enabled; only the forward update will be attempted. Effectively, the + /// request will be treated as if it only asked for forward updates. + /// + /// If updates in a given direction are enabled, and a request asks for + /// updates in that direction, failing to match the request to a list + /// of servers is an error which will be logged and the request will be + /// discarded. + /// + /// Finally, If conflict resolution is enabled, it will instantiate either + /// a NameAddTransaction or a NameRemoveTransaction. If disabled it will + /// instantiate either a SimpleAddTransaction or a SimpleRemoveTransaction. + /// + /// @param ncr the NameChangeRequest for which to create a transaction. + /// + /// @throw D2UpdateMgrError if a transaction for this DHCID already + /// exists. Note this would be programmatic error. + void makeTransaction(isc::dhcp_ddns::NameChangeRequestPtr& ncr); + +public: + /// @brief Gets the D2UpdateMgr's IOService. + /// + /// @return returns a reference to the IOService + const asiolink::IOServicePtr& getIOService() { + return (io_service_); + } + + /// @brief Returns the maximum number of concurrent transactions. + size_t getMaxTransactions() const { + return (max_transactions_); + } + + /// @brief Sets the maximum number of entries allowed in the queue. + /// + /// @param max_transactions is the new maximum number of transactions + /// + /// @throw Throws D2QueueMgrError if the new value is less than one or if + /// the new value is less than the number of entries currently in the + /// queue. + void setMaxTransactions(const size_t max_transactions); + + /// @brief Search the transaction list for the given key. + /// + /// @param key the transaction key value for which to search. + /// + /// @return Iterator pointing to the entry found. If no entry is + /// it will point to the list end position. + TransactionList::iterator findTransaction(const TransactionKey& key); + + /// @brief Returns the transaction list end position. + TransactionList::iterator transactionListEnd(); + + /// @brief Returns the transaction list beg position. + TransactionList::iterator transactionListBegin(); + + /// @brief Convenience method that checks transaction list for the given key + /// + /// @param key the transaction key value for which to search. + /// + /// @return Returns true if the key is found within the list, false + /// otherwise. + bool hasTransaction(const TransactionKey& key); + + /// @brief Removes the entry pointed to by key from the transaction list. + /// + /// Removes the entry referred to by key if it exists. It has no effect + /// if the entry is not found. + /// + /// @param key of the transaction to remove + void removeTransaction(const TransactionKey& key); + + /// @brief Immediately discards all entries in the transaction list. + /// + /// @todo For now this just wipes them out. We might need something + /// more elegant, that allows a cancel first. + void clearTransactionList(); + + /// @brief Convenience method that returns the number of requests queued. + size_t getQueueCount() const; + + /// @brief Returns the current number of transactions. + size_t getTransactionCount() const; + +private: + /// @brief Pointer to the queue manager. + D2QueueMgrPtr queue_mgr_; + + /// @brief Pointer to the configuration manager. + D2CfgMgrPtr cfg_mgr_; + + /// @brief Primary IOService instance. + /// This is the IOService that the upper layer(s) use for IO events, such + /// as shutdown and configuration commands. It is the IOService that is + /// passed into transactions to manager their IO events. + /// (For future reference, multi-threaded transactions would each use their + /// own IOService instance.) + asiolink::IOServicePtr io_service_; + + /// @brief Maximum number of concurrent transactions. + size_t max_transactions_; + + /// @brief List of transactions. + TransactionList transaction_list_; +}; + +/// @brief Defines a pointer to a D2UpdateMgr instance. +typedef boost::shared_ptr<D2UpdateMgr> D2UpdateMgrPtr; + + +} // namespace isc::d2 +} // namespace isc +#endif diff --git a/src/bin/d2/images/abstract_app_classes.svg b/src/bin/d2/images/abstract_app_classes.svg new file mode 100644 index 0000000..52af2f6 --- /dev/null +++ b/src/bin/d2/images/abstract_app_classes.svg @@ -0,0 +1,222 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="770" height="641" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="588" y="279" width="3" height="193" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="474" y="469" width="117" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="470" y="275" width="118" height="194" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" font-style="italic" text-anchor="middle" x="529" y="289">DCfgMgrBase</text> + <line stroke="black" stroke-opacity="1" x1="470" y1="291" x2="588" y2="291" /> + <line stroke="black" stroke-opacity="1" x1="470" y1="299" x2="588" y2="299" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="474" y="313">DCfgMgrBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="474" y="327">~DCfgMgrBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="474" y="341">parseConfig()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="474" y="355">addToParseOrder()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="474" y="369">getParseOrder()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="474" y="383">getContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="474" y="397">buildParams()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="474" y="411">createConfigParser()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="474" y="425">createNewContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="474" y="439">resetContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="474" y="453">setContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="474" y="467">buildAndCommit()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="754" y="385" width="3" height="243" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="640" y="625" width="117" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="636" y="381" width="118" height="244" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" font-style="italic" text-anchor="middle" x="695" y="395">DCfgContextBase</text> + <line stroke="black" stroke-opacity="1" x1="636" y1="397" x2="754" y2="397" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="640" y="411">OPTIONAL</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="640" y="425">REQUIRED</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="439">boolean_values_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="453">uint32_values_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="467">string_values_</text> + <line stroke="black" stroke-opacity="1" x1="636" y1="469" x2="754" y2="469" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="483">DCfgContextBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="497">~DCfgContextBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="511">getParam()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="525">getParam()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="539">getParam()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="553">getBooleanStorage()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="567">getUint32Storage()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="581">getStringStorage()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="640" y="595">clone()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="609">DCfgContextBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="640" y="623">operator =()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="175" y="14" width="3" height="453" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="17" y="464" width="161" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="13" y="10" width="162" height="454" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" font-style="italic" text-anchor="middle" x="94" y="24">DControllerBase</text> + <line stroke="black" stroke-opacity="1" x1="13" y1="26" x2="175" y2="26" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="40">app_name_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="54">bin_name_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="68">verbose_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="82">spec_file_name_</text> + <line stroke="black" stroke-opacity="1" x1="13" y1="84" x2="175" y2="84" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="98">DControllerBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="112">~DControllerBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="126">launch()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="140">updateConfig()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="154">configFromFile()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="168">executeCommand()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="182">getAppName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="196">getBinName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="210">customOption()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="17" y="224">createProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="238">customControllerCommand()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="252">getUsageText()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="266">getCustomOpts()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="280">isVerbose()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="294">setVerbose()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="308">getIOService()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="322">getSpecFileName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="336">setSpecFileName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="17" y="350">getController()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="17" y="364">setController()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="378">parseArgs()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="392">initProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="406">runProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="420">shutdownProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="434">getConfigFile()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="448">getProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="462">usage()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="274" y="165" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="198" y="209" width="79" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="194" y="161" width="80" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="234" y="175"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="234" y="191">IOServicePtr</text> + <line stroke="black" stroke-opacity="1" x1="194" y1="193" x2="274" y2="193" /> + <line stroke="black" stroke-opacity="1" x1="194" y1="201" x2="274" y2="201" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="179" y1="118" x2="236" y2="118" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="179,118 185,112 191,118 185,124" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="236" y1="160" x2="242" y2="154" /> + <line stroke="black" stroke-opacity="1" x1="236" y1="160" x2="230" y2="154" /> + <line stroke="black" stroke-opacity="1" x1="236" y1="118" x2="236" y2="160" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="426" y="148" width="3" height="229" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="322" y="374" width="107" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="318" y="144" width="108" height="230" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" font-style="italic" text-anchor="middle" x="372" y="158">DProcessBase</text> + <line stroke="black" stroke-opacity="1" x1="318" y1="160" x2="426" y2="160" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="322" y="174">app_name_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="322" y="188">shut_down_flag_</text> + <line stroke="black" stroke-opacity="1" x1="318" y1="190" x2="426" y2="190" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="322" y="204">DProcessBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="322" y="218">init()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="322" y="232">run()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="322" y="246">shutdown()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="322" y="260">configure()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="322" y="274">command()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="322" y="288">~DProcessBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="322" y="302">shouldShutdown()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="322" y="316">setShutdownFlag()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="322" y="330">getAppName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="322" y="344">getIoService()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="322" y="358">stopIOService()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="322" y="372">getCfgMgr()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="317" y1="260" x2="236" y2="260" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="317,260 311,266 305,260 311,254" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="236" y1="213" x2="230" y2="219" /> + <line stroke="black" stroke-opacity="1" x1="236" y1="213" x2="242" y2="219" /> + <line stroke="black" stroke-opacity="1" x1="236" y1="260" x2="236" y2="213" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="424" y="72" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="320" y="116" width="107" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="316" y="68" width="108" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="370" y="82"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="370" y="98">DProcessBasePtr</text> + <line stroke="black" stroke-opacity="1" x1="316" y1="100" x2="424" y2="100" /> + <line stroke="black" stroke-opacity="1" x1="316" y1="108" x2="424" y2="108" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="372" y1="143" x2="378" y2="137" /> + <line stroke="black" stroke-opacity="1" x1="372" y1="143" x2="366" y2="137" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="372" y1="120" x2="372" y2="143" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="179" y1="39" x2="372" y2="39" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="179,39 185,33 191,39 185,45" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="372" y1="67" x2="378" y2="61" /> + <line stroke="black" stroke-opacity="1" x1="372" y1="67" x2="366" y2="61" /> + <line stroke="black" stroke-opacity="1" x1="372" y1="39" x2="372" y2="67" /> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="382" y="63">process_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="246" y="231">io_service_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="246" y="156">io_service_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="696" y="302">context_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="534" y="196">cfg_mgr_</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="747" y="311" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="625" y="355" width="125" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="621" y="307" width="126" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="684" y="321"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="684" y="337">DCfgContextBasePtr</text> + <line stroke="black" stroke-opacity="1" x1="621" y1="339" x2="747" y2="339" /> + <line stroke="black" stroke-opacity="1" x1="621" y1="347" x2="747" y2="347" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="688" y1="380" x2="693" y2="373" /> + <line stroke="black" stroke-opacity="1" x1="688" y1="380" x2="681" y2="374" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="687" y1="359" x2="688" y2="380" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="592" y1="289" x2="686" y2="289" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="592,289 598,283 604,289 598,295" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="686" y1="306" x2="692" y2="300" /> + <line stroke="black" stroke-opacity="1" x1="686" y1="306" x2="680" y2="300" /> + <line stroke="black" stroke-opacity="1" x1="686" y1="289" x2="686" y2="306" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="574" y="205" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="474" y="249" width="103" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="470" y="201" width="104" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="522" y="215"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="522" y="231">DCfgMgrBasePtr</text> + <line stroke="black" stroke-opacity="1" x1="470" y1="233" x2="574" y2="233" /> + <line stroke="black" stroke-opacity="1" x1="470" y1="241" x2="574" y2="241" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="524" y1="274" x2="530" y2="268" /> + <line stroke="black" stroke-opacity="1" x1="524" y1="274" x2="518" y2="268" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="524" y1="253" x2="524" y2="274" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="430" y1="168" x2="524" y2="168" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="430,168 436,162 442,168 436,174" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="524" y1="200" x2="530" y2="194" /> + <line stroke="black" stroke-opacity="1" x1="524" y1="200" x2="518" y2="194" /> + <line stroke="black" stroke-opacity="1" x1="524" y1="168" x2="524" y2="200" /> +</g> +</svg> diff --git a/src/bin/d2/images/add_state_model.svg b/src/bin/d2/images/add_state_model.svg new file mode 100644 index 0000000..c68773c --- /dev/null +++ b/src/bin/d2/images/add_state_model.svg @@ -0,0 +1,301 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="840" height="777" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="365" y="49" width="74" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="361" y="45" width="74" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="398" y="61">READY_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="312" y="193" width="180" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="308" y="189" width="180" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="398" y="205">SELECTING_FWD_SERVER_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="46" y="527" width="178" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="42" y="523" width="178" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="131" y="539">SELECTING_REV_SERVER_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="324" y="299" width="156" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="320" y="295" width="156" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="398" y="311">ADDING_FWD_ADDRS_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="466" y="414" width="176" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="462" y="410" width="176" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="550" y="426">REPLACING_FWD_ADDRS_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="662" y="600" width="166" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="658" y="596" width="166" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="741" y="612">PROCESS_ADD_FAILED_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="233" y="735" width="151" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="229" y="731" width="151" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="305" y="747">PROCESS_ADD_OK_ST</text> +</g> +<g> + <ellipse fill="white" stroke="black" stroke-width="1" stroke-opacity="1" cx="743" cy="746" rx="11.5" ry="11.5" /> + <ellipse fill="black" cx="743" cy="746" rx="8.5" ry="8.5" /> +</g> +<ellipse fill="black" cx="137" cy="57" rx="8.5" ry="8.5" /> +<polygon fill="white" stroke="black" stroke-opacity="1" points ="291,492 300,475 309,492 300,509" /> +<g> + <rect fill="none" stroke="black" stroke-width="1" stroke-opacity="1" x="575" y="473" width="118" height="43" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="634" y="489"><<DNS IO Callback>></text> +</g> +<g> + <rect fill="none" stroke="black" stroke-width="1" stroke-opacity="1" x="178" y="244" width="118" height="43" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="237" y="260"><<DNS IO Callback>></text> +</g> +<g> + <rect fill="none" stroke="black" stroke-width="1" stroke-opacity="1" x="12" y="722" width="118" height="43" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="71" y="738"><<DNS IO Callback>></text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="610" y1="436" x2="604" y2="442" /> + <line stroke="black" stroke-opacity="1" x1="610" y1="436" x2="616" y2="441" /> + <line stroke="black" stroke-opacity="1" x1="611" y1="472" x2="610" y2="436" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="360" y1="57" x2="354" y2="51" /> + <line stroke="black" stroke-opacity="1" x1="360" y1="57" x2="354" y2="63" /> + <line stroke="black" stroke-opacity="1" x1="146" y1="57" x2="360" y2="57" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="400" y1="294" x2="406" y2="288" /> + <line stroke="black" stroke-opacity="1" x1="400" y1="294" x2="394" y2="288" /> + <line stroke="black" stroke-opacity="1" x1="400" y1="215" x2="400" y2="294" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="355" y1="294" x2="323" y2="253" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="352" y1="215" x2="343" y2="216" /> + <line stroke="black" stroke-opacity="1" x1="352" y1="215" x2="353" y2="223" /> + <line stroke="black" stroke-opacity="1" x1="323" y1="253" x2="352" y2="215" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="492" y1="201" x2="743" y2="201" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="743" y1="595" x2="749" y2="589" /> + <line stroke="black" stroke-opacity="1" x1="743" y1="595" x2="737" y2="589" /> + <line stroke="black" stroke-opacity="1" x1="743" y1="201" x2="743" y2="595" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="480" y1="308" x2="743" y2="308" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="743" y1="595" x2="749" y2="589" /> + <line stroke="black" stroke-opacity="1" x1="743" y1="595" x2="737" y2="589" /> + <line stroke="black" stroke-opacity="1" x1="743" y1="308" x2="743" y2="595" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="642" y1="423" x2="743" y2="423" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="743" y1="595" x2="749" y2="589" /> + <line stroke="black" stroke-opacity="1" x1="743" y1="595" x2="737" y2="589" /> + <line stroke="black" stroke-opacity="1" x1="743" y1="423" x2="743" y2="595" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="370" y1="321" x2="370" y2="423" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="461" y1="423" x2="455" y2="417" /> + <line stroke="black" stroke-opacity="1" x1="461" y1="423" x2="455" y2="429" /> + <line stroke="black" stroke-opacity="1" x1="370" y1="423" x2="461" y2="423" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="523" y1="409" x2="522" y2="357" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="434" y1="321" x2="437" y2="328" /> + <line stroke="black" stroke-opacity="1" x1="434" y1="321" x2="441" y2="317" /> + <line stroke="black" stroke-opacity="1" x1="522" y1="357" x2="434" y2="321" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="587" y1="409" x2="587" y2="238" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="484" y1="215" x2="488" y2="222" /> + <line stroke="black" stroke-opacity="1" x1="484" y1="215" x2="491" y2="210" /> + <line stroke="black" stroke-opacity="1" x1="587" y1="238" x2="484" y2="215" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="319" y1="312" x2="300" y2="312" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="300" y1="473" x2="306" y2="467" /> + <line stroke="black" stroke-opacity="1" x1="300" y1="473" x2="294" y2="467" /> + <line stroke="black" stroke-opacity="1" x1="300" y1="312" x2="300" y2="473" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="506" y1="436" x2="506" y2="492" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="312" y1="492" x2="318" y2="498" /> + <line stroke="black" stroke-opacity="1" x1="312" y1="492" x2="318" y2="486" /> + <line stroke="black" stroke-opacity="1" x1="506" y1="492" x2="312" y2="492" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="298" y1="730" x2="304" y2="724" /> + <line stroke="black" stroke-opacity="1" x1="298" y1="730" x2="292" y2="723" /> + <line stroke="black" stroke-opacity="1" x1="300" y1="510" x2="298" y2="730" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="287" y1="491" x2="132" y2="491" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="132" y1="522" x2="138" y2="516" /> + <line stroke="black" stroke-opacity="1" x1="132" y1="522" x2="126" y2="516" /> + <line stroke="black" stroke-opacity="1" x1="132" y1="491" x2="132" y2="522" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="743" y1="733" x2="749" y2="727" /> + <line stroke="black" stroke-opacity="1" x1="743" y1="733" x2="737" y2="727" /> + <line stroke="black" stroke-opacity="1" x1="743" y1="622" x2="743" y2="733" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="730" y1="745" x2="724" y2="738" /> + <line stroke="black" stroke-opacity="1" x1="730" y1="745" x2="723" y2="750" /> + <line stroke="black" stroke-opacity="1" x1="384" y1="744" x2="730" y2="745" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="234" y1="287" x2="234" y2="303" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="319" y1="303" x2="313" y2="297" /> + <line stroke="black" stroke-opacity="1" x1="319" y1="303" x2="313" y2="309" /> + <line stroke="black" stroke-opacity="1" x1="234" y1="303" x2="319" y2="303" /> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="616" y="301">UPDATE_FAILED_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="196" y="236">SERVER_IO_ERROR_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="336" y="440">FQDN_IN_USE_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="436" y="251">SERVER_IO_ERROR_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="378" y="374">FQDN_NOT_IN_USE_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="328" y="485">UPDATE_OK_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="309" y="540">No reverse change requested</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="286" y="51">START_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="530" y="740">END_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="197" y="455">UPDATE_OK_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="616" y="400">UPDATE_FAILED_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="149" y="484">Reverse change requested</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="674" y="695">END_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="588" y="196">NO_MORE_SERVERS_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="420" y="291">SERVER_SELECTED_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="168" y="324">IO_COMPLETED_EVT</text> +</g> +<polygon fill="white" stroke="black" stroke-opacity="1" points ="391,122 400,105 409,122 400,139" /> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="34" y="642" width="207" height="23" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="30" y="638" width="207" height="23" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="134" y="654">REPLACING_REV_PTRS_ST</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="400" y1="103" x2="406" y2="97" /> + <line stroke="black" stroke-opacity="1" x1="400" y1="103" x2="394" y2="97" /> + <line stroke="black" stroke-opacity="1" x1="400" y1="71" x2="400" y2="103" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="400" y1="188" x2="406" y2="182" /> + <line stroke="black" stroke-opacity="1" x1="400" y1="188" x2="394" y2="182" /> + <line stroke="black" stroke-opacity="1" x1="400" y1="140" x2="400" y2="188" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="387" y1="121" x2="132" y2="121" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="132" y1="522" x2="138" y2="516" /> + <line stroke="black" stroke-opacity="1" x1="132" y1="522" x2="126" y2="516" /> + <line stroke="black" stroke-opacity="1" x1="132" y1="121" x2="132" y2="522" /> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="410" y="156">Forward change requested</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="179" y="116">Only reverse change requested</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="175" y1="549" x2="208" y2="590" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="181" y1="637" x2="189" y2="634" /> + <line stroke="black" stroke-opacity="1" x1="181" y1="637" x2="178" y2="628" /> + <line stroke="black" stroke-opacity="1" x1="208" y1="590" x2="181" y2="637" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="83" y1="637" x2="52" y2="602" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="86" y1="549" x2="77" y2="550" /> + <line stroke="black" stroke-opacity="1" x1="86" y1="549" x2="87" y2="557" /> + <line stroke="black" stroke-opacity="1" x1="52" y1="602" x2="86" y2="549" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="147" y1="665" x2="147" y2="695" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="147" y1="695" x2="298" y2="695" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="298" y1="730" x2="304" y2="724" /> + <line stroke="black" stroke-opacity="1" x1="298" y1="730" x2="292" y2="724" /> + <line stroke="black" stroke-opacity="1" x1="298" y1="695" x2="298" y2="730" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="71" y1="665" x2="65" y2="671" /> + <line stroke="black" stroke-opacity="1" x1="71" y1="665" x2="77" y2="671" /> + <line stroke="black" stroke-opacity="1" x1="71" y1="721" x2="71" y2="665" /> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="31" y="577">SERVER_IO_ERROR_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="124" y="620">SERVER_SELECTED_ST</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="197" y="691">UPDATE_OK_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="714">IO_COMPLETED_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="553" y="460">IO_COMPLETED_EVT</text> +</g> +</svg> diff --git a/src/bin/d2/images/config_data_classes.svg b/src/bin/d2/images/config_data_classes.svg new file mode 100644 index 0000000..7953b88 --- /dev/null +++ b/src/bin/d2/images/config_data_classes.svg @@ -0,0 +1,299 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="744" height="590" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="163" y="23" width="3" height="127" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="77" y="147" width="89" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="73" y="19" width="90" height="128" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="118" y="32">D2CfgContext</text> + <line stroke="black" stroke-opacity="1" x1="73" y1="34" x2="163" y2="34" /> + <line stroke="black" stroke-opacity="1" x1="73" y1="42" x2="163" y2="42" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="77" y="55">D2CfgContext()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="77" y="68">~D2CfgContext()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="77" y="81">clone()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="77" y="94">getForwardMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="77" y="107">getReverseMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="77" y="120">getKeys()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="77" y="133">D2CfgContext()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="77" y="146">operator =()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="728" y="342" width="3" height="213" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="600" y="552" width="131" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="596" y="338" width="132" height="214" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="662" y="351">DnsServerInfo</text> + <line stroke="black" stroke-opacity="1" x1="596" y1="353" x2="728" y2="353" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="600" y="366">STANDARD_DNS_PORT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="600" y="379">EMPTY_IP_STR</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="392">hostname_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="405">ip_address_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="418">port_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="431">enabled_</text> + <line stroke="black" stroke-opacity="1" x1="596" y1="433" x2="728" y2="433" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="446">DnsServerInfo()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="459">~DnsServerInfo()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="472">getHostname()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="485">getPort()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="498">getIpAddress()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="511">isEnabled()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="524">enable()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="537">disable()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="600" y="550">toText()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="551" y="64" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="467" y="106" width="87" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="463" y="60" width="88" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="507" y="73"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="507" y="88">TSIGKeyInfoPtr</text> + <line stroke="black" stroke-opacity="1" x1="463" y1="90" x2="551" y2="90" /> + <line stroke="black" stroke-opacity="1" x1="463" y1="98" x2="551" y2="98" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="158" y="532" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="64" y="574" width="97" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="60" y="528" width="98" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="109" y="541"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="109" y="556">DdnsDomainMap</text> + <line stroke="black" stroke-opacity="1" x1="60" y1="558" x2="158" y2="558" /> + <line stroke="black" stroke-opacity="1" x1="60" y1="566" x2="158" y2="566" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="437" y="460" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="303" y="502" width="137" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="299" y="456" width="138" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="368" y="469"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="368" y="484">DnsServerInfoStoragePtr</text> + <line stroke="black" stroke-opacity="1" x1="299" y1="486" x2="437" y2="486" /> + <line stroke="black" stroke-opacity="1" x1="299" y1="494" x2="437" y2="494" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="180" y="195" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="54" y="237" width="129" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="50" y="191" width="130" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="115" y="204"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="115" y="219">DdnsDomainListMgrPtr</text> + <line stroke="black" stroke-opacity="1" x1="50" y1="221" x2="180" y2="221" /> + <line stroke="black" stroke-opacity="1" x1="50" y1="229" x2="180" y2="229" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="548" y="355" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="454" y="397" width="97" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="450" y="351" width="98" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="499" y="364"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="499" y="379">DnsServerInfoPtr</text> + <line stroke="black" stroke-opacity="1" x1="450" y1="381" x2="548" y2="381" /> + <line stroke="black" stroke-opacity="1" x1="450" y1="389" x2="548" y2="389" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="666" y="63" width="3" height="121" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="586" y="181" width="83" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="582" y="59" width="84" height="122" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="624" y="72">TSIGKeyInfo</text> + <line stroke="black" stroke-opacity="1" x1="582" y1="74" x2="666" y2="74" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="586" y="87">name_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="586" y="100">algorithm_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="586" y="113">secret_</text> + <line stroke="black" stroke-opacity="1" x1="582" y1="115" x2="666" y2="115" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="586" y="128">TSIGKeyInfo()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="586" y="141">~TSIGKeyInfo()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="586" y="154">getName()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="586" y="167">getAlgorithm()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="586" y="180">getSecret()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="152" y1="190" x2="158" y2="184" /> + <line stroke="black" stroke-opacity="1" x1="152" y1="190" x2="146" y2="184" /> + <line stroke="black" stroke-opacity="1" x1="152" y1="151" x2="152" y2="190" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="152,151 158,157 152,163 146,157" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="85" y1="190" x2="91" y2="184" /> + <line stroke="black" stroke-opacity="1" x1="85" y1="190" x2="79" y2="184" /> + <line stroke="black" stroke-opacity="1" x1="85" y1="151" x2="85" y2="190" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="85,151 91,157 85,163 79,157" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="595" y1="369" x2="589" y2="362" /> + <line stroke="black" stroke-opacity="1" x1="595" y1="369" x2="588" y2="374" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="552" y1="368" x2="595" y2="369" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="581" y1="72" x2="575" y2="65" /> + <line stroke="black" stroke-opacity="1" x1="581" y1="72" x2="574" y2="77" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="555" y1="71" x2="581" y2="72" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="177" y="274" width="3" height="147" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="51" y="418" width="129" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="47" y="270" width="130" height="148" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="112" y="283">DdnsDomainListMgr</text> + <line stroke="black" stroke-opacity="1" x1="47" y1="285" x2="177" y2="285" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="51" y="298">wildcard_domain_name_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="51" y="311">name_</text> + <line stroke="black" stroke-opacity="1" x1="47" y1="313" x2="177" y2="313" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="51" y="326">DdnsDomainListMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="51" y="339">~DdnsDomainListMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="51" y="352">matchDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="51" y="365">getName()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="51" y="378">size()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="51" y="391">getWildcardDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="51" y="404">getDomains()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="51" y="417">setDomains()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="428" y="527" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="308" y="569" width="123" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="304" y="523" width="124" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="366" y="536"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="366" y="551">DnsServerInfoStorage</text> + <line stroke="black" stroke-opacity="1" x1="304" y1="553" x2="428" y2="553" /> + <line stroke="black" stroke-opacity="1" x1="304" y1="561" x2="428" y2="561" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="115" y1="269" x2="121" y2="263" /> + <line stroke="black" stroke-opacity="1" x1="115" y1="269" x2="109" y2="262" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="116" y1="241" x2="115" y2="269" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="432" y1="548" x2="501" y2="548" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="501" y1="401" x2="495" y2="407" /> + <line stroke="black" stroke-opacity="1" x1="501" y1="401" x2="507" y2="407" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="501" y1="548" x2="501" y2="401" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="368" y1="522" x2="374" y2="516" /> + <line stroke="black" stroke-opacity="1" x1="368" y1="522" x2="362" y2="515" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="369" y1="506" x2="368" y2="522" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="295" y="316" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="209" y="358" width="89" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="205" y="312" width="90" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="250" y="325"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="250" y="340">DdnsDomainPtr</text> + <line stroke="black" stroke-opacity="1" x1="205" y1="342" x2="295" y2="342" /> + <line stroke="black" stroke-opacity="1" x1="205" y1="350" x2="295" y2="350" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="181" y1="297" x2="251" y2="297" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="181,297 187,291 193,297 187,303" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="251" y1="311" x2="257" y2="305" /> + <line stroke="black" stroke-opacity="1" x1="251" y1="311" x2="245" y2="305" /> + <line stroke="black" stroke-opacity="1" x1="251" y1="297" x2="251" y2="311" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="162" y1="553" x2="251" y2="553" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="251" y1="362" x2="245" y2="368" /> + <line stroke="black" stroke-opacity="1" x1="251" y1="362" x2="257" y2="368" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="251" y1="553" x2="251" y2="362" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="414" y="318" width="3" height="109" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="334" y="424" width="83" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="330" y="314" width="84" height="110" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="372" y="327">DdnsDomain</text> + <line stroke="black" stroke-opacity="1" x1="330" y1="329" x2="414" y2="329" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="334" y="342">name_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="334" y="355">key_name_</text> + <line stroke="black" stroke-opacity="1" x1="330" y1="357" x2="414" y2="357" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="334" y="370">DdnsDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="334" y="383">~DdnsDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="334" y="396">getName()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="334" y="409">getKeyName()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="334" y="422">getServers()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="370" y1="455" x2="376" y2="449" /> + <line stroke="black" stroke-opacity="1" x1="370" y1="455" x2="364" y2="448" /> + <line stroke="black" stroke-opacity="1" x1="371" y1="428" x2="370" y2="455" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="371,428 376,434 370,439 364,433" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="329" y1="337" x2="323" y2="330" /> + <line stroke="black" stroke-opacity="1" x1="329" y1="337" x2="322" y2="342" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="299" y1="336" x2="329" y2="337" /> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="9" y="183">reverse_mgr_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="162" y="187">forward_mgr_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="254" y="78">keys_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="229" y="285">wildcard_domain_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="380" y="452">servers_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="128" y="446">domains_</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="408" y="65" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="302" y="107" width="109" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="298" y="61" width="110" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="353" y="74"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="353" y="89">TSIGKeyInfoMapPtr</text> + <line stroke="black" stroke-opacity="1" x1="298" y1="91" x2="408" y2="91" /> + <line stroke="black" stroke-opacity="1" x1="298" y1="99" x2="408" y2="99" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="167" y="461" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="59" y="503" width="111" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="55" y="457" width="112" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="111" y="470"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="111" y="485">DdnsDomainMapPtr</text> + <line stroke="black" stroke-opacity="1" x1="55" y1="487" x2="167" y2="487" /> + <line stroke="black" stroke-opacity="1" x1="55" y1="495" x2="167" y2="495" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="297" y1="85" x2="291" y2="79" /> + <line stroke="black" stroke-opacity="1" x1="297" y1="85" x2="291" y2="91" /> + <line stroke="black" stroke-opacity="1" x1="167" y1="85" x2="297" y2="85" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="167,85 173,79 179,85 173,91" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="111" y1="527" x2="117" y2="521" /> + <line stroke="black" stroke-opacity="1" x1="111" y1="527" x2="105" y2="520" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="112" y1="507" x2="111" y2="527" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="113" y1="456" x2="119" y2="450" /> + <line stroke="black" stroke-opacity="1" x1="113" y1="456" x2="107" y2="450" /> + <line stroke="black" stroke-opacity="1" x1="113" y1="422" x2="113" y2="456" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="113,422 119,428 113,434 107,428" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="395" y="136" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="303" y="178" width="95" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="299" y="132" width="96" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="347" y="145"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="347" y="160">TSIGKeyInfoMap</text> + <line stroke="black" stroke-opacity="1" x1="299" y1="162" x2="395" y2="162" /> + <line stroke="black" stroke-opacity="1" x1="299" y1="170" x2="395" y2="170" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="399" y1="157" x2="509" y2="157" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="509" y1="110" x2="503" y2="116" /> + <line stroke="black" stroke-opacity="1" x1="509" y1="110" x2="515" y2="116" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="509" y1="157" x2="509" y2="110" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="350" y1="131" x2="356" y2="125" /> + <line stroke="black" stroke-opacity="1" x1="350" y1="131" x2="344" y2="124" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="352" y1="111" x2="350" y2="131" /> +</g> +</svg> diff --git a/src/bin/d2/images/config_from_file_sequence.svg b/src/bin/d2/images/config_from_file_sequence.svg new file mode 100644 index 0000000..b5f53f7 --- /dev/null +++ b/src/bin/d2/images/config_from_file_sequence.svg @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="746" height="226" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="261" y="8" width="3" height="18" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="167" y="23" width="97" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="163" y="4" width="98" height="19" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="212" y="20">:DControllerBase</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="488" y="8" width="3" height="18" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="402" y="23" width="89" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="398" y="4" width="90" height="19" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="443" y="20">:DProcessBase</text> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="214" y1="45" x2="214" y2="226" /> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="445" y1="45" x2="445" y2="226" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="730" y="8" width="3" height="18" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="648" y="23" width="85" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="644" y="4" width="86" height="19" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="687" y="20">:DCfgMgrBase</text> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="689" y1="45" x2="689" y2="226" /> +</g> +<g> + <rect fill="none" stroke="black" stroke-width="1" stroke-opacity="1" x="440" y="175" width="10" height="28" /> +</g> +<g> + <rect fill="none" stroke="black" stroke-width="1" stroke-opacity="1" x="684" y="180" width="10" height="24" /> +</g> +<g> + <rect fill="none" stroke="black" stroke-width="1" stroke-opacity="1" x="209" y="67" width="10" height="147" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="39" y1="72" x2="209" y2="72" /> + <polygon fill="#000000" stroke="none" points="209,72 205,68 205,76" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="451" y1="194" x2="684" y2="194" /> + <polygon fill="#000000" stroke="none" points="684,194 680,190 680,198" /> +</g> +<ellipse fill="black" stroke="none" cx="34.5" cy="73.5" rx="4.5" ry="4.5" /> +<g> + <rect fill="none" stroke="black" stroke-width="1" stroke-opacity="1" x="215" y="99" width="10" height="24" /> +</g> +<g> + <rect fill="none" stroke="black" stroke-width="1" stroke-opacity="1" x="215" y="164" width="10" height="37" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 227 165 L 244 165 L 244 172 L 227 172" /> + <polygon fill="#000000" stroke="none" points="227,172 231,176 231,168" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="226" y1="187" x2="440" y2="187" /> + <polygon fill="#000000" stroke="none" points="440,187 436,183 436,191" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 227 100 L 244 100 L 244 107 L 227 107" /> + <polygon fill="#000000" stroke="none" points="227,107 231,111 231,103" /> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="591" y="191">parseConfig()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="111" y="67">configFromFile()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="224" y="95">getConfigFile()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="372" y="182">configure()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="231" y="160">updateConfig()</text> +</g> +</svg> diff --git a/src/bin/d2/images/config_parser_classes.svg b/src/bin/d2/images/config_parser_classes.svg new file mode 100644 index 0000000..974bf0f --- /dev/null +++ b/src/bin/d2/images/config_parser_classes.svg @@ -0,0 +1,262 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="1126" height="713" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="146" y="343" width="3" height="147" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="20" y="487" width="129" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="16" y="339" width="130" height="148" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="81" y="352">DdnsDomainListMgr</text> + <line stroke="black" stroke-opacity="1" x1="16" y1="354" x2="146" y2="354" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="20" y="367">wildcard_domain_name_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="20" y="380">name_</text> + <line stroke="black" stroke-opacity="1" x1="16" y1="382" x2="146" y2="382" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="20" y="395">DdnsDomainListMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="20" y="408">~DdnsDomainListMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="20" y="421">matchDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="20" y="434">getName()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="20" y="447">size()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="20" y="460">getWildcardDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="20" y="473">getDomains()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="20" y="486">setDomains()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="750" y="474" width="3" height="213" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="622" y="684" width="131" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="618" y="470" width="132" height="214" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="684" y="483">DnsServerInfo</text> + <line stroke="black" stroke-opacity="1" x1="618" y1="485" x2="750" y2="485" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="622" y="498">STANDARD_DNS_PORT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="622" y="511">EMPTY_IP_STR</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="524">hostname_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="537">ip_address_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="550">port_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="563">enabled_</text> + <line stroke="black" stroke-opacity="1" x1="618" y1="565" x2="750" y2="565" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="578">DnsServerInfo()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="591">~DnsServerInfo()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="604">getHostname()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="617">getPort()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="630">getIpAddress()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="643">isEnabled()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="656">enable()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="669">disable()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="622" y="682">toText()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="422" y="422" width="3" height="109" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="342" y="528" width="83" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="338" y="418" width="84" height="110" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="380" y="431">DdnsDomain</text> + <line stroke="black" stroke-opacity="1" x1="338" y1="433" x2="422" y2="433" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="342" y="446">name_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="342" y="459">key_name_</text> + <line stroke="black" stroke-opacity="1" x1="338" y1="461" x2="422" y2="461" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="342" y="474">DdnsDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="342" y="487">~DdnsDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="342" y="500">getName()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="342" y="513">getKeyName()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="342" y="526">getServers()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="598" y="316" width="3" height="95" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="462" y="408" width="139" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="458" y="312" width="140" height="96" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="528" y="325">DnsServerInfoListParser</text> + <line stroke="black" stroke-opacity="1" x1="458" y1="327" x2="598" y2="327" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="462" y="340">list_name_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="462" y="353">parsers_</text> + <line stroke="black" stroke-opacity="1" x1="458" y1="355" x2="598" y2="355" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="462" y="368">DnsServerInfoListParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="462" y="381">~DnsServerInfoListParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="462" y="394">build()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="462" y="407">commit()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="738" y="348" width="3" height="95" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="618" y="440" width="123" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="614" y="344" width="124" height="96" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="676" y="357">DnsServerInfoParser</text> + <line stroke="black" stroke-opacity="1" x1="614" y1="359" x2="738" y2="359" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="618" y="372">entry_name_</text> + <line stroke="black" stroke-opacity="1" x1="614" y1="374" x2="738" y2="374" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="618" y="387">DnsServerInfoParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="618" y="400">~DnsServerInfoParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="618" y="413">build()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="618" y="426">createConfigParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="618" y="439">commit()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="160" y="205" width="3" height="95" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="10" y="297" width="153" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="6" y="201" width="154" height="96" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="83" y="214">DdnsDomainListMgrParser</text> + <line stroke="black" stroke-opacity="1" x1="6" y1="216" x2="160" y2="216" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="10" y="229">entry_name_</text> + <line stroke="black" stroke-opacity="1" x1="6" y1="231" x2="160" y2="231" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="10" y="244">DdnsDomainListMgrParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="10" y="257">~DdnsDomainListMgrParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="10" y="270">build()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="10" y="283">createConfigParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="10" y="296">commit()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="585" y="65" width="3" height="95" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="473" y="157" width="115" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="469" y="61" width="116" height="96" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="527" y="74">TSIGKeyInfoParser</text> + <line stroke="black" stroke-opacity="1" x1="469" y1="76" x2="585" y2="76" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="473" y="89">entry_name_</text> + <line stroke="black" stroke-opacity="1" x1="469" y1="91" x2="585" y2="91" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="473" y="104">TSIGKeyInfoParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="473" y="117">~TSIGKeyInfoParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="473" y="130">build()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="473" y="143">createConfigParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="473" y="156">commit()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="680" y1="469" x2="686" y2="463" /> + <line stroke="black" stroke-opacity="1" x1="680" y1="469" x2="674" y2="463" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="680" y1="444" x2="680" y2="469" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="602" y1="324" x2="675" y2="324" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="83" y1="338" x2="89" y2="332" /> + <line stroke="black" stroke-opacity="1" x1="83" y1="338" x2="77" y2="331" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="84" y1="301" x2="83" y2="338" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="675" y1="343" x2="681" y2="337" /> + <line stroke="black" stroke-opacity="1" x1="675" y1="343" x2="669" y2="337" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="675" y1="324" x2="675" y2="343" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="434" y="33" width="3" height="95" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="304" y="125" width="133" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="300" y="29" width="134" height="96" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="367" y="42">TSIGKeyInfoListParser</text> + <line stroke="black" stroke-opacity="1" x1="300" y1="44" x2="434" y2="44" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="57">list_name_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="70">parsers_</text> + <line stroke="black" stroke-opacity="1" x1="300" y1="72" x2="434" y2="72" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="85">TSIGKeyInfoListParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="98">~TSIGKeyInfoListParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="111">build()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="124">commit()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="443" y="276" width="3" height="95" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="329" y="368" width="117" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="325" y="272" width="118" height="96" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="384" y="285">DdnsDomainParser</text> + <line stroke="black" stroke-opacity="1" x1="325" y1="287" x2="443" y2="287" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="329" y="300">entry_name_</text> + <line stroke="black" stroke-opacity="1" x1="325" y1="302" x2="443" y2="302" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="329" y="315">DdnsDomainParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="329" y="328">~DdnsDomainParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="329" y="341">build()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="329" y="354">createConfigParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="329" y="367">commit()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="311" y="241" width="3" height="95" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="181" y="333" width="133" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="177" y="237" width="134" height="96" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="244" y="250">DdnsDomainListParser</text> + <line stroke="black" stroke-opacity="1" x1="177" y1="252" x2="311" y2="252" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="181" y="265">list_name_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="181" y="278">parsers_</text> + <line stroke="black" stroke-opacity="1" x1="177" y1="280" x2="311" y2="280" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="181" y="293">DdnsDomainListParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="181" y="306">~DdnsDomainListParser()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="181" y="319">build()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="181" y="332">commit()</text> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="315" y1="253" x2="386" y2="253" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="164" y1="220" x2="246" y2="220" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="383" y1="417" x2="389" y2="411" /> + <line stroke="black" stroke-opacity="1" x1="383" y1="417" x2="377" y2="410" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="384" y1="372" x2="383" y2="417" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="447" y1="287" x2="530" y2="287" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="438" y1="41" x2="529" y2="41" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="529" y1="60" x2="535" y2="54" /> + <line stroke="black" stroke-opacity="1" x1="529" y1="60" x2="523" y2="54" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="529" y1="41" x2="529" y2="60" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="530" y1="311" x2="536" y2="305" /> + <line stroke="black" stroke-opacity="1" x1="530" y1="311" x2="524" y2="305" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="530" y1="287" x2="530" y2="311" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="246" y1="236" x2="252" y2="230" /> + <line stroke="black" stroke-opacity="1" x1="246" y1="236" x2="240" y2="230" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="246" y1="220" x2="246" y2="236" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="386" y1="271" x2="392" y2="265" /> + <line stroke="black" stroke-opacity="1" x1="386" y1="271" x2="380" y2="265" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="386" y1="253" x2="386" y2="271" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="723" y="99" width="3" height="121" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="643" y="217" width="83" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="639" y="95" width="84" height="122" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="681" y="108">TSIGKeyInfo</text> + <line stroke="black" stroke-opacity="1" x1="639" y1="110" x2="723" y2="110" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="643" y="123">name_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="643" y="136">algorithm_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="643" y="149">secret_</text> + <line stroke="black" stroke-opacity="1" x1="639" y1="151" x2="723" y2="151" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="643" y="164">TSIGKeyInfo()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="643" y="177">~TSIGKeyInfo()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="643" y="190">getName()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="643" y="203">getAlgorithm()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="643" y="216">getSecret()</text> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="589" y1="74" x2="683" y2="74" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="149" y="9" width="3" height="161" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="11" y="167" width="141" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="7" y="5" width="142" height="162" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="78" y="18">D2CfgMgr</text> + <line stroke="black" stroke-opacity="1" x1="7" y1="20" x2="149" y2="20" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="11" y="33">IPV4_REV_ZONE_SUFFIX</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="11" y="46">IPV6_REV_ZONE_SUFFIX</text> + <line stroke="black" stroke-opacity="1" x1="7" y1="48" x2="149" y2="48" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="61">D2CfgMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="74">~D2CfgMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="87">getD2CfgContext()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="100">matchForward()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="113">matchReverse()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="11" y="126">reverseIpAddress()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="11" y="139">reverseV4Address()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="11" y="152">reverseV6Address()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="165">createConfigParser()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="683" y1="94" x2="689" y2="88" /> + <line stroke="black" stroke-opacity="1" x1="683" y1="94" x2="677" y2="88" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="683" y1="74" x2="683" y2="94" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="82" y1="200" x2="88" y2="194" /> + <line stroke="black" stroke-opacity="1" x1="82" y1="200" x2="76" y2="194" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="82" y1="171" x2="82" y2="200" /> +</g> +</svg> diff --git a/src/bin/d2/images/cpl_signal_classes.svg b/src/bin/d2/images/cpl_signal_classes.svg new file mode 100644 index 0000000..f70e302 --- /dev/null +++ b/src/bin/d2/images/cpl_signal_classes.svg @@ -0,0 +1,393 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="804" height="836" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="344" y="10" width="3" height="495" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="186" y="502" width="161" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="182" y="6" width="162" height="496" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" font-style="italic" text-anchor="middle" x="263" y="20">DControllerBase</text> + <line stroke="black" stroke-opacity="1" x1="182" y1="22" x2="344" y2="22" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="36">app_name_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="50">bin_name_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="64">verbose_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="78">spec_file_name_</text> + <line stroke="black" stroke-opacity="1" x1="182" y1="80" x2="344" y2="80" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="94">DControllerBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="108">~DControllerBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="122">launch()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="136">updateConfig()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="150">configFromFile()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="164">executeCommand()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="178">getAppName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="192">getBinName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="206">customOption()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="186" y="220">createProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="234">customControllerCommand()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="248">getUsageText()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="262">getCustomOpts()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="276">processSignal()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="290">isVerbose()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="304">setVerbose()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="318">getIOService()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="332">getSpecFileName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="346">setSpecFileName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="186" y="360">getController()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="186" y="374">setController()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="388">parseArgs()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="402">initProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="416">runProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="430">shutdownProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="444">initSignalHandling()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="458">osSignalHandler()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="472">ioSignalHandler()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="486">getProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="186" y="500">usage()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="737" y="207" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="667" y="251" width="73" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="663" y="203" width="74" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="700" y="217"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="700" y="233">IOSignalPtr</text> + <line stroke="black" stroke-opacity="1" x1="663" y1="235" x2="737" y2="235" /> + <line stroke="black" stroke-opacity="1" x1="663" y1="243" x2="737" y2="243" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="467" y="353" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="371" y="397" width="99" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="367" y="349" width="100" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="417" y="363"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="417" y="379">IOSignalHandler</text> + <line stroke="black" stroke-opacity="1" x1="367" y1="381" x2="467" y2="381" /> + <line stroke="black" stroke-opacity="1" x1="367" y1="389" x2="467" y2="389" /> +</g> + <rect fill="#c0ffff" stroke="none" stroke-opacity="1" x="323" y="800" width="268" height="25" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" x="323" y="812">Blue class integrate signal handling into D2</text> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="348" y1="278" x2="419" y2="278" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="419" y1="348" x2="425" y2="342" /> + <line stroke="black" stroke-opacity="1" x1="419" y1="348" x2="413" y2="342" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="419" y1="278" x2="419" y2="348" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="751" y="309" width="3" height="103" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="653" y="409" width="101" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="649" y="305" width="102" height="104" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="700" y="319">IOSignal</text> + <line stroke="black" stroke-opacity="1" x1="649" y1="321" x2="751" y2="321" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="653" y="335">signum_</text> + <line stroke="black" stroke-opacity="1" x1="649" y1="337" x2="751" y2="337" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="653" y="351">IOSignal()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="653" y="365">~IOSignal()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="653" y="379">nextSequenceId()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="653" y="393">getSequenceId()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="653" y="407">getSignum()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="698" y1="304" x2="703" y2="297" /> + <line stroke="black" stroke-opacity="1" x1="698" y1="304" x2="691" y2="298" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="697" y1="255" x2="698" y2="304" /> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="538" y="197">signals_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="100" y="444">signal_set_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="566" y="314">sequence_id_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="193" y="703">onreceipt_handler_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="454" y="27">io_signal_queue_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="599" y="729">io_service_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="433" y="729">io_service_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="549" y="424">sequence_id_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="429" y="419">handler_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="724" y="539">timer_</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="669" y="10" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="563" y="54" width="109" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="559" y="6" width="110" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="614" y="20"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="614" y="36">IOSignalQueuePtr</text> + <line stroke="black" stroke-opacity="1" x1="559" y1="38" x2="669" y2="38" /> + <line stroke="black" stroke-opacity="1" x1="559" y1="46" x2="669" y2="46" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="558" y1="31" x2="552" y2="25" /> + <line stroke="black" stroke-opacity="1" x1="558" y1="31" x2="552" y2="37" /> + <line stroke="black" stroke-opacity="1" x1="348" y1="31" x2="558" y2="31" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="348,31 354,25 360,31 354,37" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="668" y="77" width="3" height="95" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="568" y="169" width="103" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="564" y="73" width="104" height="96" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="616" y="87">IOSignalQueue</text> + <line stroke="black" stroke-opacity="1" x1="564" y1="89" x2="668" y2="89" /> + <line stroke="black" stroke-opacity="1" x1="564" y1="97" x2="668" y2="97" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="568" y="111">IOSignalQueue()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="568" y="125">~IOSignalQueue()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="568" y="139">pushSignal()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="568" y="153">popSignal()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="568" y="167">clear()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="618" y1="72" x2="624" y2="66" /> + <line stroke="black" stroke-opacity="1" x1="618" y1="72" x2="612" y2="66" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="618" y1="58" x2="618" y2="72" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="168" y="532" width="3" height="285" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="24" y="814" width="147" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="20" y="528" width="148" height="286" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="94" y="542">SignalSet</text> + <line stroke="black" stroke-opacity="1" x1="20" y1="544" x2="168" y2="544" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="558">local_signals_</text> + <line stroke="black" stroke-opacity="1" x1="20" y1="560" x2="168" y2="560" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="574">SignalSet()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="588">SignalSet()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="602">SignalSet()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="616">~SignalSet()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="630">add()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="644">clear()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="658">getNext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="672">handleNext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="686">remove()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="24" y="700">setOnReceiptHandler()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="24" y="714">clearOnReceiptHandler()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="24" y="728">invokeOnReceiptHandler()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="742">block()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="756">erase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="770">insert()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="784">maskSignals()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="798">popNext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="24" y="812">unblock()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="566" y="206" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="490" y="250" width="79" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="486" y="202" width="80" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="526" y="216"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="526" y="232">IOSignalMap</text> + <line stroke="black" stroke-opacity="1" x1="486" y1="234" x2="566" y2="234" /> + <line stroke="black" stroke-opacity="1" x1="486" y1="242" x2="566" y2="242" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="585" y="712" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="509" y="756" width="79" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="505" y="708" width="80" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="545" y="722"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="545" y="738">IOServicePtr</text> + <line stroke="black" stroke-opacity="1" x1="505" y1="740" x2="585" y2="740" /> + <line stroke="black" stroke-opacity="1" x1="505" y1="748" x2="585" y2="748" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="563" y1="123" x2="528" y2="123" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="563,123 557,129 551,123 557,117" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="528" y1="201" x2="534" y2="195" /> + <line stroke="black" stroke-opacity="1" x1="528" y1="201" x2="522" y2="195" /> + <line stroke="black" stroke-opacity="1" x1="528" y1="123" x2="528" y2="201" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="662" y1="228" x2="656" y2="222" /> + <line stroke="black" stroke-opacity="1" x1="662" y1="228" x2="656" y2="234" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="570" y1="228" x2="662" y2="228" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="672" y1="120" x2="783" y2="120" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="672,120 678,114 684,120 678,126" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="783" y1="120" x2="784" y2="733" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="589" y1="733" x2="595" y2="739" /> + <line stroke="black" stroke-opacity="1" x1="589" y1="733" x2="595" y2="727" /> + <line stroke="black" stroke-opacity="1" x1="784" y1="733" x2="589" y2="733" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="336" y1="506" x2="336" y2="734" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="336,506 342,512 336,518 330,512" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="504" y1="734" x2="498" y2="728" /> + <line stroke="black" stroke-opacity="1" x1="504" y1="734" x2="498" y2="740" /> + <line stroke="black" stroke-opacity="1" x1="336" y1="734" x2="504" y2="734" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="127" y="453" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="53" y="497" width="77" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="49" y="449" width="78" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="88" y="463"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="88" y="479">SignalSetPtr</text> + <line stroke="black" stroke-opacity="1" x1="49" y1="481" x2="127" y2="481" /> + <line stroke="black" stroke-opacity="1" x1="49" y1="489" x2="127" y2="489" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="318" y="584" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="208" y="628" width="113" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="204" y="580" width="114" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="261" y="594"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="261" y="610">BoolSignalHandler</text> + <line stroke="black" stroke-opacity="1" x1="204" y1="612" x2="318" y2="612" /> + <line stroke="black" stroke-opacity="1" x1="204" y1="620" x2="318" y2="620" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="574" y="358" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="504" y="402" width="73" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="500" y="354" width="74" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="537" y="368"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="537" y="384">IOSignalId</text> + <line stroke="black" stroke-opacity="1" x1="500" y1="386" x2="574" y2="386" /> + <line stroke="black" stroke-opacity="1" x1="500" y1="394" x2="574" y2="394" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="90" y1="527" x2="96" y2="521" /> + <line stroke="black" stroke-opacity="1" x1="90" y1="527" x2="84" y2="521" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="90" y1="501" x2="90" y2="527" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="263" y1="632" x2="257" y2="638" /> + <line stroke="black" stroke-opacity="1" x1="263" y1="632" x2="269" y2="638" /> + <line stroke="black" stroke-opacity="1" x1="263" y1="673" x2="263" y2="632" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="181" y1="401" x2="90" y2="401" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="181,401 175,407 169,401 175,395" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="172" y1="673" x2="263" y2="673" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="172,673 178,667 184,673 178,679" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="90" y1="448" x2="96" y2="442" /> + <line stroke="black" stroke-opacity="1" x1="90" y1="448" x2="84" y2="442" /> + <line stroke="black" stroke-opacity="1" x1="90" y1="401" x2="90" y2="448" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="648" y1="327" x2="560" y2="327" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="648,327 642,333 636,327 642,321" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="560" y1="353" x2="566" y2="347" /> + <line stroke="black" stroke-opacity="1" x1="560" y1="353" x2="554" y2="347" /> + <line stroke="black" stroke-opacity="1" x1="560" y1="327" x2="560" y2="353" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="499" y1="377" x2="493" y2="370" /> + <line stroke="black" stroke-opacity="1" x1="499" y1="377" x2="492" y2="382" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="471" y1="376" x2="499" y2="377" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="267" y1="579" x2="273" y2="573" /> + <line stroke="black" stroke-opacity="1" x1="267" y1="579" x2="261" y2="572" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="268" y1="506" x2="267" y2="579" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="531" y1="353" x2="536" y2="346" /> + <line stroke="black" stroke-opacity="1" x1="531" y1="353" x2="524" y2="347" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="529" y1="254" x2="531" y2="353" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="588" y="551" width="3" height="123" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="500" y="671" width="91" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="496" y="547" width="92" height="124" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="542" y="561">IntervalTimer</text> + <line stroke="black" stroke-opacity="1" x1="496" y1="563" x2="588" y2="563" /> + <line stroke="black" stroke-opacity="1" x1="496" y1="571" x2="588" y2="571" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="500" y="585">IntervalTimer()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="500" y="599">operator =()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="500" y="613">IntervalTimer()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="500" y="627">~IntervalTimer()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="500" y="641">setup()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="500" y="655">cancel()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="500" y="669">getInterval()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="546" y1="707" x2="551" y2="700" /> + <line stroke="black" stroke-opacity="1" x1="546" y1="707" x2="539" y2="701" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="545" y1="675" x2="546" y2="707" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="585" y="463" width="3" height="53" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="497" y="513" width="91" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="493" y="459" width="92" height="54" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="539" y="473">TimerCallback</text> + <line stroke="black" stroke-opacity="1" x1="493" y1="475" x2="585" y2="475" /> + <line stroke="black" stroke-opacity="1" x1="493" y1="483" x2="585" y2="483" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="497" y="497">TimerCallback()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="497" y="511">operator ()()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="763" y="548" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="667" y="592" width="99" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="663" y="544" width="100" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="713" y="558"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="713" y="574">IntervalTimerPtr</text> + <line stroke="black" stroke-opacity="1" x1="663" y1="576" x2="763" y2="576" /> + <line stroke="black" stroke-opacity="1" x1="663" y1="584" x2="763" y2="584" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="671" y1="488" x2="671" y2="425" /> +<ellipse fill="none" stroke="black" stroke-width="1" stroke-opacity="1" cx="671" cy="419" rx="5" ry="5" /> + <line stroke="black" stroke-opacity="1" x1="666" y1="419" x2="676" y2="419" /> + <line stroke="black" stroke-opacity="1" x1="671" y1="414" x2="671" y2="424" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="589" y1="488" x2="671" y2="488" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="539" y1="406" x2="533" y2="412" /> + <line stroke="black" stroke-opacity="1" x1="539" y1="406" x2="545" y2="411" /> + <line stroke="black" stroke-opacity="1" x1="540" y1="458" x2="539" y2="406" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="540,458 533,452 539,446 545,451" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="492" y1="488" x2="419" y2="488" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="492,488 486,494 480,488 486,482" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="419" y1="401" x2="413" y2="407" /> + <line stroke="black" stroke-opacity="1" x1="419" y1="401" x2="425" y2="407" /> + <line stroke="black" stroke-opacity="1" x1="419" y1="488" x2="419" y2="401" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="541" y1="517" x2="535" y2="523" /> + <line stroke="black" stroke-opacity="1" x1="541" y1="517" x2="547" y2="523" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="541" y1="546" x2="541" y2="517" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="714" y1="596" x2="714" y2="611" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="592" y1="611" x2="598" y2="617" /> + <line stroke="black" stroke-opacity="1" x1="592" y1="611" x2="598" y2="605" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="714" y1="611" x2="592" y2="611" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="714" y1="543" x2="719" y2="536" /> + <line stroke="black" stroke-opacity="1" x1="714" y1="543" x2="707" y2="537" /> + <line stroke="black" stroke-opacity="1" x1="713" y1="413" x2="714" y2="543" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="713,413 719,418 713,424 707,419" /> +</g> +</svg> diff --git a/src/bin/d2/images/cpl_signal_sequence.svg b/src/bin/d2/images/cpl_signal_sequence.svg new file mode 100644 index 0000000..b57f5b1 --- /dev/null +++ b/src/bin/d2/images/cpl_signal_sequence.svg @@ -0,0 +1,318 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="848" height="803" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="92" y1="45" x2="92" y2="803" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="139" y="8" width="3" height="18" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="45" y="23" width="97" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="41" y="4" width="98" height="19" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="90" y="20">:DControllerBase</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="384" y="74" width="3" height="18" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="296" y="89" width="91" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="292" y="70" width="92" height="19" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="338" y="86">:IOSignalQueue</text> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="340" y1="111" x2="340" y2="803" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="469" y="101" width="3" height="18" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="411" y="116" width="61" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="407" y="97" width="62" height="19" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="438" y="113">:SignalSet</text> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="440" y1="138" x2="440" y2="803" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="559" y="350" width="3" height="18" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="507" y="365" width="55" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="503" y="346" width="56" height="19" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="531" y="362">:IOSignal</text> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="533" y1="387" x2="533" y2="803" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="662" y="373" width="3" height="18" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="586" y="388" width="79" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="582" y="369" width="80" height="19" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="622" y="385">:IntervalTimer</text> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="624" y1="410" x2="624" y2="803" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="769" y="421" width="3" height="18" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="686" y="436" width="86" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="682" y="417" width="87" height="19" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="726" y="433">:TimerCallback</text> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="727" y1="458" x2="727" y2="803" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="832" y="8" width="3" height="18" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="773" y="23" width="62" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="769" y="4" width="63" height="19" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="801" y="20">:IOService</text> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="802" y1="45" x2="802" y2="803" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="262" y="8" width="3" height="18" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="176" y="23" width="89" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="172" y="4" width="90" height="19" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="217" y="20">:DProcessBase</text> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="219" y1="45" x2="219" y2="803" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="87" y="60" width="10" height="726" /> +</g> +<g> + <polygon fill="#c0ffff" stroke="black" stroke-opacity="1" points="262,661 416,661 416,671 426,671 426,709 262,709 262,661" /> + <line stroke="black" stroke-opacity="1" x1="416" y1="661" x2="426" y2="671" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="272" y="683">Details of configFromFile</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="272" y="695">omitted for clarity</text> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="528" y="399" width="10" height="106" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="93" y="242" width="10" height="521" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="335" y="120" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="435" y="166" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="435" y="203" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="435" y="285" width="10" height="152" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="335" y="341" width="10" height="82" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="619" y="419" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="619" y="466" width="10" height="41" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="722" y="485" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="797" y="253" width="10" height="527" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="722" y="549" width="10" height="195" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="214" y="249" width="10" height="542" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="335" y="587" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="528" y="619" width="10" height="24" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="539" y1="471" x2="619" y2="471" /> + <polygon fill="#000000" stroke="none" points="619,471 615,467 615,475" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="33" y1="66" x2="87" y2="66" /> + <polygon fill="#000000" stroke="none" points="87,66 83,62 83,70" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="310" y1="290" x2="435" y2="290" /> + <polygon fill="#000000" stroke="none" points="435,290 431,286 431,294" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="630" y1="492" x2="722" y2="492" /> + <polygon fill="#000000" stroke="none" points="722,492 718,488 718,496" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="733" y1="554" x2="797" y2="554" /> + <polygon fill="#000000" stroke="none" points="733,554 737,550 737,558" /> +</g> +<g> + <polygon fill="#c0ffff" stroke="black" stroke-opacity="1" points="29,288 173,288 173,298 183,298 183,348 29,348 29,288" /> + <line stroke="black" stroke-opacity="1" x1="173" y1="288" x2="183" y2="298" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="39" y="310">Sometime after</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="39" y="322">runProcess is called</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="39" y="334">SIGHUP is sent</text> +</g> +<ellipse fill="black" stroke="none" cx="28.5" cy="67.5" rx="4.5" ry="4.5" /> +<ellipse fill="black" stroke="none" cx="305.5" cy="291.5" rx="4.5" ry="4.5" /> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="183" y1="306" x2="300" y2="291" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="93" y="98" width="10" height="122" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="99" y="562" width="10" height="195" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="441" y="313" width="10" height="116" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="220" y="722" width="10" height="24" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 105 99 L 122 99 L 122 106 L 105 106" /> + <polygon fill="#000000" stroke="none" points="105,106 109,110 109,102" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="104" y1="134" x2="335" y2="134" /> + <polygon fill="#000000" stroke="none" points="335,134 331,130 331,138" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="104" y1="178" x2="435" y2="178" /> + <polygon fill="#000000" stroke="none" points="435,178 431,174 431,182" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="104" y1="208" x2="435" y2="208" /> + <polygon fill="#000000" stroke="none" points="435,208 431,204 431,212" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 453 314 L 470 314 L 470 321 L 453 321" /> + <polygon fill="#000000" stroke="none" points="453,321 457,325 457,317" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="346" y1="351" x2="441" y2="351" /> + <polygon fill="#000000" stroke="none" points="346,351 350,347 350,355" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="110" y1="567" x2="722" y2="567" /> + <polygon fill="#000000" stroke="none" points="110,567 114,563 114,571" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="110" y1="592" x2="335" y2="592" /> + <polygon fill="#000000" stroke="none" points="335,592 331,588 331,596" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="110" y1="624" x2="528" y2="624" /> + <polygon fill="#000000" stroke="none" points="528,624 524,620 524,628" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="105" y="652" width="10" height="100" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="346" y1="404" x2="528" y2="404" /> + <polygon fill="#000000" stroke="none" points="528,404 524,400 524,408" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="539" y1="431" x2="619" y2="431" /> + <polygon fill="#000000" stroke="none" points="619,431 615,427 615,435" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 117 653 L 134 653 L 134 660 L 117 660" /> + <polygon fill="#000000" stroke="none" points="117,660 121,664 121,656" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="111" y="686" width="10" height="61" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 123 687 L 140 687 L 140 694 L 123 694" /> + <polygon fill="#000000" stroke="none" points="123,694 127,698 127,690" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="122" y1="727" x2="220" y2="727" /> + <polygon fill="#000000" stroke="none" points="220,727 216,723 216,731" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="261" y1="687" x2="141" y2="691" /> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="38" y="61">launch()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="106" y="91">initSignalHandling()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="113" y="121">IOSignalQueue(io_service)</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="116" y="161">setOnReceiptHandler(DControllerBase::osSignalHandler)</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="140" y="202">SignalSet(SIGHUP,SIGINT,SIGTERM)</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="288" y="279">internalHandler(SIGHUP)</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="359" y="309">invokeOnReceiptHandler(SIGHUP)</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="314" y="340">pushSignal(SIGHUP, DControllerBase::ioSignalHandler)</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="353" y="399">IOSignal(io_service, SIGHUP, handler)</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="548" y="418">IntervalTimer(io_service)</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="431" y="459">setup(TimerCallBack(sequence_id, handler), 1, ONE_SHOT))</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="637" y="478">TimerCallback(sequence_id, handler)</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="737" y="547">operator ()()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="722" y="256">run()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="118" y="240">runProcess()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="146" y="254">run()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="128" y="562">ioSignalHandler(sequence_id)</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="191" y="587">popSignal()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="448" y="615">getSignum()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="117" y="647">processSignal(SIGHUP)</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="126" y="682">configFromFile()</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="142" y="722">configure()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="225" y1="260" x2="797" y2="260" /> + <polygon fill="#000000" stroke="none" points="797,260 793,256 793,264" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 105 243 L 122 243 L 122 250 L 105 250" /> + <polygon fill="#000000" stroke="none" points="105,250 109,254 109,246" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="104" y1="259" x2="214" y2="259" /> + <polygon fill="#000000" stroke="none" points="214,259 210,255 210,263" /> +</g> +</svg> diff --git a/src/bin/d2/images/d2_app_classes.svg b/src/bin/d2/images/d2_app_classes.svg new file mode 100644 index 0000000..1befe23 --- /dev/null +++ b/src/bin/d2/images/d2_app_classes.svg @@ -0,0 +1,345 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="812" height="795" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="175" y="14" width="3" height="453" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="17" y="464" width="161" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="13" y="10" width="162" height="454" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" font-style="italic" text-anchor="middle" x="94" y="24">DControllerBase</text> + <line stroke="black" stroke-opacity="1" x1="13" y1="26" x2="175" y2="26" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="40">app_name_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="54">bin_name_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="68">verbose_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="82">spec_file_name_</text> + <line stroke="black" stroke-opacity="1" x1="13" y1="84" x2="175" y2="84" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="98">DControllerBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="112">~DControllerBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="126">launch()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="140">updateConfig()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="154">configFromFile()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="168">executeCommand()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="182">getAppName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="196">getBinName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="210">customOption()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="17" y="224">createProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="238">customControllerCommand()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="252">getUsageText()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="266">getCustomOpts()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="280">isVerbose()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="294">setVerbose()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="308">getIOService()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="322">getSpecFileName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="336">setSpecFileName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="17" y="350">getController()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="17" y="364">setController()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="378">parseArgs()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="392">initProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="406">runProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="420">shutdownProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="434">getConfigFile()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="448">getProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="17" y="462">usage()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="629" y="261" width="3" height="193" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="515" y="451" width="117" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="511" y="257" width="118" height="194" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" font-style="italic" text-anchor="middle" x="570" y="271">DCfgMgrBase</text> + <line stroke="black" stroke-opacity="1" x1="511" y1="273" x2="629" y2="273" /> + <line stroke="black" stroke-opacity="1" x1="511" y1="281" x2="629" y2="281" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="515" y="295">DCfgMgrBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="515" y="309">~DCfgMgrBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="515" y="323">parseConfig()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="515" y="337">addToParseOrder()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="515" y="351">getParseOrder()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="515" y="365">getContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="515" y="379">buildParams()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="515" y="393">createConfigParser()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="515" y="407">createNewContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="515" y="421">resetContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="515" y="435">setContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="515" y="449">buildAndCommit()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="796" y="361" width="3" height="243" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="682" y="601" width="117" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="678" y="357" width="118" height="244" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" font-style="italic" text-anchor="middle" x="737" y="371">DCfgContextBase</text> + <line stroke="black" stroke-opacity="1" x1="678" y1="373" x2="796" y2="373" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="682" y="387">OPTIONAL</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="682" y="401">REQUIRED</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="415">boolean_values_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="429">uint32_values_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="443">string_values_</text> + <line stroke="black" stroke-opacity="1" x1="678" y1="445" x2="796" y2="445" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="459">DCfgContextBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="473">~DCfgContextBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="487">getParam()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="501">getParam()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="515">getParam()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="529">getBooleanStorage()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="543">getUint32Storage()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="557">getStringStorage()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="682" y="571">clone()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="585">DCfgContextBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="682" y="599">operator =()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="134" y="554" width="3" height="103" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="46" y="654" width="91" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="42" y="550" width="92" height="104" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="88" y="564">D2Controller</text> + <line stroke="black" stroke-opacity="1" x1="42" y1="566" x2="134" y2="566" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="46" y="580">d2_app_name_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="46" y="594">d2_bin_name_</text> + <line stroke="black" stroke-opacity="1" x1="42" y1="596" x2="134" y2="596" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="46" y="610">instance()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="46" y="624">~D2Controller()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="46" y="638">createProcess()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="46" y="652">D2Controller()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="90" y1="549" x2="90" y2="473" /> + <line stroke="black" stroke-opacity="1" x1="91" y1="468" x2="84" y2="473" /> + <line stroke="black" stroke-opacity="1" x1="91" y1="468" x2="96" y2="474" /> + <line stroke="black" stroke-opacity="1" x1="84" y1="473" x2="96" y2="474" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="274" y="165" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="198" y="209" width="79" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="194" y="161" width="80" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="234" y="175"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="234" y="191">IOServicePtr</text> + <line stroke="black" stroke-opacity="1" x1="194" y1="193" x2="274" y2="193" /> + <line stroke="black" stroke-opacity="1" x1="194" y1="201" x2="274" y2="201" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="179" y1="118" x2="236" y2="118" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="179,118 185,112 191,118 185,124" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="236" y1="160" x2="242" y2="154" /> + <line stroke="black" stroke-opacity="1" x1="236" y1="160" x2="230" y2="154" /> + <line stroke="black" stroke-opacity="1" x1="236" y1="118" x2="236" y2="160" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="424" y="153" width="3" height="229" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="320" y="379" width="107" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="316" y="149" width="108" height="230" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" font-style="italic" text-anchor="middle" x="370" y="163">DProcessBase</text> + <line stroke="black" stroke-opacity="1" x1="316" y1="165" x2="424" y2="165" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="320" y="179">app_name_</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="320" y="193">shut_down_flag_</text> + <line stroke="black" stroke-opacity="1" x1="316" y1="195" x2="424" y2="195" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="320" y="209">DProcessBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="320" y="223">init()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="320" y="237">run()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="320" y="251">shutdown()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="320" y="265">configure()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-style="italic" x="320" y="279">command()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="320" y="293">~DProcessBase()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="320" y="307">shouldShutdown()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="320" y="321">setShutdownFlag()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="320" y="335">getAppName()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="320" y="349">getIoService()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="320" y="363">stopIOService()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="320" y="377">getCfgMgr()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="315" y1="265" x2="236" y2="265" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="315,265 309,271 303,265 309,259" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="236" y1="213" x2="230" y2="219" /> + <line stroke="black" stroke-opacity="1" x1="236" y1="213" x2="242" y2="219" /> + <line stroke="black" stroke-opacity="1" x1="236" y1="265" x2="236" y2="213" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="424" y="72" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="320" y="116" width="107" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="316" y="68" width="108" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="370" y="82"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="370" y="98">DProcessBasePtr</text> + <line stroke="black" stroke-opacity="1" x1="316" y1="100" x2="424" y2="100" /> + <line stroke="black" stroke-opacity="1" x1="316" y1="108" x2="424" y2="108" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="448" y="468" width="3" height="313" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="276" y="778" width="175" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="272" y="464" width="176" height="314" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="360" y="478">D2Process</text> + <line stroke="black" stroke-opacity="1" x1="272" y1="480" x2="448" y2="480" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="276" y="494">QUEUE_RESTART_PERCENT</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="508">reconf_queue_flag_</text> + <line stroke="black" stroke-opacity="1" x1="272" y1="510" x2="448" y2="510" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="524">D2Process()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="538">init()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="552">run()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="566">shutdown()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="580">configure()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="594">command()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="608">~D2Process()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="622">checkQueueStatus()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="636">reconfigureQueueMgr()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="650">runIO()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="664">canShutdown()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="678">setReconfQueueFlag()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="692">setShutdownType()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="706">getD2CfgMgr()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="720">getD2QueueMgr()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="734">getD2UpdateMgr()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="748">getReconfQueueFlag()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="276" y="762">getShutdownType()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="276" y="776">getShutdownTypeStr()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="372" y1="148" x2="378" y2="142" /> + <line stroke="black" stroke-opacity="1" x1="372" y1="148" x2="366" y2="142" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="372" y1="120" x2="372" y2="148" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="179" y1="39" x2="372" y2="39" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="179,39 185,33 191,39 185,45" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="372" y1="67" x2="378" y2="61" /> + <line stroke="black" stroke-opacity="1" x1="372" y1="67" x2="366" y2="61" /> + <line stroke="black" stroke-opacity="1" x1="372" y1="39" x2="372" y2="67" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="366" y1="463" x2="367" y2="388" /> + <line stroke="black" stroke-opacity="1" x1="368" y1="383" x2="361" y2="388" /> + <line stroke="black" stroke-opacity="1" x1="368" y1="383" x2="373" y2="389" /> + <line stroke="black" stroke-opacity="1" x1="361" y1="388" x2="373" y2="389" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="271" y1="607" x2="265" y2="600" /> + <line stroke="black" stroke-opacity="1" x1="271" y1="607" x2="264" y2="612" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="138" y1="606" x2="271" y2="607" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="649" y="539" width="3" height="243" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="499" y="779" width="153" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="495" y="535" width="154" height="244" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="572" y="549">D2CfgMgr</text> + <line stroke="black" stroke-opacity="1" x1="495" y1="551" x2="649" y2="551" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="499" y="565">IPV4_REV_ZONE_SUFFIX</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="499" y="579">IPV6_REV_ZONE_SUFFIX</text> + <line stroke="black" stroke-opacity="1" x1="495" y1="581" x2="649" y2="581" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="499" y="595">D2CfgMgr()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="499" y="609">~D2CfgMgr()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="499" y="623">getD2CfgContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="499" y="637">forwardUpdatesEnabled()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="499" y="651">reverseUpdatesEnabled()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="499" y="665">matchForward()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="499" y="679">matchReverse()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="499" y="693">reverseIpAddress()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="499" y="707">reverseV4Address()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-decoration="underline" x="499" y="721">reverseV6Address()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="499" y="735">getD2Params()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="499" y="749">buildParams()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="499" y="763">createConfigParser()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="499" y="777">createNewContext()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="573" y1="534" x2="572" y2="460" /> + <line stroke="black" stroke-opacity="1" x1="572" y1="455" x2="566" y2="461" /> + <line stroke="black" stroke-opacity="1" x1="572" y1="455" x2="578" y2="460" /> + <line stroke="black" stroke-opacity="1" x1="566" y1="461" x2="578" y2="460" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="494" y1="636" x2="487" y2="630" /> + <line stroke="black" stroke-opacity="1" x1="494" y1="636" x2="488" y2="642" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="452" y1="637" x2="494" y2="636" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="788" y="629" width="3" height="151" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="694" y="777" width="97" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="690" y="625" width="98" height="152" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="739" y="639">D2CfgContext</text> + <line stroke="black" stroke-opacity="1" x1="690" y1="641" x2="788" y2="641" /> + <line stroke="black" stroke-opacity="1" x1="690" y1="649" x2="788" y2="649" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="694" y="663">D2CfgContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="694" y="677">~D2CfgContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="694" y="691">clone()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="694" y="705">getD2Params()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="694" y="719">getForwardMgr()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="694" y="733">getReverseMgr()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="694" y="747">getKeys()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="694" y="761">D2CfgContext()</text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="694" y="775">operator =()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="740" y1="624" x2="739" y2="610" /> + <line stroke="black" stroke-opacity="1" x1="739" y1="605" x2="733" y2="611" /> + <line stroke="black" stroke-opacity="1" x1="739" y1="605" x2="745" y2="610" /> + <line stroke="black" stroke-opacity="1" x1="733" y1="611" x2="745" y2="610" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="689" y1="678" x2="682" y2="672" /> + <line stroke="black" stroke-opacity="1" x1="689" y1="678" x2="683" y2="684" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="653" y1="679" x2="689" y2="678" /> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="246" y="231">io_service_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="382" y="63">process_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="246" y="156">io_service_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="736" y="285">context_</text> +</g> +<g> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" x="572" y="183">cfg_mgr_</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="788" y="294" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="666" y="338" width="125" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="662" y="290" width="126" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="725" y="304"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="725" y="320">DCfgContextBasePtr</text> + <line stroke="black" stroke-opacity="1" x1="662" y1="322" x2="788" y2="322" /> + <line stroke="black" stroke-opacity="1" x1="662" y1="330" x2="788" y2="330" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="728" y1="356" x2="734" y2="350" /> + <line stroke="black" stroke-opacity="1" x1="728" y1="356" x2="722" y2="350" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="728" y1="342" x2="728" y2="356" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="633" y1="271" x2="726" y2="271" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="633,271 639,265 645,271 639,277" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="726" y1="289" x2="732" y2="283" /> + <line stroke="black" stroke-opacity="1" x1="726" y1="289" x2="720" y2="283" /> + <line stroke="black" stroke-opacity="1" x1="726" y1="271" x2="726" y2="289" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="612" y="192" width="3" height="47" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="512" y="236" width="103" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="508" y="188" width="104" height="48" /> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" text-anchor="middle" x="560" y="202"><<typedef>></text> + <text font-family="Helvetica" font-size="12" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="560" y="218">DCfgMgrBasePtr</text> + <line stroke="black" stroke-opacity="1" x1="508" y1="220" x2="612" y2="220" /> + <line stroke="black" stroke-opacity="1" x1="508" y1="228" x2="612" y2="228" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="564" y1="256" x2="569" y2="249" /> + <line stroke="black" stroke-opacity="1" x1="564" y1="256" x2="557" y2="250" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="563" y1="240" x2="564" y2="256" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="428" y1="173" x2="562" y2="173" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="428,173 434,167 440,173 434,179" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="562" y1="187" x2="568" y2="181" /> + <line stroke="black" stroke-opacity="1" x1="562" y1="187" x2="556" y2="181" /> + <line stroke="black" stroke-opacity="1" x1="562" y1="173" x2="562" y2="187" /> +</g> +</svg> diff --git a/src/bin/d2/images/nc_trans_sequence.svg b/src/bin/d2/images/nc_trans_sequence.svg new file mode 100644 index 0000000..ae0daeb --- /dev/null +++ b/src/bin/d2/images/nc_trans_sequence.svg @@ -0,0 +1,230 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="848" height="647" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="593" y="8" width="3" height="16" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="541" y="21" width="55" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="537" y="4" width="56" height="17" /> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="565" y="18">:DNSClient</text> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="567" y1="45" x2="567" y2="647" /> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="143" y1="45" x2="143" y2="647" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="177" y="8" width="3" height="16" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="109" y="21" width="71" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="105" y="4" width="72" height="17" /> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="141" y="18">:D2UpdateMgr</text> +</g> + <text font-family="Helvetica" font-size="20" fill="#000000" xml:space="preserve" x="603" y="71">Sequence depicting a</text> + <text font-family="Helvetica" font-size="20" fill="#000000" xml:space="preserve" x="603" y="91">simple state model which</text> + <text font-family="Helvetica" font-size="20" fill="#000000" xml:space="preserve" x="603" y="111">performs a single update.</text> +<g> + <polygon fill="#c0ffff" stroke="black" stroke-opacity="1" points="6,115 120,115 120,125 130,125 130,185 6,185 6,115" /> + <line stroke="black" stroke-opacity="1" x1="120" y1="115" x2="130" y2="125" /> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="16" y="135">As part of Update</text> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="16" y="145">Manager's sweep()</text> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="16" y="155">between events it</text> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="16" y="165">creates and starts the</text> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="16" y="175">transaction</text> +</g> +<g> + <polygon fill="#c0ffff" stroke="black" stroke-opacity="1" points="611,473 765,473 765,483 775,483 775,525 611,525 611,473" /> + <line stroke="black" stroke-opacity="1" x1="765" y1="473" x2="775" y2="483" /> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="621" y="493">runStateModel() Iterates through</text> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="621" y="503">states until DONE_ST is</text> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="621" y="513">reached</text> +</g> +<g> + <line stroke="black" stroke-dasharray="18,6" stroke-opacity="1" x1="336" y1="45" x2="336" y2="647" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="138" y="131" width="10" height="213" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="396" y="8" width="3" height="16" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="277" y="21" width="122" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="273" y="4" width="123" height="17" /> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" text-decoration="underline" text-anchor="middle" x="335" y="18">:NameChangeTransaction</text> +</g> +<g> + <polygon fill="#c0ffff" stroke="black" stroke-opacity="1" points="611,330 769,330 769,340 779,340 779,388 611,388 611,330" /> + <line stroke="black" stroke-opacity="1" x1="769" y1="330" x2="779" y2="340" /> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="621" y="350">At some point later, DNSClient</text> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="621" y="360">invokes callback when IO</text> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="621" y="370">completes</text> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="331" y="77" width="10" height="25" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="331" y="406" width="10" height="210" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="562" y="283" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="138" y="77" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="331" y="128" width="10" height="219" /> +</g> +<g> + <polygon fill="#c0ffff" stroke="black" stroke-opacity="1" points="596,206 766,206 766,216 776,216 776,252 596,252 596,206" /> + <line stroke="black" stroke-opacity="1" x1="766" y1="206" x2="776" y2="216" /> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="606" y="226">runStateModel() Iterates through</text> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="606" y="236">states until an update is initiated</text> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="562" y="409" width="10" height="214" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="670" y1="388" x2="572" y2="417" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="149" y1="142" x2="331" y2="142" /> + <polygon fill="#000000" stroke="none" points="331,142 327,138 327,146" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="342" y1="414" x2="562" y2="414" /> + <polygon fill="#000000" stroke="none" points="342,414 346,410 346,418" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="149" y1="82" x2="331" y2="82" /> + <polygon fill="#000000" stroke="none" points="331,82 327,78 327,86" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="337" y="221" width="10" height="115" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="337" y="424" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="337" y="175" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="337" y="136" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="337" y="466" width="10" height="126" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 349 467 L 366 467 L 366 474 L 349 474" /> + <polygon fill="#000000" stroke="none" points="349,474 353,478 353,470" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 349 222 L 366 222 L 366 229 L 349 229" /> + <polygon fill="#000000" stroke="none" points="349,229 353,233 353,225" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 349 425 L 366 425 L 366 432 L 349 432" /> + <polygon fill="#000000" stroke="none" points="349,432 353,436 353,428" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 349 137 L 366 137 L 366 144 L 349 144" /> + <polygon fill="#000000" stroke="none" points="349,144 353,148 353,140" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 349 176 L 366 176 L 366 183 L 349 183" /> + <polygon fill="#000000" stroke="none" points="349,183 353,187 353,179" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" stroke-dasharray="4,4" x1="342" y1="611" x2="562" y2="611" /> + <path fill="none" stroke="black" stroke-opacity="1" d="M 558 607 L 562 611 L 558 615" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="595" y1="228" x2="367" y2="227" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="610" y1="492" x2="367" y2="472" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="343" y="491" width="10" height="95" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="343" y="247" width="10" height="83" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 355 248 L 372 248 L 372 255 L 355 255" /> + <polygon fill="#000000" stroke="none" points="355,255 359,259 359,251" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 355 492 L 372 492 L 372 499 L 355 499" /> + <polygon fill="#000000" stroke="none" points="355,499 359,503 359,495" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="354" y1="288" x2="562" y2="288" /> + <path fill="none" stroke="black" stroke-opacity="1" d="M 558 284 L 562 288 L 558 292" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" stroke-dasharray="4,4" x1="149" y1="339" x2="331" y2="339" /> + <path fill="none" stroke="black" stroke-opacity="1" d="M 153 335 L 149 339 L 153 343" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="349" y="515" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="349" y="556" width="10" height="24" /> +</g> +<g> + <rect fill="#ffffff" stroke="black" stroke-width="1" stroke-opacity="1" x="349" y="300" width="10" height="24" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 361 516 L 378 516 L 378 523 L 361 523" /> + <polygon fill="#000000" stroke="none" points="361,523 365,527 365,519" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 361 557 L 378 557 L 378 564 L 361 564" /> + <polygon fill="#000000" stroke="none" points="361,564 365,568 365,560" /> +</g> +<g> + <path fill="none" stroke="black" stroke-opacity="1" d="M 361 301 L 378 301 L 378 308 L 361 308" /> + <polygon fill="#000000" stroke="none" points="361,308 365,312 365,304" /> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="439" y="283">doUpdate()</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="361" y="173">setState(READY_ST)</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="352" y="216">runStateModel(START_TRANSACTION_EVT)</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="211" y="137">startTransaction()</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="383" y="250">(getStateHandler())()</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="363" y="410">operator ()()</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="357" y="133">initStateHandlerMap()</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="371" y="433">setDnsUpdateStatus()</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="350" y="465">runStateModel(IO_COMPLETED_EVT)</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="183" y="77">NameChangeTransaction()</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="377" y="499">(getStateHandler)()</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="374" y="579">setNextEvent(NOP_EVT)</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="382" y="521">setState(DONE_ST)</text> +</g> +<g> + <text font-family="Helvetica" font-size="10" fill="#000000" xml:space="preserve" x="391" y="310">setNextEvent(NOP_EVT)</text> +</g> +</svg> diff --git a/src/bin/d2/images/remove_state_model.svg b/src/bin/d2/images/remove_state_model.svg new file mode 100644 index 0000000..7dfb6da --- /dev/null +++ b/src/bin/d2/images/remove_state_model.svg @@ -0,0 +1,304 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="788" height="792" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="331" y="16" width="74" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="327" y="12" width="74" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="364" y="28">READY_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="280" y="138" width="180" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="276" y="134" width="180" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="366" y="150">SELECTING_FWD_SERVER_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="56" y="493" width="178" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="52" y="489" width="178" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="141" y="505">SELECTING_REV_SERVER_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="285" y="234" width="172" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="281" y="230" width="172" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="367" y="246">REMOVING_FWD_ADDRS_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="293" y="349" width="156" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="289" y="345" width="156" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="367" y="361">REMOVING_FWD_RRS_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="588" y="610" width="188" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="584" y="606" width="188" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="678" y="622">PROCESS_REMOVE_FAILED_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="277" y="687" width="166" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="273" y="683" width="166" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="356" y="699">PROCESS_REMOVE_OK_ST</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="62" y="608" width="160" height="22" rx="10" /> + <rect fill="#ffffc0" stroke="black" stroke-opacity="1" x="58" y="604" width="160" height="22" rx="10" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="138" y="620">REMOVING_REV_PTRS_ST</text> +</g> +<polygon fill="white" stroke="black" stroke-opacity="1" points ="358,88 367,71 376,88 367,105" /> +<ellipse fill="black" cx="163" cy="26" rx="8.5" ry="8.5" /> +<g> + <ellipse fill="white" stroke="black" stroke-width="1" stroke-opacity="1" cx="531" cy="768" rx="11.5" ry="11.5" /> + <ellipse fill="black" cx="531" cy="768" rx="8.5" ry="8.5" /> +</g> +<polygon fill="white" stroke="black" stroke-opacity="1" points ="346,451 355,434 364,451 355,468" /> +<g> + <rect fill="none" stroke="black" stroke-width="1" stroke-opacity="1" x="153" y="255" width="118" height="43" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="212" y="271"><<DNS IO Callback>></text> +</g> +<g> + <rect fill="none" stroke="black" stroke-width="1" stroke-opacity="1" x="11" y="666" width="118" height="43" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="70" y="682"><<DNS IO Callback>></text> +</g> +<g> + <rect fill="none" stroke="black" stroke-width="1" stroke-opacity="1" x="154" y="368" width="118" height="43" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="213" y="384"><<DNS IO Callback>></text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="326" y1="25" x2="320" y2="19" /> + <line stroke="black" stroke-opacity="1" x1="326" y1="25" x2="320" y2="31" /> + <line stroke="black" stroke-opacity="1" x1="172" y1="25" x2="326" y2="25" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="366" y1="69" x2="372" y2="63" /> + <line stroke="black" stroke-opacity="1" x1="366" y1="69" x2="360" y2="63" /> + <line stroke="black" stroke-opacity="1" x1="366" y1="38" x2="366" y2="69" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="367" y1="133" x2="373" y2="127" /> + <line stroke="black" stroke-opacity="1" x1="367" y1="133" x2="361" y2="127" /> + <line stroke="black" stroke-opacity="1" x1="367" y1="106" x2="367" y2="133" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="354" y1="88" x2="143" y2="88" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="143" y1="488" x2="149" y2="482" /> + <line stroke="black" stroke-opacity="1" x1="143" y1="488" x2="137" y2="482" /> + <line stroke="black" stroke-opacity="1" x1="143" y1="88" x2="143" y2="488" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="368" y1="229" x2="374" y2="223" /> + <line stroke="black" stroke-opacity="1" x1="368" y1="229" x2="362" y2="223" /> + <line stroke="black" stroke-opacity="1" x1="368" y1="160" x2="368" y2="229" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="351" y1="229" x2="293" y2="190" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="340" y1="160" x2="331" y2="158" /> + <line stroke="black" stroke-opacity="1" x1="340" y1="160" x2="338" y2="168" /> + <line stroke="black" stroke-opacity="1" x1="293" y1="190" x2="340" y2="160" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="368" y1="344" x2="374" y2="338" /> + <line stroke="black" stroke-opacity="1" x1="368" y1="344" x2="362" y2="337" /> + <line stroke="black" stroke-opacity="1" x1="369" y1="256" x2="368" y2="344" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="460" y1="147" x2="680" y2="147" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="680" y1="605" x2="686" y2="599" /> + <line stroke="black" stroke-opacity="1" x1="680" y1="605" x2="674" y2="599" /> + <line stroke="black" stroke-opacity="1" x1="680" y1="147" x2="680" y2="605" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="457" y1="243" x2="680" y2="243" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="680" y1="605" x2="686" y2="599" /> + <line stroke="black" stroke-opacity="1" x1="680" y1="605" x2="674" y2="599" /> + <line stroke="black" stroke-opacity="1" x1="680" y1="243" x2="680" y2="605" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="449" y1="357" x2="680" y2="357" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="680" y1="605" x2="686" y2="599" /> + <line stroke="black" stroke-opacity="1" x1="680" y1="605" x2="674" y2="599" /> + <line stroke="black" stroke-opacity="1" x1="680" y1="357" x2="680" y2="605" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="429" y1="371" x2="429" y2="398" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="429" y1="398" x2="679" y2="398" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="679" y1="605" x2="685" y2="599" /> + <line stroke="black" stroke-opacity="1" x1="679" y1="605" x2="673" y2="599" /> + <line stroke="black" stroke-opacity="1" x1="679" y1="398" x2="679" y2="605" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="680" y1="632" x2="680" y2="767" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="543" y1="767" x2="549" y2="773" /> + <line stroke="black" stroke-opacity="1" x1="543" y1="767" x2="549" y2="761" /> + <line stroke="black" stroke-opacity="1" x1="680" y1="767" x2="543" y2="767" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="358" y1="709" x2="358" y2="767" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="518" y1="767" x2="512" y2="761" /> + <line stroke="black" stroke-opacity="1" x1="518" y1="767" x2="512" y2="773" /> + <line stroke="black" stroke-opacity="1" x1="358" y1="767" x2="518" y2="767" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="161" y1="515" x2="219" y2="561" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="156" y1="603" x2="164" y2="604" /> + <line stroke="black" stroke-opacity="1" x1="156" y1="603" x2="157" y2="594" /> + <line stroke="black" stroke-opacity="1" x1="219" y1="561" x2="156" y2="603" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="125" y1="603" x2="72" y2="559" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="125" y1="515" x2="116" y2="514" /> + <line stroke="black" stroke-opacity="1" x1="125" y1="515" x2="124" y2="523" /> + <line stroke="black" stroke-opacity="1" x1="72" y1="559" x2="125" y2="515" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="342" y1="450" x2="143" y2="450" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="143" y1="488" x2="149" y2="482" /> + <line stroke="black" stroke-opacity="1" x1="143" y1="488" x2="137" y2="482" /> + <line stroke="black" stroke-opacity="1" x1="143" y1="450" x2="143" y2="488" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="354" y1="432" x2="360" y2="426" /> + <line stroke="black" stroke-opacity="1" x1="354" y1="432" x2="348" y2="426" /> + <line stroke="black" stroke-opacity="1" x1="354" y1="371" x2="354" y2="432" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="358" y1="682" x2="364" y2="676" /> + <line stroke="black" stroke-opacity="1" x1="358" y1="682" x2="352" y2="676" /> + <line stroke="black" stroke-opacity="1" x1="358" y1="469" x2="358" y2="682" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="234" y1="501" x2="680" y2="501" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="680" y1="605" x2="686" y2="599" /> + <line stroke="black" stroke-opacity="1" x1="680" y1="605" x2="674" y2="599" /> + <line stroke="black" stroke-opacity="1" x1="680" y1="501" x2="680" y2="605" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="583" y1="618" x2="577" y2="611" /> + <line stroke="black" stroke-opacity="1" x1="583" y1="618" x2="576" y2="623" /> + <line stroke="black" stroke-opacity="1" x1="222" y1="617" x2="583" y2="618" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="163" y1="630" x2="163" y2="696" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="272" y1="696" x2="266" y2="690" /> + <line stroke="black" stroke-opacity="1" x1="272" y1="696" x2="266" y2="702" /> + <line stroke="black" stroke-opacity="1" x1="163" y1="696" x2="272" y2="696" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="213" y1="367" x2="213" y2="358" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="209" y1="254" x2="209" y2="243" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="73" y1="667" x2="74" y2="667" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="74" y1="630" x2="68" y2="636" /> + <line stroke="black" stroke-opacity="1" x1="74" y1="630" x2="80" y2="636" /> + <line stroke="black" stroke-opacity="1" x1="74" y1="667" x2="74" y2="630" /> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="527" y="497">NO_MORE_SERVERS_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="655" y="695">END_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="298" y="420">UPDATE_OK_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="551" y="352">UPDATE_FAILED_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="374" y="202">SERVER_SELECTED_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="176" y="688">UPDATE_OK_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="171" y="350">IO_COMPLETED_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="133" y="580">SERVER_SELECTED_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="366" y="681">No reverse change requested</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="332" y="733">END_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="164" y="448">Reverse change requested</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="452" y="612">UPDATE_FAILED_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="529" y="394">SERVER_IO_ERROR_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="525" y="140">NO_MORE_SERVERS_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="381" y="332">UPDATE_OK_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="548" y="234">UPDATE_FAILED_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="213" y="192">SERVER_IO_ERROR_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="164" y="82">Only reverse change requested</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="203" y="14">START_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="384" y="115">Forward change requested</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="19" y="554">SERVER_IO_ERROR_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="156" y="237">IO_COMPLETED_EVT</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="655">IO_COMPLETED_EVT</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="288" y1="358" x2="282" y2="352" /> + <line stroke="black" stroke-opacity="1" x1="288" y1="358" x2="282" y2="364" /> + <line stroke="black" stroke-opacity="1" x1="213" y1="358" x2="288" y2="358" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="280" y1="243" x2="274" y2="237" /> + <line stroke="black" stroke-opacity="1" x1="280" y1="243" x2="274" y2="249" /> + <line stroke="black" stroke-opacity="1" x1="209" y1="243" x2="280" y2="243" /> +</g> +</svg> diff --git a/src/bin/d2/images/request_mgt_classes.svg b/src/bin/d2/images/request_mgt_classes.svg new file mode 100644 index 0000000..600c187 --- /dev/null +++ b/src/bin/d2/images/request_mgt_classes.svg @@ -0,0 +1,316 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="791" height="846" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="169" y="10" width="3" height="291" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="11" y="298" width="161" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="7" y="6" width="162" height="292" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="88" y="19">D2Process</text> + <line stroke="black" stroke-opacity="1" x1="7" y1="21" x2="169" y2="21" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="11" y="34">QUEUE_RESTART_PERCENT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="47">reconf_queue_flag_</text> + <line stroke="black" stroke-opacity="1" x1="7" y1="49" x2="169" y2="49" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="62">D2Process()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="75">init()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="88">run()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="101">shutdown()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="114">configure()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="127">command()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="140">~D2Process()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="153">checkQueueStatus()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="166">reconfigureQueueMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="179">runIO()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="192">canShutdown()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="205">setReconfQueueFlag()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="218">setShutdownType()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="231">getD2CfgMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="244">getD2QueueMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="257">getD2UpdateMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="270">getReconfQueueFlag()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="11" y="283">getShutdownType()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="11" y="296">getShutdownTypeStr()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="367" y="446" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="297" y="488" width="73" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="293" y="442" width="74" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="330" y="455"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="330" y="470">IOServicePtr</text> + <line stroke="black" stroke-opacity="1" x1="293" y1="472" x2="367" y2="472" /> + <line stroke="black" stroke-opacity="1" x1="293" y1="480" x2="367" y2="480" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="587" y="319" width="3" height="199" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="463" y="515" width="127" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="459" y="315" width="128" height="200" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" font-style="italic" text-anchor="middle" x="523" y="328">NameChangeListener</text> + <line stroke="black" stroke-opacity="1" x1="459" y1="330" x2="587" y2="330" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="463" y="343">listening_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="463" y="356">io_pending_</text> + <line stroke="black" stroke-opacity="1" x1="459" y1="358" x2="587" y2="358" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="463" y="371">NameChangeListener()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="463" y="384">~NameChangeListener()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="463" y="397">startListening()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="463" y="410">stopListening()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="463" y="423">receiveNext()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="463" y="436">invokeRecvHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-style="italic" x="463" y="449">open()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-style="italic" x="463" y="462">close()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-style="italic" x="463" y="475">doReceive()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="463" y="488">amListening()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="463" y="501">isIoPending()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="463" y="514">setListening()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="371" y1="464" x2="377" y2="470" /> + <line stroke="black" stroke-opacity="1" x1="371" y1="464" x2="377" y2="458" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="458" y1="464" x2="371" y2="464" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="775" y="218" width="3" height="615" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="653" y="830" width="125" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="649" y="214" width="126" height="616" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="712" y="227">NameChangeRequest</text> + <line stroke="black" stroke-opacity="1" x1="649" y1="229" x2="775" y2="229" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="242">forward_change_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="255">reverse_change_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="268">fqdn_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="281">lease_expires_on_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="294">lease_length_</text> + <line stroke="black" stroke-opacity="1" x1="649" y1="296" x2="775" y2="296" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="309">NameChangeRequest()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="322">NameChangeRequest()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="653" y="335">fromFormat()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="348">toFormat()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="653" y="361">fromJSON()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="374">toJSON()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="387">validateContent()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="400">getChangeType()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="413">setChangeType()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="426">setChangeType()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="439">isForwardChange()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="452">setForwardChange()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="465">setForwardChange()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="478">isReverseChange()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="491">setReverseChange()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="504">setReverseChange()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="517">getFqdn()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="530">setFqdn()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="543">setFqdn()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="556">getIpAddress()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="569">getIpIoAddress()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="582">isV4()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="595">isV6()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="608">setIpAddress()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="621">setIpAddress()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="634">getDhcid()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="647">setDhcid()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="660">setDhcid()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="673">getLeaseExpiresOn()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="686">getLeaseExpiresOnStr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="699">setLeaseExpiresOn()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="712">setLeaseExpiresOn()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="725">getLeaseLength()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="738">setLeaseLength()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="751">setLeaseLength()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="764">getStatus()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="777">setStatus()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="790">getElement()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="803">toText()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="816">operator ==()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="653" y="829">operator !=()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="377" y="113" width="3" height="277" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="249" y="387" width="131" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="245" y="109" width="132" height="278" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="311" y="122">D2QueueMgr</text> + <line stroke="black" stroke-opacity="1" x1="245" y1="124" x2="377" y2="124" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="137">MAX_QUEUE_DEFAULT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="150">max_queue_size_</text> + <line stroke="black" stroke-opacity="1" x1="245" y1="152" x2="377" y2="152" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="165">D2QueueMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="178">~D2QueueMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="191">initUDPListener()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="204">startListening()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="217">operator ()()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="230">stopListening()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="243">removeListener()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="256">getQueueSize()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="269">getMaxQueueSize()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="282">setMaxQueueSize()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="295">getMgrState()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="308">peek()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="321">peekAt()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="334">dequeueAt()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="347">dequeue()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="360">enqueue()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="373">clearQueue()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="386">updateStopState()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="590" y="227" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="458" y="269" width="135" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="454" y="223" width="136" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="522" y="236"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="522" y="251">NameChangeListenerPtr</text> + <line stroke="black" stroke-opacity="1" x1="454" y1="253" x2="590" y2="253" /> + <line stroke="black" stroke-opacity="1" x1="454" y1="261" x2="590" y2="261" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="604" y="547" width="3" height="173" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="456" y="717" width="151" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="452" y="543" width="152" height="174" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="528" y="556">NameChangeUDPListener</text> + <line stroke="black" stroke-opacity="1" x1="452" y1="558" x2="604" y2="558" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="456" y="571">RECV_BUF_MAX</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="456" y="584">port_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="456" y="597">asio_socket_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="456" y="610">reuse_address_</text> + <line stroke="black" stroke-opacity="1" x1="452" y1="612" x2="604" y2="612" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="456" y="625">NameChangeUDPListener()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="456" y="638">~NameChangeUDPListener()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="456" y="651">open()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="456" y="664">close()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="456" y="677">doReceive()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="456" y="690">receiveCompletionHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="456" y="703">NameChangeUDPListener()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="456" y="716">operator =()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="331" y1="441" x2="337" y2="435" /> + <line stroke="black" stroke-opacity="1" x1="331" y1="441" x2="325" y2="435" /> + <line stroke="black" stroke-opacity="1" x1="331" y1="391" x2="331" y2="441" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="331,391 337,397 331,403 325,397" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="524" y1="314" x2="530" y2="308" /> + <line stroke="black" stroke-opacity="1" x1="524" y1="314" x2="518" y2="308" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="524" y1="273" x2="524" y2="314" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="453" y1="245" x2="447" y2="239" /> + <line stroke="black" stroke-opacity="1" x1="453" y1="245" x2="447" y2="251" /> + <line stroke="black" stroke-opacity="1" x1="381" y1="245" x2="453" y2="245" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="381,245 387,239 393,245 387,251" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="527" y1="542" x2="526" y2="524" /> + <line stroke="black" stroke-opacity="1" x1="526" y1="519" x2="520" y2="525" /> + <line stroke="black" stroke-opacity="1" x1="526" y1="519" x2="532" y2="524" /> + <line stroke="black" stroke-opacity="1" x1="520" y1="525" x2="532" y2="524" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="554" y="129" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="472" y="171" width="85" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="468" y="125" width="86" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="511" y="138"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="511" y="153">RequestQueue</text> + <line stroke="black" stroke-opacity="1" x1="468" y1="155" x2="554" y2="155" /> + <line stroke="black" stroke-opacity="1" x1="468" y1="163" x2="554" y2="163" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="173" y="353" width="3" height="129" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="35" y="479" width="141" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="31" y="349" width="142" height="130" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="102" y="362"><<enum>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="102" y="377">State</text> + <line stroke="black" stroke-opacity="1" x1="31" y1="379" x2="173" y2="379" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="35" y="392">NOT_INITTED</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="35" y="405">INITTED</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="35" y="418">RUNNING</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="35" y="431">STOPPING</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="35" y="444">STOPPED_QUEUE_FULL</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="35" y="457">STOPPED_RECV_ERROR</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="35" y="470">STOPPED</text> + <line stroke="black" stroke-opacity="1" x1="31" y1="472" x2="173" y2="472" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="467" y1="150" x2="461" y2="144" /> + <line stroke="black" stroke-opacity="1" x1="467" y1="150" x2="461" y2="156" /> + <line stroke="black" stroke-opacity="1" x1="381" y1="150" x2="467" y2="150" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="381,150 387,144 393,150 387,156" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="244" y1="319" x2="102" y2="319" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="244,319 238,325 232,319 238,313" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="102" y1="348" x2="108" y2="342" /> + <line stroke="black" stroke-opacity="1" x1="102" y1="348" x2="96" y2="342" /> + <line stroke="black" stroke-opacity="1" x1="102" y1="319" x2="102" y2="348" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="285" y1="391" x2="285" y2="416" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="285,391 291,397 285,403 279,397" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="177" y1="416" x2="183" y2="422" /> + <line stroke="black" stroke-opacity="1" x1="177" y1="416" x2="183" y2="410" /> + <line stroke="black" stroke-opacity="1" x1="285" y1="416" x2="177" y2="416" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="177" y1="367" x2="232" y2="366" /> +<ellipse fill="none" stroke="black" stroke-width="1" stroke-opacity="1" cx="238" cy="366" rx="5" ry="5" /> + <line stroke="black" stroke-opacity="1" x1="233" y1="366" x2="243" y2="366" /> + <line stroke="black" stroke-opacity="1" x1="238" y1="361" x2="238" y2="371" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="774" y="129" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="642" y="171" width="135" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="638" y="125" width="136" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="706" y="138"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="706" y="153">NameChangeRequestPtr</text> + <line stroke="black" stroke-opacity="1" x1="638" y1="155" x2="774" y2="155" /> + <line stroke="black" stroke-opacity="1" x1="638" y1="163" x2="774" y2="163" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="708" y1="213" x2="714" y2="207" /> + <line stroke="black" stroke-opacity="1" x1="708" y1="213" x2="702" y2="207" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="708" y1="175" x2="708" y2="213" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="637" y1="150" x2="631" y2="144" /> + <line stroke="black" stroke-opacity="1" x1="637" y1="150" x2="631" y2="156" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="558" y1="150" x2="637" y2="150" /> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="187" y="54">queue_mgr_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="341" y="438">io_service_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="187" y="413">target_stop_state_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="401" y="242">listener_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="112" y="345">mgr_state_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="399" y="147">ncr_queue_</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="352" y="36" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="264" y="78" width="91" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="260" y="32" width="92" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="306" y="45"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="306" y="60">D2QueueMgrPtr</text> + <line stroke="black" stroke-opacity="1" x1="260" y1="62" x2="352" y2="62" /> + <line stroke="black" stroke-opacity="1" x1="260" y1="70" x2="352" y2="70" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="259" y1="57" x2="252" y2="51" /> + <line stroke="black" stroke-opacity="1" x1="259" y1="57" x2="253" y2="63" /> + <line stroke="black" stroke-opacity="1" x1="173" y1="58" x2="259" y2="57" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="173,58 178,51 184,57 179,63" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="308" y1="108" x2="314" y2="102" /> + <line stroke="black" stroke-opacity="1" x1="308" y1="108" x2="302" y2="102" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="308" y1="82" x2="308" y2="108" /> +</g> +</svg> diff --git a/src/bin/d2/images/state_model_classes.svg b/src/bin/d2/images/state_model_classes.svg new file mode 100644 index 0000000..d212694 --- /dev/null +++ b/src/bin/d2/images/state_model_classes.svg @@ -0,0 +1,246 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="645" height="712" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="212" y="431" width="3" height="63" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="170" y="491" width="45" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="166" y="427" width="46" height="64" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="189" y="440">State</text> + <line stroke="black" stroke-opacity="1" x1="166" y1="442" x2="212" y2="442" /> + <line stroke="black" stroke-opacity="1" x1="166" y1="450" x2="212" y2="450" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="170" y="463">State()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="170" y="476">~State()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="170" y="489">run()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="227" y="531" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="161" y="573" width="69" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="157" y="527" width="70" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="192" y="540"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="192" y="555">StatePtr</text> + <line stroke="black" stroke-opacity="1" x1="157" y1="557" x2="227" y2="557" /> + <line stroke="black" stroke-opacity="1" x1="157" y1="565" x2="227" y2="565" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="191" y1="495" x2="185" y2="501" /> + <line stroke="black" stroke-opacity="1" x1="191" y1="495" x2="197" y2="500" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="192" y1="526" x2="191" y2="495" /> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="403" y="271">states_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="255" y="125">map_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="108" y="458">handler_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="426" y="86">events_</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="94" y="441" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="22" y="483" width="75" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="18" y="437" width="76" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="56" y="450"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="56" y="465">StateHandler</text> + <line stroke="black" stroke-opacity="1" x1="18" y1="467" x2="94" y2="467" /> + <line stroke="black" stroke-opacity="1" x1="18" y1="475" x2="94" y2="475" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="98" y1="461" x2="104" y2="467" /> + <line stroke="black" stroke-opacity="1" x1="98" y1="461" x2="104" y2="455" /> + <line stroke="black" stroke-opacity="1" x1="165" y1="461" x2="98" y2="461" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="165,461 159,467 153,461 159,455" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="84" y="302" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="18" y="344" width="69" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="14" y="298" width="70" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="49" y="311"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="49" y="326">Event</text> + <line stroke="black" stroke-opacity="1" x1="14" y1="328" x2="84" y2="328" /> + <line stroke="black" stroke-opacity="1" x1="14" y1="336" x2="84" y2="336" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="84" y="183" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="18" y="225" width="69" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="14" y="179" width="70" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="49" y="192"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="49" y="207">EventPtr</text> + <line stroke="black" stroke-opacity="1" x1="14" y1="209" x2="84" y2="209" /> + <line stroke="black" stroke-opacity="1" x1="14" y1="217" x2="84" y2="217" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="629" y="32" width="3" height="667" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="485" y="696" width="147" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="481" y="28" width="148" height="668" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="555" y="41">StateModel</text> + <line stroke="black" stroke-opacity="1" x1="481" y1="43" x2="629" y2="43" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="485" y="56">NEW_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="485" y="69">END_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="485" y="82">SM_DERIVED_STATE_MIN</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="485" y="95">NOP_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="485" y="108">START_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="485" y="121">END_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="485" y="134">FAIL_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="485" y="147">SM_DERIVED_EVENT_MIN</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="160">dictionaries_initted_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="173">curr_state_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="186">prev_state_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="199">last_event_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="212">next_event_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="225">on_entry_flag_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="238">on_exit_flag_</text> + <line stroke="black" stroke-opacity="1" x1="481" y1="240" x2="629" y2="240" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="253">StateModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="266">~StateModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="279">startModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="292">runModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="305">endModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="318">nopStateHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="331">initDictionaries()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="344">defineEvents()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="357">defineEvent()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="370">getEvent()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="383">verifyEvents()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="396">defineStates()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="409">defineState()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="422">getState()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="435">verifyStates()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="448">onModelFailure()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="461">transition()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="474">abortModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="487">setState()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="500">postNextEvent()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="513">doOnEntry()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="526">doOnExit()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="539">getCurrState()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="552">getPrevState()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="565">getLastEvent()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="578">getNextEvent()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="591">isModelNew()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="604">isModelRunning()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="617">isModelWaiting()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="630">isModelDone()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="643">didModelFail()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="656">getEventLabel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="669">getStateLabel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="682">getContextStr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="485" y="695">getPrevContextStr()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="412" y="69" width="3" height="121" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="304" y="187" width="111" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="300" y="65" width="112" height="122" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="356" y="78">LabeledValueSet</text> + <line stroke="black" stroke-opacity="1" x1="300" y1="80" x2="412" y2="80" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="304" y="93">UNDEFINED_LABEL</text> + <line stroke="black" stroke-opacity="1" x1="300" y1="95" x2="412" y2="95" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="108">LabeledValueSet()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="121">~LabeledValueSet()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="134">add()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="147">add()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="160">get()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="173">isDefined()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="304" y="186">getLabel()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="416" y1="89" x2="422" y2="95" /> + <line stroke="black" stroke-opacity="1" x1="416" y1="89" x2="422" y2="83" /> + <line stroke="black" stroke-opacity="1" x1="480" y1="89" x2="416" y2="89" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="480,89 474,95 468,89 474,83" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="241" y="107" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="145" y="149" width="99" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="141" y="103" width="100" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="191" y="116"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="191" y="131">LabeledValueMap</text> + <line stroke="black" stroke-opacity="1" x1="141" y1="133" x2="241" y2="133" /> + <line stroke="black" stroke-opacity="1" x1="141" y1="141" x2="241" y2="141" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="389" y="239" width="3" height="75" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="329" y="311" width="63" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="325" y="235" width="64" height="76" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="357" y="248">StateSet</text> + <line stroke="black" stroke-opacity="1" x1="325" y1="250" x2="389" y2="250" /> + <line stroke="black" stroke-opacity="1" x1="325" y1="258" x2="389" y2="258" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="329" y="271">StateSet()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="329" y="284">~StateSet()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="329" y="297">add()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="329" y="310">getState()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="245" y1="128" x2="251" y2="134" /> + <line stroke="black" stroke-opacity="1" x1="245" y1="128" x2="251" y2="122" /> + <line stroke="black" stroke-opacity="1" x1="299" y1="128" x2="245" y2="128" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="299,128 293,134 287,128 293,122" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="358" y1="234" x2="358" y2="197" /> + <line stroke="black" stroke-opacity="1" x1="358" y1="191" x2="352" y2="197" /> + <line stroke="black" stroke-opacity="1" x1="358" y1="191" x2="364" y2="197" /> + <line stroke="black" stroke-opacity="1" x1="352" y1="197" x2="364" y2="197" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="393" y1="274" x2="399" y2="280" /> + <line stroke="black" stroke-opacity="1" x1="393" y1="274" x2="399" y2="268" /> + <line stroke="black" stroke-opacity="1" x1="480" y1="274" x2="393" y2="274" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="480,274 474,280 468,274 474,268" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="240" y="185" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="150" y="227" width="93" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="146" y="181" width="94" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="193" y="194"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="193" y="209">LabeledValuePtr</text> + <line stroke="black" stroke-opacity="1" x1="146" y1="211" x2="240" y2="211" /> + <line stroke="black" stroke-opacity="1" x1="146" y1="219" x2="240" y2="219" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="145" y1="205" x2="139" y2="198" /> + <line stroke="black" stroke-opacity="1" x1="145" y1="205" x2="138" y2="210" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="88" y1="204" x2="145" y2="205" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="194" y1="180" x2="199" y2="173" /> + <line stroke="black" stroke-opacity="1" x1="194" y1="180" x2="187" y2="174" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="193" y1="153" x2="194" y2="180" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="234" y="260" width="3" height="135" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="150" y="392" width="87" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="146" y="256" width="88" height="136" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="190" y="269">LabeledValue</text> + <line stroke="black" stroke-opacity="1" x1="146" y1="271" x2="234" y2="271" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="150" y="284">value_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="150" y="297">label_</text> + <line stroke="black" stroke-opacity="1" x1="146" y1="299" x2="234" y2="299" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="150" y="312">LabeledValue()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="150" y="325">~LabeledValue()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="150" y="338">getValue()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="150" y="351">getLabel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="150" y="364">operator ==()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="150" y="377">operator !=()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="150" y="390">operator <()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="191" y1="426" x2="191" y2="402" /> + <line stroke="black" stroke-opacity="1" x1="191" y1="396" x2="185" y2="402" /> + <line stroke="black" stroke-opacity="1" x1="191" y1="396" x2="197" y2="402" /> + <line stroke="black" stroke-opacity="1" x1="185" y1="402" x2="197" y2="402" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="145" y1="324" x2="139" y2="317" /> + <line stroke="black" stroke-opacity="1" x1="145" y1="324" x2="138" y2="329" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="88" y1="323" x2="145" y2="324" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="193" y1="255" x2="199" y2="249" /> + <line stroke="black" stroke-opacity="1" x1="193" y1="255" x2="187" y2="248" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="194" y1="231" x2="193" y2="255" /> +</g> +</svg> diff --git a/src/bin/d2/images/trans_classes.svg b/src/bin/d2/images/trans_classes.svg new file mode 100644 index 0000000..5023c02 --- /dev/null +++ b/src/bin/d2/images/trans_classes.svg @@ -0,0 +1,208 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="733" height="836" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="717" y="431" width="3" height="303" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="541" y="731" width="179" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="537" y="427" width="180" height="304" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="627" y="440">NameAddTransaction</text> + <line stroke="black" stroke-opacity="1" x1="537" y1="442" x2="717" y2="442" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="541" y="455">ADDING_FWD_ADDRS_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="541" y="468">REPLACING_FWD_ADDRS_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="541" y="481">REPLACING_REV_PTRS_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="541" y="494">FQDN_IN_USE_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="541" y="507">FQDN_NOT_IN_USE_EVT</text> + <line stroke="black" stroke-opacity="1" x1="537" y1="509" x2="717" y2="509" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="522">NameAddTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="535">~NameAddTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="548">defineEvents()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="561">verifyEvents()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="574">defineStates()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="587">verifyStates()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="600">readyHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="613">selectingFwdServerHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="626">selectingRevServerHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="639">addingFwdAddrsHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="652">replacingFwdAddrsHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="665">replacingRevPtrsHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="678">processAddOkHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="691">processAddFailedHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="704">buildAddFwdAddressRequest()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="717">buildReplaceFwdAddressRequest()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="541" y="730">buildReplaceRevPtrsRequest()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="445" y="12" width="3" height="811" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="249" y="820" width="199" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="245" y="8" width="200" height="812" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="345" y="21">NameChangeTransaction</text> + <line stroke="black" stroke-opacity="1" x1="245" y1="23" x2="445" y2="23" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="36">READY_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="49">SELECTING_FWD_SERVER_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="62">SELECTING_REV_SERVER_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="75">PROCESS_TRANS_OK_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="88">PROCESS_TRANS_FAILED_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="101">NCT_DERIVED_STATE_MIN</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="114">SELECT_SERVER_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="127">SERVER_SELECTED_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="140">SERVER_IO_ERROR_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="153">NO_MORE_SERVERS_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="166">IO_COMPLETED_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="179">UPDATE_OK_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="192">UPDATE_FAILED_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="205">NCT_DERIVED_EVENT_MIN</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="218">DNS_UPDATE_DEFAULT_TIMEOUT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="249" y="231">MAX_UPDATE_TRIES_PER_SERVER</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="244">forward_change_completed_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="257">reverse_change_completed_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="270">next_server_pos_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="283">update_attempts_</text> + <line stroke="black" stroke-opacity="1" x1="245" y1="285" x2="445" y2="285" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="298">NameChangeTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="311">~NameChangeTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="324">startTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="337">operator ()()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="350">sendUpdate()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="363">defineEvents()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="376">verifyEvents()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="389">defineStates()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="402">verifyStates()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="415">onModelFailure()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="428">retryTransition()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="441">setDnsUpdateRequest()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="454">clearDnsUpdateRequest()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="467">setDnsUpdateStatus()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="480">setDnsUpdateResponse()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="493">clearDnsUpdateResponse()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="506">setForwardChangeCompleted()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="519">setReverseChangeCompleted()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="532">setNcrStatus()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="545">initServerSelection()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="558">selectNextServer()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="571">setUpdateAttempts()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="584">getIOService()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="597">prepNewRequest()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="610">addLeaseAddressRdata()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="623">addDhcidRdata()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="636">addPtrRdata()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="649">getNcr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="662">getTransactionKey()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="675">getNcrStatus()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="688">getForwardDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="701">getReverseDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="714">getCurrentServer()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="727">getDNSClient()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="740">getDnsUpdateRequest()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="753">getDnsUpdateStatus()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="766">getDnsUpdateResponse()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="779">getForwardChangeCompleted()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="792">getReverseChangeCompleted()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="805">getUpdateAttempts()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="249" y="818">getAddressRRType()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="536" y1="596" x2="455" y2="596" /> + <line stroke="black" stroke-opacity="1" x1="449" y1="596" x2="455" y2="602" /> + <line stroke="black" stroke-opacity="1" x1="449" y1="596" x2="455" y2="590" /> + <line stroke="black" stroke-opacity="1" x1="455" y1="602" x2="455" y2="590" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="716" y="87" width="3" height="277" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="540" y="361" width="179" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="536" y="83" width="180" height="278" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="626" y="96">NameRemoveTransaction</text> + <line stroke="black" stroke-opacity="1" x1="536" y1="98" x2="716" y2="98" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="540" y="111">REMOVING_FWD_ADDRS_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="540" y="124">REMOVING_FWD_RRS_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="540" y="137">REMOVING_REV_PTRS_ST</text> + <line stroke="black" stroke-opacity="1" x1="536" y1="139" x2="716" y2="139" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="152">NameRemoveTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="165">~NameRemoveTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="178">defineEvents()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="191">verifyEvents()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="204">defineStates()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="217">verifyStates()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="230">readyHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="243">selectingFwdServerHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="256">selectingRevServerHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="269">removingFwdAddrsHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="282">removingFwdRRsHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="295">removingRevPtrsHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="308">processRemoveOkHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="321">processRemoveFailedHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="334">buildRemoveFwdAddressRequest()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="347">buildRemoveFwdRRsRequest()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="540" y="360">buildRemoveRevPtrsRequest()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="177" y="87" width="3" height="667" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="33" y="751" width="147" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="29" y="83" width="148" height="668" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="103" y="96">StateModel</text> + <line stroke="black" stroke-opacity="1" x1="29" y1="98" x2="177" y2="98" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="33" y="111">NEW_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="33" y="124">END_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="33" y="137">SM_DERIVED_STATE_MIN</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="33" y="150">NOP_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="33" y="163">START_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="33" y="176">END_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="33" y="189">FAIL_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="33" y="202">SM_DERIVED_EVENT_MIN</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="215">dictionaries_initted_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="228">curr_state_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="241">prev_state_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="254">last_event_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="267">next_event_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="280">on_entry_flag_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="293">on_exit_flag_</text> + <line stroke="black" stroke-opacity="1" x1="29" y1="295" x2="177" y2="295" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="308">StateModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="321">~StateModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="334">startModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="347">runModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="360">endModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="373">nopStateHandler()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="386">initDictionaries()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="399">defineEvents()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="412">defineEvent()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="425">getEvent()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="438">verifyEvents()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="451">defineStates()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="464">defineState()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="477">getState()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="490">verifyStates()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="503">onModelFailure()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="516">transition()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="529">abortModel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="542">setState()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="555">postNextEvent()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="568">doOnEntry()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="581">doOnExit()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="594">getCurrState()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="607">getPrevState()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="620">getLastEvent()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="633">getNextEvent()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="646">isModelNew()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="659">isModelRunning()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="672">isModelWaiting()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="685">isModelDone()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="698">didModelFail()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="711">getEventLabel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="724">getStateLabel()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="737">getContextStr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="33" y="750">getPrevContextStr()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="535" y1="228" x2="454" y2="227" /> + <line stroke="black" stroke-opacity="1" x1="449" y1="227" x2="454" y2="233" /> + <line stroke="black" stroke-opacity="1" x1="449" y1="227" x2="455" y2="221" /> + <line stroke="black" stroke-opacity="1" x1="454" y1="233" x2="455" y2="221" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="244" y1="401" x2="187" y2="401" /> + <line stroke="black" stroke-opacity="1" x1="181" y1="401" x2="187" y2="407" /> + <line stroke="black" stroke-opacity="1" x1="181" y1="401" x2="187" y2="395" /> + <line stroke="black" stroke-opacity="1" x1="187" y1="407" x2="187" y2="395" /> +</g> +</svg> diff --git a/src/bin/d2/images/update_exec_classes.svg b/src/bin/d2/images/update_exec_classes.svg new file mode 100644 index 0000000..d815a09 --- /dev/null +++ b/src/bin/d2/images/update_exec_classes.svg @@ -0,0 +1,387 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Bouml (http://bouml.free.fr/) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="847" height="932" version="1.1" xmlns="http://www.w3.org/2000/svg"> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="175" y="5" width="3" height="291" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="17" y="293" width="161" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="13" y="1" width="162" height="292" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="94" y="14">D2Process</text> + <line stroke="black" stroke-opacity="1" x1="13" y1="16" x2="175" y2="16" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="17" y="29">QUEUE_RESTART_PERCENT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="42">reconf_queue_flag_</text> + <line stroke="black" stroke-opacity="1" x1="13" y1="44" x2="175" y2="44" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="57">D2Process()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="70">init()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="83">run()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="96">shutdown()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="109">configure()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="122">command()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="135">~D2Process()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="148">checkQueueStatus()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="161">reconfigureQueueMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="174">runIO()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="187">canShutdown()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="200">setReconfQueueFlag()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="213">setShutdownType()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="226">getD2CfgMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="239">getD2QueueMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="252">getD2UpdateMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="265">getReconfQueueFlag()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="17" y="278">getShutdownType()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="17" y="291">getShutdownTypeStr()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="332" y="412" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="262" y="454" width="73" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="258" y="408" width="74" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="295" y="421"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="295" y="436">IOServicePtr</text> + <line stroke="black" stroke-opacity="1" x1="258" y1="438" x2="332" y2="438" /> + <line stroke="black" stroke-opacity="1" x1="258" y1="446" x2="332" y2="446" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="617" y="285" width="3" height="511" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="421" y="793" width="199" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="417" y="281" width="200" height="512" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="517" y="294">NameChangeTransaction</text> + <line stroke="black" stroke-opacity="1" x1="417" y1="296" x2="617" y2="296" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="309">READY_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="322">SELECTING_FWD_SERVER_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="335">SELECTING_REV_SERVER_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="348">PROCESS_TRANS_OK_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="361">PROCESS_TRANS_FAILED_ST</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="374">NCT_DERIVED_STATE_MIN</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="387">SELECT_SERVER_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="400">SERVER_SELECTED_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="413">SERVER_IO_ERROR_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="426">NO_MORE_SERVERS_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="439">IO_COMPLETED_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="452">UPDATE_OK_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="465">UPDATE_FAILED_EVT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="478">NCT_DERIVED_EVENT_MIN</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="491">DNS_UPDATE_DEFAULT_TIMEOUT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="421" y="504">MAX_UPDATE_TRIES_PER_SERVER</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="517">forward_change_completed_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="530">reverse_change_completed_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="543">next_server_pos_</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="556">update_attempts_</text> + <line stroke="black" stroke-opacity="1" x1="417" y1="558" x2="617" y2="558" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="571">NameChangeTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="584">~NameChangeTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="597">startTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="610">operator ()()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="623">getNcr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="636">getTransactionKey()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="649">getNcrStatus()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="662">getForwardDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="675">getReverseDomain()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="688">getCurrentServer()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="701">getDNSClient()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="714">getDnsUpdateRequest()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="727">getDnsUpdateStatus()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="740">getDnsUpdateResponse()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="753">getForwardChangeCompleted()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="766">getReverseChangeCompleted()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="779">getUpdateAttempts()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="421" y="792">getAddressRRType()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="336" y1="432" x2="342" y2="438" /> + <line stroke="black" stroke-opacity="1" x1="336" y1="432" x2="342" y2="426" /> + <line stroke="black" stroke-opacity="1" x1="416" y1="432" x2="336" y2="432" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="416,432 410,438 404,432 410,426" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="586" y="211" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="436" y="253" width="153" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="432" y="207" width="154" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="509" y="220"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="509" y="235">NameChangeTransactionPtr</text> + <line stroke="black" stroke-opacity="1" x1="432" y1="237" x2="586" y2="237" /> + <line stroke="black" stroke-opacity="1" x1="432" y1="245" x2="586" y2="245" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="511" y1="280" x2="517" y2="274" /> + <line stroke="black" stroke-opacity="1" x1="511" y1="280" x2="505" y2="274" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="511" y1="257" x2="511" y2="280" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="361" y="487" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="229" y="529" width="135" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="225" y="483" width="136" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="293" y="496"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="293" y="511">NameChangeRequestPtr</text> + <line stroke="black" stroke-opacity="1" x1="225" y1="513" x2="361" y2="513" /> + <line stroke="black" stroke-opacity="1" x1="225" y1="521" x2="361" y2="521" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="365" y1="507" x2="371" y2="513" /> + <line stroke="black" stroke-opacity="1" x1="365" y1="507" x2="371" y2="501" /> + <line stroke="black" stroke-opacity="1" x1="416" y1="507" x2="365" y2="507" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="416,507 410,513 404,507 410,501" /> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="506" y="138">transaction_list_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="650" y="529">dns_update_request_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="312" y="650">reverse_domain_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="626" y="637">dns_update_response_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="312" y="580">forward_domain_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="714" y="313">dns_client_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="134" y="354">queue_mgr_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="311" y="35">update_mgr_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="375" y="504">ncr_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="303" y="404">io_service_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="346" y="429">io_service_</text> +</g> +<g> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="85" y="329">queue_mgr_</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="346" y="588" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="260" y="630" width="89" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="256" y="584" width="90" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="301" y="597"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="301" y="612">DdnsDomainPtr</text> + <line stroke="black" stroke-opacity="1" x1="256" y1="614" x2="346" y2="614" /> + <line stroke="black" stroke-opacity="1" x1="256" y1="622" x2="346" y2="622" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="120" y="337" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="32" y="379" width="91" height="3" /> + <rect fill="#c0ffff" stroke="black" stroke-width="1" stroke-opacity="1" x="28" y="333" width="92" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="74" y="346"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="74" y="361">D2QueueMgrPtr</text> + <line stroke="black" stroke-opacity="1" x1="28" y1="363" x2="120" y2="363" /> + <line stroke="black" stroke-opacity="1" x1="28" y1="371" x2="120" y2="371" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="740" y="321" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="668" y="363" width="75" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="664" y="317" width="76" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="702" y="330"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="702" y="345">DNSClientPtr</text> + <line stroke="black" stroke-opacity="1" x1="664" y1="347" x2="740" y2="347" /> + <line stroke="black" stroke-opacity="1" x1="664" y1="355" x2="740" y2="355" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="760" y="553" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="644" y="595" width="119" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="640" y="549" width="120" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="700" y="562"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="700" y="577">D2UpdateMessagePtr</text> + <line stroke="black" stroke-opacity="1" x1="640" y1="579" x2="760" y2="579" /> + <line stroke="black" stroke-opacity="1" x1="640" y1="587" x2="760" y2="587" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="416" y1="563" x2="302" y2="563" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="416,563 410,569 404,563 410,557" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="416" y1="656" x2="302" y2="656" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="416,656 410,662 404,656 410,650" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="75" y1="332" x2="80" y2="325" /> + <line stroke="black" stroke-opacity="1" x1="75" y1="332" x2="68" y2="326" /> + <line stroke="black" stroke-opacity="1" x1="74" y1="297" x2="75" y2="332" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="74,297 80,302 74,308 68,303" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="621" y1="295" x2="704" y2="295" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="621,295 627,289 633,295 627,301" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="621" y1="534" x2="682" y2="534" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="621,534 627,528 633,534 627,540" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="621" y1="618" x2="688" y2="618" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="621,618 627,612 633,618 627,624" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="688" y1="599" x2="682" y2="605" /> + <line stroke="black" stroke-opacity="1" x1="688" y1="599" x2="694" y2="605" /> + <line stroke="black" stroke-opacity="1" x1="688" y1="618" x2="688" y2="599" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="302" y1="634" x2="296" y2="640" /> + <line stroke="black" stroke-opacity="1" x1="302" y1="634" x2="308" y2="640" /> + <line stroke="black" stroke-opacity="1" x1="302" y1="656" x2="302" y2="634" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="302" y1="583" x2="308" y2="577" /> + <line stroke="black" stroke-opacity="1" x1="302" y1="583" x2="296" y2="577" /> + <line stroke="black" stroke-opacity="1" x1="302" y1="563" x2="302" y2="583" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="704" y1="316" x2="710" y2="310" /> + <line stroke="black" stroke-opacity="1" x1="704" y1="316" x2="698" y2="310" /> + <line stroke="black" stroke-opacity="1" x1="704" y1="295" x2="704" y2="316" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="682" y1="548" x2="688" y2="542" /> + <line stroke="black" stroke-opacity="1" x1="682" y1="548" x2="676" y2="542" /> + <line stroke="black" stroke-opacity="1" x1="682" y1="534" x2="682" y2="548" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="383" y="112" width="3" height="265" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="209" y="374" width="177" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="205" y="108" width="178" height="266" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="294" y="121">D2UpdateMgr</text> + <line stroke="black" stroke-opacity="1" x1="205" y1="123" x2="383" y2="123" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="209" y="136">MAX_TRANSACTIONS_DEFAULT</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="149">max_transactions_</text> + <line stroke="black" stroke-opacity="1" x1="205" y1="151" x2="383" y2="151" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="164">D2UpdateMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="177">~D2UpdateMgr()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="190">sweep()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="203">checkFinishedTransactions()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="216">pickNextJob()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="229">makeTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="242">getIOService()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="255">getMaxTransactions()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="268">setMaxTransactions()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="281">findTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="294">transactionListEnd()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="307">transactionListBegin()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="320">hasTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="333">removeTransaction()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="346">clearTransactionList()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="359">getQueueCount()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="209" y="372">getTransactionCount()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="756" y="385" width="3" height="115" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="670" y="497" width="89" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="666" y="381" width="90" height="116" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="711" y="394">DNSClient</text> + <line stroke="black" stroke-opacity="1" x1="666" y1="396" x2="756" y2="396" /> + <line stroke="black" stroke-opacity="1" x1="666" y1="404" x2="756" y2="404" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="670" y="417">DNSClient()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="670" y="430">~DNSClient()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="670" y="443">DNSClient()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="670" y="456">operator =()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="670" y="469">getMaxTimeout()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="670" y="482">doUpdate()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="670" y="495">doUpdate()</text> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="124" y1="357" x2="130" y2="362" /> + <line stroke="black" stroke-opacity="1" x1="124" y1="357" x2="129" y2="350" /> + <line stroke="black" stroke-opacity="1" x1="204" y1="356" x2="124" y2="357" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="204,356 198,362 192,356 197,350" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="706" y1="380" x2="712" y2="374" /> + <line stroke="black" stroke-opacity="1" x1="706" y1="380" x2="700" y2="374" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="706" y1="367" x2="706" y2="380" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="293" y1="407" x2="298" y2="400" /> + <line stroke="black" stroke-opacity="1" x1="293" y1="407" x2="286" y2="401" /> + <line stroke="black" stroke-opacity="1" x1="292" y1="378" x2="293" y2="407" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="292,378 298,383 292,389 286,384" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="831" y="654" width="3" height="265" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="727" y="916" width="107" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="723" y="650" width="108" height="266" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="777" y="663">D2UpdateMessage</text> + <line stroke="black" stroke-opacity="1" x1="723" y1="665" x2="831" y2="665" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="678">message_</text> + <line stroke="black" stroke-opacity="1" x1="723" y1="680" x2="831" y2="680" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="693">D2UpdateMessage()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="706">D2UpdateMessage()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="719">operator =()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="732">getQRFlag()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="745">getId()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="758">setId()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="771">getRcode()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="784">setRcode()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="797">getRRCount()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="810">beginSection()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="823">endSection()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="836">setZone()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="849">getZone()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="862">addRRset()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="875">toWire()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="888">fromWire()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-decoration="underline" x="727" y="901">ddnsToDnsSection()</text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" x="727" y="914">validateResponse()</text> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="349" y="43" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="259" y="85" width="93" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="255" y="39" width="94" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="302" y="52"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="302" y="67">D2UpdateMgrPtr</text> + <line stroke="black" stroke-opacity="1" x1="255" y1="69" x2="349" y2="69" /> + <line stroke="black" stroke-opacity="1" x1="255" y1="77" x2="349" y2="77" /> +</g> +<g> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="764" y1="574" x2="776" y2="574" /> +</g> +<g> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="539" y="146" width="3" height="45" /> + <rect fill="#cbcbcb" stroke="none" stroke-opacity="1" x="455" y="188" width="87" height="3" /> + <rect fill="#ffffc0" stroke="black" stroke-width="1" stroke-opacity="1" x="451" y="142" width="88" height="46" /> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" text-anchor="middle" x="495" y="155"><<typedef>></text> + <text font-family="Helvetica" font-size="11" fill="#000000" xml:space="preserve" font-weight="bold" text-anchor="middle" x="495" y="170">TransactionList</text> + <line stroke="black" stroke-opacity="1" x1="451" y1="172" x2="539" y2="172" /> + <line stroke="black" stroke-opacity="1" x1="451" y1="180" x2="539" y2="180" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="179" y1="17" x2="301" y2="17" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="179,17 185,11 191,17 185,23" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="504" y1="206" x2="509" y2="199" /> + <line stroke="black" stroke-opacity="1" x1="504" y1="206" x2="497" y2="200" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="502" y1="192" x2="504" y2="206" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="301" y1="107" x2="307" y2="101" /> + <line stroke="black" stroke-opacity="1" x1="301" y1="107" x2="295" y2="100" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="302" y1="89" x2="301" y2="107" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="387" y1="120" x2="497" y2="120" /> + <polygon fill="#000000" stroke="black" stroke-opacity="1" points="387,120 393,114 399,120 393,126" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="776" y1="649" x2="782" y2="643" /> + <line stroke="black" stroke-opacity="1" x1="776" y1="649" x2="770" y2="643" /> + <line stroke-dasharray="4,4" stroke="black" stroke-opacity="1" x1="776" y1="574" x2="776" y2="649" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="497" y1="141" x2="503" y2="135" /> + <line stroke="black" stroke-opacity="1" x1="497" y1="141" x2="491" y2="135" /> + <line stroke="black" stroke-opacity="1" x1="497" y1="120" x2="497" y2="141" /> +</g> +<g> + <line stroke="black" stroke-opacity="1" x1="301" y1="38" x2="307" y2="32" /> + <line stroke="black" stroke-opacity="1" x1="301" y1="38" x2="295" y2="32" /> + <line stroke="black" stroke-opacity="1" x1="301" y1="17" x2="301" y2="38" /> +</g> +</svg> diff --git a/src/bin/d2/location.hh b/src/bin/d2/location.hh new file mode 100644 index 0000000..4145366 --- /dev/null +++ b/src/bin/d2/location.hh @@ -0,0 +1,306 @@ +// A Bison parser, made by GNU Bison 3.8.2. + +// Locations for Bison parsers in C++ + +// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +// As a special exception, you may create a larger work that contains +// part or all of the Bison parser skeleton and distribute that work +// under terms of your choice, so long as that work isn't itself a +// parser generator using the skeleton or a modified version thereof +// as a parser skeleton. Alternatively, if you modify or redistribute +// the parser skeleton itself, you may (at your option) remove this +// special exception, which will cause the skeleton and the resulting +// Bison output files to be licensed under the GNU General Public +// License without this special exception. + +// This special exception was added by the Free Software Foundation in +// version 2.2 of Bison. + +/** + ** \file location.hh + ** Define the isc::d2::location class. + */ + +#ifndef YY_D2_PARSER_LOCATION_HH_INCLUDED +# define YY_D2_PARSER_LOCATION_HH_INCLUDED + +# include <iostream> +# include <string> + +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +#line 14 "d2_parser.yy" +namespace isc { namespace d2 { +#line 59 "location.hh" + + /// A point in a source file. + class position + { + public: + /// Type for file name. + typedef const std::string filename_type; + /// Type for line and column numbers. + typedef int counter_type; + + /// Construct a position. + explicit position (filename_type* f = YY_NULLPTR, + counter_type l = 1, + counter_type c = 1) + : filename (f) + , line (l) + , column (c) + {} + + + /// Initialization. + void initialize (filename_type* fn = YY_NULLPTR, + counter_type l = 1, + counter_type c = 1) + { + filename = fn; + line = l; + column = c; + } + + /** \name Line and Column related manipulators + ** \{ */ + /// (line related) Advance to the COUNT next lines. + void lines (counter_type count = 1) + { + if (count) + { + column = 1; + line = add_ (line, count, 1); + } + } + + /// (column related) Advance to the COUNT next columns. + void columns (counter_type count = 1) + { + column = add_ (column, count, 1); + } + /** \} */ + + /// File name to which this position refers. + filename_type* filename; + /// Current line number. + counter_type line; + /// Current column number. + counter_type column; + + private: + /// Compute max (min, lhs+rhs). + static counter_type add_ (counter_type lhs, counter_type rhs, counter_type min) + { + return lhs + rhs < min ? min : lhs + rhs; + } + }; + + /// Add \a width columns, in place. + inline position& + operator+= (position& res, position::counter_type width) + { + res.columns (width); + return res; + } + + /// Add \a width columns. + inline position + operator+ (position res, position::counter_type width) + { + return res += width; + } + + /// Subtract \a width columns, in place. + inline position& + operator-= (position& res, position::counter_type width) + { + return res += -width; + } + + /// Subtract \a width columns. + inline position + operator- (position res, position::counter_type width) + { + return res -= width; + } + + /** \brief Intercept output stream redirection. + ** \param ostr the destination output stream + ** \param pos a reference to the position to redirect + */ + template <typename YYChar> + std::basic_ostream<YYChar>& + operator<< (std::basic_ostream<YYChar>& ostr, const position& pos) + { + if (pos.filename) + ostr << *pos.filename << ':'; + return ostr << pos.line << '.' << pos.column; + } + + /// Two points in a source file. + class location + { + public: + /// Type for file name. + typedef position::filename_type filename_type; + /// Type for line and column numbers. + typedef position::counter_type counter_type; + + /// Construct a location from \a b to \a e. + location (const position& b, const position& e) + : begin (b) + , end (e) + {} + + /// Construct a 0-width location in \a p. + explicit location (const position& p = position ()) + : begin (p) + , end (p) + {} + + /// Construct a 0-width location in \a f, \a l, \a c. + explicit location (filename_type* f, + counter_type l = 1, + counter_type c = 1) + : begin (f, l, c) + , end (f, l, c) + {} + + + /// Initialization. + void initialize (filename_type* f = YY_NULLPTR, + counter_type l = 1, + counter_type c = 1) + { + begin.initialize (f, l, c); + end = begin; + } + + /** \name Line and Column related manipulators + ** \{ */ + public: + /// Reset initial location to final location. + void step () + { + begin = end; + } + + /// Extend the current location to the COUNT next columns. + void columns (counter_type count = 1) + { + end += count; + } + + /// Extend the current location to the COUNT next lines. + void lines (counter_type count = 1) + { + end.lines (count); + } + /** \} */ + + + public: + /// Beginning of the located region. + position begin; + /// End of the located region. + position end; + }; + + /// Join two locations, in place. + inline location& + operator+= (location& res, const location& end) + { + res.end = end.end; + return res; + } + + /// Join two locations. + inline location + operator+ (location res, const location& end) + { + return res += end; + } + + /// Add \a width columns to the end position, in place. + inline location& + operator+= (location& res, location::counter_type width) + { + res.columns (width); + return res; + } + + /// Add \a width columns to the end position. + inline location + operator+ (location res, location::counter_type width) + { + return res += width; + } + + /// Subtract \a width columns to the end position, in place. + inline location& + operator-= (location& res, location::counter_type width) + { + return res += -width; + } + + /// Subtract \a width columns to the end position. + inline location + operator- (location res, location::counter_type width) + { + return res -= width; + } + + /** \brief Intercept output stream redirection. + ** \param ostr the destination output stream + ** \param loc a reference to the location to redirect + ** + ** Avoid duplicate information. + */ + template <typename YYChar> + std::basic_ostream<YYChar>& + operator<< (std::basic_ostream<YYChar>& ostr, const location& loc) + { + location::counter_type end_col + = 0 < loc.end.column ? loc.end.column - 1 : 0; + ostr << loc.begin; + if (loc.end.filename + && (!loc.begin.filename + || *loc.begin.filename != *loc.end.filename)) + ostr << '-' << loc.end.filename << ':' << loc.end.line << '.' << end_col; + else if (loc.begin.line < loc.end.line) + ostr << '-' << loc.end.line << '.' << end_col; + else if (loc.begin.column < end_col) + ostr << '-' << end_col; + return ostr; + } + +#line 14 "d2_parser.yy" +} } // isc::d2 +#line 305 "location.hh" + +#endif // !YY_D2_PARSER_LOCATION_HH_INCLUDED diff --git a/src/bin/d2/main.cc b/src/bin/d2/main.cc new file mode 100644 index 0000000..25975a7 --- /dev/null +++ b/src/bin/d2/main.cc @@ -0,0 +1,58 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <d2/d2_controller.h> +#include <d2srv/d2_log.h> +#include <exceptions/exceptions.h> +#include <log/logger_manager.h> +#include <log/logger_support.h> + +#include <iostream> + +using namespace isc::d2; +using namespace isc::process; +using namespace std; + +/// This file contains entry point (main() function) for standard DHCP-DDNS +/// process, kea-dhcp-ddns, component of Kea software suite. It fetches +/// the D2Controller singleton instance and invokes its launch method. +/// The exit value of the program will be EXIT_SUCCESS if there were no +/// errors, EXIT_FAILURE otherwise. +int main(int argc, char* argv[]) { + int ret = EXIT_SUCCESS; + + // Launch the controller passing in command line arguments. + // Exit program with the controller's return code. + try { + // Instantiate/fetch the DHCP-DDNS application controller singleton. + DControllerBasePtr& controller = D2Controller::instance(); + + // 'false' value disables test mode. + ret = controller->launch(argc, argv, false); + } catch (const VersionMessage& ex) { + std::string msg(ex.what()); + if (!msg.empty()) { + std::cout << msg << std::endl; + } + } catch (const InvalidUsage& ex) { + std::string msg(ex.what()); + if (!msg.empty()) { + std::cerr << msg << std::endl; + } + ret = EXIT_FAILURE; + } catch (const std::exception& ex) { + std::cerr << "Service failed: " << ex.what() << std::endl; + ret = EXIT_FAILURE; + } catch (...) { + std::cerr << "Service failed" << std::endl; + ret = EXIT_FAILURE; + } + + D2Controller::instance().reset(); + + return (ret); +} diff --git a/src/bin/d2/nc_add.cc b/src/bin/d2/nc_add.cc new file mode 100644 index 0000000..79f4647 --- /dev/null +++ b/src/bin/d2/nc_add.cc @@ -0,0 +1,707 @@ +// Copyright (C) 2013-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/. + +#include <config.h> + +#include <d2/nc_add.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/d2_log.h> + +#include <util/buffer.h> +#include <dns/rdataclass.h> + +#include <functional> + +namespace isc { +namespace d2 { + +// NameAddTransaction states +const int NameAddTransaction::ADDING_FWD_ADDRS_ST; +const int NameAddTransaction::REPLACING_FWD_ADDRS_ST; +const int NameAddTransaction::REPLACING_REV_PTRS_ST; + +// NameAddTransaction events +const int NameAddTransaction::FQDN_IN_USE_EVT; +const int NameAddTransaction::FQDN_NOT_IN_USE_EVT; + +NameAddTransaction:: +NameAddTransaction(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) { + if (ncr->getChangeType() != isc::dhcp_ddns::CHG_ADD) { + isc_throw (NameAddTransactionError, + "NameAddTransaction, request type must be CHG_ADD"); + } +} + +NameAddTransaction::~NameAddTransaction(){ +} + +void +NameAddTransaction::defineEvents() { + // Call superclass impl first. + NameChangeTransaction::defineEvents(); + + // Define NameAddTransaction events. + defineEvent(FQDN_IN_USE_EVT, "FQDN_IN_USE_EVT"); + defineEvent(FQDN_NOT_IN_USE_EVT, "FQDN_NOT_IN_USE_EVT"); +} + +void +NameAddTransaction::verifyEvents() { + // Call superclass implementation first to verify its events. These are + // events common to all transactions, and they must be defined. + // SELECT_SERVER_EVT + // SERVER_SELECTED_EVT + // SERVER_IO_ERROR_EVT + // NO_MORE_SERVERS_EVT + // IO_COMPLETED_EVT + // UPDATE_OK_EVT + // UPDATE_FAILED_EVT + NameChangeTransaction::verifyEvents(); + + // Verify NameAddTransaction events by attempting to fetch them. + getEvent(FQDN_IN_USE_EVT); + getEvent(FQDN_NOT_IN_USE_EVT); +} + +void +NameAddTransaction::defineStates() { + // Call superclass impl first. + NameChangeTransaction::defineStates(); + + // Define NameAddTransaction states. + defineState(READY_ST, "READY_ST", + std::bind(&NameAddTransaction::readyHandler, this)); + + defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST", + std::bind(&NameAddTransaction::selectingFwdServerHandler, this)); + + defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST", + std::bind(&NameAddTransaction::selectingRevServerHandler, this)); + + defineState(ADDING_FWD_ADDRS_ST, "ADDING_FWD_ADDRS_ST", + std::bind(&NameAddTransaction::addingFwdAddrsHandler, this)); + + defineState(REPLACING_FWD_ADDRS_ST, "REPLACING_FWD_ADDRS_ST", + std::bind(&NameAddTransaction::replacingFwdAddrsHandler, this)); + + defineState(REPLACING_REV_PTRS_ST, "REPLACING_REV_PTRS_ST", + std::bind(&NameAddTransaction::replacingRevPtrsHandler, this)); + + defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST", + std::bind(&NameAddTransaction::processAddOkHandler, this)); + + defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST", + std::bind(&NameAddTransaction::processAddFailedHandler, this)); +} + +void +NameAddTransaction::verifyStates() { + // Call superclass implementation first to verify its states. These are + // states common to all transactions, and they must be defined. + // READY_ST + // SELECTING_FWD_SERVER_ST + // SELECTING_REV_SERVER_ST + // PROCESS_TRANS_OK_ST + // PROCESS_TRANS_FAILED_ST + NameChangeTransaction::verifyStates(); + + // Verify NameAddTransaction states by attempting to fetch them. + getStateInternal(ADDING_FWD_ADDRS_ST); + getStateInternal(REPLACING_FWD_ADDRS_ST); + getStateInternal(REPLACING_REV_PTRS_ST); +} + +void +NameAddTransaction::readyHandler() { + switch(getNextEvent()) { + case START_EVT: + if (getForwardDomain()) { + // Request includes a forward change, do that first. + transition(SELECTING_FWD_SERVER_ST, SELECT_SERVER_EVT); + } else { + // Reverse change only, transition accordingly. + transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT); + } + + break; + default: + // Event is invalid. + isc_throw(NameAddTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +NameAddTransaction::selectingFwdServerHandler() { + switch(getNextEvent()) { + case SELECT_SERVER_EVT: + // First time through for this transaction, so initialize server + // selection. + initServerSelection(getForwardDomain()); + break; + case SERVER_IO_ERROR_EVT: + // We failed to communicate with current server. Attempt to select + // another one below. + break; + default: + // Event is invalid. + isc_throw(NameAddTransactionError, + "Wrong event for context: " << getContextStr()); + } + + // Select the next server from the list of forward servers. + if (selectNextServer()) { + // We have a server to try. + transition(ADDING_FWD_ADDRS_ST, SERVER_SELECTED_EVT); + } else { + // Server list is exhausted, so fail the transaction. + transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT); + } +} + +void +NameAddTransaction::addingFwdAddrsHandler() { + if (doOnEntry()) { + // Clear the update attempts count on initial transition. + clearUpdateAttempts(); + } + + switch(getNextEvent()) { + case SERVER_SELECTED_EVT: + try { + clearDnsUpdateRequest(); + buildAddFwdAddressRequest(); + } catch (const std::exception& ex) { + // While unlikely, the build might fail if we have invalid + // data. Should that be the case, we need to fail the + // transaction. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE) + .arg(getRequestId()) + .arg(getNcr()->toText()) + .arg(ex.what()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } + + // Call sendUpdate() to initiate the async send. Note it also sets + // next event to NOP_EVT. + sendUpdate("Forward Add"); + break; + + case IO_COMPLETED_EVT: { + switch (getDnsUpdateStatus()) { + case DNSClient::SUCCESS: { + // We successfully received a response packet from the server. + const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode(); + if (rcode == dns::Rcode::NOERROR()) { + // We were able to add it. Mark it as done. + setForwardChangeCompleted(true); + + // If request calls for reverse update then do that next, + // otherwise we can process ok. + if (getReverseDomain()) { + transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT); + } else { + transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT); + } + } else if (rcode == dns::Rcode::YXDOMAIN()) { + // FQDN is in use so we need to attempt to replace + // forward address. + transition(REPLACING_FWD_ADDRS_ST, FQDN_IN_USE_EVT); + } else { + // Per RFC4703 any other value means cease. + // If we get not authorized should we try the next server in + // the list? @todo This needs some discussion perhaps. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_REJECTED) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()) + .arg(rcode.getCode()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + } + + break; + } + + case DNSClient::TIMEOUT: + case DNSClient::OTHER: + // We couldn't send to the current server, log it and set up + // to select the next server for a retry. + // @note For now we treat OTHER as an IO error like TIMEOUT. It + // is not entirely clear if this is accurate. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_IO_ERROR) + .arg(getRequestId()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + retryTransition(SELECTING_FWD_SERVER_ST); + break; + + case DNSClient::INVALID_RESPONSE: + // A response was received but was corrupt. Retry it like an IO + // error. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()); + + retryTransition(SELECTING_FWD_SERVER_ST); + break; + + default: + // Any other value and we will fail this transaction, something + // bigger is wrong. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS) + .arg(getRequestId()) + .arg(getDnsUpdateStatus()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } // end switch on dns_status + + break; + } // end case IO_COMPLETE_EVT + + default: + // Event is invalid. + isc_throw(NameAddTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +NameAddTransaction::replacingFwdAddrsHandler() { + if (doOnEntry()) { + // Clear the update attempts count on initial transition. + clearUpdateAttempts(); + } + + switch(getNextEvent()) { + case FQDN_IN_USE_EVT: + case SERVER_SELECTED_EVT: + try { + clearDnsUpdateRequest(); + buildReplaceFwdAddressRequest(); + } catch (const std::exception& ex) { + // While unlikely, the build might fail if we have invalid + // data. Should that be the case, we need to fail the + // transaction. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE) + .arg(getRequestId()) + .arg(getNcr()->toText()) + .arg(ex.what()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } + + // Call sendUpdate() to initiate the async send. Note it also sets + // next event to NOP_EVT. + sendUpdate("Forward Replace"); + break; + + case IO_COMPLETED_EVT: { + switch (getDnsUpdateStatus()) { + case DNSClient::SUCCESS: { + // We successfully received a response packet from the server. + const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode(); + if (rcode == dns::Rcode::NOERROR()) { + // We were able to replace the forward mapping. Mark it as done. + setForwardChangeCompleted(true); + + // If request calls for reverse update then do that next, + // otherwise we can process ok. + if (getReverseDomain()) { + transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT); + } else { + transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT); + } + } else if (rcode == dns::Rcode::NXDOMAIN()) { + // FQDN is NOT in use so go back and do the forward add address. + // Covers the case that it was there when we tried to add it, + // but has since been removed per RFC 4703. + transition(ADDING_FWD_ADDRS_ST, SERVER_SELECTED_EVT); + } else { + // Per RFC4703 any other value means cease. + // If we get not authorized should try the next server in + // the list? @todo This needs some discussion perhaps. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REPLACE_REJECTED) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()) + .arg(rcode.getCode()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + } + + break; + } + + case DNSClient::TIMEOUT: + case DNSClient::OTHER: + // We couldn't send to the current server, log it and set up + // to select the next server for a retry. + // @note For now we treat OTHER as an IO error like TIMEOUT. It + // is not entirely clear if this is accurate. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REPLACE_IO_ERROR) + .arg(getRequestId()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + // If we are out of retries on this server, we go back and start + // all over on a new server. + retryTransition(SELECTING_FWD_SERVER_ST); + break; + + case DNSClient::INVALID_RESPONSE: + // A response was received but was corrupt. Retry it like an IO + // error. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()); + + // If we are out of retries on this server, we go back and start + // all over on a new server. + retryTransition(SELECTING_FWD_SERVER_ST); + break; + + default: + // Any other value and we will fail this transaction, something + // bigger is wrong. + LOG_ERROR(d2_to_dns_logger, + DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS) + .arg(getRequestId()) + .arg(getDnsUpdateStatus()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } // end switch on dns_status + + break; + } // end case IO_COMPLETE_EVT + + default: + // Event is invalid. + isc_throw(NameAddTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +NameAddTransaction::selectingRevServerHandler() { + switch(getNextEvent()) { + case SELECT_SERVER_EVT: + // First time through for this transaction, so initialize server + // selection. + initServerSelection(getReverseDomain()); + break; + case SERVER_IO_ERROR_EVT: + // We failed to communicate with current server. Attempt to select + // another one below. + break; + default: + // Event is invalid. + isc_throw(NameAddTransactionError, + "Wrong event for context: " << getContextStr()); + } + + // Select the next server from the list of forward servers. + if (selectNextServer()) { + // We have a server to try. + transition(REPLACING_REV_PTRS_ST, SERVER_SELECTED_EVT); + } else { + // Server list is exhausted, so fail the transaction. + transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT); + } +} + + +void +NameAddTransaction::replacingRevPtrsHandler() { + if (doOnEntry()) { + // Clear the update attempts count on initial transition. + clearUpdateAttempts(); + } + + switch(getNextEvent()) { + case SERVER_SELECTED_EVT: + try { + clearDnsUpdateRequest(); + buildReplaceRevPtrsRequest(); + } catch (const std::exception& ex) { + // While unlikely, the build might fail if we have invalid + // data. Should that be the case, we need to fail the + // transaction. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE) + .arg(getRequestId()) + .arg(getNcr()->toText()) + .arg(ex.what()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } + + // Call sendUpdate() to initiate the async send. Note it also sets + // next event to NOP_EVT. + sendUpdate("Reverse Replace"); + break; + + case IO_COMPLETED_EVT: { + switch (getDnsUpdateStatus()) { + case DNSClient::SUCCESS: { + // We successfully received a response packet from the server. + const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode(); + if (rcode == dns::Rcode::NOERROR()) { + // We were able to update the reverse mapping. Mark it as done. + setReverseChangeCompleted(true); + transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT); + } else { + // Per RFC4703 any other value means cease. + // If we get not authorized should try the next server in + // the list? @todo This needs some discussion perhaps. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_REJECTED) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()) + .arg(rcode.getCode()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + } + + break; + } + + case DNSClient::TIMEOUT: + case DNSClient::OTHER: + // We couldn't send to the current server, log it and set up + // to select the next server for a retry. + // @note For now we treat OTHER as an IO error like TIMEOUT. It + // is not entirely clear if this is accurate. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_IO_ERROR) + .arg(getRequestId()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + // If we are out of retries on this server, we go back and start + // all over on a new server. + retryTransition(SELECTING_REV_SERVER_ST); + break; + + case DNSClient::INVALID_RESPONSE: + // A response was received but was corrupt. Retry it like an IO + // error. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()); + + // If we are out of retries on this server, we go back and start + // all over on a new server. + retryTransition(SELECTING_REV_SERVER_ST); + break; + + default: + // Any other value and we will fail this transaction, something + // bigger is wrong. + LOG_ERROR(d2_to_dns_logger, + DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS) + .arg(getRequestId()) + .arg(getDnsUpdateStatus()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } // end switch on dns_status + + break; + } // end case IO_COMPLETE_EVT + + default: + // Event is invalid. + isc_throw(NameAddTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +NameAddTransaction::processAddOkHandler() { + switch(getNextEvent()) { + case UPDATE_OK_EVT: + LOG_INFO(d2_to_dns_logger, DHCP_DDNS_ADD_SUCCEEDED) + .arg(getRequestId()) + .arg(getNcr()->toText()); + setNcrStatus(dhcp_ddns::ST_COMPLETED); + endModel(); + break; + default: + // Event is invalid. + isc_throw(NameAddTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +NameAddTransaction::processAddFailedHandler() { + switch(getNextEvent()) { + case UPDATE_FAILED_EVT: + case NO_MORE_SERVERS_EVT: + setNcrStatus(dhcp_ddns::ST_FAILED); + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_ADD_FAILED) + .arg(getRequestId()) + .arg(transactionOutcomeString()); + endModel(); + break; + default: + // Event is invalid. + isc_throw(NameAddTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +NameAddTransaction::buildAddFwdAddressRequest() { + // Construct an empty request. + D2UpdateMessagePtr request = prepNewRequest(getForwardDomain()); + + // Construct dns::Name from NCR fqdn. + dns::Name fqdn(dns::Name(getNcr()->getFqdn())); + + // Content on this request is based on RFC 4703, section 5.3.1 + // First build the Prerequisite Section. + + // Create 'FQDN Is Not In Use' prerequisite and add it to the + // prerequisite section. + // Based on RFC 2136, section 2.4.5 + dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::NONE(), + dns::RRType::ANY(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq); + + // Next build the Update Section. + + // Create the TTL based on lease length. + dns::RRTTL lease_ttl(getNcr()->getLeaseLength()); + + // Create the FQDN/IP 'add' RR and add it to the to update section. + // Based on RFC 2136, section 2.5.1 + dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::IN(), + getAddressRRType(), lease_ttl)); + + addLeaseAddressRdata(update); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Now create the FQDN/DHCID 'add' RR and add it to update section. + // Based on RFC 2136, section 2.5.1 + update.reset(new dns::RRset(fqdn, dns::RRClass::IN(), + dns::RRType::DHCID(), lease_ttl)); + addDhcidRdata(update); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Set the transaction's update request to the new request. + setDnsUpdateRequest(request); +} + +void +NameAddTransaction::buildReplaceFwdAddressRequest() { + // Construct an empty request. + D2UpdateMessagePtr request = prepNewRequest(getForwardDomain()); + + // Construct dns::Name from NCR fqdn. + dns::Name fqdn(dns::Name(getNcr()->getFqdn())); + + // Content on this request is based on RFC 4703, section 5.3.2 + // First build the Prerequisite Section. + + // Create an 'FQDN Is In Use' prerequisite and add it to the + // pre-requisite section. + // Based on RFC 2136, section 2.4.4 + dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::ANY(), + dns::RRType::ANY(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq); + + // Create an DHCID matches prerequisite RR and add it to the + // pre-requisite section. + // Based on RFC 2136, section 2.4.2. + prereq.reset(new dns::RRset(fqdn, dns::RRClass::IN(), + dns::RRType::DHCID(), dns::RRTTL(0))); + addDhcidRdata(prereq); + request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq); + + // Next build the Update Section. + + // Create the TTL based on lease length. + dns::RRTTL lease_ttl(getNcr()->getLeaseLength()); + + // Create the FQDN/IP 'delete' RR and add it to the update section. + // Based on RFC 2136, section 2.5.2 + dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::ANY(), + getAddressRRType(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Create the FQDN/IP 'add' RR and add it to the update section. + // Based on RFC 2136, section 2.5.1 + update.reset(new dns::RRset(fqdn, dns::RRClass::IN(), + getAddressRRType(), lease_ttl)); + addLeaseAddressRdata(update); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Set the transaction's update request to the new request. + setDnsUpdateRequest(request); +} + +void +NameAddTransaction::buildReplaceRevPtrsRequest() { + // Construct an empty request. + D2UpdateMessagePtr request = prepNewRequest(getReverseDomain()); + + // Create the reverse IP address "FQDN". + std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress()); + dns::Name rev_ip(rev_addr); + + // Create the TTL based on lease length. + dns::RRTTL lease_ttl(getNcr()->getLeaseLength()); + + // Content on this request is based on RFC 4703, section 5.4 + // Reverse replacement has no prerequisites so straight on to + // building the Update section. + + // Create the PTR 'delete' RR and add it to update section. + dns::RRsetPtr update(new dns::RRset(rev_ip, dns::RRClass::ANY(), + dns::RRType::PTR(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Create the DHCID 'delete' RR and add it to the update section. + update.reset(new dns::RRset(rev_ip, dns::RRClass::ANY(), + dns::RRType::DHCID(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Create the FQDN/IP PTR 'add' RR, add the FQDN as the PTR Rdata + // then add it to update section. + update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(), + dns::RRType::PTR(), lease_ttl)); + addPtrRdata(update); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Create the FQDN/IP PTR 'add' RR, add the DHCID Rdata + // then add it to update section. + update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(), + dns::RRType::DHCID(), lease_ttl)); + addDhcidRdata(update); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Set the transaction's update request to the new request. + setDnsUpdateRequest(request); +} + +} // namespace isc::d2 +} // namespace isc diff --git a/src/bin/d2/nc_add.h b/src/bin/d2/nc_add.h new file mode 100644 index 0000000..6bb8ce3 --- /dev/null +++ b/src/bin/d2/nc_add.h @@ -0,0 +1,445 @@ +// 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_ADD_H +#define NC_ADD_H + +/// @file nc_add.h This file defines the class NameAddTransaction. + +#include <d2srv/nc_trans.h> +#include <dns/rdata.h> + +namespace isc { +namespace d2 { + +/// @brief Thrown if the NameAddTransaction encounters a general error. +class NameAddTransactionError : public isc::Exception { +public: + NameAddTransactionError(const char* file, size_t line, + const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Embodies the "life-cycle" required to carry out a DDNS Add update. +/// +/// NameAddTransaction implements a state machine for adding (or replacing) a +/// forward and/or reverse DNS mapping. This state machine is based upon the +/// processing logic described in RFC 4703, Sections 5.3 and 5.4. That logic +/// may be paraphrased as follows: +/// +/// @code +/// +/// If the request includes a forward change: +/// Select a forward server +/// Send the server a request to add the forward entry +/// If the server responds with already in use: +/// Send a server a request to delete and then add forward entry +/// +/// If the forward update is unsuccessful: +/// abandon the update +/// +/// If the request includes a reverse change: +/// Select a reverse server +/// Send a server a request to delete and then add reverse entry +/// +/// @endcode +/// +/// This class derives from NameChangeTransaction from which it inherits +/// states, events, and methods common to NameChangeRequest processing. +class NameAddTransaction : public NameChangeTransaction { +public: + + //@{ Additional states needed for NameAdd state model. + /// @brief State that attempts to add forward address records. + static const int ADDING_FWD_ADDRS_ST = NCT_DERIVED_STATE_MIN + 1; + + /// @brief State that attempts to replace forward address records. + static const int REPLACING_FWD_ADDRS_ST = NCT_DERIVED_STATE_MIN + 2; + + /// @brief State that attempts to replace reverse PTR records + static const int REPLACING_REV_PTRS_ST = NCT_DERIVED_STATE_MIN + 3; + //@} + + //@{ Additional events needed for NameAdd state model. + /// @brief Event sent when an add attempt fails with address in use. + static const int FQDN_IN_USE_EVT = NCT_DERIVED_EVENT_MIN + 1; + + /// @brief Event sent when replace attempt to fails with address not in use. + static const int FQDN_NOT_IN_USE_EVT = NCT_DERIVED_EVENT_MIN + 2; + //@} + + /// @brief Constructor + /// + /// Instantiates an Add 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 pointer to the configuration manager + /// + /// @throw NameAddTransaction error if given request is not a CHG_ADD, + /// NameChangeTransaction error for base class construction errors. + NameAddTransaction(asiolink::IOServicePtr& io_service, + dhcp_ddns::NameChangeRequestPtr& ncr, + DdnsDomainPtr& forward_domain, + DdnsDomainPtr& reverse_domain, + D2CfgMgrPtr& cfg_mgr); + + /// @brief Destructor + virtual ~NameAddTransaction(); + +protected: + /// @brief Adds events defined by NameAddTransaction to the event set. + /// + /// Invokes NameChangeTransaction's implementation and then defines the + /// events unique to NCR Add transaction processing. + /// + /// @throw StateModelError if an event definition is invalid or a duplicate. + virtual void defineEvents(); + + /// @brief Validates the contents of the set of events. + /// + /// Invokes NameChangeTransaction's implementation and then verifies the + /// Add transaction's. This tests that the needed events are in the event + /// dictionary. + /// + /// @throw StateModelError if an event value is undefined. + virtual void verifyEvents(); + + /// @brief Adds states defined by NameAddTransaction to the state set. + /// + /// Invokes NameChangeTransaction's implementation and then defines the + /// states unique to NCR Add transaction processing. + /// + /// @throw StateModelError if an state definition is invalid or a duplicate. + virtual void defineStates(); + + /// @brief Validates the contents of the set of states. + /// + /// Invokes NameChangeTransaction's implementation and then verifies the + /// Add transaction's states. This tests that the needed states are in the + /// state dictionary. + /// + /// @throw StateModelError if an event value is undefined. + virtual void verifyStates(); + + /// @brief State handler for READY_ST. + /// + /// Entered from: + /// - INIT_ST with next event of START_EVT + /// + /// The READY_ST is the state the model transitions into when the inherited + /// method, startTransaction() is invoked. This handler, therefore, is the + /// entry point into the state model execution.h Its primary task is to + /// determine whether to start with a forward DNS change or a reverse DNS + /// change. + /// + /// Transitions to: + /// - SELECTING_FWD_SERVER_ST with next event of SERVER_SELECT_ST if request + /// includes a forward change. + /// + /// - SELECTING_REV_SERVER_ST with next event of SERVER_SELECT_ST if request + /// includes only a reverse change. + /// + /// @throw NameAddTransactionError if upon entry next event is not + /// START_EVT. + void readyHandler(); + + /// @brief State handler for SELECTING_FWD_SERVER_ST. + /// + /// Entered from: + /// - READY_ST with next event of SELECT_SERVER_EVT + /// - ADDING_FWD_ADDRS_ST with next event of SERVER_IO_ERROR_EVT + /// - REPLACING_FWD_ADDRS_ST with next event of SERVER_IO_ERROR_EVT + /// + /// Selects the server to be used from the forward domain for the forward + /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes + /// the forward domain's server selection mechanism and then attempts to + /// select the next server. If next event is SERVER_IO_ERROR_EVT then the + /// handler simply attempts to select the next server. + /// + /// Transitions to: + /// - ADDING_FWD_ADDRS_ST with next event of SERVER_SELECTED upon successful + /// server selection + /// + /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon + /// failure to select a server + /// + /// @throw NameAddTransactionError if upon entry next event is not + /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT. + void selectingFwdServerHandler(); + + /// @brief State handler for SELECTING_REV_SERVER_ST. + /// + /// Entered from: + /// - READY_ST with next event of SELECT_SERVER_EVT + /// - ADDING_FWD_ADDRS_ST with next event of SELECT_SERVER_EVT + /// - REPLACING_FWD_ADDRS_ST with next event of SELECT_SERVER_EVT + /// - REPLACING_REV_PTRS_ST with next event of SERVER_IO_ERROR_EVT + /// + /// Selects the server to be used from the reverse domain for the reverse + /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes + /// the reverse domain's server selection mechanism and then attempts to + /// select the next server. If next event is SERVER_IO_ERROR_EVT then the + /// handler simply attempts to select the next server. + /// + /// Transitions to: + /// - ADDING_REV_PTRS_ST with next event of SERVER_SELECTED upon successful + /// server selection + /// + /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon + /// failure to select a server + /// + /// @throw NameAddTransactionError if upon entry next event is not + /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT. + void selectingRevServerHandler(); + + /// @brief State handler for ADD_FWD_ADDRS_ST. + /// + /// Entered from: + /// - SELECTING_FWD_SERVER with next event of SERVER_SELECTED_EVT + /// - REPLACING_FWD_ADDRS_ST with next event of SERVER_SELECTED_EVT + /// + /// Attempts to add a forward DNS entry for a given FQDN. If this is + /// first invocation of the handler after transitioning into this state, + /// any previous update request context is deleted. If next event + /// is SERVER_SELECTED_EVT, the handler builds the forward add request, + /// schedules an asynchronous send via sendUpdate(), and returns. Note + /// that sendUpdate will post NOP_EVT as next event. + /// + /// Posting the NOP_EVT will cause runModel() to suspend execution of + /// the state model thus affecting a "wait" for the update IO to complete. + /// Update completion occurs via the DNSClient callback operator() method + /// inherited from NameChangeTransaction. When invoked this callback will + /// post a next event of IO_COMPLETED_EVT and then invoke runModel which + /// resumes execution of the state model. + /// + /// When the handler is invoked with a next event of IO_COMPLETED_EVT, + /// the DNS update status is checked and acted upon accordingly: + /// + /// Transitions to: + /// - SELECTING_REV_SERVER_ST with next event of SELECT_SERVER_EVT upon + /// successful addition and the request includes a reverse DNS update. + /// + /// - PROCESS_TRANS_OK_ST with next event of UPDATE_OK_EVT upon successful + /// addition and no reverse DNS update is required. + /// + /// - REPLACING_FWD_ADDRS_ST with next event of FQDN_IN_USE_EVT if the DNS + /// server response indicates that an entry for the given FQDN already + /// exists. + /// + /// - PROCESS_TRANS_FAILED_ST with next event of UPDATE_FAILED_EVT if the + /// DNS server rejected the update for any other reason or the IO completed + /// with an unrecognized status. + /// + /// - RE-ENTER this states with next event of SERVER_SELECTED_EVT_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has not been exhausted. + /// + /// - SELECTING_FWD_SERVER_ST with next event of SERVER_IO_ERROR_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has been exhausted. + /// + /// @throw NameAddTransactionError if upon entry next event is not + /// SERVER_SELECTED_EVT or IO_COMPLETE_EVT. + void addingFwdAddrsHandler(); + + /// @brief State handler for REPLACING_FWD_ADDRS_ST. + /// + /// Entered from: + /// - ADDING_FWD_ADDRS_ST with next event of FQDN_IN_USE_EVT + /// + /// Attempts to delete and then add a forward DNS entry for a given + /// FQDN. If this is first invocation of the handler after transitioning + /// into this state, any previous update request context is deleted. If + /// next event is FDQN_IN_USE_EVT or SERVER_SELECTED_EVT, the handler + /// builds the forward replacement request, schedules an asynchronous send + /// via sendUpdate(), and returns. Note that sendUpdate will post NOP_EVT + /// as the next event. + /// + /// Posting the NOP_EVT will cause runModel() to suspend execution of + /// the state model thus affecting a "wait" for the update IO to complete. + /// Update completion occurs via the DNSClient callback operator() method + /// inherited from NameChangeTransaction. When invoked this callback will + /// post a next event of IO_COMPLETED_EVT and then invoke runModel which + /// resumes execution of the state model. + /// + /// When the handler is invoked with a next event of IO_COMPLETED_EVT, + /// the DNS update status is checked and acted upon accordingly: + /// + /// Transitions to: + /// - SELECTING_REV_SERVER_ST with a next event of SELECT_SERVER_EVT upon + /// successful replacement and the request includes a reverse DNS update. + /// + /// - PROCESS_TRANS_OK_ST with next event of UPDATE_OK_EVT upon successful + /// replacement and the request does not include a reverse DNS update. + /// + /// - ADDING_FWD_ADDR_STR with a next event of SERVER_SELECTED_EVT if the + /// DNS server response indicates that the FQDN is not in use. This could + /// occur if a previous add attempt indicated the FQDN was in use, but + /// that entry has since been removed by another entity prior to this + /// replacement attempt. + /// + /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT if the + /// DNS server rejected the update for any other reason or the IO completed + /// with an unrecognized status. + /// + /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has not been exhausted. + /// + /// - SELECTING_FWD_SERVER_ST with next event of SERVER_IO_ERROR_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has been exhausted. + /// + /// @throw NameAddTransactionError if upon entry next event is not: + /// FQDN_IN_USE_EVT, SERVER_SELECTED_EVT or IO_COMPLETE_EVT. + void replacingFwdAddrsHandler(); + + /// @brief State handler for REPLACING_REV_PTRS_ST. + /// + /// Entered from: + /// - SELECTING_REV_SERVER_ST with a next event of SERVER_SELECTED_EVT + /// + /// Attempts to delete and then add a reverse DNS entry for a given FQDN. + /// If this is first invocation of the handler after transitioning into + /// this state, any previous update request context is deleted. If next + /// event is SERVER_SELECTED_EVT, the handler builds the reverse replacement + /// add request, schedules an asynchronous send via sendUpdate(), and + /// returns. Note that sendUpdate will post NOP_EVT as next event. + /// + /// Posting the NOP_EVT will cause runModel() to suspend execution of + /// the state model thus affecting a "wait" for the update IO to complete. + /// Update completion occurs via the DNSClient callback operator() method + /// inherited from NameChangeTransaction. When invoked this callback will + /// post a next event of IO_COMPLETED_EVT and then invoke runModel which + /// resumes execution of the state model. + /// + /// When the handler is invoked with a next event of IO_COMPLETED_EVT, + /// the DNS update status is checked and acted upon accordingly: + /// + /// Transitions to: + /// - PROCESS_TRANS_OK_ST with a next event of UPDATE_OK_EVT upon + /// successful replacement. + /// + /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT If the + /// DNS server rejected the update for any reason or the IO completed + /// with an unrecognized status. + /// + /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has not been exhausted. + /// + /// - SELECTING_REV_SERVER_ST with next event of SERVER_IO_ERROR_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has been exhausted. + /// + /// @throw NameAddTransactionError if upon entry next event is not: + /// SERVER_SELECTED_EVT or IO_COMPLETED_EVT + void replacingRevPtrsHandler(); + + /// @brief State handler for PROCESS_TRANS_OK_ST. + /// + /// Entered from: + /// - ADDING_FWD_ADDRS_ST with a next event of UPDATE_OK_EVT + /// - REPLACING_FWD_ADDRS_ST with a next event of UPDATE_OK_EVT + /// - REPLACING_REV_PTRS_ST with a next event of UPDATE_OK_EVT + /// + /// Sets the transaction action status to indicate success and ends + /// model execution. + /// + /// Transitions to: + /// - END_ST with a next event of END_EVT. + /// + /// @throw NameAddTransactionError if upon entry next event is not: + /// UPDATE_OK_EVT + void processAddOkHandler(); + + /// @brief State handler for PROCESS_TRANS_FAILED_ST. + /// + /// Entered from: + /// - SELECTING_FWD_SERVER_ST with a next event of NO_MORE_SERVERS + /// - ADDING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT + /// - REPLACING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT + /// - SELECTING_REV_SERVER_ST with a next event of NO_MORE_SERVERS + /// - REPLACING_REV_PTRS_ST with a next event of UPDATE_FAILED_EVT + /// + /// Sets the transaction status to indicate failure and ends + /// model execution. + /// + /// Transitions to: + /// - END_ST with a next event of FAIL_EVT. + /// + /// @throw NameAddTransactionError if upon entry next event is not: + /// UPDATE_FAILED_EVT + void processAddFailedHandler(); + + /// @brief Builds a DNS request to add an forward DNS entry for an FQDN + /// + /// Constructs a DNS update request, based upon the NCR, for adding a + /// forward DNS mapping. Once constructed, the request is stored as + /// the transaction's DNS update request. + /// + /// The request content is adherent to RFC 4703 section 5.3.1: + /// + /// Prerequisite RRsets: + /// 1. An assertion that the FQDN does not exist + /// + /// Updates RRsets: + /// 1. An FQDN/IP RR addition (type A for IPv4, AAAA for IPv6) + /// 2. An FQDN/DHCID RR addition (type DHCID) + /// + /// @throw This method does not throw but underlying methods may. + void buildAddFwdAddressRequest(); + + /// @brief Builds a DNS request to replace forward DNS entry for an FQDN + /// + /// Constructs a DNS update request, based upon the NCR, for replacing a + /// forward DNS mapping. Once constructed, the request is stored as + /// the transaction's DNS update request. + /// + /// The request content is adherent to RFC 4703 section 5.3.2: + /// + /// Prerequisite RRsets: + /// 1. An assertion that the FQDN is in use + /// 2. An assertion that the FQDN/DHCID RR exists for the lease client's + /// DHCID. + /// + /// Updates RRsets: + /// 1. A deletion of any existing FQDN RRs (type A for IPv4, AAAA for IPv6) + /// 2. A FQDN/IP RR addition (type A for IPv4, AAAA for IPv6) + /// + /// @throw This method does not throw but underlying methods may. + void buildReplaceFwdAddressRequest(); + + /// @brief Builds a DNS request to replace a reverse DNS entry for an FQDN + /// + /// Constructs a DNS update request, based upon the NCR, for replacing a + /// reverse DNS mapping. Once constructed, the request is stored as + /// the transaction's DNS update request. + /// + /// The request content is adherent to RFC 4703 section 5.4: + /// + /// Prerequisite RRsets: + /// - There are not prerequisites. + /// + /// Updates RRsets: + /// 1. A delete of any existing PTR RRs for the lease address + /// 2. A delete of any existing DHCID RRs for the lease address + /// 3. A PTR RR addition for the lease address and FQDN + /// 4. A DHCID RR addition for the lease address and lease client DHCID + /// + /// @throw This method does not throw but underlying methods may. + void buildReplaceRevPtrsRequest(); +}; + +/// @brief Defines a pointer to a NameAddTransaction. +typedef boost::shared_ptr<NameAddTransaction> NameAddTransactionPtr; + +} // namespace isc::d2 +} // namespace isc +#endif diff --git a/src/bin/d2/nc_remove.cc b/src/bin/d2/nc_remove.cc new file mode 100644 index 0000000..98785c9 --- /dev/null +++ b/src/bin/d2/nc_remove.cc @@ -0,0 +1,701 @@ +// Copyright (C) 2013-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/. + +#include <config.h> + +#include <d2/nc_remove.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/d2_log.h> + +#include <functional> + +namespace isc { +namespace d2 { + + +// NameRemoveTransaction states +const int NameRemoveTransaction::REMOVING_FWD_ADDRS_ST; +const int NameRemoveTransaction::REMOVING_FWD_RRS_ST; +const int NameRemoveTransaction::REMOVING_REV_PTRS_ST; + +// NameRemoveTransaction events +// Currently NameRemoveTransaction does not define any events. + +NameRemoveTransaction:: +NameRemoveTransaction(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) { + if (ncr->getChangeType() != isc::dhcp_ddns::CHG_REMOVE) { + isc_throw (NameRemoveTransactionError, + "NameRemoveTransaction, request type must be CHG_REMOVE"); + } +} + +NameRemoveTransaction::~NameRemoveTransaction(){ +} + +void +NameRemoveTransaction::defineEvents() { + // Call superclass impl first. + NameChangeTransaction::defineEvents(); + + // Define NameRemoveTransaction events. + // Currently NameRemoveTransaction does not define any events. + // defineEvent(TBD_EVENT, "TBD_EVT"); +} + +void +NameRemoveTransaction::verifyEvents() { + // Call superclass implementation first to verify its events. These are + // events common to all transactions, and they must be defined. + // SELECT_SERVER_EVT + // SERVER_SELECTED_EVT + // SERVER_IO_ERROR_EVT + // NO_MORE_SERVERS_EVT + // IO_COMPLETED_EVT + // UPDATE_OK_EVT + // UPDATE_FAILED_EVT + NameChangeTransaction::verifyEvents(); + + // Verify NameRemoveTransaction events by attempting to fetch them. + // Currently NameRemoveTransaction does not define any events. + // getEvent(TBD_EVENT); +} + +void +NameRemoveTransaction::defineStates() { + // Call superclass impl first. + NameChangeTransaction::defineStates(); + + // Define NameRemoveTransaction states. + defineState(READY_ST, "READY_ST", + std::bind(&NameRemoveTransaction::readyHandler, this)); + + defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST", + std::bind(&NameRemoveTransaction::selectingFwdServerHandler, + this)); + + defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST", + std::bind(&NameRemoveTransaction::selectingRevServerHandler, + this)); + + defineState(REMOVING_FWD_ADDRS_ST, "REMOVING_FWD_ADDRS_ST", + std::bind(&NameRemoveTransaction::removingFwdAddrsHandler, + this)); + + defineState(REMOVING_FWD_RRS_ST, "REMOVING_FWD_RRS_ST", + std::bind(&NameRemoveTransaction::removingFwdRRsHandler, + this)); + + defineState(REMOVING_REV_PTRS_ST, "REMOVING_REV_PTRS_ST", + std::bind(&NameRemoveTransaction::removingRevPtrsHandler, + this)); + + defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST", + std::bind(&NameRemoveTransaction::processRemoveOkHandler, + this)); + + defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST", + std::bind(&NameRemoveTransaction::processRemoveFailedHandler, + this)); +} + +void +NameRemoveTransaction::verifyStates() { + // Call superclass implementation first to verify its states. These are + // states common to all transactions, and they must be defined. + // READY_ST + // SELECTING_FWD_SERVER_ST + // SELECTING_REV_SERVER_ST + // PROCESS_TRANS_OK_ST + // PROCESS_TRANS_FAILED_ST + NameChangeTransaction::verifyStates(); + + // Verify NameRemoveTransaction states by attempting to fetch them. + getStateInternal(REMOVING_FWD_ADDRS_ST); + getStateInternal(REMOVING_FWD_RRS_ST); + getStateInternal(REMOVING_REV_PTRS_ST); +} + +void +NameRemoveTransaction::readyHandler() { + switch(getNextEvent()) { + case START_EVT: + if (getForwardDomain()) { + // Request includes a forward change, do that first. + transition(SELECTING_FWD_SERVER_ST, SELECT_SERVER_EVT); + } else { + // Reverse change only, transition accordingly. + transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT); + } + + break; + default: + // Event is invalid. + isc_throw(NameRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +NameRemoveTransaction::selectingFwdServerHandler() { + switch(getNextEvent()) { + case SELECT_SERVER_EVT: + // First time through for this transaction, so initialize server + // selection. + initServerSelection(getForwardDomain()); + break; + case SERVER_IO_ERROR_EVT: + // We failed to communicate with current server. Attempt to select + // another one below. + break; + default: + // Event is invalid. + isc_throw(NameRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } + + // Select the next server from the list of forward servers. + if (selectNextServer()) { + // We have a server to try. + transition(REMOVING_FWD_ADDRS_ST, SERVER_SELECTED_EVT); + } else { + // Server list is exhausted, so fail the transaction. + transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT); + } +} + +void +NameRemoveTransaction::removingFwdAddrsHandler() { + if (doOnEntry()) { + // Clear the update attempts count on initial transition. + clearUpdateAttempts(); + } + + switch(getNextEvent()) { + case SERVER_SELECTED_EVT: + try { + clearDnsUpdateRequest(); + buildRemoveFwdAddressRequest(); + } catch (const std::exception& ex) { + // While unlikely, the build might fail if we have invalid + // data. Should that be the case, we need to fail the + // transaction. + LOG_ERROR(d2_to_dns_logger, + DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE) + .arg(getRequestId()) + .arg(getNcr()->toText()) + .arg(ex.what()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } + + // Call sendUpdate() to initiate the async send. Note it also sets + // next event to NOP_EVT. + sendUpdate("Forward A/AAAA Remove"); + break; + + case IO_COMPLETED_EVT: { + switch (getDnsUpdateStatus()) { + case DNSClient::SUCCESS: { + // We successfully received a response packet from the server. + // The RCODE will be based on a value-dependent RRset search, + // see RFC 2136 section 3.2.3/3.2.4. + const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode(); + if ((rcode == dns::Rcode::NOERROR()) || + (rcode == dns::Rcode::NXRRSET())) { + // We were able to remove it or it wasn't there, now we + // need to remove any other RRs for this FQDN. + transition(REMOVING_FWD_RRS_ST, UPDATE_OK_EVT); + } else { + // Per RFC4703 any other value means cease. + // If we get not authorized should we try the next server in + // the list? @todo This needs some discussion perhaps. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()) + .arg(rcode.getCode()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + } + + break; + } + + case DNSClient::TIMEOUT: + case DNSClient::OTHER: + // We couldn't send to the current server, log it and set up + // to select the next server for a retry. + // @note For now we treat OTHER as an IO error like TIMEOUT. It + // is not entirely clear if this is accurate. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR) + .arg(getRequestId()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + retryTransition(SELECTING_FWD_SERVER_ST); + break; + + case DNSClient::INVALID_RESPONSE: + // A response was received but was corrupt. Retry it like an IO + // error. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()); + + retryTransition(SELECTING_FWD_SERVER_ST); + break; + + default: + // Any other value and we will fail this transaction, something + // bigger is wrong. + LOG_ERROR(d2_to_dns_logger, + DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS) + .arg(getRequestId()) + .arg(getDnsUpdateStatus()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } // end switch on dns_status + + break; + } // end case IO_COMPLETE_EVT + + default: + // Event is invalid. + isc_throw(NameRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + + +void +NameRemoveTransaction::removingFwdRRsHandler() { + if (doOnEntry()) { + // Clear the update attempts count on initial transition. + clearUpdateAttempts(); + } + + switch(getNextEvent()) { + case UPDATE_OK_EVT: + case SERVER_SELECTED_EVT: + try { + clearDnsUpdateRequest(); + buildRemoveFwdRRsRequest(); + } catch (const std::exception& ex) { + // While unlikely, the build might fail if we have invalid + // data. Should that be the case, we need to fail the + // transaction. + LOG_ERROR(d2_to_dns_logger, + DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE) + .arg(getRequestId()) + .arg(getNcr()->toText()) + .arg(ex.what()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } + + // Call sendUpdate() to initiate the async send. Note it also sets + // next event to NOP_EVT. + sendUpdate("Forward RR Remove"); + break; + + case IO_COMPLETED_EVT: { + switch (getDnsUpdateStatus()) { + case DNSClient::SUCCESS: { + // We successfully received a response packet from the server. + // The RCODE will be based on a value-dependent RRset search, + // see RFC 2136 section 3.2.3/3.2.4. + const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode(); + if ((rcode == dns::Rcode::NOERROR()) || + (rcode == dns::Rcode::NXRRSET())) { + // We were able to remove them or they were not there ( + // Rcode of NXRRSET means there are no matching RRsets). + // In either case, we consider it success and mark it as done. + setForwardChangeCompleted(true); + + // If request calls for reverse update then do that next, + // otherwise we can process ok. + if (getReverseDomain()) { + transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT); + } else { + transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT); + } + } else { + // Per RFC4703 any other value means cease. + // If we get not authorized should try the next server in + // the list? @todo This needs some discussion perhaps. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()) + .arg(rcode.getCode()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + } + + break; + } + + case DNSClient::TIMEOUT: + case DNSClient::OTHER: + // We couldn't send to the current server, log it and set up + // to select the next server for a retry. + // @note For now we treat OTHER as an IO error like TIMEOUT. It + // is not entirely clear if this is accurate. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR) + .arg(getRequestId()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + // @note If we exhaust the IO retries for the current server + // due to IO failures, we will abort the remaining updates. + // The rational is that we are only in this state, if the remove + // of the forward address RR succeeded (removingFwdAddrsHandler) + // on the current server. Therefore we should not attempt another + // removal on a different server. This is perhaps a point + // for discussion. + // @todo Should we go ahead with the reverse remove? + retryTransition(PROCESS_TRANS_FAILED_ST); + break; + + case DNSClient::INVALID_RESPONSE: + // A response was received but was corrupt. Retry it like an IO + // error. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()); + + // If we are out of retries on this server abandon the transaction. + // (Same logic as the case for TIMEOUT above). + retryTransition(PROCESS_TRANS_FAILED_ST); + break; + + default: + // Any other value and we will fail this transaction, something + // bigger is wrong. + LOG_ERROR(d2_to_dns_logger, + DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS) + .arg(getRequestId()) + .arg(getDnsUpdateStatus()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } // end switch on dns_status + + break; + } // end case IO_COMPLETE_EVT + + default: + // Event is invalid. + isc_throw(NameRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + + +void +NameRemoveTransaction::selectingRevServerHandler() { + switch(getNextEvent()) { + case SELECT_SERVER_EVT: + // First time through for this transaction, so initialize server + // selection. + initServerSelection(getReverseDomain()); + break; + case SERVER_IO_ERROR_EVT: + // We failed to communicate with current server. Attempt to select + // another one below. + break; + default: + // Event is invalid. + isc_throw(NameRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } + + // Select the next server from the list of forward servers. + if (selectNextServer()) { + // We have a server to try. + transition(REMOVING_REV_PTRS_ST, SERVER_SELECTED_EVT); + } else { + // Server list is exhausted, so fail the transaction. + transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT); + } +} + + +void +NameRemoveTransaction::removingRevPtrsHandler() { + if (doOnEntry()) { + // Clear the update attempts count on initial transition. + clearUpdateAttempts(); + } + + switch(getNextEvent()) { + case SERVER_SELECTED_EVT: + try { + clearDnsUpdateRequest(); + buildRemoveRevPtrsRequest(); + } catch (const std::exception& ex) { + // While unlikely, the build might fail if we have invalid + // data. Should that be the case, we need to fail the + // transaction. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE) + .arg(getRequestId()) + .arg(getNcr()->toText()) + .arg(ex.what()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } + + // Call sendUpdate() to initiate the async send. Note it also sets + // next event to NOP_EVT. + sendUpdate("Reverse Remove"); + break; + + case IO_COMPLETED_EVT: { + switch (getDnsUpdateStatus()) { + case DNSClient::SUCCESS: { + // We successfully received a response packet from the server. + // The RCODE will be based on a value-dependent RRset search, + // see RFC 2136 section 3.2.3/3.2.4. + const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode(); + if ((rcode == dns::Rcode::NOERROR()) || + (rcode == dns::Rcode::NXRRSET())) { + // We were able to remove the reverse mapping or they were + // not there (Rcode of NXRRSET means there are no matching + // RRsets). In either case, mark it as done. + setReverseChangeCompleted(true); + transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT); + } else { + // Per RFC4703 any other value means cease. + // If we get not authorized should try the next server in + // the list? @todo This needs some discussion perhaps. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_REJECTED) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()) + .arg(rcode.getCode()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + } + + break; + } + + case DNSClient::TIMEOUT: + case DNSClient::OTHER: + // We couldn't send to the current server, log it and set up + // to select the next server for a retry. + // @note For now we treat OTHER as an IO error like TIMEOUT. It + // is not entirely clear if this is accurate. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_IO_ERROR) + .arg(getRequestId()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + // If we are out of retries on this server, we go back and start + // all over on a new server. + retryTransition(SELECTING_REV_SERVER_ST); + break; + + case DNSClient::INVALID_RESPONSE: + // A response was received but was corrupt. Retry it like an IO + // error. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()); + + // If we are out of retries on this server, we go back and start + // all over on a new server. + retryTransition(SELECTING_REV_SERVER_ST); + break; + + default: + // Any other value and we will fail this transaction, something + // bigger is wrong. + LOG_ERROR(d2_to_dns_logger, + DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS) + .arg(getRequestId()) + .arg(getDnsUpdateStatus()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } // end switch on dns_status + + break; + } // end case IO_COMPLETE_EVT + + default: + // Event is invalid. + isc_throw(NameRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + + +void +NameRemoveTransaction::processRemoveOkHandler() { + switch(getNextEvent()) { + case UPDATE_OK_EVT: + LOG_INFO(d2_to_dns_logger, DHCP_DDNS_REMOVE_SUCCEEDED) + .arg(getRequestId()) + .arg(getNcr()->toText()); + setNcrStatus(dhcp_ddns::ST_COMPLETED); + endModel(); + break; + default: + // Event is invalid. + isc_throw(NameRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +NameRemoveTransaction::processRemoveFailedHandler() { + switch(getNextEvent()) { + case UPDATE_FAILED_EVT: + case NO_MORE_SERVERS_EVT: + case SERVER_IO_ERROR_EVT: + setNcrStatus(dhcp_ddns::ST_FAILED); + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REMOVE_FAILED) + .arg(getRequestId()) + .arg(transactionOutcomeString()); + endModel(); + break; + default: + // Event is invalid. + isc_throw(NameRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +NameRemoveTransaction::buildRemoveFwdAddressRequest() { + // Construct an empty request. + D2UpdateMessagePtr request = prepNewRequest(getForwardDomain()); + + // Content on this request is based on RFC 4703, section 5.5, paragraph 4. + // Construct dns::Name from NCR fqdn. + dns::Name fqdn(dns::Name(getNcr()->getFqdn())); + // First build the Prerequisite Section + + // Create an DHCID matches prerequisite RR and add it to the + // pre-requisite section + // Based on RFC 2136, section 2.4.2. + dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::IN(), + dns::RRType::DHCID(), dns::RRTTL(0))); + addDhcidRdata(prereq); + request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq); + + // Next build the Update Section + + // Create the FQDN/IP 'delete' RR and add it to the update section. + // Add the RR to update section. + // Based on 2136 section 2.5.4 + dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::NONE(), + getAddressRRType(), dns::RRTTL(0))); + addLeaseAddressRdata(update); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Set the transaction's update request to the new request. + setDnsUpdateRequest(request); +} + +void +NameRemoveTransaction::buildRemoveFwdRRsRequest() { + // Construct an empty request. + D2UpdateMessagePtr request = prepNewRequest(getForwardDomain()); + + // Construct dns::Name from NCR fqdn. + dns::Name fqdn(dns::Name(getNcr()->getFqdn())); + + // Content on this request is based on RFC 4703, section 5.5, paragraph 5. + // First build the Prerequisite Section. + + // Now create an DHCID matches prerequisite RR. + // Set the RR's RData to DHCID. + // Add it to the pre-requisite section. + // Based on RFC 2136, section 2.4.2. + dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::IN(), + dns::RRType::DHCID(), dns::RRTTL(0))); + addDhcidRdata(prereq); + request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq); + + // Create an assertion that there are no A RRs for the FQDN. + // Add it to the pre-reqs. + // Based on RFC 2136, section 2.4.3. + prereq.reset(new dns::RRset(fqdn, dns::RRClass::NONE(), + dns::RRType::A(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq); + + // Create an assertion that there are no A RRs for the FQDN. + // Add it to the pre-reqs. + // Based on RFC 2136, section 2.4.3. + prereq.reset(new dns::RRset(fqdn, dns::RRClass::NONE(), + dns::RRType::AAAA(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq); + + // Next build the Update Section. + + // Create the 'delete' of all RRs for FQDN. + // Set the message RData to lease address. + // Add the RR to update section. + // Based on RFC 2136, section 2.5.3. + dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::ANY(), + dns::RRType::ANY(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Set the transaction's update request to the new request. + setDnsUpdateRequest(request); +} + +void +NameRemoveTransaction::buildRemoveRevPtrsRequest() { + // Construct an empty request. + D2UpdateMessagePtr request = prepNewRequest(getReverseDomain()); + + // Create the reverse IP address "FQDN". + std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress()); + dns::Name rev_ip(rev_addr); + + // Content on this request is based on RFC 4703, section 5.5, paragraph 2. + // First build the Prerequisite Section. + // (Note that per RFC 4703, section 5.4, there is no need to validate + // DHCID RR for PTR entries.) + + // Create an assertion that the PTRDNAME in the PTR record matches the + // client's FQDN for the address that was released. + // Based on RFC 2136, section 3.2.3 + dns::RRsetPtr prereq(new dns::RRset(rev_ip, dns::RRClass::IN(), + dns::RRType::PTR(), dns::RRTTL(0))); + addPtrRdata(prereq); + request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq); + + // Now, build the Update section. + + // Create a delete of any RRs for the FQDN and add it to update section. + // Based on RFC 2136, section 3.4.2.3 + dns::RRsetPtr update(new dns::RRset(rev_ip, dns::RRClass::ANY(), + dns::RRType::ANY(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Set the transaction's update request to the new request. + setDnsUpdateRequest(request); +} + +} // namespace isc::d2 +} // namespace isc diff --git a/src/bin/d2/nc_remove.h b/src/bin/d2/nc_remove.h new file mode 100644 index 0000000..672a3d9 --- /dev/null +++ b/src/bin/d2/nc_remove.h @@ -0,0 +1,429 @@ +// 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_REMOVE_H +#define NC_REMOVE_H + +/// @file nc_remove.h This file defines the class NameRemoveTransaction. + +#include <d2srv/nc_trans.h> + +namespace isc { +namespace d2 { + +/// @brief Thrown if the NameRemoveTransaction encounters a general error. +class NameRemoveTransactionError : public isc::Exception { +public: + NameRemoveTransactionError(const char* file, size_t line, + const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Embodies the "life-cycle" required to carry out a DDNS Remove update. +/// +/// NameRemoveTransaction implements a state machine for removing a forward +/// and/or reverse DNS mappings. This state machine is based upon the processing +/// logic described in RFC 4703, Section 5.5. That logic may be paraphrased as +/// follows: +/// +/// @code +/// +/// If the request includes a forward change: +/// Select a forward server +/// Send the server a request to remove client's specific forward address RR +/// If it succeeds or the server responds with name no longer in use +/// Send a server a request to delete any other RRs for that FQDN, such +/// as the DHCID RR. +/// otherwise +/// abandon the update +/// +/// If the request includes a reverse change: +/// Select a reverse server +/// Send a server a request to delete reverse entry (PTR RR) +/// +/// @endcode +/// +/// This class derives from NameChangeTransaction from which it inherits +/// states, events, and methods common to NameChangeRequest processing. +class NameRemoveTransaction : public NameChangeTransaction { +public: + + //@{ Additional states needed for NameRemove state model. + /// @brief State that attempts to remove specific forward address record. + static const int REMOVING_FWD_ADDRS_ST = NCT_DERIVED_STATE_MIN + 1; + + /// @brief State that attempts to remove any other forward RRs for the DHCID + static const int REMOVING_FWD_RRS_ST = NCT_DERIVED_STATE_MIN + 2; + + /// @brief State that attempts to remove reverse PTR records + static const int REMOVING_REV_PTRS_ST = NCT_DERIVED_STATE_MIN + 3; + //@} + + //@{ Additional events needed for NameRemove state model. + /// @brief Event sent when replace attempt to fails with address not in use. + /// @todo Currently none have been identified. + //@} + + /// @brief Constructor + /// + /// Instantiates an Remove 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 pointer to the configuration manager + /// + /// @throw NameRemoveTransaction error if given request is not a CHG_REMOVE, + /// NameChangeTransaction error for base class construction errors. + NameRemoveTransaction(asiolink::IOServicePtr& io_service, + dhcp_ddns::NameChangeRequestPtr& ncr, + DdnsDomainPtr& forward_domain, + DdnsDomainPtr& reverse_domain, + D2CfgMgrPtr& cfg_mgr); + + /// @brief Destructor + virtual ~NameRemoveTransaction(); + +protected: + /// @brief Adds events defined by NameRemoveTransaction to the event set. + /// + /// Invokes NameChangeTransaction's implementation and then defines the + /// events unique to NCR Remove transaction processing. + /// + /// @throw StateModelError if an event definition is invalid or a duplicate. + virtual void defineEvents(); + + /// @brief Validates the contents of the set of events. + /// + /// Invokes NameChangeTransaction's implementation and then verifies the + /// Remove transaction's events. This tests that the needed events are in + /// the event dictionary. + /// + /// @throw StateModelError if an event value is undefined. + virtual void verifyEvents(); + + /// @brief Adds states defined by NameRemoveTransaction to the state set. + /// + /// Invokes NameChangeTransaction's implementation and then defines the + /// states unique to NCR Remove transaction processing. + /// + /// @throw StateModelError if an state definition is invalid or a duplicate. + virtual void defineStates(); + + /// @brief Validates the contents of the set of states. + /// + /// Invokes NameChangeTransaction's implementation and then verifies the + /// Remove transaction's states. This tests that the needed states are in + /// the state dictionary. + /// + /// @throw StateModelError if an event value is undefined. + virtual void verifyStates(); + + /// @brief State handler for READY_ST. + /// + /// Entered from: + /// - INIT_ST with next event of START_EVT + /// + /// The READY_ST is the state the model transitions into when the inherited + /// method, startTransaction() is invoked. This handler, therefore, is the + /// entry point into the state model execution. Its primary task is to + /// determine whether to start with a forward DNS change or a reverse DNS + /// change. + /// + /// Transitions to: + /// - SELECTING_FWD_SERVER_ST with next event of SERVER_SELECT_ST if request + /// includes a forward change. + /// + /// - SELECTING_REV_SERVER_ST with next event of SERVER_SELECT_ST if request + /// includes only a reverse change. + /// + /// @throw NameRemoveTransactionError if upon entry next event is not + /// START_EVT. + void readyHandler(); + + /// @brief State handler for SELECTING_FWD_SERVER_ST. + /// + /// Entered from: + /// - READY_ST with next event of SELECT_SERVER_EVT + /// - REMOVING_FWD_ADDRS_ST with next event of SERVER_IO_ERROR_EVT + /// + /// Selects the server to be used from the forward domain for the forward + /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes + /// the forward domain's server selection mechanism and then attempts to + /// select the next server. If next event is SERVER_IO_ERROR_EVT then the + /// handler simply attempts to select the next server. + /// + /// Transitions to: + /// - REMOVING_FWD_ADDRS_ST with next event of SERVER_SELECTED upon + /// successful server selection + /// + /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon + /// failure to select a server + /// + /// @throw NameRemoveTransactionError if upon entry next event is not + /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT. + void selectingFwdServerHandler(); + + /// @brief State handler for SELECTING_REV_SERVER_ST. + /// + /// Entered from: + /// - READY_ST with next event of SELECT_SERVER_EVT + /// - REMOVING_FWD_RRS_ST with next event of SELECT_SERVER_EVT + /// - REMOVING_REV_PTRS_ST with next event of SERVER_IO_ERROR_EVT + /// + /// Selects the server to be used from the reverse domain for the reverse + /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes + /// the reverse domain's server selection mechanism and then attempts to + /// select the next server. If next event is SERVER_IO_ERROR_EVT then the + /// handler simply attempts to select the next server. + /// + /// Transitions to: + /// - REMOVING_REV_PTRS_ST with next event of SERVER_SELECTED upon + /// successful server selection + /// + /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon + /// failure to select a server + /// + /// @throw NameRemoveTransactionError if upon entry next event is not + /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT. + void selectingRevServerHandler(); + + /// @brief State handler for REMOVING_FWD_ADDRS_ST. + /// + /// Entered from: + /// - SELECTING_FWD_SERVER with next event of SERVER_SELECTED_EVT + /// + /// Attempts to remove the forward DNS entry for a given FQDN, provided + /// a DHCID RR exists which matches the requesting DHCID. If this is + /// first invocation of the handler after transitioning into this state, + /// any previous update request context is deleted. If next event + /// is SERVER_SELECTED_EVT, the handler builds the forward remove request, + /// schedules an asynchronous send via sendUpdate(), and returns. Note + /// that sendUpdate will post NOP_EVT as next event. + /// + /// Posting the NOP_EVT will cause runModel() to suspend execution of + /// the state model thus affecting a "wait" for the update IO to complete. + /// Update completion occurs via the DNSClient callback operator() method + /// inherited from NameChangeTransaction. When invoked this callback will + /// post a next event of IO_COMPLETED_EVT and then invoke runModel which + /// resumes execution of the state model. + /// + /// When the handler is invoked with a next event of IO_COMPLETED_EVT, + /// the DNS update status is checked and acted upon accordingly: + /// + /// Transitions to: + /// - REMOVING_FWD_RRS_ST with next event of UPDATE_OK_EVT upon successful + /// removal or RCODE of indication FQDN is no longer in use (NXDOMAIN). + /// + /// - PROCESS_TRANS_FAILED_ST with next event of UPDATE_FAILED_EVT if the + /// DNS server rejected the update for any reason or the IO completed + /// with an unrecognized status. + /// + /// - RE-ENTER this state with next event of SERVER_SELECTED_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has not been exhausted. + /// + /// - SELECTING_FWD_SERVER_ST with next event of SERVER_IO_ERROR_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has been exhausted. + /// + /// @throw NameRemoveTransactionError if upon entry next event is not + /// SERVER_SELECTED_EVT or IO_COMPLETE_EVT + void removingFwdAddrsHandler(); + + /// @brief State handler for REMOVING_FWD_RRS_ST. + /// + /// Entered from: + /// - REMOVING_FWD_ADDRS_ST with next event of UPDATE_OK_EVT + /// + /// Attempts to delete any remaining RRs associated with the given FQDN + /// such as the DHCID RR. If this is first invocation of the handler after + /// transitioning into this state, any previous update request context is + /// deleted and the handler builds the forward remove request. It then + /// schedules an asynchronous send via sendUpdate(), + /// and returns. Note that sendUpdate will post NOP_EVT as the next event. + /// + /// Posting the NOP_EVT will cause runModel() to suspend execution of + /// the state model thus affecting a "wait" for the update IO to complete. + /// Update completion occurs via the DNSClient callback operator() method + /// inherited from NameChangeTransaction. When invoked this callback will + /// post a next event of IO_COMPLETED_EVT and then invoke runModel which + /// resumes execution of the state model. + /// + /// When the handler is invoked with a next event of IO_COMPLETED_EVT, + /// the DNS update status is checked and acted upon accordingly: + /// + /// Transitions to: + /// - SELECTING_REV_SERVER_ST with a next event of SELECT_SERVER_EVT upon + /// successful completion and the request includes a reverse DNS update. + /// + /// - PROCESS_TRANS_OK_ST with next event of UPDATE_OK_EVT upon successful + /// completion and the request does not include a reverse DNS update. + /// + /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT if the + /// DNS server rejected the update for any other reason or the IO completed + /// with an unrecognized status. + /// + /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has not been exhausted. + /// + /// - PROCESS_TRANS_FAILED_ST with a next event of SERVER_IO_ERROR_EVT if + /// there we have reached maximum number of retries without success on the + /// current server. + /// + /// @note If we exhaust the IO retries for the current server due to IO + /// failures, we will abort the remaining updates. The rational is that + /// we are only in this state, if the remove of the forward address RR + /// succeeded (removingFwdAddrsHandler) on the current server so we should + /// not attempt another removal on a different server. This is perhaps a + /// point for discussion. @todo Should we go ahead with the reverse remove? + /// + /// @throw NameRemoveTransactionError if upon entry next event is not: + /// UPDATE_OK_EVT or IO_COMPLETE_EVT + void removingFwdRRsHandler(); + + /// @brief State handler for REMOVING_REV_PTRS_ST. + /// + /// Entered from: + /// - SELECTING_REV_SERVER_ST with a next event of SERVER_SELECTED_EVT + /// + /// Attempts to delete a reverse DNS entry for a given FQDN. If this is + /// first invocation of the handler after transitioning into this state, + /// any previous update request context is deleted. If next event is + /// SERVER_SELECTED_EVT, the handler builds the reverse remove request, + /// schedules an asynchronous send via sendUpdate(), and then returns. + /// Note that sendUpdate will post NOP_EVT as next event. + /// + /// Posting the NOP_EVT will cause runModel() to suspend execution of + /// the state model thus affecting a "wait" for the update IO to complete. + /// Update completion occurs via the DNSClient callback operator() method + /// inherited from NameChangeTransaction. When invoked this callback will + /// post a next event of IO_COMPLETED_EVT and then invoke runModel which + /// resumes execution of the state model. + /// + /// When the handler is invoked with a next event of IO_COMPLETED_EVT, + /// the DNS update status is checked and acted upon accordingly: + /// + /// Transitions to: + /// - PROCESS_TRANS_OK_ST with a next event of UPDATE_OK_EVT upon + /// successful completion. + /// + /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT If the + /// DNS server rejected the update for any reason or the IO completed + /// with an unrecognized status. + /// + /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has not been exhausted. + /// + /// - SELECTING_REV_SERVER_ST with next event of SERVER_IO_ERROR_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has been exhausted. + /// + /// @throw NameRemoveTransactionError if upon entry next event is not: + /// SERVER_SELECTED_EVT or IO_COMPLETED_EVT + void removingRevPtrsHandler(); + + /// @brief State handler for PROCESS_TRANS_OK_ST. + /// + /// Entered from: + /// - REMOVING_FWD_RRS_ST with a next event of UPDATE_OK_EVT + /// - REMOVING_REV_PTRS_ST with a next event of UPDATE_OK_EVT + /// + /// Sets the transaction action status to indicate success and ends + /// model execution. + /// + /// Transitions to: + /// - END_ST with a next event of END_EVT. + /// + /// @throw NameRemoveTransactionError if upon entry next event is not: + /// UPDATE_OK_EVT + void processRemoveOkHandler(); + + /// @brief State handler for PROCESS_TRANS_FAILED_ST. + /// + /// Entered from: + /// - SELECTING_FWD_SERVER_ST with a next event of NO_MORE_SERVERS + /// - REMOVING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT + /// - REMOVING_FWD_RRS_ST with a next event of UPDATE_FAILED_EVT + /// - REMOVING_FWD_RRS_ST with a next event of SERVER_IO_ERROR_EVT + /// - SELECTING_REV_SERVER_ST with a next event of NO_MORE_SERVERS + /// - REMOVING_REV_PTRS_ST with a next event of UPDATE_FAILED_EVT + /// + /// Sets the transaction status to indicate failure and ends + /// model execution. + /// + /// Transitions to: + /// - END_ST with a next event of FAIL_EVT. + /// + /// @throw NameRemoveTransactionError if upon entry next event is not: + /// UPDATE_FAILED_EVT + void processRemoveFailedHandler(); + + /// @brief Builds a DNS request to remove a forward DNS address for a FQDN. + /// + /// Constructs a DNS update request, based upon the NCR, for removing a + /// forward DNS address mapping. Once constructed, the request is stored as + /// the transaction's DNS update request. + /// + /// The request content is adherent to RFC 4703 section 5.5, paragraph 4. + /// + /// Prerequisite RRsets: + /// 1. An assertion that a matching DHCID RR exists + /// + /// Updates RRsets: + /// 1. A delete of the FQDN/IP RR (type A for IPv4, AAAA for IPv6) + /// + /// @throw This method does not throw but underlying methods may. + void buildRemoveFwdAddressRequest(); + + /// @brief Builds a DNS request to remove all forward DNS RRs for a FQDN. + /// + /// Constructs a DNS update request, based upon the NCR, for removing any + /// remaining forward DNS RRs, once all A or AAAA entries for the FQDN + /// have been removed. Once constructed, the request is stored as the + /// transaction's DNS update request. + /// + /// The request content is adherent to RFC 4703 section 5.5, paragraph 5. + /// + /// Prerequisite RRsets: + /// 1. An assertion that a matching DHCID RR exists + /// 2. An assertion that no A RRs for the FQDN exist + /// 3. An assertion that no AAAA RRs for the FQDN exist + /// + /// Updates RRsets: + /// 1. A delete of all RRs for the FQDN + /// + /// @throw This method does not throw but underlying methods may. + void buildRemoveFwdRRsRequest(); + + /// @brief Builds a DNS request to remove a reverse DNS entry for a FQDN + /// + /// Constructs a DNS update request, based upon the NCR, for removing a + /// reverse DNS mapping. Once constructed, the request is stored as + /// the transaction's DNS update request. + /// + /// The request content is adherent to RFC 4703 section 5.5, paragraph 2: + /// + /// Prerequisite RRsets: + /// 1. An assertion that a PTR record matching the client's FQDN exists. + /// + /// Updates RRsets: + /// 1. A delete of all RRs for the FQDN + /// + /// @throw This method does not throw but underlying methods may. + void buildRemoveRevPtrsRequest(); +}; + +/// @brief Defines a pointer to a NameRemoveTransaction. +typedef boost::shared_ptr<NameRemoveTransaction> NameRemoveTransactionPtr; + + +} // namespace isc::d2 +} // namespace isc +#endif diff --git a/src/bin/d2/parser_context.cc b/src/bin/d2/parser_context.cc new file mode 100644 index 0000000..f27a628 --- /dev/null +++ b/src/bin/d2/parser_context.cc @@ -0,0 +1,221 @@ +// 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 <config.h> + +#include <d2/d2_parser.h> +#include <d2/parser_context.h> +#include <d2srv/d2_log.h> +#include <exceptions/exceptions.h> +#include <cc/data.h> +#include <boost/lexical_cast.hpp> +#include <fstream> +#include <sstream> +#include <limits> + +namespace isc { +namespace d2 { + +D2ParserContext::D2ParserContext() + : sfile_(0), ctx_(NO_KEYWORD), trace_scanning_(false), trace_parsing_(false) +{ +} + +D2ParserContext::~D2ParserContext() +{ +} + +isc::data::ElementPtr +D2ParserContext::parseString(const std::string& str, ParserType parser_type) +{ + scanStringBegin(str, parser_type); + return (parseCommon()); +} + +isc::data::ElementPtr +D2ParserContext::parseFile(const std::string& filename, ParserType parser_type) { + FILE* f = fopen(filename.c_str(), "r"); + if (!f) { + isc_throw(D2ParseError, "Unable to open file " << filename); + } + scanFileBegin(f, filename, parser_type); + return (parseCommon()); +} + +isc::data::ElementPtr +D2ParserContext::parseCommon() { + isc::d2::D2Parser parser(*this); + // Uncomment this to get detailed parser logs. + // trace_parsing_ = true; + parser.set_debug_level(trace_parsing_); + try { + int res = parser.parse(); + if (res != 0) { + isc_throw(D2ParseError, "Parser abort"); + } + scanEnd(); + } + catch (...) { + scanEnd(); + throw; + } + if (stack_.size() == 1) { + return (stack_[0]); + } else { + isc_throw(D2ParseError, "Expected exactly one terminal Element expected, found " + << stack_.size()); + } +} + +void +D2ParserContext::error(const isc::d2::location& loc, + const std::string& what, + size_t pos) +{ + if (pos == 0) { + isc_throw(D2ParseError, loc << ": " << what); + } else { + isc_throw(D2ParseError, loc << " (near " << pos << "): " << what); + } +} + +void +D2ParserContext::error (const std::string& what) +{ + isc_throw(D2ParseError, what); +} + +void +D2ParserContext::fatal (const std::string& what) +{ + isc_throw(D2ParseError, what); +} + +isc::data::Element::Position +D2ParserContext::loc2pos(isc::d2::location& loc) +{ + const std::string& file = *loc.begin.filename; + const uint32_t line = loc.begin.line; + const uint32_t pos = loc.begin.column; + return (isc::data::Element::Position(file, line, pos)); +} + +void +D2ParserContext::require(const std::string& name, + isc::data::Element::Position open_loc, + isc::data::Element::Position close_loc) +{ + ConstElementPtr value = stack_.back()->get(name); + if (!value) { + isc_throw(D2ParseError, + "missing parameter '" << name << "' (" + << stack_.back()->getPosition() << ") [" + << contextName() << " map between " + << open_loc << " and " << close_loc << "]"); + } +} + +void +D2ParserContext::unique(const std::string& name, + isc::data::Element::Position loc) +{ + ConstElementPtr value = stack_.back()->get(name); + if (value) { + if (ctx_ != NO_KEYWORD) { + isc_throw(D2ParseError, loc << ": duplicate " << name + << " entries in " << contextName() + << " map (previous at " << value->getPosition() << ")"); + } else { + isc_throw(D2ParseError, loc << ": duplicate " << name + << " entries in JSON" + << " map (previous at " << value->getPosition() << ")"); + } + } +} + +void +D2ParserContext::enter(const ParserContext& ctx) +{ + cstack_.push_back(ctx_); + ctx_ = ctx; +} + +void +D2ParserContext::leave() +{ + if (cstack_.empty()) { + fatal("unbalanced syntactic context"); + } + + ctx_ = cstack_.back(); + cstack_.pop_back(); +} + +const std::string +D2ParserContext::contextName() +{ + switch (ctx_) { + case NO_KEYWORD: + return ("__no keyword__"); + case CONFIG: + return ("toplevel"); + case DHCPDDNS: + return ("DhcpDdns"); + case TSIG_KEY: + return ("tsig-key"); + case TSIG_KEYS: + return ("tsig-keys"); + case ALGORITHM: + return("algorithm"); + case DIGEST_BITS: + return("digest-bits"); + case SECRET: + return("secret"); + case FORWARD_DDNS: + return("forward-ddns"); + case REVERSE_DDNS: + return("reverse-ddns"); + case DDNS_DOMAIN: + return("ddns-domain"); + case DDNS_DOMAINS: + return("ddns-domains"); + case DNS_SERVER: + return("dns-server"); + case DNS_SERVERS: + return("dns-servers"); + case CONTROL_SOCKET: + return("control-socket"); + case LOGGERS: + return ("loggers"); + case OUTPUT_OPTIONS: + return ("output-options"); + case NCR_PROTOCOL: + return ("ncr-protocol"); + case NCR_FORMAT: + return ("ncr-format"); + case HOOKS_LIBRARIES: + return ("hooks-libraries"); + default: + return ("__unknown__"); + } +} + +void +D2ParserContext::warning(const isc::d2::location& loc, + const std::string& what) { + std::ostringstream msg; + msg << loc << ": " << what; + LOG_WARN(d2_to_dns_logger, DHCP_DDNS_CONFIG_SYNTAX_WARNING) + .arg(msg.str()); +} + +void +D2ParserContext::warnAboutExtraCommas(const isc::d2::location& loc) { + warning(loc, "Extraneous comma. A piece of configuration may have been omitted."); +} + +} // namespace d2 +} // namespace isc diff --git a/src/bin/d2/parser_context.h b/src/bin/d2/parser_context.h new file mode 100644 index 0000000..e838ffd --- /dev/null +++ b/src/bin/d2/parser_context.h @@ -0,0 +1,351 @@ +// 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 PARSER_CONTEXT_H +#define PARSER_CONTEXT_H +#include <string> +#include <map> +#include <vector> +#include <d2/d2_parser.h> +#include <d2/parser_context_decl.h> +#include <exceptions/exceptions.h> + +// Tell Flex the lexer's prototype ... +#define YY_DECL isc::d2::D2Parser::symbol_type d2_parser_lex (D2ParserContext& driver) + +// ... and declare it for the parser's sake. +YY_DECL; + +namespace isc { +namespace d2 { + +/// @brief Evaluation error exception raised when trying to parse. +/// +/// @todo: This probably should be common for Dhcp4 and Dhcp6. +class D2ParseError : public isc::Exception { +public: + D2ParseError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Evaluation context, an interface to the expression evaluation. +class D2ParserContext +{ +public: + + /// @brief Defines currently supported scopes + /// + /// D2Parser may eventually support multiple levels of parsing scope. + /// Currently it supports only the D2 module scope which expects the data + /// to be parsed to be a map containing the DhcpDdns element and its + /// constituents. + /// + typedef enum { + /// This parser will parse the content as generic JSON. + PARSER_JSON, + + ///< Used for parsing top level (contains DhcpDdns) + PARSER_DHCPDDNS, + + ///< Used for parsing content of DhcpDdns. + PARSER_SUB_DHCPDDNS, + + ///< Used for parsing content of a TSIG key. + PARSER_TSIG_KEY, + + ///< Used for parsing a list of TSIG Keys. + PARSER_TSIG_KEYS, + + ///< Used for parsing content of a DDNS Domain. + PARSER_DDNS_DOMAIN, + + ///< Used for parsing a list a DDNS Domains. + PARSER_DDNS_DOMAINS, + + ///< Used for parsing content of a DNS Server. + PARSER_DNS_SERVER, + + ///< Used for parsing a list of DNS servers. + PARSER_DNS_SERVERS, + + ///< Used for parsing content of hooks libraries. + PARSER_HOOKS_LIBRARY + } ParserType; + + /// @brief Default constructor. + D2ParserContext(); + + /// @brief destructor. + virtual ~D2ParserContext(); + + /// @brief JSON elements being parsed. + std::vector<isc::data::ElementPtr> stack_; + + /// @brief Method called before scanning starts on a string. + /// + /// @param str string to be parsed + /// @param type specifies expected content + void scanStringBegin(const std::string& str, ParserType type); + + /// @brief Method called before scanning starts on a file. + /// + /// @param f stdio FILE pointer + /// @param filename file to be parsed + /// @param type specifies expected content + void scanFileBegin(FILE* f, const std::string& filename, ParserType type); + + /// @brief Method called after the last tokens are scanned. + void scanEnd(); + + /// @brief Divert input to an include file. + /// + /// @param filename file to be included + void includeFile(const std::string& filename); + + /// @brief Run the parser on the string specified. + /// + /// This method parses specified string. Depending on the value of + /// parser_type, parser may either check only that the input is valid + /// JSON, or may do more specific syntax checking. See @ref ParserType + /// for supported syntax checkers. + /// + /// @param str string to be parsed + /// @param parser_type specifies expected content + /// @return Element structure representing parsed text. + isc::data::ElementPtr parseString(const std::string& str, + ParserType parser_type); + + /// @brief Run the parser on the file specified. + /// + /// This method parses specified file. Depending on the value of + /// parser_type, parser may either check only that the input is valid + /// JSON, or may do more specific syntax checking. See @ref ParserType + /// for supported syntax checkers. + /// + /// @param filename file to be parsed + /// @param parser_type specifies expected content + /// @return Element structure representing parsed text. + isc::data::ElementPtr parseFile(const std::string& filename, + ParserType parser_type); + + /// @brief Error handler + /// + /// @note The optional position for an error in a string begins by 1 + /// so the caller should add 1 to the position of the C++ string. + /// + /// @param loc location within the parsed file where the problem was experienced. + /// @param what string explaining the nature of the error. + /// @param pos optional position for in string errors. + /// @throw D2ParseError + void error(const isc::d2::location& loc, + const std::string& what, + size_t pos = 0); + + /// @brief Error handler + /// + /// This is a simplified error reporting tool for reporting + /// parsing errors. + /// + /// @param what string explaining the nature of the error. + /// @throw D2ParseError + void error(const std::string& what); + + /// @brief Fatal error handler + /// + /// This is for should not happen but fatal errors. + /// Used by YY_FATAL_ERROR macro so required to be static. + /// + /// @param what string explaining the nature of the error. + /// @throw D2ParseError + static void fatal(const std::string& what); + + /// @brief Converts bison's position to one understood by isc::data::Element + /// + /// Convert a bison location into an element position + /// (take the begin, the end is lost) + /// + /// @param loc location in bison format + /// @return Position in format accepted by Element + isc::data::Element::Position loc2pos(isc::d2::location& loc); + + /// @brief Check if a required parameter is present + /// + /// Check if a required parameter is present in the map at the top + /// of the stack and raise an error when it is not. + /// + /// @param name name of the parameter to check + /// @param open_loc location of the opening curly bracket + /// @param close_loc location of the closing curly bracket + /// @throw D2ParseError + void require(const std::string& name, + isc::data::Element::Position open_loc, + isc::data::Element::Position close_loc); + + /// @brief Check if a parameter is already present + /// + /// Check if a parameter is already present in the map at the top + /// of the stack and raise an error when it is. + /// + /// @param name name of the parameter to check + /// @param loc location of the current parameter + /// @throw D2ParseError + void unique(const std::string& name, + isc::data::Element::Position loc); + + /// @brief Warning handler + /// + /// @param loc location within the parsed file where the problem was experienced + /// @param what string explaining the nature of the error + /// + /// @throw ParseError + void warning(const isc::d2::location& loc, const std::string& what); + + /// @brief Warning for extra commas + /// + /// @param loc location within the parsed file of the extra comma + /// + /// @throw ParseError + void warnAboutExtraCommas(const isc::d2::location& loc); + + /// @brief Defines syntactic contexts for lexical tie-ins + typedef enum { + ///< This one is used in pure JSON mode. + NO_KEYWORD, + + ///< Used while parsing top level (contains DhcpDdns). + CONFIG, + + ///< Used while parsing content of DhcpDdns. + DHCPDDNS, + + ///< Used while parsing content of a tsig-key + TSIG_KEY, + + ///< Used while parsing a list of tsig-keys + TSIG_KEYS, + + ///< Used while parsing content of DhcpDdns/tsig-keys/algorithm + ALGORITHM, + + ///< Used while parsing content of DhcpDdns/tsig-keys/digest-bits + DIGEST_BITS, + + ///< Used while parsing content of DhcpDdns/tsig-keys/secret + SECRET, + + ///< Used while parsing content of DhcpDdns/forward-ddns + FORWARD_DDNS, + + ///< Used while parsing content of DhcpDdns/reverse-ddns + REVERSE_DDNS, + + ///< Used while parsing content of a ddns-domain + DDNS_DOMAIN, + + ///< Used while parsing a list of ddns-domains + DDNS_DOMAINS, + + ///< Used while parsing content of a dns-server + DNS_SERVER, + + ///< Used while parsing content of list of dns-servers + DNS_SERVERS, + + ///< Used while parsing content of a control-socket + CONTROL_SOCKET, + + /// Used while parsing DhcpDdns/loggers structures. + LOGGERS, + + /// Used while parsing DhcpDdns/loggers/output_options structures. + OUTPUT_OPTIONS, + + /// Used while parsing DhcpDdns/ncr-protocol + NCR_PROTOCOL, + + /// Used while parsing DhcpDdns/ncr-format + NCR_FORMAT, + + /// Used while parsing DhcpDdns/hooks-libraries. + HOOKS_LIBRARIES + + } ParserContext; + + /// @brief File name + std::string file_; + + /// @brief File name stack + std::vector<std::string> files_; + + /// @brief Location of the current token + /// + /// The lexer will keep updating it. This variable will be useful + /// for logging errors. + isc::d2::location loc_; + + /// @brief Location stack + std::vector<isc::d2::location> locs_; + + /// @brief Lexer state stack + std::vector<struct yy_buffer_state*> states_; + + /// @brief sFile (aka FILE) + FILE* sfile_; + + /// @brief sFile (aka FILE) stack + /// + /// This is a stack of files. Typically there's only one file (the + /// one being currently parsed), but there may be more if one + /// file includes another. + std::vector<FILE*> sfiles_; + + /// @brief Current syntactic context + ParserContext ctx_; + + /// @brief Enter a new syntactic context + /// + /// Entering a new syntactic context is useful in several ways. + /// First, it allows the parser to avoid conflicts. Second, it + /// allows the lexer to return different tokens depending on + /// context (e.g. if "name" string is detected, the lexer + /// will return STRING token if in JSON mode or NAME if + /// in TSIG_KEY mode. Finally, the syntactic context allows the + /// error message to be more descriptive if the input string + /// does not parse properly. + /// + /// @param ctx the syntactic context to enter into + void enter(const ParserContext& ctx); + + /// @brief Leave a syntactic context + /// + /// @throw isc::Unexpected if unbalanced + void leave(); + + /// @brief Get the syntax context name + /// + /// @return printable name of the context. + const std::string contextName(); + + private: + /// @brief Flag determining scanner debugging. + bool trace_scanning_; + + /// @brief Flag determining parser debugging. + bool trace_parsing_; + + /// @brief Syntactic context stack + std::vector<ParserContext> cstack_; + + /// @brief Common part of parseXXX + /// + /// @return Element structure representing parsed text. + isc::data::ElementPtr parseCommon(); +}; + +} // end of isc::eval namespace +} // end of isc namespace + +#endif diff --git a/src/bin/d2/parser_context_decl.h b/src/bin/d2/parser_context_decl.h new file mode 100644 index 0000000..a927ac1 --- /dev/null +++ b/src/bin/d2/parser_context_decl.h @@ -0,0 +1,20 @@ +// Copyright (C) 2017 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_PARSER_CONTEXT_DECL_H +#define D2_PARSER_CONTEXT_DECL_H + +/// @file d2/parser_context_decl.h Forward declaration of the ParserContext class + +namespace isc { +namespace d2 { + +class D2ParserContext; + +}; // end of isc::dhcp namespace +}; // end of isc namespace + +#endif diff --git a/src/bin/d2/simple_add.cc b/src/bin/d2/simple_add.cc new file mode 100644 index 0000000..129c5ed --- /dev/null +++ b/src/bin/d2/simple_add.cc @@ -0,0 +1,536 @@ +// 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/. + +#include <config.h> + +#include <d2/simple_add.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/d2_log.h> + +#include <util/buffer.h> +#include <dns/rdataclass.h> + +#include <functional> + +namespace isc { +namespace d2 { + +// SimpleAddTransaction states +const int SimpleAddTransaction::REPLACING_FWD_ADDRS_ST; +const int SimpleAddTransaction::REPLACING_REV_PTRS_ST; + +// SimpleAddTransaction events +const int SimpleAddTransaction::FQDN_IN_USE_EVT; +const int SimpleAddTransaction::FQDN_NOT_IN_USE_EVT; + +SimpleAddTransaction:: +SimpleAddTransaction(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) { + if (ncr->getChangeType() != isc::dhcp_ddns::CHG_ADD) { + isc_throw (SimpleAddTransactionError, + "SimpleAddTransaction, request type must be CHG_ADD"); + } +} + +SimpleAddTransaction::~SimpleAddTransaction(){ +} + +void +SimpleAddTransaction::defineEvents() { + // Call superclass impl first. + NameChangeTransaction::defineEvents(); + + // Define SimpleAddTransaction events. + defineEvent(FQDN_IN_USE_EVT, "FQDN_IN_USE_EVT"); + defineEvent(FQDN_NOT_IN_USE_EVT, "FQDN_NOT_IN_USE_EVT"); +} + +void +SimpleAddTransaction::verifyEvents() { + // Call superclass implementation first to verify its events. These are + // events common to all transactions, and they must be defined. + // SELECT_SERVER_EVT + // SERVER_SELECTED_EVT + // SERVER_IO_ERROR_EVT + // NO_MORE_SERVERS_EVT + // IO_COMPLETED_EVT + // UPDATE_OK_EVT + // UPDATE_FAILED_EVT + NameChangeTransaction::verifyEvents(); + + // Verify SimpleAddTransaction events by attempting to fetch them. + getEvent(FQDN_IN_USE_EVT); + getEvent(FQDN_NOT_IN_USE_EVT); +} + +void +SimpleAddTransaction::defineStates() { + // Call superclass impl first. + NameChangeTransaction::defineStates(); + + // Define SimpleAddTransaction states. + defineState(READY_ST, "READY_ST", + std::bind(&SimpleAddTransaction::readyHandler, this)); + + defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST", + std::bind(&SimpleAddTransaction::selectingFwdServerHandler, this)); + + defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST", + std::bind(&SimpleAddTransaction::selectingRevServerHandler, this)); + + defineState(REPLACING_FWD_ADDRS_ST, "REPLACING_FWD_ADDRS_ST", + std::bind(&SimpleAddTransaction::replacingFwdAddrsHandler, this)); + + defineState(REPLACING_REV_PTRS_ST, "REPLACING_REV_PTRS_ST", + std::bind(&SimpleAddTransaction::replacingRevPtrsHandler, this)); + + defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST", + std::bind(&SimpleAddTransaction::processAddOkHandler, this)); + + defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST", + std::bind(&SimpleAddTransaction::processAddFailedHandler, this)); +} + +void +SimpleAddTransaction::verifyStates() { + // Call superclass implementation first to verify its states. These are + // states common to all transactions, and they must be defined. + // READY_ST + // SELECTING_FWD_SERVER_ST + // SELECTING_REV_SERVER_ST + // PROCESS_TRANS_OK_ST + // PROCESS_TRANS_FAILED_ST + NameChangeTransaction::verifyStates(); + + // Verify SimpleAddTransaction states by attempting to fetch them. + getStateInternal(REPLACING_FWD_ADDRS_ST); + getStateInternal(REPLACING_REV_PTRS_ST); +} + +void +SimpleAddTransaction::readyHandler() { + switch(getNextEvent()) { + case START_EVT: + if (getForwardDomain()) { + // Request includes a forward change, do that first. + transition(SELECTING_FWD_SERVER_ST, SELECT_SERVER_EVT); + } else { + // Reverse change only, transition accordingly. + transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT); + } + + break; + default: + // Event is invalid. + isc_throw(SimpleAddTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +SimpleAddTransaction::selectingFwdServerHandler() { + switch(getNextEvent()) { + case SELECT_SERVER_EVT: + // First time through for this transaction, so initialize server + // selection. + initServerSelection(getForwardDomain()); + break; + case SERVER_IO_ERROR_EVT: + // We failed to communicate with current server. Attempt to select + // another one below. + break; + default: + // Event is invalid. + isc_throw(SimpleAddTransactionError, + "Wrong event for context: " << getContextStr()); + } + + // Select the next server from the list of forward servers. + if (selectNextServer()) { + // We have a server to try. + transition(REPLACING_FWD_ADDRS_ST, SERVER_SELECTED_EVT); + } else { + // Server list is exhausted, so fail the transaction. + transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT); + } +} + +void +SimpleAddTransaction::replacingFwdAddrsHandler() { + if (doOnEntry()) { + // Clear the update attempts count on initial transition. + clearUpdateAttempts(); + } + + switch(getNextEvent()) { + case SERVER_SELECTED_EVT: + try { + clearDnsUpdateRequest(); + buildReplaceFwdAddressRequest(); + } catch (const std::exception& ex) { + // While unlikely, the build might fail if we have invalid + // data. Should that be the case, we need to fail the + // transaction. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE) + .arg(getRequestId()) + .arg(getNcr()->toText()) + .arg(ex.what()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } + + // Call sendUpdate() to initiate the async send. Note it also sets + // next event to NOP_EVT. + sendUpdate("Forward Add"); + break; + + case IO_COMPLETED_EVT: { + switch (getDnsUpdateStatus()) { + case DNSClient::SUCCESS: { + // We successfully received a response packet from the server. + const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode(); + if (rcode == dns::Rcode::NOERROR()) { + // We were able to add it. Mark it as done. + setForwardChangeCompleted(true); + + // If request calls for reverse update then do that next, + // otherwise we can process ok. + if (getReverseDomain()) { + transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT); + } else { + transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT); + } + } else { + // Any other value means cease. Really shouldn't happen. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_REJECTED) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()) + .arg(rcode.getCode()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + } + + break; + } + + case DNSClient::TIMEOUT: + case DNSClient::OTHER: + // We couldn't send to the current server, log it and set up + // to select the next server for a retry. + // @note For now we treat OTHER as an IO error like TIMEOUT. It + // is not entirely clear if this is accurate. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_IO_ERROR) + .arg(getRequestId()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + retryTransition(SELECTING_FWD_SERVER_ST); + break; + + case DNSClient::INVALID_RESPONSE: + // A response was received but was corrupt. Retry it like an IO + // error. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()); + + retryTransition(SELECTING_FWD_SERVER_ST); + break; + + default: + // Any other value and we will fail this transaction, something + // bigger is wrong. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS) + .arg(getRequestId()) + .arg(getDnsUpdateStatus()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } // end switch on dns_status + + break; + } // end case IO_COMPLETE_EVT + + default: + // Event is invalid. + isc_throw(SimpleAddTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +SimpleAddTransaction::selectingRevServerHandler() { + switch(getNextEvent()) { + case SELECT_SERVER_EVT: + // First time through for this transaction, so initialize server + // selection. + initServerSelection(getReverseDomain()); + break; + case SERVER_IO_ERROR_EVT: + // We failed to communicate with current server. Attempt to select + // another one below. + break; + default: + // Event is invalid. + isc_throw(SimpleAddTransactionError, + "Wrong event for context: " << getContextStr()); + } + + // Select the next server from the list of forward servers. + if (selectNextServer()) { + // We have a server to try. + transition(REPLACING_REV_PTRS_ST, SERVER_SELECTED_EVT); + } else { + // Server list is exhausted, so fail the transaction. + transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT); + } +} + + +void +SimpleAddTransaction::replacingRevPtrsHandler() { + if (doOnEntry()) { + // Clear the update attempts count on initial transition. + clearUpdateAttempts(); + } + + switch(getNextEvent()) { + case SERVER_SELECTED_EVT: + try { + clearDnsUpdateRequest(); + buildReplaceRevPtrsRequest(); + } catch (const std::exception& ex) { + // While unlikely, the build might fail if we have invalid + // data. Should that be the case, we need to fail the + // transaction. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE) + .arg(getRequestId()) + .arg(getNcr()->toText()) + .arg(ex.what()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } + + // Call sendUpdate() to initiate the async send. Note it also sets + // next event to NOP_EVT. + sendUpdate("Reverse Replace"); + break; + + case IO_COMPLETED_EVT: { + switch (getDnsUpdateStatus()) { + case DNSClient::SUCCESS: { + // We successfully received a response packet from the server. + const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode(); + if (rcode == dns::Rcode::NOERROR()) { + // We were able to update the reverse mapping. Mark it as done. + setReverseChangeCompleted(true); + transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT); + } else { + // Per RFC4703 any other value means cease. + // If we get not authorized should try the next server in + // the list? @todo This needs some discussion perhaps. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_REJECTED) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()) + .arg(rcode.getCode()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + } + + break; + } + + case DNSClient::TIMEOUT: + case DNSClient::OTHER: + // We couldn't send to the current server, log it and set up + // to select the next server for a retry. + // @note For now we treat OTHER as an IO error like TIMEOUT. It + // is not entirely clear if this is accurate. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_IO_ERROR) + .arg(getRequestId()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + // If we are out of retries on this server, we go back and start + // all over on a new server. + retryTransition(SELECTING_REV_SERVER_ST); + break; + + case DNSClient::INVALID_RESPONSE: + // A response was received but was corrupt. Retry it like an IO + // error. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()); + + // If we are out of retries on this server, we go back and start + // all over on a new server. + retryTransition(SELECTING_REV_SERVER_ST); + break; + + default: + // Any other value and we will fail this transaction, something + // bigger is wrong. + LOG_ERROR(d2_to_dns_logger, + DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS) + .arg(getRequestId()) + .arg(getDnsUpdateStatus()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } // end switch on dns_status + + break; + } // end case IO_COMPLETE_EVT + + default: + // Event is invalid. + isc_throw(SimpleAddTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +SimpleAddTransaction::processAddOkHandler() { + switch(getNextEvent()) { + case UPDATE_OK_EVT: + LOG_INFO(d2_to_dns_logger, DHCP_DDNS_ADD_SUCCEEDED) + .arg(getRequestId()) + .arg(getNcr()->toText()); + setNcrStatus(dhcp_ddns::ST_COMPLETED); + endModel(); + break; + default: + // Event is invalid. + isc_throw(SimpleAddTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +SimpleAddTransaction::processAddFailedHandler() { + switch(getNextEvent()) { + case UPDATE_FAILED_EVT: + case NO_MORE_SERVERS_EVT: + setNcrStatus(dhcp_ddns::ST_FAILED); + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_ADD_FAILED) + .arg(getRequestId()) + .arg(transactionOutcomeString()); + endModel(); + break; + default: + // Event is invalid. + isc_throw(SimpleAddTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +SimpleAddTransaction::buildReplaceFwdAddressRequest() { + // Construct an empty request. + D2UpdateMessagePtr request = prepNewRequest(getForwardDomain()); + + // Construct dns::Name from NCR fqdn. + dns::Name fqdn(dns::Name(getNcr()->getFqdn())); + + // There are no prerequisites. + + // Build the Update Section. First we delete any pre-existing + // FQDN/IP and DHCID RRs. Then we add new ones. + + // Create the FQDN/IP 'delete' RR and add it to update section. + dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::ANY(), + getAddressRRType(), dns::RRTTL(0))); + + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Create the DHCID 'delete' RR and add it to the update section. + update.reset(new dns::RRset(fqdn, dns::RRClass::ANY(), + dns::RRType::DHCID(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Now make the new RRs. + // Create the TTL based on lease length. + dns::RRTTL lease_ttl(getNcr()->getLeaseLength()); + + // Create the FQDN/IP 'add' RR and add it to the to update section. + // Based on RFC 2136, section 2.5.1 + update.reset(new dns::RRset(fqdn, dns::RRClass::IN(), + getAddressRRType(), lease_ttl)); + + addLeaseAddressRdata(update); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Now create the FQDN/DHCID 'add' RR and add it to update section. + // Based on RFC 2136, section 2.5.1 + update.reset(new dns::RRset(fqdn, dns::RRClass::IN(), + dns::RRType::DHCID(), lease_ttl)); + + // We add the DHCID for auditing purposes and in the event + // conflict resolution is later enabled. + addDhcidRdata(update); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Set the transaction's update request to the new request. + setDnsUpdateRequest(request); +} + +void +SimpleAddTransaction::buildReplaceRevPtrsRequest() { + // Construct an empty request. + D2UpdateMessagePtr request = prepNewRequest(getReverseDomain()); + + // Create the reverse IP address "FQDN". + std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress()); + dns::Name rev_ip(rev_addr); + + // Create the TTL based on lease length. + dns::RRTTL lease_ttl(getNcr()->getLeaseLength()); + + // There are no prerequisites. + + // Create the FQDN/IP PTR 'delete' RR for this IP and add it to + // the update section. + dns::RRsetPtr update(new dns::RRset(rev_ip, dns::RRClass::ANY(), + dns::RRType::PTR(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Create the DHCID 'delete' RR and add it to the update section. + update.reset(new dns::RRset(rev_ip, dns::RRClass::ANY(), + dns::RRType::DHCID(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Create the FQDN/IP PTR 'add' RR, add the FQDN as the PTR Rdata + // then add it to update section. + update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(), + dns::RRType::PTR(), lease_ttl)); + addPtrRdata(update); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Create the FQDN/IP PTR 'add' RR, add the DHCID Rdata + // then add it to update section. + update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(), + dns::RRType::DHCID(), lease_ttl)); + addDhcidRdata(update); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Set the transaction's update request to the new request. + setDnsUpdateRequest(request); +} + +} // namespace isc::d2 +} // namespace isc diff --git a/src/bin/d2/simple_add.h b/src/bin/d2/simple_add.h new file mode 100644 index 0000000..bf617a5 --- /dev/null +++ b/src/bin/d2/simple_add.h @@ -0,0 +1,355 @@ +// 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 SIMPLE_ADD_H +#define SIMPLE_ADD_H + +/// @file nc_add.h This file defines the class SimpleAddTransaction. + +#include <d2srv/nc_trans.h> +#include <dns/rdata.h> + +namespace isc { +namespace d2 { + +/// @brief Thrown if the SimpleAddTransaction encounters a general error. +class SimpleAddTransactionError : public isc::Exception { +public: + SimpleAddTransactionError(const char* file, size_t line, + const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Embodies the "life-cycle" required to carry out a DDNS Add update. +/// +/// SimpleAddTransaction implements a state machine for adding (or replacing) a +/// forward and/or reverse DNS mapping. This state machine follows a basic +/// remove and replace scheme, that does not attempt to avoid conflicts +/// between updating clients. The logic may be paraphrased as follows: +/// +/// @code +/// +/// If the request includes a forward change: +/// Select a forward server +/// Send the server a request to delete and then add forward entry +/// +/// If the request includes a reverse change: +/// Select a reverse server +/// Send a server a request to delete and then add reverse entry +/// +/// @endcode +/// +/// This class derives from NameChangeTransaction from which it inherits +/// states, events, and methods common to NameChangeRequest processing. +class SimpleAddTransaction : public NameChangeTransaction { +public: + + //@{ Additional states needed for SimpleAdd state model. + /// @brief State that attempts to add forward address records. + static const int REPLACING_FWD_ADDRS_ST = NCT_DERIVED_STATE_MIN + 1; + + /// @brief State that attempts to replace reverse PTR records + static const int REPLACING_REV_PTRS_ST = NCT_DERIVED_STATE_MIN + 3; + //@} + + //@{ Additional events needed for SimpleAdd state model. + /// @brief Event sent when an add attempt fails with address in use. + static const int FQDN_IN_USE_EVT = NCT_DERIVED_EVENT_MIN + 1; + + /// @brief Event sent when replace attempt to fails with address not in use. + static const int FQDN_NOT_IN_USE_EVT = NCT_DERIVED_EVENT_MIN + 2; + //@} + + /// @brief Constructor + /// + /// Instantiates an Add 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 pointer to the configuration manager + /// + /// @throw SimpleAddTransaction error if given request is not a CHG_ADD, + /// NameChangeTransaction error for base class construction errors. + SimpleAddTransaction(asiolink::IOServicePtr& io_service, + dhcp_ddns::NameChangeRequestPtr& ncr, + DdnsDomainPtr& forward_domain, + DdnsDomainPtr& reverse_domain, + D2CfgMgrPtr& cfg_mgr); + + /// @brief Destructor + virtual ~SimpleAddTransaction(); + +protected: + /// @brief Adds events defined by SimpleAddTransaction to the event set. + /// + /// Invokes NameChangeTransaction's implementation and then defines the + /// events unique to NCR Add transaction processing. + /// + /// @throw StateModelError if an event definition is invalid or a duplicate. + virtual void defineEvents(); + + /// @brief Validates the contents of the set of events. + /// + /// Invokes NameChangeTransaction's implementation and then verifies the + /// Add transaction's. This tests that the needed events are in the event + /// dictionary. + /// + /// @throw StateModelError if an event value is undefined. + virtual void verifyEvents(); + + /// @brief Adds states defined by SimpleAddTransaction to the state set. + /// + /// Invokes NameChangeTransaction's implementation and then defines the + /// states unique to NCR Add transaction processing. + /// + /// @throw StateModelError if an state definition is invalid or a duplicate. + virtual void defineStates(); + + /// @brief Validates the contents of the set of states. + /// + /// Invokes NameChangeTransaction's implementation and then verifies the + /// Add transaction's states. This tests that the needed states are in the + /// state dictionary. + /// + /// @throw StateModelError if an event value is undefined. + virtual void verifyStates(); + + /// @brief State handler for READY_ST. + /// + /// Entered from: + /// - INIT_ST with next event of START_EVT + /// + /// The READY_ST is the state the model transitions into when the inherited + /// method, startTransaction() is invoked. This handler, therefore, is the + /// entry point into the state model execution.h Its primary task is to + /// determine whether to start with a forward DNS change or a reverse DNS + /// change. + /// + /// Transitions to: + /// - SELECTING_FWD_SERVER_ST with next event of SERVER_SELECT_ST if request + /// includes a forward change. + /// + /// - SELECTING_REV_SERVER_ST with next event of SERVER_SELECT_ST if request + /// includes only a reverse change. + /// + /// @throw SimpleAddTransactionError if upon entry next event is not + /// START_EVT. + void readyHandler(); + + /// @brief State handler for SELECTING_FWD_SERVER_ST. + /// + /// Entered from: + /// - READY_ST with next event of SELECT_SERVER_EVT + /// - REPLACING_FWD_ADDRS_ST with next event of SERVER_IO_ERROR_EVT + /// + /// Selects the server to be used from the forward domain for the forward + /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes + /// the forward domain's server selection mechanism and then attempts to + /// select the next server. If next event is SERVER_IO_ERROR_EVT then the + /// handler simply attempts to select the next server. + /// + /// Transitions to: + /// - REPLACING_FWD_ADDRS_ST with next event of SERVER_SELECTED upon + /// successful server selection + /// + /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon + /// failure to select a server + /// + /// @throw SimpleAddTransactionError if upon entry next event is not + /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT. + void selectingFwdServerHandler(); + + /// @brief State handler for SELECTING_REV_SERVER_ST. + /// + /// Entered from: + /// - READY_ST with next event of SELECT_SERVER_EVT + /// - REPLACING_FWD_ADDRS_ST with next event of SELECT_SERVER_EVT + /// - REPLACING_REV_PTRS_ST with next event of SERVER_IO_ERROR_EVT + /// + /// Selects the server to be used from the reverse domain for the reverse + /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes + /// the reverse domain's server selection mechanism and then attempts to + /// select the next server. If next event is SERVER_IO_ERROR_EVT then the + /// handler simply attempts to select the next server. + /// + /// Transitions to: + /// - REPLACING_REV_PTRS_ST with next event of SERVER_SELECTED upon + /// successful server selection + /// + /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon + /// failure to select a server + /// + /// @throw SimpleAddTransactionError if upon entry next event is not + /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT. + void selectingRevServerHandler(); + + /// @brief State handler for REPLACING_FWD_ADDRS_ST. + /// + /// Entered from: + /// - SELECTING_FWD_SERVER with next event of SERVER_SELECTED_EVT + /// + /// Attempts to replace a forward DNS entry for a given FQDN. If this is + /// first invocation of the handler after transitioning into this state, + /// any previous update request context is deleted. If next event + /// is SERVER_SELECTED_EVT, the handler builds the forward add request, + /// schedules an asynchronous send via sendUpdate(), and returns. Note + /// that sendUpdate will post NOP_EVT as next event. + /// + /// Posting the NOP_EVT will cause runModel() to suspend execution of + /// the state model thus affecting a "wait" for the update IO to complete. + /// Update completion occurs via the DNSClient callback operator() method + /// inherited from NameChangeTransaction. When invoked this callback will + /// post a next event of IO_COMPLETED_EVT and then invoke runModel which + /// resumes execution of the state model. + /// + /// When the handler is invoked with a next event of IO_COMPLETED_EVT, + /// the DNS update status is checked and acted upon accordingly: + /// + /// Transitions to: + /// - SELECTING_REV_SERVER_ST with next event of SELECT_SERVER_EVT upon + /// successful addition and the request includes a reverse DNS update. + /// + /// - PROCESS_TRANS_OK_ST with next event of UPDATE_OK_EVT upon successful + /// addition and no reverse DNS update is required. + /// + /// - PROCESS_TRANS_FAILED_ST with next event of UPDATE_FAILED_EVT if the + /// DNS server rejected the update for any other reason or the IO completed + /// with an unrecognized status. + /// + /// - RE-ENTER this states with next event of SERVER_SELECTED_EVT_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has not been exhausted. + /// + /// - SELECTING_FWD_SERVER_ST with next event of SERVER_IO_ERROR_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has been exhausted. + /// + /// @throw SimpleAddTransactionError if upon entry next event is not + /// SERVER_SELECTED_EVT or IO_COMPLETE_EVT. + void replacingFwdAddrsHandler(); + + /// @brief State handler for REPLACING_REV_PTRS_ST. + /// + /// Entered from: + /// - SELECTING_REV_SERVER_ST with a next event of SERVER_SELECTED_EVT + /// + /// Attempts to delete and then add a reverse DNS entry for a given FQDN. + /// If this is first invocation of the handler after transitioning into + /// this state, any previous update request context is deleted. If next + /// event is SERVER_SELECTED_EVT, the handler builds the reverse replacement + /// add request, schedules an asynchronous send via sendUpdate(), and + /// returns. Note that sendUpdate will post NOP_EVT as next event. + /// + /// Posting the NOP_EVT will cause runModel() to suspend execution of + /// the state model thus affecting a "wait" for the update IO to complete. + /// Update completion occurs via the DNSClient callback operator() method + /// inherited from NameChangeTransaction. When invoked this callback will + /// post a next event of IO_COMPLETED_EVT and then invoke runModel which + /// resumes execution of the state model. + /// + /// When the handler is invoked with a next event of IO_COMPLETED_EVT, + /// the DNS update status is checked and acted upon accordingly: + /// + /// Transitions to: + /// - PROCESS_TRANS_OK_ST with a next event of UPDATE_OK_EVT upon + /// successful replacement. + /// + /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT If the + /// DNS server rejected the update for any reason or the IO completed + /// with an unrecognized status. + /// + /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has not been exhausted. + /// + /// - SELECTING_REV_SERVER_ST with next event of SERVER_IO_ERROR_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has been exhausted. + /// + /// @throw SimpleAddTransactionError if upon entry next event is not: + /// SERVER_SELECTED_EVT or IO_COMPLETED_EVT + void replacingRevPtrsHandler(); + + /// @brief State handler for PROCESS_TRANS_OK_ST. + /// + /// Entered from: + /// - REPLACING_FWD_ADDRS_ST with a next event of UPDATE_OK_EVT + /// - REPLACING_REV_PTRS_ST with a next event of UPDATE_OK_EVT + /// + /// Sets the transaction action status to indicate success and ends + /// model execution. + /// + /// Transitions to: + /// - END_ST with a next event of END_EVT. + /// + /// @throw SimpleAddTransactionError if upon entry next event is not: + /// UPDATE_OK_EVT + void processAddOkHandler(); + + /// @brief State handler for PROCESS_TRANS_FAILED_ST. + /// + /// Entered from: + /// - SELECTING_FWD_SERVER_ST with a next event of NO_MORE_SERVERS + /// - REPLACING_FWD_ADDRS_ST with a next event of UPDATE_FAILED_EVT + /// - SELECTING_REV_SERVER_ST with a next event of NO_MORE_SERVERS + /// - REPLACING_REV_PTRS_ST with a next event of UPDATE_FAILED_EVT + /// + /// Sets the transaction status to indicate failure and ends + /// model execution. + /// + /// Transitions to: + /// - END_ST with a next event of FAIL_EVT. + /// + /// @throw SimpleAddTransactionError if upon entry next event is not: + /// UPDATE_FAILED_EVT + void processAddFailedHandler(); + + /// @brief Builds a DNS request to add/replace a forward DNS entry for an + /// FQDN + /// + /// Constructs a DNS update request, based upon the NCR, for adding a + /// forward DNS mapping. Once constructed, the request is stored as + /// the transaction's DNS update request. + /// + /// Prerequisite RRsets: + /// - There are no prerequisites. + /// + /// Updates RRsets: + /// -# A delete of any existing PTR RRs for the lease address + /// -# A delete of any existing DHCID RRs for the lease address + /// -# An FQDN/IP RR addition (type A for IPv4, AAAA for IPv6) + /// -# An FQDN/DHCID RR addition (type DHCID) + /// + /// @throw This method does not throw but underlying methods may. + void buildReplaceFwdAddressRequest(); + + /// @brief Builds a DNS request to replace a reverse DNS entry for an FQDN + /// + /// Constructs a DNS update request, based upon the NCR, for replacing a + /// reverse DNS mapping. Once constructed, the request is stored as + /// the transaction's DNS update request. + /// + /// Prerequisite RRsets: + /// - There are no prerequisites. + /// + /// Updates RRsets: + /// -# A delete of any existing PTR RRs for the lease address + /// -# A delete of any existing DHCID RRs for the lease address + /// -# A PTR RR addition for the lease address and FQDN + /// -# A DHCID RR addition for the lease address and lease client DHCID + /// + /// @throw This method does not throw but underlying methods may. + void buildReplaceRevPtrsRequest(); +}; + +/// @brief Defines a pointer to a SimpleAddTransaction. +typedef boost::shared_ptr<SimpleAddTransaction> SimpleAddTransactionPtr; + +} // namespace isc::d2 +} // namespace isc +#endif diff --git a/src/bin/d2/simple_remove.cc b/src/bin/d2/simple_remove.cc new file mode 100644 index 0000000..48a446e --- /dev/null +++ b/src/bin/d2/simple_remove.cc @@ -0,0 +1,519 @@ +// Copyright (C) 2013-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/. + +#include <config.h> + +#include <d2/simple_remove.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/d2_log.h> + +#include <functional> + +namespace isc { +namespace d2 { + + +// SimpleRemoveTransaction states +const int SimpleRemoveTransaction::REMOVING_FWD_RRS_ST; +const int SimpleRemoveTransaction::REMOVING_REV_PTRS_ST; + +// SimpleRemoveTransaction events +// Currently SimpleRemoveTransaction does not define any events. + +SimpleRemoveTransaction:: +SimpleRemoveTransaction(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) { + if (ncr->getChangeType() != isc::dhcp_ddns::CHG_REMOVE) { + isc_throw (SimpleRemoveTransactionError, + "SimpleRemoveTransaction, request type must be CHG_REMOVE"); + } +} + +SimpleRemoveTransaction::~SimpleRemoveTransaction(){ +} + +void +SimpleRemoveTransaction::defineEvents() { + // Call superclass impl first. + NameChangeTransaction::defineEvents(); + + // Define SimpleRemoveTransaction events. + // Currently SimpleRemoveTransaction does not define any events. + // defineEvent(TBD_EVENT, "TBD_EVT"); +} + +void +SimpleRemoveTransaction::verifyEvents() { + // Call superclass implementation first to verify its events. These are + // events common to all transactions, and they must be defined. + // SELECT_SERVER_EVT + // SERVER_SELECTED_EVT + // SERVER_IO_ERROR_EVT + // NO_MORE_SERVERS_EVT + // IO_COMPLETED_EVT + // UPDATE_OK_EVT + // UPDATE_FAILED_EVT + NameChangeTransaction::verifyEvents(); + + // Verify SimpleRemoveTransaction events by attempting to fetch them. + // Currently SimpleRemoveTransaction does not define any events. + // getEvent(TBD_EVENT); +} + +void +SimpleRemoveTransaction::defineStates() { + // Call superclass impl first. + NameChangeTransaction::defineStates(); + + // Define SimpleRemoveTransaction states. + defineState(READY_ST, "READY_ST", + std::bind(&SimpleRemoveTransaction::readyHandler, this)); + + defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST", + std::bind(&SimpleRemoveTransaction::selectingFwdServerHandler, + this)); + + defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST", + std::bind(&SimpleRemoveTransaction::selectingRevServerHandler, + this)); + + defineState(REMOVING_FWD_RRS_ST, "REMOVING_FWD_RRS_ST", + std::bind(&SimpleRemoveTransaction::removingFwdRRsHandler, + this)); + + defineState(REMOVING_REV_PTRS_ST, "REMOVING_REV_PTRS_ST", + std::bind(&SimpleRemoveTransaction::removingRevPtrsHandler, + this)); + + defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST", + std::bind(&SimpleRemoveTransaction::processRemoveOkHandler, + this)); + + defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST", + std::bind(&SimpleRemoveTransaction::processRemoveFailedHandler, + this)); +} + +void +SimpleRemoveTransaction::verifyStates() { + // Call superclass implementation first to verify its states. These are + // states common to all transactions, and they must be defined. + // READY_ST + // SELECTING_FWD_SERVER_ST + // SELECTING_REV_SERVER_ST + // PROCESS_TRANS_OK_ST + // PROCESS_TRANS_FAILED_ST + NameChangeTransaction::verifyStates(); + + // Verify SimpleRemoveTransaction states by attempting to fetch them. + getStateInternal(REMOVING_FWD_RRS_ST); + getStateInternal(REMOVING_REV_PTRS_ST); +} + +void +SimpleRemoveTransaction::readyHandler() { + switch(getNextEvent()) { + case START_EVT: + if (getForwardDomain()) { + // Request includes a forward change, do that first. + transition(SELECTING_FWD_SERVER_ST, SELECT_SERVER_EVT); + } else { + // Reverse change only, transition accordingly. + transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT); + } + + break; + default: + // Event is invalid. + isc_throw(SimpleRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +SimpleRemoveTransaction::selectingFwdServerHandler() { + switch(getNextEvent()) { + case SELECT_SERVER_EVT: + // First time through for this transaction, so initialize server + // selection. + initServerSelection(getForwardDomain()); + break; + case SERVER_IO_ERROR_EVT: + // We failed to communicate with current server. Attempt to select + // another one below. + break; + default: + // Event is invalid. + isc_throw(SimpleRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } + + // Select the next server from the list of forward servers. + if (selectNextServer()) { + // We have a server to try. + transition(REMOVING_FWD_RRS_ST, SERVER_SELECTED_EVT); + } else { + // Server list is exhausted, so fail the transaction. + transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT); + } +} + +void +SimpleRemoveTransaction::removingFwdRRsHandler() { + if (doOnEntry()) { + // Clear the update attempts count on initial transition. + clearUpdateAttempts(); + } + + switch(getNextEvent()) { + case UPDATE_OK_EVT: + case SERVER_SELECTED_EVT: + try { + clearDnsUpdateRequest(); + buildRemoveFwdRRsRequest(); + } catch (const std::exception& ex) { + // While unlikely, the build might fail if we have invalid + // data. Should that be the case, we need to fail the + // transaction. + LOG_ERROR(d2_to_dns_logger, + DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE) + .arg(getRequestId()) + .arg(getNcr()->toText()) + .arg(ex.what()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } + + // Call sendUpdate() to initiate the async send. Note it also sets + // next event to NOP_EVT. + sendUpdate("Forward RR Remove"); + break; + + case IO_COMPLETED_EVT: { + switch (getDnsUpdateStatus()) { + case DNSClient::SUCCESS: { + // We successfully received a response packet from the server. + // The RCODE will be based on a value-dependent RRset search, + // see RFC 2136 section 3.2.3/3.2.4. + const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode(); + if ((rcode == dns::Rcode::NOERROR()) || + (rcode == dns::Rcode::NXRRSET())) { + // We were able to remove them or they were not there ( + // Rcode of NXRRSET means there are no matching RRsets). + // In either case, we consider it success and mark it as done. + setForwardChangeCompleted(true); + + // If request calls for reverse update then do that next, + // otherwise we can process ok. + if (getReverseDomain()) { + transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT); + } else { + transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT); + } + } else { + // Any other value means cease. + // If we get not authorized should try the next server in + // the list? @todo This needs some discussion perhaps. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()) + .arg(rcode.getCode()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + } + + break; + } + + case DNSClient::TIMEOUT: + case DNSClient::OTHER: + // We couldn't send to the current server, log it and set up + // to select the next server for a retry. + // @note For now we treat OTHER as an IO error like TIMEOUT. It + // is not entirely clear if this is accurate. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR) + .arg(getRequestId()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + retryTransition(SELECTING_FWD_SERVER_ST); + break; + + case DNSClient::INVALID_RESPONSE: + // A response was received but was corrupt. Retry it like an IO + // error. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()); + + retryTransition(SELECTING_FWD_SERVER_ST); + break; + + default: + // Any other value and we will fail this transaction, something + // bigger is wrong. + LOG_ERROR(d2_to_dns_logger, + DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS) + .arg(getRequestId()) + .arg(getDnsUpdateStatus()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } // end switch on dns_status + + break; + } // end case IO_COMPLETE_EVT + + default: + // Event is invalid. + isc_throw(SimpleRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +SimpleRemoveTransaction::selectingRevServerHandler() { + switch(getNextEvent()) { + case SELECT_SERVER_EVT: + // First time through for this transaction, so initialize server + // selection. + initServerSelection(getReverseDomain()); + break; + case SERVER_IO_ERROR_EVT: + // We failed to communicate with current server. Attempt to select + // another one below. + break; + default: + // Event is invalid. + isc_throw(SimpleRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } + + // Select the next server from the list of forward servers. + if (selectNextServer()) { + // We have a server to try. + transition(REMOVING_REV_PTRS_ST, SERVER_SELECTED_EVT); + } else { + // Server list is exhausted, so fail the transaction. + transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT); + } +} + + +void +SimpleRemoveTransaction::removingRevPtrsHandler() { + if (doOnEntry()) { + // Clear the update attempts count on initial transition. + clearUpdateAttempts(); + } + + switch(getNextEvent()) { + case SERVER_SELECTED_EVT: + try { + clearDnsUpdateRequest(); + buildRemoveRevPtrsRequest(); + } catch (const std::exception& ex) { + // While unlikely, the build might fail if we have invalid + // data. Should that be the case, we need to fail the + // transaction. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE) + .arg(getRequestId()) + .arg(getNcr()->toText()) + .arg(ex.what()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } + + // Call sendUpdate() to initiate the async send. Note it also sets + // next event to NOP_EVT. + sendUpdate("Reverse Remove"); + break; + + case IO_COMPLETED_EVT: { + switch (getDnsUpdateStatus()) { + case DNSClient::SUCCESS: { + // We successfully received a response packet from the server. + // The RCODE will be based on a value-dependent RRset search, + // see RFC 2136 section 3.2.3/3.2.4. + const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode(); + if ((rcode == dns::Rcode::NOERROR()) || + (rcode == dns::Rcode::NXRRSET())) { + // We were able to remove the reverse mapping or they were + // not there (Rcode of NXRRSET means there are no matching + // RRsets). In either case, mark it as done. + setReverseChangeCompleted(true); + transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT); + } else { + // Per RFC4703 any other value means cease. + // If we get not authorized should try the next server in + // the list? @todo This needs some discussion perhaps. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_REJECTED) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()) + .arg(rcode.getCode()); + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + } + + break; + } + + case DNSClient::TIMEOUT: + case DNSClient::OTHER: + // We couldn't send to the current server, log it and set up + // to select the next server for a retry. + // @note For now we treat OTHER as an IO error like TIMEOUT. It + // is not entirely clear if this is accurate. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_IO_ERROR) + .arg(getRequestId()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + // If we are out of retries on this server, we go back and start + // all over on a new server. + retryTransition(SELECTING_REV_SERVER_ST); + break; + + case DNSClient::INVALID_RESPONSE: + // A response was received but was corrupt. Retry it like an IO + // error. + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT) + .arg(getRequestId()) + .arg(getCurrentServer()->toText()) + .arg(getNcr()->getFqdn()); + + // If we are out of retries on this server, we go back and start + // all over on a new server. + retryTransition(SELECTING_REV_SERVER_ST); + break; + + default: + // Any other value and we will fail this transaction, something + // bigger is wrong. + LOG_ERROR(d2_to_dns_logger, + DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS) + .arg(getRequestId()) + .arg(getDnsUpdateStatus()) + .arg(getNcr()->getFqdn()) + .arg(getCurrentServer()->toText()); + + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + break; + } // end switch on dns_status + + break; + } // end case IO_COMPLETE_EVT + + default: + // Event is invalid. + isc_throw(SimpleRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + + +void +SimpleRemoveTransaction::processRemoveOkHandler() { + switch(getNextEvent()) { + case UPDATE_OK_EVT: + LOG_INFO(d2_to_dns_logger, DHCP_DDNS_REMOVE_SUCCEEDED) + .arg(getRequestId()) + .arg(getNcr()->toText()); + setNcrStatus(dhcp_ddns::ST_COMPLETED); + endModel(); + break; + default: + // Event is invalid. + isc_throw(SimpleRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +SimpleRemoveTransaction::processRemoveFailedHandler() { + switch(getNextEvent()) { + case UPDATE_FAILED_EVT: + case NO_MORE_SERVERS_EVT: + case SERVER_IO_ERROR_EVT: + setNcrStatus(dhcp_ddns::ST_FAILED); + LOG_ERROR(d2_to_dns_logger, DHCP_DDNS_REMOVE_FAILED) + .arg(getRequestId()) + .arg(transactionOutcomeString()); + endModel(); + break; + default: + // Event is invalid. + isc_throw(SimpleRemoveTransactionError, + "Wrong event for context: " << getContextStr()); + } +} + +void +SimpleRemoveTransaction::buildRemoveFwdRRsRequest() { + // Construct an empty request. + D2UpdateMessagePtr request = prepNewRequest(getForwardDomain()); + + // There are no pre-requisites. + + // Build the Update Section + // Construct dns::Name from NCR fqdn. + dns::Name fqdn(dns::Name(getNcr()->getFqdn())); + + // Build the Update Section. + + // Create the FQDN/IP 'delete' RR and add it to update section. + dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::ANY(), + getAddressRRType(), dns::RRTTL(0))); + + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Create the DHCID 'delete' RR and add it to the update section. + update.reset(new dns::RRset(fqdn, dns::RRClass::ANY(), + dns::RRType::DHCID(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Set the transaction's update request to the new request. + setDnsUpdateRequest(request); +} + +void +SimpleRemoveTransaction::buildRemoveRevPtrsRequest() { + // Construct an empty request. + D2UpdateMessagePtr request = prepNewRequest(getReverseDomain()); + + // Create the reverse IP address "FQDN". + std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress()); + dns::Name rev_ip(rev_addr); + + // There are no pre-requisites. + + // Build the Update section. + + // Create the FQDN/IP PTR 'delete' RR for this IP and add it to + // the update section. + dns::RRsetPtr update(new dns::RRset(rev_ip, dns::RRClass::ANY(), + dns::RRType::PTR(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Create the DHCID 'delete' RR and add it to the update section. + update.reset(new dns::RRset(rev_ip, dns::RRClass::ANY(), + dns::RRType::DHCID(), dns::RRTTL(0))); + request->addRRset(D2UpdateMessage::SECTION_UPDATE, update); + + // Set the transaction's update request to the new request. + setDnsUpdateRequest(request); +} + +} // namespace isc::d2 +} // namespace isc diff --git a/src/bin/d2/simple_remove.h b/src/bin/d2/simple_remove.h new file mode 100644 index 0000000..957cb13 --- /dev/null +++ b/src/bin/d2/simple_remove.h @@ -0,0 +1,351 @@ +// 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 SIMPLE_REMOVE_H +#define SIMPLE_REMOVE_H + +/// @file nc_remove.h This file defines the class SimpleRemoveTransaction. + +#include <d2srv/nc_trans.h> + +namespace isc { +namespace d2 { + +/// @brief Thrown if the SimpleRemoveTransaction encounters a general error. +class SimpleRemoveTransactionError : public isc::Exception { +public: + SimpleRemoveTransactionError(const char* file, size_t line, + const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Embodies the "life-cycle" required to carry out a DDNS Remove update. +/// +/// SimpleRemoveTransaction implements a state machine for removing a forward +/// and/or reverse DNS mappings. This state machine follows a basic +/// removal scheme, that does not attempt to avoid conflicts between updating +/// clients. The logic may be paraphrased as follows: +/// +/// @code +/// +/// If the request includes a forward change: +/// Select a forward server +/// Send the server a request to remove client's specific forward address +/// RR and DHCID RR. +/// If it does not succeed +/// abandon the update +/// +/// If the request includes a reverse change: +/// Select a reverse server +/// Send a server a request to delete the matching PTR and DHCID RRs. +/// +/// @endcode +/// +/// This class derives from NameChangeTransaction from which it inherits +/// states, events, and methods common to NameChangeRequest processing. +class SimpleRemoveTransaction : public NameChangeTransaction { +public: + + //@{ Additional states needed for SimpleRemove state model. + /// @brief State that attempts to remove FQDN/IP and DHCID RRs for an FQDN + static const int REMOVING_FWD_RRS_ST = NCT_DERIVED_STATE_MIN + 2; + + /// @brief State that attempts to remove reverse PTR records + static const int REMOVING_REV_PTRS_ST = NCT_DERIVED_STATE_MIN + 3; + //@} + + //@{ Additional events needed for SimpleRemove state model. + /// @brief Event sent when replace attempt to fails with address not in use. + /// @todo Currently none have been identified. + //@} + + /// @brief Constructor + /// + /// Instantiates an Remove 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 pointer to the configuration manager + /// + /// @throw SimpleRemoveTransaction error if given request is not a CHG_REMOVE, + /// NameChangeTransaction error for base class construction errors. + SimpleRemoveTransaction(asiolink::IOServicePtr& io_service, + dhcp_ddns::NameChangeRequestPtr& ncr, + DdnsDomainPtr& forward_domain, + DdnsDomainPtr& reverse_domain, + D2CfgMgrPtr& cfg_mgr); + + /// @brief Destructor + virtual ~SimpleRemoveTransaction(); + +protected: + /// @brief Adds events defined by SimpleRemoveTransaction to the event set. + /// + /// Invokes NameChangeTransaction's implementation and then defines the + /// events unique to NCR Remove transaction processing. + /// + /// @throw StateModelError if an event definition is invalid or a duplicate. + virtual void defineEvents(); + + /// @brief Validates the contents of the set of events. + /// + /// Invokes NameChangeTransaction's implementation and then verifies the + /// Remove transaction's events. This tests that the needed events are in + /// the event dictionary. + /// + /// @throw StateModelError if an event value is undefined. + virtual void verifyEvents(); + + /// @brief Adds states defined by SimpleRemoveTransaction to the state set. + /// + /// Invokes NameChangeTransaction's implementation and then defines the + /// states unique to NCR Remove transaction processing. + /// + /// @throw StateModelError if an state definition is invalid or a duplicate. + virtual void defineStates(); + + /// @brief Validates the contents of the set of states. + /// + /// Invokes NameChangeTransaction's implementation and then verifies the + /// Remove transaction's states. This tests that the needed states are in + /// the state dictionary. + /// + /// @throw StateModelError if an event value is undefined. + virtual void verifyStates(); + + /// @brief State handler for READY_ST. + /// + /// Entered from: + /// - INIT_ST with next event of START_EVT + /// + /// The READY_ST is the state the model transitions into when the inherited + /// method, startTransaction() is invoked. This handler, therefore, is the + /// entry point into the state model execution. Its primary task is to + /// determine whether to start with a forward DNS change or a reverse DNS + /// change. + /// + /// Transitions to: + /// - SELECTING_FWD_SERVER_ST with next event of SERVER_SELECT_ST if request + /// includes a forward change. + /// + /// - SELECTING_REV_SERVER_ST with next event of SERVER_SELECT_ST if request + /// includes only a reverse change. + /// + /// @throw SimpleRemoveTransactionError if upon entry next event is not + /// START_EVT. + void readyHandler(); + + /// @brief State handler for SELECTING_FWD_SERVER_ST. + /// + /// Entered from: + /// - READY_ST with next event of SELECT_SERVER_EVT + /// - REMOVING_FWD_RRS_ST with next event of SERVER_IO_ERROR_EVT + /// + /// Selects the server to be used from the forward domain for the forward + /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes + /// the forward domain's server selection mechanism and then attempts to + /// select the next server. If next event is SERVER_IO_ERROR_EVT then the + /// handler simply attempts to select the next server. + /// + /// Transitions to: + /// - REMOVING_FWD_RRS_ST with next event of SERVER_SELECTED upon + /// successful server selection + /// + /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon + /// failure to select a server + /// + /// @throw SimpleRemoveTransactionError if upon entry next event is not + /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT. + void selectingFwdServerHandler(); + + /// @brief State handler for SELECTING_REV_SERVER_ST. + /// + /// Entered from: + /// - READY_ST with next event of SELECT_SERVER_EVT + /// - REMOVING_FWD_RRS_ST with next event of SELECT_SERVER_EVT + /// - REMOVING_REV_PTRS_ST with next event of SERVER_IO_ERROR_EVT + /// + /// Selects the server to be used from the reverse domain for the reverse + /// DNS update. If next event is SELECT_SERVER_EVT the handler initializes + /// the reverse domain's server selection mechanism and then attempts to + /// select the next server. If next event is SERVER_IO_ERROR_EVT then the + /// handler simply attempts to select the next server. + /// + /// Transitions to: + /// - REMOVING_REV_PTRS_ST with next event of SERVER_SELECTED upon + /// successful server selection + /// + /// - PROCESS_TRANS_FAILED with next event of NO_MORE_SERVERS_EVT upon + /// failure to select a server + /// + /// @throw SimpleRemoveTransactionError if upon entry next event is not + /// SELECT_SERVER_EVT or SERVER_IO_ERROR_EVT. + void selectingRevServerHandler(); + + /// @brief State handler for REMOVING_FWD_RRS_ST. + /// + /// Entered from: + /// - SELECTING_FWD_SERVER_ST with next event SEVER_SELECTED_EVT + /// + /// Attempts to delete any remaining RRs associated with the given FQDN + /// such as the DHCID RR. If this is first invocation of the handler after + /// transitioning into this state, any previous update request context is + /// deleted and the handler builds the forward remove request. It then + /// schedules an asynchronous send via sendUpdate(), + /// and returns. Note that sendUpdate will post NOP_EVT as the next event. + /// + /// Posting the NOP_EVT will cause runModel() to suspend execution of + /// the state model thus affecting a "wait" for the update IO to complete. + /// Update completion occurs via the DNSClient callback operator() method + /// inherited from NameChangeTransaction. When invoked this callback will + /// post a next event of IO_COMPLETED_EVT and then invoke runModel which + /// resumes execution of the state model. + /// + /// When the handler is invoked with a next event of IO_COMPLETED_EVT, + /// the DNS update status is checked and acted upon accordingly: + /// + /// Transitions to: + /// - SELECTING_REV_SERVER_ST with a next event of SELECT_SERVER_EVT upon + /// successful completion and the request includes a reverse DNS update. + /// + /// - PROCESS_TRANS_OK_ST with next event of UPDATE_OK_EVT upon successful + /// completion and the request does not include a reverse DNS update. + /// + /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT if the + /// DNS server rejected the update for any other reason or the IO completed + /// with an unrecognized status. + /// + /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has not been exhausted. + /// + /// - PROCESS_TRANS_FAILED_ST with a next event of SERVER_IO_ERROR_EVT if + /// there we have reached maximum number of retries without success on the + /// current server. + /// + /// @throw SimpleRemoveTransactionError if upon entry next event is not: + /// UPDATE_OK_EVT or IO_COMPLETE_EVT + void removingFwdRRsHandler(); + + /// @brief State handler for REMOVING_REV_PTRS_ST. + /// + /// Entered from: + /// - SELECTING_REV_SERVER_ST with a next event of SERVER_SELECTED_EVT + /// + /// Attempts to delete a reverse DNS entry for a given FQDN. If this is + /// first invocation of the handler after transitioning into this state, + /// any previous update request context is deleted. If next event is + /// SERVER_SELECTED_EVT, the handler builds the reverse remove request, + /// schedules an asynchronous send via sendUpdate(), and then returns. + /// Note that sendUpdate will post NOP_EVT as next event. + /// + /// Posting the NOP_EVT will cause runModel() to suspend execution of + /// the state model thus affecting a "wait" for the update IO to complete. + /// Update completion occurs via the DNSClient callback operator() method + /// inherited from NameChangeTransaction. When invoked this callback will + /// post a next event of IO_COMPLETED_EVT and then invoke runModel which + /// resumes execution of the state model. + /// + /// When the handler is invoked with a next event of IO_COMPLETED_EVT, + /// the DNS update status is checked and acted upon accordingly: + /// + /// Transitions to: + /// - PROCESS_TRANS_OK_ST with a next event of UPDATE_OK_EVT upon + /// successful completion. + /// + /// - PROCESS_TRANS_FAILED_ST with a next event of UPDATE_FAILED_EVT If the + /// DNS server rejected the update for any reason or the IO completed + /// with an unrecognized status. + /// + /// - RE-ENTER this state with a next event of SERVER_SELECTED_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has not been exhausted. + /// + /// - SELECTING_REV_SERVER_ST with next event of SERVER_IO_ERROR_EVT if + /// there was an IO error communicating with the server and the number of + /// per server retries has been exhausted. + /// + /// @throw SimpleRemoveTransactionError if upon entry next event is not: + /// SERVER_SELECTED_EVT or IO_COMPLETED_EVT + void removingRevPtrsHandler(); + + /// @brief State handler for PROCESS_TRANS_OK_ST. + /// + /// Entered from: + /// - REMOVING_FWD_RRS_ST with a next event of UPDATE_OK_EVT + /// - REMOVING_REV_PTRS_ST with a next event of UPDATE_OK_EVT + /// + /// Sets the transaction action status to indicate success and ends + /// model execution. + /// + /// Transitions to: + /// - END_ST with a next event of END_EVT. + /// + /// @throw SimpleRemoveTransactionError if upon entry next event is not: + /// UPDATE_OK_EVT + void processRemoveOkHandler(); + + /// @brief State handler for PROCESS_TRANS_FAILED_ST. + /// + /// Entered from: + /// - SELECTING_FWD_SERVER_ST with a next event of NO_MORE_SERVERS + /// - REMOVING_FWD_RRS_ST with a next event of UPDATE_FAILED_EVT + /// - REMOVING_FWD_RRS_ST with a next event of SERVER_IO_ERROR_EVT + /// - SELECTING_REV_SERVER_ST with a next event of NO_MORE_SERVERS + /// - REMOVING_REV_PTRS_ST with a next event of UPDATE_FAILED_EVT + /// + /// Sets the transaction status to indicate failure and ends + /// model execution. + /// + /// Transitions to: + /// - END_ST with a next event of FAIL_EVT. + /// + /// @throw SimpleRemoveTransactionError if upon entry next event is not: + /// UPDATE_FAILED_EVT + void processRemoveFailedHandler(); + + /// @brief Builds a DNS request to remove all forward DNS RRs for a FQDN. + /// + /// Constructs a DNS update request, based upon the NCR, to remove the + /// forward DNS (A or AAAA) RR and DHCID RR. Once constructed, the request + /// is stored as the transaction's DNS update request. + /// + /// Prerequisite RRsets: + /// - None + /// + /// Updates RRsets: + /// -# A delete of FQDN/IP RR for the FQDN + /// -# A delete of DHCID RR for the FQDN + /// + /// @throw This method does not throw but underlying methods may. + void buildRemoveFwdRRsRequest(); + + /// @brief Builds a DNS request to remove a reverse DNS entry for a FQDN + /// + /// Constructs a DNS update request, based upon the NCR, for removing a + /// reverse DNS mapping. Once constructed, the request is stored as + /// the transaction's DNS update request. + /// + /// Prerequisite RRsets: + /// - None + /// + /// Updates RRsets: + /// -# A delete of PTR RR for the IP + /// -# A delete of DHCID RR for the IP + /// + /// @throw This method does not throw but underlying methods may. + void buildRemoveRevPtrsRequest(); +}; + +/// @brief Defines a pointer to a SimpleRemoveTransaction. +typedef boost::shared_ptr<SimpleRemoveTransaction> SimpleRemoveTransactionPtr; + + +} // namespace isc::d2 +} // namespace isc +#endif diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am new file mode 100644 index 0000000..0da992b --- /dev/null +++ b/src/bin/d2/tests/Makefile.am @@ -0,0 +1,133 @@ +SUBDIRS = . + +# Add to the tarball: +EXTRA_DIST = +EXTRA_DIST += testdata/d2_cfg_tests.json +EXTRA_DIST += testdata/get_config.json + +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +# Shell tests +SHTESTS = d2_process_tests.sh + +# Run shell tests on "make check". +check_SCRIPTS = $(SHTESTS) +TESTS = $(SHTESTS) + +# As with every file generated by ./configure, clean them up when running +# "make distclean", but not on "make clean". +DISTCLEANFILES = $(SHTESTS) +DISTCLEANFILES += d2_process_tests.sh +DISTCLEANFILES += test_data_files_config.h +DISTCLEANFILES += test_callout_libraries.h +DISTCLEANFILES += test_configured_libraries.h + +# Don't install shell tests. +noinst_SCRIPTS = $(SHTESTS) + +if HAVE_GTEST + +# C++ tests +PROGRAM_TESTS = d2_unittests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/d2/tests\" +AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" +AM_CPPFLAGS += -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples/ddns\" +AM_CPPFLAGS += -DSYNTAX_FILE=\"$(abs_srcdir)/../d2_parser.yy\" + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +d2_unittests_SOURCES = d2_unittests.cc +d2_unittests_SOURCES += d2_process_unittests.cc +d2_unittests_SOURCES += d2_cfg_mgr_unittests.cc +d2_unittests_SOURCES += d2_queue_mgr_unittests.cc +d2_unittests_SOURCES += d2_update_mgr_unittests.cc +d2_unittests_SOURCES += nc_add_unittests.cc +d2_unittests_SOURCES += nc_remove_unittests.cc +d2_unittests_SOURCES += d2_controller_unittests.cc +d2_unittests_SOURCES += d2_simple_parser_unittest.cc +d2_unittests_SOURCES += parser_unittest.cc parser_unittest.h +d2_unittests_SOURCES += get_config_unittest.cc +d2_unittests_SOURCES += d2_command_unittest.cc +d2_unittests_SOURCES += simple_add_unittests.cc +d2_unittests_SOURCES += simple_remove_unittests.cc + +d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +d2_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +if HAVE_MYSQL +d2_unittests_LDFLAGS += $(MYSQL_LIBS) +endif +if HAVE_PGSQL +d2_unittests_LDFLAGS += $(PGSQL_LIBS) +endif +d2_unittests_LDFLAGS += $(GTEST_LDFLAGS) + +d2_unittests_LDADD = $(top_builddir)/src/bin/d2/libd2.la +d2_unittests_LDADD += $(top_builddir)/src/lib/d2srv/testutils/libd2srvtest.la +d2_unittests_LDADD += $(top_builddir)/src/lib/d2srv/libkea-d2srv.la +d2_unittests_LDADD += $(top_builddir)/src/lib/process/testutils/libprocesstest.la +d2_unittests_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la +d2_unittests_LDADD += $(top_builddir)/src/lib/process/libkea-process.la +d2_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la +d2_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la +d2_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libkea-asiodns.la +d2_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la +d2_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +d2_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la +d2_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +d2_unittests_LDADD += $(top_builddir)/src/lib/database/libkea-database.la +d2_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +d2_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +d2_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +d2_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +d2_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +d2_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +d2_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +d2_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +d2_unittests_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) +d2_unittests_LDADD += $(BOOST_LIBS) $(GTEST_LDADD) + +# The basic callout library - contains standard callouts +libcallout_la_SOURCES = callout_library.cc +libcallout_la_CXXFLAGS = $(AM_CXXFLAGS) +libcallout_la_CPPFLAGS = $(AM_CPPFLAGS) +libcallout_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libcallout_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +libcallout_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libcallout_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere + +# The d2_srv_configured callout library +libconfigured_la_SOURCES = configured_library.cc +libconfigured_la_CXXFLAGS = $(AM_CXXFLAGS) +libconfigured_la_CPPFLAGS = $(AM_CPPFLAGS) +libconfigured_la_LIBADD = $(top_builddir)/src/lib/hooks/libkea-hooks.la +libconfigured_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libconfigured_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libconfigured_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libconfigured_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libconfigured_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libconfigured_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) +libconfigured_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere + +noinst_LTLIBRARIES = libcallout.la libconfigured.la + +nodist_d2_unittests_SOURCES = +nodist_d2_unittests_SOURCES += test_data_files_config.h +nodist_d2_unittests_SOURCES += test_callout_libraries.h +nodist_d2_unittests_SOURCES += test_configured_libraries.h + +# Run C++ tests on "make check". +TESTS += $(PROGRAM_TESTS) + +# Don't install C++ tests. +noinst_PROGRAMS = $(PROGRAM_TESTS) + +endif diff --git a/src/bin/d2/tests/Makefile.in b/src/bin/d2/tests/Makefile.in new file mode 100644 index 0000000..74bc717 --- /dev/null +++ b/src/bin/d2/tests/Makefile.in @@ -0,0 +1,1427 @@ +# 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 = $(SHTESTS) $(am__EXEEXT_2) +@HAVE_GTEST_TRUE@@HAVE_MYSQL_TRUE@am__append_1 = $(MYSQL_LIBS) +@HAVE_GTEST_TRUE@@HAVE_PGSQL_TRUE@am__append_2 = $(PGSQL_LIBS) + +# Run C++ tests on "make check". +@HAVE_GTEST_TRUE@am__append_3 = $(PROGRAM_TESTS) +@HAVE_GTEST_TRUE@noinst_PROGRAMS = $(am__EXEEXT_1) +subdir = src/bin/d2/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 = d2_process_tests.sh test_callout_libraries.h \ + test_configured_libraries.h test_data_files_config.h +CONFIG_CLEAN_VPATH_FILES = +@HAVE_GTEST_TRUE@am__EXEEXT_1 = d2_unittests$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +@HAVE_GTEST_TRUE@libcallout_la_DEPENDENCIES = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la +am__libcallout_la_SOURCES_DIST = callout_library.cc +@HAVE_GTEST_TRUE@am_libcallout_la_OBJECTS = \ +@HAVE_GTEST_TRUE@ libcallout_la-callout_library.lo +libcallout_la_OBJECTS = $(am_libcallout_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 = +libcallout_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libcallout_la_CXXFLAGS) $(CXXFLAGS) $(libcallout_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@HAVE_GTEST_TRUE@am_libcallout_la_rpath = +am__DEPENDENCIES_1 = +@HAVE_GTEST_TRUE@libconfigured_la_DEPENDENCIES = $(top_builddir)/src/lib/hooks/libkea-hooks.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/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) +am__libconfigured_la_SOURCES_DIST = configured_library.cc +@HAVE_GTEST_TRUE@am_libconfigured_la_OBJECTS = \ +@HAVE_GTEST_TRUE@ libconfigured_la-configured_library.lo +libconfigured_la_OBJECTS = $(am_libconfigured_la_OBJECTS) +libconfigured_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libconfigured_la_CXXFLAGS) $(CXXFLAGS) \ + $(libconfigured_la_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_GTEST_TRUE@am_libconfigured_la_rpath = +am__d2_unittests_SOURCES_DIST = d2_unittests.cc \ + d2_process_unittests.cc d2_cfg_mgr_unittests.cc \ + d2_queue_mgr_unittests.cc d2_update_mgr_unittests.cc \ + nc_add_unittests.cc nc_remove_unittests.cc \ + d2_controller_unittests.cc d2_simple_parser_unittest.cc \ + parser_unittest.cc parser_unittest.h get_config_unittest.cc \ + d2_command_unittest.cc simple_add_unittests.cc \ + simple_remove_unittests.cc +@HAVE_GTEST_TRUE@am_d2_unittests_OBJECTS = \ +@HAVE_GTEST_TRUE@ d2_unittests-d2_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-d2_process_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-d2_cfg_mgr_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-d2_queue_mgr_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-d2_update_mgr_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-nc_add_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-nc_remove_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-d2_controller_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-d2_simple_parser_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-parser_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-get_config_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-d2_command_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-simple_add_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ d2_unittests-simple_remove_unittests.$(OBJEXT) +nodist_d2_unittests_OBJECTS = +d2_unittests_OBJECTS = $(am_d2_unittests_OBJECTS) \ + $(nodist_d2_unittests_OBJECTS) +@HAVE_GTEST_TRUE@d2_unittests_DEPENDENCIES = \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/bin/d2/libd2.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/d2srv/testutils/libd2srvtest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/d2srv/libkea-d2srv.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/testutils/libprocesstest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.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/testutils/libkea-testutils.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/database/libkea-database.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.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/hooks/libkea-hooks.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__DEPENDENCIES_1) +d2_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(d2_unittests_LDFLAGS) $(LDFLAGS) -o $@ +SCRIPTS = $(noinst_SCRIPTS) +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)/d2_unittests-d2_cfg_mgr_unittests.Po \ + ./$(DEPDIR)/d2_unittests-d2_command_unittest.Po \ + ./$(DEPDIR)/d2_unittests-d2_controller_unittests.Po \ + ./$(DEPDIR)/d2_unittests-d2_process_unittests.Po \ + ./$(DEPDIR)/d2_unittests-d2_queue_mgr_unittests.Po \ + ./$(DEPDIR)/d2_unittests-d2_simple_parser_unittest.Po \ + ./$(DEPDIR)/d2_unittests-d2_unittests.Po \ + ./$(DEPDIR)/d2_unittests-d2_update_mgr_unittests.Po \ + ./$(DEPDIR)/d2_unittests-get_config_unittest.Po \ + ./$(DEPDIR)/d2_unittests-nc_add_unittests.Po \ + ./$(DEPDIR)/d2_unittests-nc_remove_unittests.Po \ + ./$(DEPDIR)/d2_unittests-parser_unittest.Po \ + ./$(DEPDIR)/d2_unittests-simple_add_unittests.Po \ + ./$(DEPDIR)/d2_unittests-simple_remove_unittests.Po \ + ./$(DEPDIR)/libcallout_la-callout_library.Plo \ + ./$(DEPDIR)/libconfigured_la-configured_library.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 = $(libcallout_la_SOURCES) $(libconfigured_la_SOURCES) \ + $(d2_unittests_SOURCES) $(nodist_d2_unittests_SOURCES) +DIST_SOURCES = $(am__libcallout_la_SOURCES_DIST) \ + $(am__libconfigured_la_SOURCES_DIST) \ + $(am__d2_unittests_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +@HAVE_GTEST_TRUE@am__EXEEXT_2 = $(am__EXEEXT_1) +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/d2_process_tests.sh.in \ + $(srcdir)/test_callout_libraries.h.in \ + $(srcdir)/test_configured_libraries.h.in \ + $(srcdir)/test_data_files_config.h.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 = . + +# Add to the tarball: +EXTRA_DIST = testdata/d2_cfg_tests.json testdata/get_config.json +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + + +# Shell tests +SHTESTS = d2_process_tests.sh + +# Run shell tests on "make check". +check_SCRIPTS = $(SHTESTS) + +# As with every file generated by ./configure, clean them up when running +# "make distclean", but not on "make clean". +DISTCLEANFILES = $(SHTESTS) d2_process_tests.sh \ + test_data_files_config.h test_callout_libraries.h \ + test_configured_libraries.h + +# Don't install shell tests. +noinst_SCRIPTS = $(SHTESTS) + +# C++ tests +@HAVE_GTEST_TRUE@PROGRAM_TESTS = d2_unittests +@HAVE_GTEST_TRUE@AM_CPPFLAGS = -I$(top_srcdir)/src/lib \ +@HAVE_GTEST_TRUE@ -I$(top_builddir)/src/lib \ +@HAVE_GTEST_TRUE@ -I$(top_srcdir)/src/bin \ +@HAVE_GTEST_TRUE@ -I$(top_builddir)/src/bin $(BOOST_INCLUDES) \ +@HAVE_GTEST_TRUE@ -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/d2/tests\" \ +@HAVE_GTEST_TRUE@ -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" \ +@HAVE_GTEST_TRUE@ -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples/ddns\" \ +@HAVE_GTEST_TRUE@ -DSYNTAX_FILE=\"$(abs_srcdir)/../d2_parser.yy\" +@HAVE_GTEST_TRUE@AM_CXXFLAGS = $(KEA_CXXFLAGS) +@HAVE_GTEST_TRUE@@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static +@HAVE_GTEST_TRUE@d2_unittests_SOURCES = d2_unittests.cc \ +@HAVE_GTEST_TRUE@ d2_process_unittests.cc \ +@HAVE_GTEST_TRUE@ d2_cfg_mgr_unittests.cc \ +@HAVE_GTEST_TRUE@ d2_queue_mgr_unittests.cc \ +@HAVE_GTEST_TRUE@ d2_update_mgr_unittests.cc \ +@HAVE_GTEST_TRUE@ nc_add_unittests.cc nc_remove_unittests.cc \ +@HAVE_GTEST_TRUE@ d2_controller_unittests.cc \ +@HAVE_GTEST_TRUE@ d2_simple_parser_unittest.cc \ +@HAVE_GTEST_TRUE@ parser_unittest.cc parser_unittest.h \ +@HAVE_GTEST_TRUE@ get_config_unittest.cc d2_command_unittest.cc \ +@HAVE_GTEST_TRUE@ simple_add_unittests.cc \ +@HAVE_GTEST_TRUE@ simple_remove_unittests.cc +@HAVE_GTEST_TRUE@d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +@HAVE_GTEST_TRUE@d2_unittests_LDFLAGS = $(AM_LDFLAGS) \ +@HAVE_GTEST_TRUE@ $(CRYPTO_LDFLAGS) $(am__append_1) \ +@HAVE_GTEST_TRUE@ $(am__append_2) $(GTEST_LDFLAGS) +@HAVE_GTEST_TRUE@d2_unittests_LDADD = \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/bin/d2/libd2.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/d2srv/testutils/libd2srvtest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/d2srv/libkea-d2srv.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/testutils/libprocesstest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.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/testutils/libkea-testutils.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/database/libkea-database.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.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/hooks/libkea-hooks.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) $(CRYPTO_LIBS) \ +@HAVE_GTEST_TRUE@ $(BOOST_LIBS) $(GTEST_LDADD) + +# The basic callout library - contains standard callouts +@HAVE_GTEST_TRUE@libcallout_la_SOURCES = callout_library.cc +@HAVE_GTEST_TRUE@libcallout_la_CXXFLAGS = $(AM_CXXFLAGS) +@HAVE_GTEST_TRUE@libcallout_la_CPPFLAGS = $(AM_CPPFLAGS) +@HAVE_GTEST_TRUE@libcallout_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la +@HAVE_GTEST_TRUE@libcallout_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere + +# The d2_srv_configured callout library +@HAVE_GTEST_TRUE@libconfigured_la_SOURCES = configured_library.cc +@HAVE_GTEST_TRUE@libconfigured_la_CXXFLAGS = $(AM_CXXFLAGS) +@HAVE_GTEST_TRUE@libconfigured_la_CPPFLAGS = $(AM_CPPFLAGS) +@HAVE_GTEST_TRUE@libconfigured_la_LIBADD = $(top_builddir)/src/lib/hooks/libkea-hooks.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/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@libconfigured_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere +@HAVE_GTEST_TRUE@noinst_LTLIBRARIES = libcallout.la libconfigured.la +@HAVE_GTEST_TRUE@nodist_d2_unittests_SOURCES = \ +@HAVE_GTEST_TRUE@ test_data_files_config.h \ +@HAVE_GTEST_TRUE@ test_callout_libraries.h \ +@HAVE_GTEST_TRUE@ test_configured_libraries.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/bin/d2/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/bin/d2/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): +d2_process_tests.sh: $(top_builddir)/config.status $(srcdir)/d2_process_tests.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +test_callout_libraries.h: $(top_builddir)/config.status $(srcdir)/test_callout_libraries.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +test_configured_libraries.h: $(top_builddir)/config.status $(srcdir)/test_configured_libraries.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +test_data_files_config.h: $(top_builddir)/config.status $(srcdir)/test_data_files_config.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +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 + +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}; \ + } + +libcallout.la: $(libcallout_la_OBJECTS) $(libcallout_la_DEPENDENCIES) $(EXTRA_libcallout_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libcallout_la_LINK) $(am_libcallout_la_rpath) $(libcallout_la_OBJECTS) $(libcallout_la_LIBADD) $(LIBS) + +libconfigured.la: $(libconfigured_la_OBJECTS) $(libconfigured_la_DEPENDENCIES) $(EXTRA_libconfigured_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libconfigured_la_LINK) $(am_libconfigured_la_rpath) $(libconfigured_la_OBJECTS) $(libconfigured_la_LIBADD) $(LIBS) + +d2_unittests$(EXEEXT): $(d2_unittests_OBJECTS) $(d2_unittests_DEPENDENCIES) $(EXTRA_d2_unittests_DEPENDENCIES) + @rm -f d2_unittests$(EXEEXT) + $(AM_V_CXXLD)$(d2_unittests_LINK) $(d2_unittests_OBJECTS) $(d2_unittests_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-d2_cfg_mgr_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-d2_command_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-d2_controller_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-d2_process_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-d2_queue_mgr_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-d2_simple_parser_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-d2_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-d2_update_mgr_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-get_config_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-nc_add_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-nc_remove_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-parser_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-simple_add_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d2_unittests-simple_remove_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcallout_la-callout_library.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libconfigured_la-configured_library.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 $@ $< + +libcallout_la-callout_library.lo: callout_library.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcallout_la_CPPFLAGS) $(CPPFLAGS) $(libcallout_la_CXXFLAGS) $(CXXFLAGS) -MT libcallout_la-callout_library.lo -MD -MP -MF $(DEPDIR)/libcallout_la-callout_library.Tpo -c -o libcallout_la-callout_library.lo `test -f 'callout_library.cc' || echo '$(srcdir)/'`callout_library.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcallout_la-callout_library.Tpo $(DEPDIR)/libcallout_la-callout_library.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_library.cc' object='libcallout_la-callout_library.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) $(libcallout_la_CPPFLAGS) $(CPPFLAGS) $(libcallout_la_CXXFLAGS) $(CXXFLAGS) -c -o libcallout_la-callout_library.lo `test -f 'callout_library.cc' || echo '$(srcdir)/'`callout_library.cc + +libconfigured_la-configured_library.lo: configured_library.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libconfigured_la_CPPFLAGS) $(CPPFLAGS) $(libconfigured_la_CXXFLAGS) $(CXXFLAGS) -MT libconfigured_la-configured_library.lo -MD -MP -MF $(DEPDIR)/libconfigured_la-configured_library.Tpo -c -o libconfigured_la-configured_library.lo `test -f 'configured_library.cc' || echo '$(srcdir)/'`configured_library.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libconfigured_la-configured_library.Tpo $(DEPDIR)/libconfigured_la-configured_library.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='configured_library.cc' object='libconfigured_la-configured_library.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) $(libconfigured_la_CPPFLAGS) $(CPPFLAGS) $(libconfigured_la_CXXFLAGS) $(CXXFLAGS) -c -o libconfigured_la-configured_library.lo `test -f 'configured_library.cc' || echo '$(srcdir)/'`configured_library.cc + +d2_unittests-d2_unittests.o: d2_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_unittests.o -MD -MP -MF $(DEPDIR)/d2_unittests-d2_unittests.Tpo -c -o d2_unittests-d2_unittests.o `test -f 'd2_unittests.cc' || echo '$(srcdir)/'`d2_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_unittests.Tpo $(DEPDIR)/d2_unittests-d2_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_unittests.cc' object='d2_unittests-d2_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_unittests.o `test -f 'd2_unittests.cc' || echo '$(srcdir)/'`d2_unittests.cc + +d2_unittests-d2_unittests.obj: d2_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_unittests.obj -MD -MP -MF $(DEPDIR)/d2_unittests-d2_unittests.Tpo -c -o d2_unittests-d2_unittests.obj `if test -f 'd2_unittests.cc'; then $(CYGPATH_W) 'd2_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_unittests.Tpo $(DEPDIR)/d2_unittests-d2_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_unittests.cc' object='d2_unittests-d2_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_unittests.obj `if test -f 'd2_unittests.cc'; then $(CYGPATH_W) 'd2_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_unittests.cc'; fi` + +d2_unittests-d2_process_unittests.o: d2_process_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_process_unittests.o -MD -MP -MF $(DEPDIR)/d2_unittests-d2_process_unittests.Tpo -c -o d2_unittests-d2_process_unittests.o `test -f 'd2_process_unittests.cc' || echo '$(srcdir)/'`d2_process_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_process_unittests.Tpo $(DEPDIR)/d2_unittests-d2_process_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_process_unittests.cc' object='d2_unittests-d2_process_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_process_unittests.o `test -f 'd2_process_unittests.cc' || echo '$(srcdir)/'`d2_process_unittests.cc + +d2_unittests-d2_process_unittests.obj: d2_process_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_process_unittests.obj -MD -MP -MF $(DEPDIR)/d2_unittests-d2_process_unittests.Tpo -c -o d2_unittests-d2_process_unittests.obj `if test -f 'd2_process_unittests.cc'; then $(CYGPATH_W) 'd2_process_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_process_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_process_unittests.Tpo $(DEPDIR)/d2_unittests-d2_process_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_process_unittests.cc' object='d2_unittests-d2_process_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_process_unittests.obj `if test -f 'd2_process_unittests.cc'; then $(CYGPATH_W) 'd2_process_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_process_unittests.cc'; fi` + +d2_unittests-d2_cfg_mgr_unittests.o: d2_cfg_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_cfg_mgr_unittests.o -MD -MP -MF $(DEPDIR)/d2_unittests-d2_cfg_mgr_unittests.Tpo -c -o d2_unittests-d2_cfg_mgr_unittests.o `test -f 'd2_cfg_mgr_unittests.cc' || echo '$(srcdir)/'`d2_cfg_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_cfg_mgr_unittests.Tpo $(DEPDIR)/d2_unittests-d2_cfg_mgr_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_cfg_mgr_unittests.cc' object='d2_unittests-d2_cfg_mgr_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_cfg_mgr_unittests.o `test -f 'd2_cfg_mgr_unittests.cc' || echo '$(srcdir)/'`d2_cfg_mgr_unittests.cc + +d2_unittests-d2_cfg_mgr_unittests.obj: d2_cfg_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_cfg_mgr_unittests.obj -MD -MP -MF $(DEPDIR)/d2_unittests-d2_cfg_mgr_unittests.Tpo -c -o d2_unittests-d2_cfg_mgr_unittests.obj `if test -f 'd2_cfg_mgr_unittests.cc'; then $(CYGPATH_W) 'd2_cfg_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_cfg_mgr_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_cfg_mgr_unittests.Tpo $(DEPDIR)/d2_unittests-d2_cfg_mgr_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_cfg_mgr_unittests.cc' object='d2_unittests-d2_cfg_mgr_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_cfg_mgr_unittests.obj `if test -f 'd2_cfg_mgr_unittests.cc'; then $(CYGPATH_W) 'd2_cfg_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_cfg_mgr_unittests.cc'; fi` + +d2_unittests-d2_queue_mgr_unittests.o: d2_queue_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_queue_mgr_unittests.o -MD -MP -MF $(DEPDIR)/d2_unittests-d2_queue_mgr_unittests.Tpo -c -o d2_unittests-d2_queue_mgr_unittests.o `test -f 'd2_queue_mgr_unittests.cc' || echo '$(srcdir)/'`d2_queue_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_queue_mgr_unittests.Tpo $(DEPDIR)/d2_unittests-d2_queue_mgr_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_queue_mgr_unittests.cc' object='d2_unittests-d2_queue_mgr_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_queue_mgr_unittests.o `test -f 'd2_queue_mgr_unittests.cc' || echo '$(srcdir)/'`d2_queue_mgr_unittests.cc + +d2_unittests-d2_queue_mgr_unittests.obj: d2_queue_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_queue_mgr_unittests.obj -MD -MP -MF $(DEPDIR)/d2_unittests-d2_queue_mgr_unittests.Tpo -c -o d2_unittests-d2_queue_mgr_unittests.obj `if test -f 'd2_queue_mgr_unittests.cc'; then $(CYGPATH_W) 'd2_queue_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_queue_mgr_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_queue_mgr_unittests.Tpo $(DEPDIR)/d2_unittests-d2_queue_mgr_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_queue_mgr_unittests.cc' object='d2_unittests-d2_queue_mgr_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_queue_mgr_unittests.obj `if test -f 'd2_queue_mgr_unittests.cc'; then $(CYGPATH_W) 'd2_queue_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_queue_mgr_unittests.cc'; fi` + +d2_unittests-d2_update_mgr_unittests.o: d2_update_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_update_mgr_unittests.o -MD -MP -MF $(DEPDIR)/d2_unittests-d2_update_mgr_unittests.Tpo -c -o d2_unittests-d2_update_mgr_unittests.o `test -f 'd2_update_mgr_unittests.cc' || echo '$(srcdir)/'`d2_update_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_update_mgr_unittests.Tpo $(DEPDIR)/d2_unittests-d2_update_mgr_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_update_mgr_unittests.cc' object='d2_unittests-d2_update_mgr_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_update_mgr_unittests.o `test -f 'd2_update_mgr_unittests.cc' || echo '$(srcdir)/'`d2_update_mgr_unittests.cc + +d2_unittests-d2_update_mgr_unittests.obj: d2_update_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_update_mgr_unittests.obj -MD -MP -MF $(DEPDIR)/d2_unittests-d2_update_mgr_unittests.Tpo -c -o d2_unittests-d2_update_mgr_unittests.obj `if test -f 'd2_update_mgr_unittests.cc'; then $(CYGPATH_W) 'd2_update_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_update_mgr_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_update_mgr_unittests.Tpo $(DEPDIR)/d2_unittests-d2_update_mgr_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_update_mgr_unittests.cc' object='d2_unittests-d2_update_mgr_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_update_mgr_unittests.obj `if test -f 'd2_update_mgr_unittests.cc'; then $(CYGPATH_W) 'd2_update_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_update_mgr_unittests.cc'; fi` + +d2_unittests-nc_add_unittests.o: nc_add_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-nc_add_unittests.o -MD -MP -MF $(DEPDIR)/d2_unittests-nc_add_unittests.Tpo -c -o d2_unittests-nc_add_unittests.o `test -f 'nc_add_unittests.cc' || echo '$(srcdir)/'`nc_add_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-nc_add_unittests.Tpo $(DEPDIR)/d2_unittests-nc_add_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nc_add_unittests.cc' object='d2_unittests-nc_add_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-nc_add_unittests.o `test -f 'nc_add_unittests.cc' || echo '$(srcdir)/'`nc_add_unittests.cc + +d2_unittests-nc_add_unittests.obj: nc_add_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-nc_add_unittests.obj -MD -MP -MF $(DEPDIR)/d2_unittests-nc_add_unittests.Tpo -c -o d2_unittests-nc_add_unittests.obj `if test -f 'nc_add_unittests.cc'; then $(CYGPATH_W) 'nc_add_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/nc_add_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-nc_add_unittests.Tpo $(DEPDIR)/d2_unittests-nc_add_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nc_add_unittests.cc' object='d2_unittests-nc_add_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-nc_add_unittests.obj `if test -f 'nc_add_unittests.cc'; then $(CYGPATH_W) 'nc_add_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/nc_add_unittests.cc'; fi` + +d2_unittests-nc_remove_unittests.o: nc_remove_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-nc_remove_unittests.o -MD -MP -MF $(DEPDIR)/d2_unittests-nc_remove_unittests.Tpo -c -o d2_unittests-nc_remove_unittests.o `test -f 'nc_remove_unittests.cc' || echo '$(srcdir)/'`nc_remove_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-nc_remove_unittests.Tpo $(DEPDIR)/d2_unittests-nc_remove_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nc_remove_unittests.cc' object='d2_unittests-nc_remove_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-nc_remove_unittests.o `test -f 'nc_remove_unittests.cc' || echo '$(srcdir)/'`nc_remove_unittests.cc + +d2_unittests-nc_remove_unittests.obj: nc_remove_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-nc_remove_unittests.obj -MD -MP -MF $(DEPDIR)/d2_unittests-nc_remove_unittests.Tpo -c -o d2_unittests-nc_remove_unittests.obj `if test -f 'nc_remove_unittests.cc'; then $(CYGPATH_W) 'nc_remove_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/nc_remove_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-nc_remove_unittests.Tpo $(DEPDIR)/d2_unittests-nc_remove_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nc_remove_unittests.cc' object='d2_unittests-nc_remove_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-nc_remove_unittests.obj `if test -f 'nc_remove_unittests.cc'; then $(CYGPATH_W) 'nc_remove_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/nc_remove_unittests.cc'; fi` + +d2_unittests-d2_controller_unittests.o: d2_controller_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_controller_unittests.o -MD -MP -MF $(DEPDIR)/d2_unittests-d2_controller_unittests.Tpo -c -o d2_unittests-d2_controller_unittests.o `test -f 'd2_controller_unittests.cc' || echo '$(srcdir)/'`d2_controller_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_controller_unittests.Tpo $(DEPDIR)/d2_unittests-d2_controller_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_controller_unittests.cc' object='d2_unittests-d2_controller_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_controller_unittests.o `test -f 'd2_controller_unittests.cc' || echo '$(srcdir)/'`d2_controller_unittests.cc + +d2_unittests-d2_controller_unittests.obj: d2_controller_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_controller_unittests.obj -MD -MP -MF $(DEPDIR)/d2_unittests-d2_controller_unittests.Tpo -c -o d2_unittests-d2_controller_unittests.obj `if test -f 'd2_controller_unittests.cc'; then $(CYGPATH_W) 'd2_controller_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_controller_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_controller_unittests.Tpo $(DEPDIR)/d2_unittests-d2_controller_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_controller_unittests.cc' object='d2_unittests-d2_controller_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_controller_unittests.obj `if test -f 'd2_controller_unittests.cc'; then $(CYGPATH_W) 'd2_controller_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d2_controller_unittests.cc'; fi` + +d2_unittests-d2_simple_parser_unittest.o: d2_simple_parser_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_simple_parser_unittest.o -MD -MP -MF $(DEPDIR)/d2_unittests-d2_simple_parser_unittest.Tpo -c -o d2_unittests-d2_simple_parser_unittest.o `test -f 'd2_simple_parser_unittest.cc' || echo '$(srcdir)/'`d2_simple_parser_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_simple_parser_unittest.Tpo $(DEPDIR)/d2_unittests-d2_simple_parser_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_simple_parser_unittest.cc' object='d2_unittests-d2_simple_parser_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_simple_parser_unittest.o `test -f 'd2_simple_parser_unittest.cc' || echo '$(srcdir)/'`d2_simple_parser_unittest.cc + +d2_unittests-d2_simple_parser_unittest.obj: d2_simple_parser_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_simple_parser_unittest.obj -MD -MP -MF $(DEPDIR)/d2_unittests-d2_simple_parser_unittest.Tpo -c -o d2_unittests-d2_simple_parser_unittest.obj `if test -f 'd2_simple_parser_unittest.cc'; then $(CYGPATH_W) 'd2_simple_parser_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/d2_simple_parser_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_simple_parser_unittest.Tpo $(DEPDIR)/d2_unittests-d2_simple_parser_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_simple_parser_unittest.cc' object='d2_unittests-d2_simple_parser_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_simple_parser_unittest.obj `if test -f 'd2_simple_parser_unittest.cc'; then $(CYGPATH_W) 'd2_simple_parser_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/d2_simple_parser_unittest.cc'; fi` + +d2_unittests-parser_unittest.o: parser_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-parser_unittest.o -MD -MP -MF $(DEPDIR)/d2_unittests-parser_unittest.Tpo -c -o d2_unittests-parser_unittest.o `test -f 'parser_unittest.cc' || echo '$(srcdir)/'`parser_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-parser_unittest.Tpo $(DEPDIR)/d2_unittests-parser_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='parser_unittest.cc' object='d2_unittests-parser_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-parser_unittest.o `test -f 'parser_unittest.cc' || echo '$(srcdir)/'`parser_unittest.cc + +d2_unittests-parser_unittest.obj: parser_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-parser_unittest.obj -MD -MP -MF $(DEPDIR)/d2_unittests-parser_unittest.Tpo -c -o d2_unittests-parser_unittest.obj `if test -f 'parser_unittest.cc'; then $(CYGPATH_W) 'parser_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/parser_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-parser_unittest.Tpo $(DEPDIR)/d2_unittests-parser_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='parser_unittest.cc' object='d2_unittests-parser_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-parser_unittest.obj `if test -f 'parser_unittest.cc'; then $(CYGPATH_W) 'parser_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/parser_unittest.cc'; fi` + +d2_unittests-get_config_unittest.o: get_config_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-get_config_unittest.o -MD -MP -MF $(DEPDIR)/d2_unittests-get_config_unittest.Tpo -c -o d2_unittests-get_config_unittest.o `test -f 'get_config_unittest.cc' || echo '$(srcdir)/'`get_config_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-get_config_unittest.Tpo $(DEPDIR)/d2_unittests-get_config_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='get_config_unittest.cc' object='d2_unittests-get_config_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-get_config_unittest.o `test -f 'get_config_unittest.cc' || echo '$(srcdir)/'`get_config_unittest.cc + +d2_unittests-get_config_unittest.obj: get_config_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-get_config_unittest.obj -MD -MP -MF $(DEPDIR)/d2_unittests-get_config_unittest.Tpo -c -o d2_unittests-get_config_unittest.obj `if test -f 'get_config_unittest.cc'; then $(CYGPATH_W) 'get_config_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/get_config_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-get_config_unittest.Tpo $(DEPDIR)/d2_unittests-get_config_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='get_config_unittest.cc' object='d2_unittests-get_config_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-get_config_unittest.obj `if test -f 'get_config_unittest.cc'; then $(CYGPATH_W) 'get_config_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/get_config_unittest.cc'; fi` + +d2_unittests-d2_command_unittest.o: d2_command_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_command_unittest.o -MD -MP -MF $(DEPDIR)/d2_unittests-d2_command_unittest.Tpo -c -o d2_unittests-d2_command_unittest.o `test -f 'd2_command_unittest.cc' || echo '$(srcdir)/'`d2_command_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_command_unittest.Tpo $(DEPDIR)/d2_unittests-d2_command_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_command_unittest.cc' object='d2_unittests-d2_command_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_command_unittest.o `test -f 'd2_command_unittest.cc' || echo '$(srcdir)/'`d2_command_unittest.cc + +d2_unittests-d2_command_unittest.obj: d2_command_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-d2_command_unittest.obj -MD -MP -MF $(DEPDIR)/d2_unittests-d2_command_unittest.Tpo -c -o d2_unittests-d2_command_unittest.obj `if test -f 'd2_command_unittest.cc'; then $(CYGPATH_W) 'd2_command_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/d2_command_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-d2_command_unittest.Tpo $(DEPDIR)/d2_unittests-d2_command_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d2_command_unittest.cc' object='d2_unittests-d2_command_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-d2_command_unittest.obj `if test -f 'd2_command_unittest.cc'; then $(CYGPATH_W) 'd2_command_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/d2_command_unittest.cc'; fi` + +d2_unittests-simple_add_unittests.o: simple_add_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-simple_add_unittests.o -MD -MP -MF $(DEPDIR)/d2_unittests-simple_add_unittests.Tpo -c -o d2_unittests-simple_add_unittests.o `test -f 'simple_add_unittests.cc' || echo '$(srcdir)/'`simple_add_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-simple_add_unittests.Tpo $(DEPDIR)/d2_unittests-simple_add_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='simple_add_unittests.cc' object='d2_unittests-simple_add_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-simple_add_unittests.o `test -f 'simple_add_unittests.cc' || echo '$(srcdir)/'`simple_add_unittests.cc + +d2_unittests-simple_add_unittests.obj: simple_add_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-simple_add_unittests.obj -MD -MP -MF $(DEPDIR)/d2_unittests-simple_add_unittests.Tpo -c -o d2_unittests-simple_add_unittests.obj `if test -f 'simple_add_unittests.cc'; then $(CYGPATH_W) 'simple_add_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/simple_add_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-simple_add_unittests.Tpo $(DEPDIR)/d2_unittests-simple_add_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='simple_add_unittests.cc' object='d2_unittests-simple_add_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-simple_add_unittests.obj `if test -f 'simple_add_unittests.cc'; then $(CYGPATH_W) 'simple_add_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/simple_add_unittests.cc'; fi` + +d2_unittests-simple_remove_unittests.o: simple_remove_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-simple_remove_unittests.o -MD -MP -MF $(DEPDIR)/d2_unittests-simple_remove_unittests.Tpo -c -o d2_unittests-simple_remove_unittests.o `test -f 'simple_remove_unittests.cc' || echo '$(srcdir)/'`simple_remove_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-simple_remove_unittests.Tpo $(DEPDIR)/d2_unittests-simple_remove_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='simple_remove_unittests.cc' object='d2_unittests-simple_remove_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-simple_remove_unittests.o `test -f 'simple_remove_unittests.cc' || echo '$(srcdir)/'`simple_remove_unittests.cc + +d2_unittests-simple_remove_unittests.obj: simple_remove_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT d2_unittests-simple_remove_unittests.obj -MD -MP -MF $(DEPDIR)/d2_unittests-simple_remove_unittests.Tpo -c -o d2_unittests-simple_remove_unittests.obj `if test -f 'simple_remove_unittests.cc'; then $(CYGPATH_W) 'simple_remove_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/simple_remove_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/d2_unittests-simple_remove_unittests.Tpo $(DEPDIR)/d2_unittests-simple_remove_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='simple_remove_unittests.cc' object='d2_unittests-simple_remove_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) $(d2_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o d2_unittests-simple_remove_unittests.obj `if test -f 'simple_remove_unittests.cc'; then $(CYGPATH_W) 'simple_remove_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/simple_remove_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_SCRIPTS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(SCRIPTS) +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: + +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) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +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 \ + clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/d2_unittests-d2_cfg_mgr_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_command_unittest.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_controller_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_process_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_queue_mgr_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_simple_parser_unittest.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_update_mgr_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-get_config_unittest.Po + -rm -f ./$(DEPDIR)/d2_unittests-nc_add_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-nc_remove_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-parser_unittest.Po + -rm -f ./$(DEPDIR)/d2_unittests-simple_add_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-simple_remove_unittests.Po + -rm -f ./$(DEPDIR)/libcallout_la-callout_library.Plo + -rm -f ./$(DEPDIR)/libconfigured_la-configured_library.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)/d2_unittests-d2_cfg_mgr_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_command_unittest.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_controller_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_process_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_queue_mgr_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_simple_parser_unittest.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-d2_update_mgr_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-get_config_unittest.Po + -rm -f ./$(DEPDIR)/d2_unittests-nc_add_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-nc_remove_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-parser_unittest.Po + -rm -f ./$(DEPDIR)/d2_unittests-simple_add_unittests.Po + -rm -f ./$(DEPDIR)/d2_unittests-simple_remove_unittests.Po + -rm -f ./$(DEPDIR)/libcallout_la-callout_library.Plo + -rm -f ./$(DEPDIR)/libconfigured_la-configured_library.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) 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-noinstLTLIBRARIES 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/bin/d2/tests/callout_library.cc b/src/bin/d2/tests/callout_library.cc new file mode 100644 index 0000000..8587eba --- /dev/null +++ b/src/bin/d2/tests/callout_library.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/. + +/// @file +/// @brief Basic callout library +/// +/// This is source of a test library for Control Agent. +/// +/// - Only the "version" framework function is supplied. +/// +/// - hookpt_one callout is supplied. + +#include <config.h> +#include <hooks/hooks.h> + +using namespace isc::hooks; +using namespace std; + +namespace { + +extern "C" { + +// Callouts. All return their result through the "result" argument. + +int +context_create(CalloutHandle& handle) { + handle.setContext("result", static_cast<int>(10)); + handle.setArgument("result", static_cast<int>(10)); + return (0); +} + +// First callout adds the passed "integer" argument to the initialized context +// value of 10. (Note that the value set by context_create is accessed through +// context and not the argument, so checking that context is correctly passed +// between callouts in the same library.) + +int +hookpt_one(CalloutHandle& handle) { + int data; + handle.getArgument("integer", data); + + int result; + handle.getArgument("result", result); + + result += data; + handle.setArgument("result", result); + + return (0); +} + +// Framework functions. + +int +version() { + return (KEA_HOOKS_VERSION); +} + +// load() initializes the user library if the main image was statically linked. +int +load(isc::hooks::LibraryHandle&) { +#ifdef USE_STATIC_LINK + hooksStaticLinkInit(); +#endif + return (0); +} + +} +} + diff --git a/src/bin/d2/tests/configured_library.cc b/src/bin/d2/tests/configured_library.cc new file mode 100644 index 0000000..3aeb94d --- /dev/null +++ b/src/bin/d2/tests/configured_library.cc @@ -0,0 +1,63 @@ +// 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/. + +/// @brief d2_srv_configured callout testing library + +#include <config.h> +#include <cc/data.h> +#include <hooks/hooks.h> + +using namespace isc::data; +using namespace isc::hooks; +using namespace std; + +namespace { + +extern "C" { + +// d2_srv_configured callout. +int +d2_srv_configured(CalloutHandle& handle) { + // Get the parameters. + ConstElementPtr cfg; + string error; + handle.getArgument("json_config", cfg); + handle.getArgument("error", error); + + if (cfg) { + ConstElementPtr uc = cfg->get("user-context"); + if (uc) { + ConstElementPtr msg = uc->get("error"); + if (msg && (msg->getType() == Element::string)) { + error += msg->stringValue(); + } + } + } + + if (!error.empty()) { + handle.setArgument("error", error); + handle.setStatus(CalloutHandle::NEXT_STEP_DROP); + } + return (0); +} + +// Framework functions. +int +version() { + return (KEA_HOOKS_VERSION); +} + +// load() initializes the user library if the main image was statically linked. +int +load(isc::hooks::LibraryHandle&) { +#ifdef USE_STATIC_LINK + hooksStaticLinkInit(); +#endif + return (0); +} + +} +} diff --git a/src/bin/d2/tests/d2_cfg_mgr_unittests.cc b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc new file mode 100644 index 0000000..5e27ff8 --- /dev/null +++ b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc @@ -0,0 +1,1080 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <d2/parser_context.h> +#include <d2/tests/parser_unittest.h> +#include <d2/tests/test_callout_libraries.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/d2_config.h> +#include <d2srv/d2_simple_parser.h> +#include <dhcpsrv/testutils/config_result_check.h> +#include <process/testutils/d_test_stubs.h> +#include <test_data_files_config.h> +#include <util/encode/base64.h> + +#include <boost/foreach.hpp> +#include <boost/scoped_ptr.hpp> +#include <gtest/gtest.h> + +using namespace std; +using namespace isc; +using namespace isc::d2; +using namespace isc::hooks; +using namespace isc::process; + +namespace { + +/// @brief Function to create full path to test data file +/// +/// The full path is dependent upon the value of D2_TEST_DATA_DIR which +/// whose value is generated from test_data_files_config.h.in +/// +/// @param name file name to which the path should be prepended +std::string testDataFile(const std::string& name) { + return (std::string(D2_TEST_DATA_DIR) + "/" + name); +} + +/// @brief Test fixture class for testing D2CfgMgr class. +/// It maintains an member instance of D2CfgMgr and provides methods for +/// converting JSON strings to configuration element sets, checking parse +/// results, and accessing the configuration context. +class D2CfgMgrTest : public ConfigParseTest { +public: + + /// @brief Constructor + D2CfgMgrTest():cfg_mgr_(new D2CfgMgr()), d2_params_() { + } + + /// @brief Destructor + ~D2CfgMgrTest() { + } + + /// @brief Configuration manager instance. + D2CfgMgrPtr cfg_mgr_; + + /// @brief Build JSON configuration string for a D2Params element + /// + /// Constructs a JSON string for "params" element using replaceable + /// parameters. + /// + /// @param ip_address string to insert as ip_address value + /// @param port integer to insert as port value + /// @param dns_server_timeout integer to insert as dns_server_timeout value + /// @param ncr_protocol string to insert as ncr_protocol value + /// @param ncr_format string to insert as ncr_format value + /// + /// @return std::string containing the JSON configuration text + std::string makeParamsConfigString(const std::string& ip_address, + const int port, + const int dns_server_timeout, + const std::string& ncr_protocol, + const std::string& ncr_format) { + std::ostringstream config; + config << + "{" + " \"ip-address\": \"" << ip_address << "\" , " + " \"port\": " << port << " , " + " \"dns-server-timeout\": " << dns_server_timeout << " , " + " \"ncr-protocol\": \"" << ncr_protocol << "\" , " + " \"ncr-format\": \"" << ncr_format << "\", " + " \"tsig-keys\": [], " + " \"forward-ddns\" : {}, " + " \"reverse-ddns\" : {} " + "}"; + + return (config.str()); + } + + /// @brief Enumeration to select between expected configuration outcomes + enum RunConfigMode { + NO_ERROR, + SYNTAX_ERROR, + LOGIC_ERROR + }; + + /// @brief Parses a configuration string and tests against a given outcome + /// + /// Convenience method which accepts JSON text and an expected pass or fail + /// outcome. It uses the D2ParserContext to parse the text under the + /// PARSE_SUB_DHCPDDNS context, then adds the D2 defaults to the resultant + /// element tree. Assuming that's successful the element tree is passed + /// to D2CfgMgr::parseConfig() method. + /// + /// @param config_str the JSON configuration text to parse + /// @param error_type indicates the type error expected, NONE, SYNTAX, + /// or LOGIC. SYNTAX errors are emitted by JSON parser, logic errors + /// are emitted by element parser(s). + /// @param exp_error exact text of the error message expected + /// defaults to SHOULD_PASS. + /// + /// @return AssertionSuccess if test passes, AssertionFailure otherwise + ::testing::AssertionResult runConfigOrFail(const std::string& json, + const RunConfigMode mode, + const std::string& exp_error) { + + try { + // Invoke the JSON parser, casting the returned element tree + // into mutable form. + D2ParserContext parser_context; + data::ElementPtr elem = + boost::const_pointer_cast<Element> + (parser_context.parseString(json, D2ParserContext:: + PARSER_SUB_DHCPDDNS)); + + // If parsing succeeded when we expected a syntax error, then fail. + if (mode == SYNTAX_ERROR) { + return ::testing::AssertionFailure() + << "Unexpected JSON parsing success" + << "\njson: [" << json << " ]"; + } + + // JSON parsed ok, so add the defaults to the element tree it produced. + D2SimpleParser::setAllDefaults(elem); + config_set_ = elem; + } catch (const std::exception& ex) { + // JSON Parsing failed + if (exp_error.empty()) { + // We did not expect an error, so fail. + return ::testing::AssertionFailure() + << "Unexpected syntax error:" << ex.what() + << "\njson: [" << json << " ]"; + } + + if (ex.what() != exp_error) { + // Expected an error not the one we got, so fail + return ::testing::AssertionFailure() + << "Wrong syntax error detected, expected: " + << exp_error << ", got: " << ex.what() + << "\njson: [" << json << " ]"; + } + + // We go the syntax error we expected, so return success + return ::testing::AssertionSuccess(); + } + + // The JSON parsed ok and we've added the defaults, pass the config + // into the Element parser and check for the expected outcome. + data::ConstElementPtr answer; + answer = cfg_mgr_->simpleParseConfig(config_set_, false); + + // Extract the result and error text from the answer. + int rcode = 0; + isc::data::ConstElementPtr comment; + comment = isc::config::parseAnswer(rcode, answer); + + if (rcode != 0) { + // Element Parsing failed. + if (exp_error.empty()) { + // We didn't expect it to, fail the test. + return ::testing::AssertionFailure() + << "Unexpected logic error: " << *comment + << "\njson: [" << json << " ]"; + } + + if (comment->stringValue() != exp_error) { + // We 't expect a different error, fail the test. + return ::testing::AssertionFailure() + << "Wrong logic error detected, expected: " + << exp_error << ", got: " << *comment + << "\njson: [" << json << " ]"; + } + } else { + // Element parsing succeeded. + if (!exp_error.empty()) { + // It was supposed to fail, so fail the test. + return ::testing::AssertionFailure() + << "Unexpected logic success, expected error:" + << exp_error + << "\njson: [" << json << " ]"; + } + } + + // Verify that the D2 context can be retrieved and is not null. + D2CfgContextPtr context; + context = cfg_mgr_->getD2CfgContext(); + if (!context) { + return ::testing::AssertionFailure() << "D2CfgContext is null"; + } + + // Verify that the global scalar container has been created. + d2_params_ = context->getD2Params(); + if (!d2_params_) { + return ::testing::AssertionFailure() << "D2Params is null"; + } + + return ::testing::AssertionSuccess(); + } + + /// @brief Replaces %LIBRARY% with specified library name + /// + /// @param config input config text (should contain "%LIBRARY%" string) + /// @param lib_name %LIBRARY% will be replaced with that name + /// @return configuration text with library name replaced + std::string pathReplacer(const char* config, const char* lib_name) { + string txt(config); + txt.replace(txt.find("%LIBRARY%"), strlen("%LIBRARY%"), string(lib_name)); + return (txt); + } + + /// @brief Pointer the D2Params most recently parsed. + D2ParamsPtr d2_params_; +}; + +/// @brief Convenience macros for invoking runOrConfig() +#define RUN_CONFIG_OK(a) (runConfigOrFail(a, NO_ERROR, "")) +#define SYNTAX_ERROR(a,b) ASSERT_TRUE(runConfigOrFail(a,SYNTAX_ERROR,b)) +#define LOGIC_ERROR(a,b) ASSERT_TRUE(runConfigOrFail(a,LOGIC_ERROR,b)) + +/// @brief Tests a basic valid configuration for D2Param. +TEST_F(D2CfgMgrTest, validParamsEntry) { + // Verify that ip_address can be valid v4 address. + std::string config = makeParamsConfigString ("192.0.0.1", 777, 333, + "UDP", "JSON"); + RUN_CONFIG_OK(config); + + EXPECT_EQ(isc::asiolink::IOAddress("192.0.0.1"), + d2_params_->getIpAddress()); + EXPECT_EQ(777, d2_params_->getPort()); + EXPECT_EQ(333, d2_params_->getDnsServerTimeout()); + EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_params_->getNcrProtocol()); + EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_params_->getNcrFormat()); + + // Verify that ip_address can be valid v6 address. + config = makeParamsConfigString ("3001::5", 777, 333, "UDP", "JSON"); + RUN_CONFIG_OK(config); + + // Verify that the global scalars have the proper values. + EXPECT_EQ(isc::asiolink::IOAddress("3001::5"), + d2_params_->getIpAddress()); + + // Verify the configuration summary. + EXPECT_EQ("listening on 3001::5, port 777, using UDP", + d2_params_->getConfigSummary()); +} + +/// @brief Tests default values for D2Params. +/// It verifies that D2Params is populated with default value for optional +/// parameter if not supplied in the configuration. +/// Currently they are all optional. +TEST_F(D2CfgMgrTest, defaultValues) { + + ElementPtr defaults = isc::d2::test::parseJSON("{ }"); + ASSERT_NO_THROW(D2SimpleParser::setAllDefaults(defaults)); + + // Check that omitting ip_address gets you its default + std::string config = + "{" + " \"port\": 777 , " + " \"dns-server-timeout\": 333 , " + " \"ncr-protocol\": \"UDP\" , " + " \"ncr-format\": \"JSON\", " + " \"tsig-keys\": [], " + " \"forward-ddns\" : {}, " + " \"reverse-ddns\" : {} " + "}"; + + RUN_CONFIG_OK(config); + ConstElementPtr deflt; + ASSERT_NO_THROW(deflt = defaults->get("ip-address")); + ASSERT_TRUE(deflt); + EXPECT_EQ(deflt->stringValue(), d2_params_->getIpAddress().toText()); + + // Check that omitting port gets you its default + config = + "{" + " \"ip-address\": \"192.0.0.1\" , " + " \"dns-server-timeout\": 333 , " + " \"ncr-protocol\": \"UDP\" , " + " \"ncr-format\": \"JSON\", " + " \"tsig-keys\": [], " + " \"forward-ddns\" : {}, " + " \"reverse-ddns\" : {} " + "}"; + + RUN_CONFIG_OK(config); + ASSERT_NO_THROW(deflt = defaults->get("port")); + ASSERT_TRUE(deflt); + EXPECT_EQ(deflt->intValue(), d2_params_->getPort()); + + // Check that omitting timeout gets you its default + config = + "{" + " \"ip-address\": \"192.0.0.1\" , " + " \"port\": 777 , " + " \"ncr-protocol\": \"UDP\" , " + " \"ncr-format\": \"JSON\", " + " \"tsig-keys\": [], " + " \"forward-ddns\" : {}, " + " \"reverse-ddns\" : {} " + "}"; + + RUN_CONFIG_OK(config); + ASSERT_NO_THROW(deflt = defaults->get("dns-server-timeout")); + ASSERT_TRUE(deflt); + EXPECT_EQ(deflt->intValue(), d2_params_->getDnsServerTimeout()); + + // Check that omitting protocol gets you its default + config = + "{" + " \"ip-address\": \"192.0.0.1\" , " + " \"port\": 777 , " + " \"dns-server-timeout\": 333 , " + " \"ncr-format\": \"JSON\", " + " \"tsig-keys\": [], " + " \"forward-ddns\" : {}, " + " \"reverse-ddns\" : {} " + "}"; + + RUN_CONFIG_OK(config); + ASSERT_NO_THROW(deflt = defaults->get("ncr-protocol")); + ASSERT_TRUE(deflt); + EXPECT_EQ(dhcp_ddns::stringToNcrProtocol(deflt->stringValue()), + d2_params_->getNcrProtocol()); + + // Check that omitting format gets you its default + config = + "{" + " \"ip-address\": \"192.0.0.1\" , " + " \"port\": 777 , " + " \"dns-server-timeout\": 333 , " + " \"ncr-protocol\": \"UDP\", " + " \"tsig-keys\": [], " + " \"forward-ddns\" : {}, " + " \"reverse-ddns\" : {} " + "}"; + + RUN_CONFIG_OK(config); + ASSERT_NO_THROW(deflt = defaults->get("ncr-format")); + ASSERT_TRUE(deflt); + EXPECT_EQ(dhcp_ddns::stringToNcrFormat(deflt->stringValue()), + d2_params_->getNcrFormat()); +} + +/// @brief Tests the unsupported scalar parameters and objects are detected. +TEST_F(D2CfgMgrTest, unsupportedTopLevelItems) { + // Check that an unsupported top level parameter fails. + std::string config = + "{" + " \"ip-address\": \"127.0.0.1\", " + " \"port\": 777 , " + " \"dns-server-timeout\": 333 , " + " \"ncr-protocol\": \"UDP\" , " + " \"ncr-format\": \"JSON\", " + " \"tsig-keys\": [], " + " \"forward-ddns\" : {}, " + " \"reverse-ddns\" : {}, " + " \"bogus-param\" : true " + "}"; + + SYNTAX_ERROR(config, "<string>:1.185-197: got unexpected " + "keyword \"bogus-param\" in DhcpDdns map."); + + // Check that unsupported top level objects fails. For + // D2 these fail as they are not in the parse order. + config = + "{" + " \"ip-address\": \"127.0.0.1\", " + " \"port\": 777 , " + " \"dns-server-timeout\": 333 , " + " \"ncr-protocol\": \"UDP\" , " + " \"ncr-format\": \"JSON\", " + " \"tsig-keys\": [], " + " \"bogus-object-one\" : {}, " + " \"forward-ddns\" : {}, " + " \"reverse-ddns\" : {}, " + " \"bogus-object-two\" : {} " + "}"; + + SYNTAX_ERROR(config, "<string>:1.141-158: got unexpected" + " keyword \"bogus-object-one\" in DhcpDdns map."); +} + + +/// @brief Tests the enforcement of data validation when parsing D2Params. +/// It verifies that: +/// -# ip_address cannot be "0.0.0.0" +/// -# ip_address cannot be "::" +/// -# port cannot be 0 +/// -# dns_server_timeout cannot be 0 +/// -# ncr_protocol must be valid +/// -# ncr_format must be valid +TEST_F(D2CfgMgrTest, invalidEntry) { + // Cannot use IPv4 ANY address + std::string config = makeParamsConfigString ("0.0.0.0", 777, 333, + "UDP", "JSON"); + LOGIC_ERROR(config, "IP address cannot be \"0.0.0.0\" (<string>:1:17)"); + + // Cannot use IPv6 ANY address + config = makeParamsConfigString ("::", 777, 333, "UDP", "JSON"); + LOGIC_ERROR(config, "IP address cannot be \"::\" (<string>:1:17)"); + + // Cannot use port 0 + config = makeParamsConfigString ("127.0.0.1", 0, 333, "UDP", "JSON"); + SYNTAX_ERROR(config, "<string>:1.40: port must be greater than zero but less than 65536"); + + // Cannot use dns server timeout of 0 + config = makeParamsConfigString ("127.0.0.1", 777, 0, "UDP", "JSON"); + SYNTAX_ERROR(config, "<string>:1.69: dns-server-timeout" + " must be greater than zero"); + + // Invalid protocol + config = makeParamsConfigString ("127.0.0.1", 777, 333, "BOGUS", "JSON"); + SYNTAX_ERROR(config, "<string>:1.92-98: syntax error," + " unexpected constant string, expecting UDP or TCP"); + + // Unsupported protocol + config = makeParamsConfigString ("127.0.0.1", 777, 333, "TCP", "JSON"); + LOGIC_ERROR(config, "ncr-protocol : TCP is not yet supported" + " (<string>:1:92)"); + + // Invalid format + config = makeParamsConfigString ("127.0.0.1", 777, 333, "UDP", "BOGUS"); + SYNTAX_ERROR(config, "<string>:1.115-121: syntax error," + " unexpected constant string, expecting JSON"); +} + +// Control socket tests in d2_process_unittests.cc + +// DdnsDomainList and TSIGKey tests moved to d2_simple_parser_unittest.cc + +/// @brief Tests construction of D2CfgMgr +/// This test verifies that a D2CfgMgr constructs properly. +TEST(D2CfgMgr, construction) { + boost::scoped_ptr<D2CfgMgr> cfg_mgr; + + // Verify that configuration manager constructions without error. + ASSERT_NO_THROW(cfg_mgr.reset(new D2CfgMgr())); + + // Verify that the context can be retrieved and is not null. + D2CfgContextPtr context; + ASSERT_NO_THROW(context = cfg_mgr->getD2CfgContext()); + EXPECT_TRUE(context); + + // Verify that the forward manager can be retrieved and is not null. + EXPECT_TRUE(context->getForwardMgr()); + + // Verify that the reverse manager can be retrieved and is not null. + EXPECT_TRUE(context->getReverseMgr()); + + // Verify that the manager can be destructed without error. + EXPECT_NO_THROW(cfg_mgr.reset()); +} + +/// @brief Tests the parsing of a complete, valid DHCP-DDNS configuration. +/// This tests passes the configuration into an instance of D2CfgMgr just +/// as it would be done by d2_process in response to a configuration update +/// event. +TEST_F(D2CfgMgrTest, fullConfig) { + // Create a configuration with all of application level parameters, plus + // both the forward and reverse ddns managers. Both managers have two + // domains with three servers per domain. + std::string config = "{ " + "\"ip-address\" : \"192.168.1.33\" , " + "\"port\" : 88 , " + "\"dns-server-timeout\": 333 , " + "\"ncr-protocol\": \"UDP\" , " + "\"ncr-format\": \"JSON\", " + "\"control-socket\" : {" + " \"socket-type\" : \"unix\" ," + " \"socket-name\" : \"/tmp/d2-ctrl-channel\" " + "}," + "\"hooks-libraries\": [" + "{" + " \"library\": \"%LIBRARY%\" , " + " \"parameters\": " + " { \"param1\": \"foo\" } " + "}" + "]," + "\"tsig-keys\": [" + "{" + " \"name\": \"d2_key.example.com\" , " + " \"algorithm\": \"hmac-md5\" , " + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " + "}," + "{" + " \"name\": \"d2_key.billcat.net\" , " + " \"algorithm\": \"hmac-md5\" , " + " \"digest-bits\": 120 , " + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " + "}" + "]," + "\"forward-ddns\" : {" + "\"ddns-domains\": [ " + "{ \"name\": \"example.com\" , " + " \"key-name\": \"d2_key.example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" } , " + " { \"ip-address\": \"127.0.0.2\" } , " + " { \"ip-address\": \"127.0.0.3\"} " + " ] } " + ", " + "{ \"name\": \"billcat.net\" , " + " \"key-name\": \"d2_key.billcat.net\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.4\" } , " + " { \"ip-address\": \"127.0.0.5\" } , " + " { \"ip-address\": \"127.0.0.6\" } " + " ] } " + "] }," + "\"reverse-ddns\" : {" + "\"ddns-domains\": [ " + "{ \"name\": \" 0.168.192.in.addr.arpa.\" , " + " \"key-name\": \"d2_key.example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.1.1\" } , " + " { \"ip-address\": \"127.0.2.1\" } , " + " { \"ip-address\": \"127.0.3.1\" } " + " ] } " + ", " + "{ \"name\": \" 0.247.106.in.addr.arpa.\" , " + " \"key-name\": \"d2_key.billcat.net\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.4.1\" }, " + " { \"ip-address\": \"127.0.5.1\" } , " + " { \"ip-address\": \"127.0.6.1\" } " + " ] } " + "] } }"; + + // Replace the library path. + std::string pr_config = pathReplacer(config.c_str(), CALLOUT_LIBRARY); + // Should parse without error. + RUN_CONFIG_OK(pr_config); + + // Verify that the D2 context can be retrieved and is not null. + D2CfgContextPtr context; + ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext()); + + // Verify that the global scalars have the proper values. + D2ParamsPtr& d2_params = context->getD2Params(); + ASSERT_TRUE(d2_params); + + EXPECT_EQ(isc::asiolink::IOAddress("192.168.1.33"), + d2_params->getIpAddress()); + EXPECT_EQ(88, d2_params->getPort()); + EXPECT_EQ(333, d2_params->getDnsServerTimeout()); + EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_params->getNcrProtocol()); + EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_params->getNcrFormat()); + + // Verify that the control socket can be retrieved. + ConstElementPtr ctrl_sock = context->getControlSocketInfo(); + ASSERT_TRUE(ctrl_sock); + ASSERT_EQ(Element::map, ctrl_sock->getType()); + EXPECT_EQ(2, ctrl_sock->size()); + ASSERT_TRUE(ctrl_sock->get("socket-type")); + EXPECT_EQ("\"unix\"", ctrl_sock->get("socket-type")->str()); + ASSERT_TRUE(ctrl_sock->get("socket-name")); + EXPECT_EQ("\"/tmp/d2-ctrl-channel\"", ctrl_sock->get("socket-name")->str()); + + // Verify that the hooks libraries can be retrieved. + const HookLibsCollection libs = context->getHooksConfig().get(); + ASSERT_EQ(1, libs.size()); + EXPECT_EQ(string(CALLOUT_LIBRARY), libs[0].first); + ASSERT_TRUE(libs[0].second); + EXPECT_EQ("{ \"param1\": \"foo\" }", libs[0].second->str()); + + // Verify that the forward manager can be retrieved. + DdnsDomainListMgrPtr mgr = context->getForwardMgr(); + ASSERT_TRUE(mgr); + EXPECT_EQ("forward-ddns", mgr->getName()); + + // Verify that the forward manager has the correct number of domains. + DdnsDomainMapPtr domains = mgr->getDomains(); + ASSERT_TRUE(domains); + int count = domains->size(); + EXPECT_EQ(2, count); + + // Verify that the server count in each of the forward manager domains. + // NOTE that since prior tests have validated server parsing, we are are + // assuming that the servers did in fact parse correctly if the correct + // number of them are there. + DdnsDomainMapPair domain_pair; + BOOST_FOREACH(domain_pair, (*domains)) { + DdnsDomainPtr domain = domain_pair.second; + DnsServerInfoStoragePtr servers = domain->getServers(); + count = servers->size(); + EXPECT_TRUE(servers); + EXPECT_EQ(3, count); + } + + // Verify that the reverse manager can be retrieved. + mgr = context->getReverseMgr(); + ASSERT_TRUE(mgr); + EXPECT_EQ("reverse-ddns", mgr->getName()); + + // Verify that the reverse manager has the correct number of domains. + domains = mgr->getDomains(); + count = domains->size(); + EXPECT_EQ(2, count); + + // Verify that the server count in each of the reverse manager domains. + // NOTE that since prior tests have validated server parsing, we are are + // assuming that the servers did in fact parse correctly if the correct + // number of them are there. + BOOST_FOREACH(domain_pair, (*domains)) { + DdnsDomainPtr domain = domain_pair.second; + DnsServerInfoStoragePtr servers = domain->getServers(); + count = servers->size(); + EXPECT_TRUE(servers); + EXPECT_EQ(3, count); + } + + // Test directional update flags. + EXPECT_TRUE(cfg_mgr_->forwardUpdatesEnabled()); + EXPECT_TRUE(cfg_mgr_->reverseUpdatesEnabled()); + + // Verify that parsing the exact same configuration a second time + // does not cause a duplicate value errors. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + ASSERT_TRUE(checkAnswer(0)); +} + +/// @brief Tests the basics of the D2CfgMgr FQDN-domain matching +/// This test uses a valid configuration to exercise the D2CfgMgr +/// forward FQDN-to-domain matching. +/// It verifies that: +/// 1. Given an FQDN which exactly matches a domain's name, that domain is +/// returned as match. +/// 2. Given a FQDN for sub-domain in the list, returns the proper match. +/// 3. Given a FQDN that matches no domain name, returns the wild card domain +/// as a match. +TEST_F(D2CfgMgrTest, forwardMatch) { + // Create configuration with one domain, one sub domain, and the wild + // card. + std::string config = "{ " + "\"ip-address\" : \"192.168.1.33\" , " + "\"port\" : 88 , " + "\"tsig-keys\": [] ," + "\"forward-ddns\" : {" + "\"ddns-domains\": [ " + "{ \"name\": \"example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" } " + " ] } " + ", " + "{ \"name\": \"one.example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.2\" } " + " ] } " + ", " + "{ \"name\": \"*\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.3\" } " + " ] } " + "] }, " + "\"reverse-ddns\" : {} " + "}"; + + // Verify that we can parse the configuration. + RUN_CONFIG_OK(config); + + // Verify that the D2 context can be retrieved and is not null. + D2CfgContextPtr context; + ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext()); + + // Test directional update flags. + EXPECT_TRUE(cfg_mgr_->forwardUpdatesEnabled()); + EXPECT_FALSE(cfg_mgr_->reverseUpdatesEnabled()); + + DdnsDomainPtr match; + // Verify that an exact match works. + EXPECT_TRUE(cfg_mgr_->matchForward("example.com", match)); + EXPECT_EQ("example.com", match->getName()); + + // Verify that search is case insensitive. + EXPECT_TRUE(cfg_mgr_->matchForward("EXAMPLE.COM", match)); + EXPECT_EQ("example.com", match->getName()); + + // Verify that an exact match works. + EXPECT_TRUE(cfg_mgr_->matchForward("one.example.com", match)); + EXPECT_EQ("one.example.com", match->getName()); + + // Verify that a FQDN for sub-domain matches. + EXPECT_TRUE(cfg_mgr_->matchForward("blue.example.com", match)); + EXPECT_EQ("example.com", match->getName()); + + // Verify that a FQDN for sub-domain matches. + EXPECT_TRUE(cfg_mgr_->matchForward("red.one.example.com", match)); + EXPECT_EQ("one.example.com", match->getName()); + + // Verify that an FQDN with no match, returns the wild card domain. + EXPECT_TRUE(cfg_mgr_->matchForward("shouldbe.wildcard", match)); + EXPECT_EQ("*", match->getName()); + + // Verify that an attempt to match an empty FQDN throws. + ASSERT_THROW(cfg_mgr_->matchForward("", match), D2CfgError); +} + +/// @brief Tests domain matching when there is no wild card domain. +/// This test verifies that matches are found only for FQDNs that match +/// some or all of a domain name. FQDNs without matches should not return +/// a match. +TEST_F(D2CfgMgrTest, matchNoWildcard) { + // Create a configuration with one domain, one sub-domain, and NO wild card. + std::string config = "{ " + "\"ip-address\" : \"192.168.1.33\" , " + "\"port\" : 88 , " + "\"tsig-keys\": [] ," + "\"forward-ddns\" : {" + "\"ddns-domains\": [ " + "{ \"name\": \"example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" } " + " ] } " + ", " + "{ \"name\": \"one.example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.2\" } " + " ] } " + "] }, " + "\"reverse-ddns\" : {} " + " }"; + + // Verify that we can parse the configuration. + RUN_CONFIG_OK(config); + + // Verify that the D2 context can be retrieved and is not null. + D2CfgContextPtr context; + ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext()); + + DdnsDomainPtr match; + // Verify that full or partial matches, still match. + EXPECT_TRUE(cfg_mgr_->matchForward("example.com", match)); + EXPECT_EQ("example.com", match->getName()); + + EXPECT_TRUE(cfg_mgr_->matchForward("blue.example.com", match)); + EXPECT_EQ("example.com", match->getName()); + + EXPECT_TRUE(cfg_mgr_->matchForward("red.one.example.com", match)); + EXPECT_EQ("one.example.com", match->getName()); + + // Verify that a FQDN with no match, fails to match. + EXPECT_FALSE(cfg_mgr_->matchForward("shouldbe.wildcard", match)); +} + +/// @brief Tests domain matching when there is ONLY a wild card domain. +/// This test verifies that any FQDN matches the wild card. +TEST_F(D2CfgMgrTest, matchAll) { + std::string config = "{ " + "\"ip-address\" : \"192.168.1.33\" , " + "\"port\" : 88 , " + "\"tsig-keys\": [] ," + "\"forward-ddns\" : {" + "\"ddns-domains\": [ " + "{ \"name\": \"*\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" } " + " ] } " + "] }, " + "\"reverse-ddns\" : {} " + "}"; + + // Verify that we can parse the configuration. + RUN_CONFIG_OK(config); + + // Verify that the D2 context can be retrieved and is not null. + D2CfgContextPtr context; + ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext()); + + // Verify that wild card domain is returned for any FQDN. + DdnsDomainPtr match; + EXPECT_TRUE(cfg_mgr_->matchForward("example.com", match)); + EXPECT_EQ("*", match->getName()); + EXPECT_TRUE(cfg_mgr_->matchForward("shouldbe.wildcard", match)); + EXPECT_EQ("*", match->getName()); + + // Verify that an attempt to match an empty FQDN still throws. + ASSERT_THROW(cfg_mgr_->matchReverse("", match), D2CfgError); + +} + +/// @brief Tests the basics of the D2CfgMgr reverse FQDN-domain matching +/// This test uses a valid configuration to exercise the D2CfgMgr's +/// reverse FQDN-to-domain matching. +/// It verifies that: +/// 1. Given an FQDN which exactly matches a domain's name, that domain is +/// returned as match. +/// 2. Given a FQDN for sub-domain in the list, returns the proper match. +/// 3. Given a FQDN that matches no domain name, returns the wild card domain +/// as a match. +TEST_F(D2CfgMgrTest, matchReverse) { + std::string config = "{ " + "\"ip-address\" : \"192.168.1.33\" , " + "\"port\" : 88 , " + "\"tsig-keys\": [] ," + "\"forward-ddns\" : {}, " + "\"reverse-ddns\" : {" + "\"ddns-domains\": [ " + "{ \"name\": \"5.100.168.192.in-addr.arpa.\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" } " + " ] }, " + "{ \"name\": \"100.200.192.in-addr.arpa.\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" } " + " ] }, " + "{ \"name\": \"170.192.in-addr.arpa.\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" } " + " ] }, " + // Note mixed case to test case insensitivity. + "{ \"name\": \"2.0.3.0.8.b.d.0.1.0.0.2.IP6.ARPA.\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" } " + " ] }," + "{ \"name\": \"*\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" } " + " ] } " + "] } }"; + + // Verify that we can parse the configuration. + RUN_CONFIG_OK(config); + + // Verify that the D2 context can be retrieved and is not null. + D2CfgContextPtr context; + ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext()); + + // Test directional update flags. + EXPECT_FALSE(cfg_mgr_->forwardUpdatesEnabled()); + EXPECT_TRUE(cfg_mgr_->reverseUpdatesEnabled()); + + DdnsDomainPtr match; + + // Verify an exact match. + EXPECT_TRUE(cfg_mgr_->matchReverse("192.168.100.5", match)); + EXPECT_EQ("5.100.168.192.in-addr.arpa.", match->getName()); + + // Verify a sub-domain match. + EXPECT_TRUE(cfg_mgr_->matchReverse("192.200.100.27", match)); + EXPECT_EQ("100.200.192.in-addr.arpa.", match->getName()); + + // Verify a sub-domain match. + EXPECT_TRUE(cfg_mgr_->matchReverse("192.170.50.30", match)); + EXPECT_EQ("170.192.in-addr.arpa.", match->getName()); + + // Verify a wild card match. + EXPECT_TRUE(cfg_mgr_->matchReverse("1.1.1.1", match)); + EXPECT_EQ("*", match->getName()); + + // Verify a IPv6 match. + EXPECT_TRUE(cfg_mgr_->matchReverse("2001:db8:302:99::",match)); + EXPECT_EQ("2.0.3.0.8.b.d.0.1.0.0.2.IP6.ARPA.", match->getName()); + + // Verify a IPv6 wild card match. + EXPECT_TRUE(cfg_mgr_->matchReverse("2001:db8:99:302::",match)); + EXPECT_EQ("*", match->getName()); + + // Verify that an attempt to match an invalid IP address throws. + ASSERT_THROW(cfg_mgr_->matchReverse("", match), D2CfgError); +} + +/// @brief Tests D2 config parsing against a wide range of config permutations. +/// +/// It tests for both syntax errors that the JSON parsing (D2ParserContext) +/// should detect as well as post-JSON parsing logic errors generated by +/// the Element parsers (i.e...SimpleParser/DhcpParser derivations) +/// +/// It iterates over all of the test configurations described in given file. +/// The file content is JSON specialized to this test. The format of the file +/// is: +/// +/// @code +/// # The file must open with a list. It's name is arbitrary. +/// +/// { "test_list" : +/// [ +/// +/// # Test one starts here: +/// { +/// +/// # Each test has: +/// # 1. description - optional text description +/// # 2. syntax-error - error JSON parser should emit (omit if none) +/// # 3. logic-error - error element parser(s) should emit (omit if none) +/// # 4. data - configuration text to parse +/// # +/// "description" : "<text describing test>", +/// "syntax-error" : "<exact text from JSON parser including position>" , +/// "logic-error" : "<exact text from element parser including position>" , +/// "data" : +/// { +/// # configuration elements here +/// "bool_val" : false, +/// "some_map" : {} +/// # : +/// } +/// } +/// +/// # Next test would start here +/// , +/// { +/// } +/// +/// ]} +/// +/// @endcode +/// +/// (The file supports comments per Element::fromJSONFile()) +/// +TEST_F(D2CfgMgrTest, configPermutations) { + std::string test_file = testDataFile("d2_cfg_tests.json"); + isc::data::ConstElementPtr tests; + + // Read contents of the file and parse it as JSON. Note it must contain + // all valid JSON, we aren't testing JSON parsing. + try { + tests = isc::data::Element::fromJSONFile(test_file, true); + } catch (const std::exception& ex) { + FAIL() << "ERROR parsing file : " << test_file << " : " << ex.what(); + } + + // Read in each test For each test, read: + // + // 1. description - optional text description + // 2. syntax-error or logic-error or neither + // 3. data - configuration text to parse + // 4. convert data into JSON text + // 5. submit JSON for parsing + isc::data::ConstElementPtr test; + ASSERT_TRUE(tests->get("test-list")); + BOOST_FOREACH(test, tests->get("test-list")->listValue()) { + // Grab the description. + std::string description = "<no desc>"; + isc::data::ConstElementPtr elem = test->get("description"); + if (elem) { + elem->getValue(description); + } + + // Grab the expected error message, if there is one. + std::string expected_error = ""; + RunConfigMode mode = NO_ERROR; + elem = test->get("syntax-error"); + if (elem) { + elem->getValue(expected_error); + mode = SYNTAX_ERROR; + } else { + elem = test->get("logic-error"); + if (elem) { + elem->getValue(expected_error); + mode = LOGIC_ERROR; + } + } + + // Grab the test's configuration data. + isc::data::ConstElementPtr data = test->get("data"); + ASSERT_TRUE(data) << "No data for test: " << test->getPosition(); + + // Convert the test data back to JSON text, then submit it for parsing. + stringstream os; + data->toJSON(os); + EXPECT_TRUE(runConfigOrFail(os.str(), mode, expected_error)) + << " failed for test: " << test->getPosition() << std::endl; + } +} + +/// @brief Tests comments. +TEST_F(D2CfgMgrTest, comments) { + std::string config = "{ " + "\"comment\": \"D2 config\" , " + "\"ip-address\" : \"192.168.1.33\" , " + "\"port\" : 88 , " + "\"control-socket\": {" + " \"comment\": \"Control channel\" , " + " \"socket-type\": \"unix\" ," + " \"socket-name\": \"/tmp/d2-ctrl-channel\" " + "}," + "\"tsig-keys\": [" + "{" + " \"user-context\": { " + " \"comment\": \"Indirect comment\" } , " + " \"name\": \"d2_key.example.com\" , " + " \"algorithm\": \"hmac-md5\" , " + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " + "}" + "]," + "\"forward-ddns\" : {" + "\"ddns-domains\": [ " + "{ \"comment\": \"A DDNS domain\" , " + " \"name\": \"example.com\" , " + " \"key-name\": \"d2_key.example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" , " + " \"user-context\": { \"version\": 1 } } " + " ] } " + "] } }"; + + // Should parse without error. + RUN_CONFIG_OK(config); + + // Check the D2 context. + D2CfgContextPtr d2_context; + ASSERT_NO_THROW(d2_context = cfg_mgr_->getD2CfgContext()); + ASSERT_TRUE(d2_context); + + // Check global user context. + ConstElementPtr ctx = d2_context->getContext(); + ASSERT_TRUE(ctx); + ASSERT_EQ(1, ctx->size()); + ASSERT_TRUE(ctx->get("comment")); + EXPECT_EQ("\"D2 config\"", ctx->get("comment")->str()); + + // Check control socket. + ConstElementPtr ctrl_sock = d2_context->getControlSocketInfo(); + ASSERT_TRUE(ctrl_sock); + ASSERT_TRUE(ctrl_sock->get("user-context")); + EXPECT_EQ("{ \"comment\": \"Control channel\" }", + ctrl_sock->get("user-context")->str()); + + // Check TSIG keys. + TSIGKeyInfoMapPtr keys = d2_context->getKeys(); + ASSERT_TRUE(keys); + ASSERT_EQ(1, keys->size()); + + // Check the TSIG key. + TSIGKeyInfoMap::iterator gotkey = keys->find("d2_key.example.com"); + ASSERT_TRUE(gotkey != keys->end()); + TSIGKeyInfoPtr key = gotkey->second; + ASSERT_TRUE(key); + + // Check the TSIG key user context. + ConstElementPtr key_ctx = key->getContext(); + ASSERT_TRUE(key_ctx); + ASSERT_EQ(1, key_ctx->size()); + ASSERT_TRUE(key_ctx->get("comment")); + EXPECT_EQ("\"Indirect comment\"", key_ctx->get("comment")->str()); + + // Check the forward manager. + DdnsDomainListMgrPtr mgr = d2_context->getForwardMgr(); + ASSERT_TRUE(mgr); + EXPECT_EQ("forward-ddns", mgr->getName()); + DdnsDomainMapPtr domains = mgr->getDomains(); + ASSERT_TRUE(domains); + ASSERT_EQ(1, domains->size()); + + // Check the DDNS domain. + DdnsDomainMap::iterator gotdns = domains->find("example.com"); + ASSERT_TRUE(gotdns != domains->end()); + DdnsDomainPtr domain = gotdns->second; + ASSERT_TRUE(domain); + + // Check the DNS server. + DnsServerInfoStoragePtr servers = domain->getServers(); + ASSERT_TRUE(servers); + ASSERT_EQ(1, servers->size()); + DnsServerInfoPtr server = (*servers)[0]; + ASSERT_TRUE(server); + + // Check the DNS server user context. + ConstElementPtr srv_ctx = server->getContext(); + ASSERT_TRUE(srv_ctx); + ASSERT_EQ(1, srv_ctx->size()); + ASSERT_TRUE(srv_ctx->get("version")); + EXPECT_EQ("1", srv_ctx->get("version")->str()); +} + +} // end of anonymous namespace diff --git a/src/bin/d2/tests/d2_command_unittest.cc b/src/bin/d2/tests/d2_command_unittest.cc new file mode 100644 index 0000000..8f956e7 --- /dev/null +++ b/src/bin/d2/tests/d2_command_unittest.cc @@ -0,0 +1,1391 @@ +// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <asiolink/interval_timer.h> +#include <asiolink/io_service.h> +#include <cc/command_interpreter.h> +#include <config/command_mgr.h> +#include <config/timeouts.h> +#include <testutils/io_utils.h> +#include <testutils/unix_control_client.h> +#include <d2/d2_controller.h> +#include <d2/d2_process.h> +#include <d2/parser_context.h> +#include <gtest/gtest.h> +#include <testutils/sandbox.h> +#include <boost/pointer_cast.hpp> +#include <fstream> +#include <iostream> +#include <sstream> +#include <thread> +#include <unistd.h> + +using namespace std; +using namespace isc; +using namespace isc::asiolink; +using namespace isc::config; +using namespace isc::d2; +using namespace isc::data; +using namespace isc::dhcp::test; +using namespace isc::process; +using namespace boost::asio; +namespace ph = std::placeholders; + +namespace isc { +namespace d2 { + +class NakedD2Controller; +typedef boost::shared_ptr<NakedD2Controller> NakedD2ControllerPtr; + +class NakedD2Controller : public D2Controller { + // "Naked" D2 controller, exposes internal methods. +public: + static DControllerBasePtr& instance() { + static DControllerBasePtr controller_ptr; + if (!controller_ptr) { + controller_ptr.reset(new NakedD2Controller()); + } + + return (controller_ptr); + } + + virtual ~NakedD2Controller() { deregisterCommands(); } + + using DControllerBase::getIOService; + using DControllerBase::initProcess; + + D2ProcessPtr getProcess() { + return (boost::dynamic_pointer_cast<D2Process>(DControllerBase::getProcess())); + } + +private: + NakedD2Controller() { } +}; + +}; // namespace isc::d2 +}; // namespace isc + +namespace { + +/// @brief Simple RAII class which stops IO service upon destruction +/// of the object. +class IOServiceWork { +public: + + /// @brief Constructor. + /// + /// @param io_service Pointer to the IO service to be stopped. + explicit IOServiceWork(const IOServicePtr& io_service) + : io_service_(io_service) { + } + + /// @brief Destructor. + /// + /// Stops IO service. + ~IOServiceWork() { + io_service_->stop(); + } + +private: + + /// @brief Pointer to the IO service to be stopped upon destruction. + IOServicePtr io_service_; + +}; + +/// @brief Fixture class intended for testing control channel in D2. +class CtrlChannelD2Test : public ::testing::Test { +public: + isc::test::Sandbox sandbox; + + /// @brief Path to the UNIX socket being used to communicate with the server. + string socket_path_; + + /// @brief Reference to the base controller object. + DControllerBasePtr& server_; + + /// @brief Cast controller object. + NakedD2Controller* d2Controller() { + return (dynamic_cast<NakedD2Controller*>(server_.get())); + } + + /// @brief Configuration file. + static const char* CFG_TEST_FILE; + + /// @brief Default constructor. + /// + /// Sets socket path to its default value. + CtrlChannelD2Test() + : server_(NakedD2Controller::instance()) { + const char* env = getenv("KEA_SOCKET_TEST_DIR"); + if (env) { + socket_path_ = string(env) + "/d2.sock"; + } else { + socket_path_ = sandbox.join("d2.sock"); + } + ::remove(socket_path_.c_str()); + } + + /// @brief Destructor. + ~CtrlChannelD2Test() { + // Deregister & co. + server_.reset(); + + // Remove files. + ::remove(CFG_TEST_FILE); + ::remove(socket_path_.c_str()); + + // Reset command manager. + CommandMgr::instance().deregisterAll(); + CommandMgr::instance().setConnectionTimeout(TIMEOUT_DHCP_SERVER_RECEIVE_COMMAND); + } + + /// @brief Returns pointer to the server's IO service. + /// + /// @return Pointer to the server's IO service or null pointer if the + /// hasn't been created server. + IOServicePtr getIOService() { + return (server_ ? d2Controller()->getIOService() : IOServicePtr()); + } + + /// @brief Runs parser in DHCPDDNS mode + /// + /// @param config input configuration + /// @param verbose display errors + /// @return element pointer representing the configuration + ElementPtr parseDHCPDDNS(const string& config, bool verbose = false) { + try { + D2ParserContext ctx; + return (ctx.parseString(config, + D2ParserContext::PARSER_SUB_DHCPDDNS)); + } catch (const std::exception& ex) { + if (verbose) { + cout << "EXCEPTION: " << ex.what() << endl; + } + throw; + } + } + + /// @brief Create a server with a command channel. + void createUnixChannelServer() { + ::remove(socket_path_.c_str()); + + // Just a simple config. The important part here is the socket + // location information. + string header = + "{" + " \"ip-address\": \"192.168.77.1\"," + " \"port\": 777," + " \"control-socket\": {" + " \"socket-type\": \"unix\"," + " \"socket-name\": \""; + + string footer = + "\"" + " }," + " \"tsig-keys\": []," + " \"forward-ddns\" : {}," + " \"reverse-ddns\" : {}" + "}"; + + // Fill in the socket-name value with socket_path_ to make + // the actual configuration text. + string config_txt = header + socket_path_ + footer; + + ASSERT_TRUE(server_); + + ConstElementPtr config; + ASSERT_NO_THROW(config = parseDHCPDDNS(config_txt, true)); + ASSERT_NO_THROW(d2Controller()->initProcess()); + D2ProcessPtr proc = d2Controller()->getProcess(); + ASSERT_TRUE(proc); + ConstElementPtr answer = proc->configure(config, false); + ASSERT_TRUE(answer); + ASSERT_NO_THROW(d2Controller()->registerCommands()); + + int status = 0; + ConstElementPtr txt = parseAnswer(status, answer); + // This should succeed. If not, print the error message. + ASSERT_EQ(0, status) << txt->str(); + + // Now check that the socket was indeed open. + ASSERT_GT(CommandMgr::instance().getControlSocketFD(), -1); + } + + /// @brief Conducts a command/response exchange via UnixCommandSocket. + /// + /// This method connects to the given server over the given socket path. + /// If successful, it then sends the given command and retrieves the + /// server's response. Note that it polls the server's I/O service + /// where needed to cause the server to process IO events on + /// the control channel sockets + /// + /// @param command the command text to execute in JSON form + /// @param response variable into which the received response should be + /// placed. + void sendUnixCommand(const string& command, string& response) { + response = ""; + boost::scoped_ptr<UnixControlClient> client; + client.reset(new UnixControlClient()); + ASSERT_TRUE(client); + + // Connect to the server. This is expected to trigger server's acceptor + // handler when IOService::poll() is run. + ASSERT_TRUE(client->connectToServer(socket_path_)); + ASSERT_NO_THROW(getIOService()->poll()); + + // Send the command. This will trigger server's handler which receives + // data over the unix domain socket. The server will start sending + // response to the client. + ASSERT_TRUE(client->sendCommand(command)); + ASSERT_NO_THROW(getIOService()->poll()); + + // Read the response generated by the server. Note that getResponse + // only fails if there an IO error or no response data was present. + // It is not based on the response content. + ASSERT_TRUE(client->getResponse(response)); + + // Now disconnect and process the close event. + client->disconnectFromServer(); + + ASSERT_NO_THROW(getIOService()->poll()); + } + + /// @brief Checks response for list-commands. + /// + /// This method checks if the list-commands response is generally sane + /// and whether specified command is mentioned in the response. + /// + /// @param rsp response sent back by the server. + /// @param command command expected to be on the list. + void checkListCommands(const ConstElementPtr& rsp, const string command) { + ConstElementPtr params; + int status_code = -1; + EXPECT_NO_THROW(params = parseAnswer(status_code, rsp)); + EXPECT_EQ(CONTROL_RESULT_SUCCESS, status_code); + ASSERT_TRUE(params); + ASSERT_EQ(Element::list, params->getType()); + + int cnt = 0; + for (size_t i = 0; i < params->size(); ++i) { + string tmp = params->get(i)->stringValue(); + if (tmp == command) { + // Command found, but that's not enough. + // Need to continue working through the list to see + // if there are no duplicates. + cnt++; + } + } + + // Exactly one command on the list is expected. + EXPECT_EQ(1, cnt) << "Command " << command << " not found"; + } + + /// @brief Check if the answer for config-write command is correct. + /// + /// @param response_txt response in text form. + /// (as read from the control socket) + /// @param exp_status expected status. + /// (0 success, 1 failure) + /// @param exp_txt for success cases this defines the expected filename, + /// for failure cases this defines the expected error message. + void checkConfigWrite(const string& response_txt, int exp_status, + const string& exp_txt = "") { + + ConstElementPtr rsp; + EXPECT_NO_THROW(rsp = Element::fromJSON(response_txt)); + ASSERT_TRUE(rsp); + + int status; + ConstElementPtr params = parseAnswer(status, rsp); + EXPECT_EQ(exp_status, status); + + if (exp_status == CONTROL_RESULT_SUCCESS) { + // Let's check couple things... + + // The parameters must include filename. + ASSERT_TRUE(params); + ASSERT_TRUE(params->get("filename")); + ASSERT_EQ(Element::string, params->get("filename")->getType()); + EXPECT_EQ(exp_txt, params->get("filename")->stringValue()); + + // The parameters must include size. And the size + // must indicate some content. + ASSERT_TRUE(params->get("size")); + ASSERT_EQ(Element::integer, params->get("size")->getType()); + int64_t size = params->get("size")->intValue(); + EXPECT_LE(1, size); + + // Now check if the file is really there and suitable for + // opening. + ifstream f(exp_txt, ios::binary | ios::ate); + ASSERT_TRUE(f.good()); + + // Now check that it is the correct size as reported. + EXPECT_EQ(size, static_cast<int64_t>(f.tellg())); + + // Finally, check that it's really a JSON. + ElementPtr from_file = Element::fromJSONFile(exp_txt); + ASSERT_TRUE(from_file); + } else if (exp_status == CONTROL_RESULT_ERROR) { + + // Let's check if the reason for failure was given. + ConstElementPtr text = rsp->get("text"); + ASSERT_TRUE(text); + ASSERT_EQ(Element::string, text->getType()); + EXPECT_EQ(exp_txt, text->stringValue()); + } else { + ADD_FAILURE() << "Invalid expected status: " << exp_status; + } + } + + /// @brief Handler for long command. + /// + /// It checks whether the received command is equal to the one specified + /// as an argument. + /// + /// @param expected_command String representing an expected command. + /// @param command_name Command name received by the handler. + /// @param arguments Command arguments received by the handler. + /// + /// @returns Success answer. + static ConstElementPtr + longCommandHandler(const string& expected_command, + const string& command_name, + const ConstElementPtr& arguments) { + // The handler is called with a command name and the structure holding + // command arguments. We have to rebuild the command from those + // two arguments so as it can be compared against expected_command. + ElementPtr entire_command = Element::createMap(); + entire_command->set("command", Element::create(command_name)); + entire_command->set("arguments", (arguments)); + + // The rebuilt command will have a different order of parameters so + // let's parse expected_command back to JSON to guarantee that + // both structures are built using the same order. + EXPECT_EQ(Element::fromJSON(expected_command)->str(), + entire_command->str()); + return (createAnswer(0, "long command received ok")); + } + + /// @brief Command handler which generates long response. + /// + /// This handler generates a large response (over 400kB). It includes + /// a list of randomly generated strings to make sure that the test + /// can catch out of order delivery. + static ConstElementPtr + longResponseHandler(const string&, const ConstElementPtr&) { + ElementPtr arguments = Element::createList(); + for (unsigned i = 0; i < 80000; ++i) { + std::ostringstream s; + s << std::setw(5) << i; + arguments->add(Element::create(s.str())); + } + return (createAnswer(0, arguments)); + } +}; + +const char* CtrlChannelD2Test::CFG_TEST_FILE = "d2-test-config.json"; + +// Test bad syntax rejected by the parser. +TEST_F(CtrlChannelD2Test, parser) { + // no empty map. + string bad1 = + "{" + " \"ip-address\": \"192.168.77.1\"," + " \"port\": 777," + " \"control-socket\": { }," + " \"tsig-keys\": []," + " \"forward-ddns\" : {}," + " \"reverse-ddns\" : {}" + "}"; + ASSERT_THROW(parseDHCPDDNS(bad1), D2ParseError); + + // unknown keyword. + string bad2 = + "{" + " \"ip-address\": \"192.168.77.1\"," + " \"port\": 777," + " \"control-socket\": {" + " \"socket-type\": \"unix\"," + " \"socket-name\": \"/tmp/d2.sock\"," + " \"bogus\": \"unknown...\"" + " }," + " \"tsig-keys\": []," + " \"forward-ddns\" : {}," + " \"reverse-ddns\" : {}" + "}"; + ASSERT_THROW(parseDHCPDDNS(bad2), D2ParseError); +} + +// Test bad syntax rejected by the process. +TEST_F(CtrlChannelD2Test, configure) { + ASSERT_TRUE(server_); + ASSERT_NO_THROW(d2Controller()->initProcess()); + D2ProcessPtr proc = d2Controller()->getProcess(); + ASSERT_TRUE(proc); + + // no type. + string bad1 = + "{" + " \"ip-address\": \"192.168.77.1\"," + " \"port\": 777," + " \"control-socket\": {" + " \"socket-name\": \"/tmp/d2.sock\"" + " }," + " \"tsig-keys\": []," + " \"forward-ddns\" : {}," + " \"reverse-ddns\" : {}" + "}"; + ConstElementPtr config; + ASSERT_NO_THROW(config = parseDHCPDDNS(bad1, true)); + + ConstElementPtr answer = proc->configure(config, false); + ASSERT_TRUE(answer); + + int status = 0; + ConstElementPtr txt = parseAnswer(status, answer); + EXPECT_EQ(1, status); + ASSERT_TRUE(txt); + ASSERT_EQ(Element::string, txt->getType()); + EXPECT_EQ("Mandatory 'socket-type' parameter missing", txt->stringValue()); + EXPECT_EQ(-1, CommandMgr::instance().getControlSocketFD()); + + // bad type. + string bad2 = + "{" + " \"ip-address\": \"192.168.77.1\"," + " \"port\": 777," + " \"control-socket\": {" + " \"socket-type\": \"bogus\"," + " \"socket-name\": \"/tmp/d2.sock\"" + " }," + " \"tsig-keys\": []," + " \"forward-ddns\" : {}," + " \"reverse-ddns\" : {}" + "}"; + ASSERT_NO_THROW(config = parseDHCPDDNS(bad2, true)); + + answer = proc->configure(config, false); + ASSERT_TRUE(answer); + + status = 0; + txt = parseAnswer(status, answer); + EXPECT_EQ(1, status); + ASSERT_TRUE(txt); + ASSERT_EQ(Element::string, txt->getType()); + EXPECT_EQ("Invalid 'socket-type' parameter value bogus", + txt->stringValue()); + EXPECT_EQ(-1, CommandMgr::instance().getControlSocketFD()); + + // no name. + string bad3 = + "{" + " \"ip-address\": \"192.168.77.1\"," + " \"port\": 777," + " \"control-socket\": {" + " \"socket-type\": \"unix\"" + " }," + " \"tsig-keys\": []," + " \"forward-ddns\" : {}," + " \"reverse-ddns\" : {}" + "}"; + ASSERT_NO_THROW(config = parseDHCPDDNS(bad3, true)); + + answer = proc->configure(config, false); + ASSERT_TRUE(answer); + + status = 0; + txt = parseAnswer(status, answer); + EXPECT_EQ(1, status); + ASSERT_TRUE(txt); + ASSERT_EQ(Element::string, txt->getType()); + EXPECT_EQ("Mandatory 'socket-name' parameter missing", + txt->stringValue()); + EXPECT_EQ(-1, CommandMgr::instance().getControlSocketFD()); +} + +// This test checks which commands are registered by the D2 server. +TEST_F(CtrlChannelD2Test, commandsRegistration) { + + ConstElementPtr list_cmds = createCommand("list-commands"); + ConstElementPtr answer; + + // By default the list should be empty (except the standard list-commands + // supported by the CommandMgr itself). + EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds)); + ASSERT_TRUE(answer); + ASSERT_TRUE(answer->get("arguments")); + EXPECT_EQ("[ \"list-commands\" ]", answer->get("arguments")->str()); + + // Created server should register several additional commands. + EXPECT_NO_THROW(createUnixChannelServer()); + + EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds)); + ASSERT_TRUE(answer); + ASSERT_TRUE(answer->get("arguments")); + string command_list = answer->get("arguments")->str(); + + EXPECT_TRUE(command_list.find("\"list-commands\"") != string::npos); + EXPECT_TRUE(command_list.find("\"build-report\"") != string::npos); + EXPECT_TRUE(command_list.find("\"config-get\"") != string::npos); + EXPECT_TRUE(command_list.find("\"config-reload\"") != string::npos); + EXPECT_TRUE(command_list.find("\"config-set\"") != string::npos); + EXPECT_TRUE(command_list.find("\"config-test\"") != string::npos); + EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos); + EXPECT_TRUE(command_list.find("\"shutdown\"") != string::npos); + EXPECT_TRUE(command_list.find("\"status-get\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-get\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-get-all\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-reset\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-reset-all\"") != string::npos); + EXPECT_TRUE(command_list.find("\"version-get\"") != string::npos); + + // Ok, and now delete the server. It should deregister its commands. + server_.reset(); + + // The list should be (almost) empty again. + EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_cmds)); + ASSERT_TRUE(answer); + ASSERT_TRUE(answer->get("arguments")); + EXPECT_EQ("[ \"list-commands\" ]", answer->get("arguments")->str()); +} + +// Tests that the server properly responds to invalid commands. +TEST_F(CtrlChannelD2Test, invalid) { + EXPECT_NO_THROW(createUnixChannelServer()); + string response; + + sendUnixCommand("{ \"command\": \"bogus\" }", response); + EXPECT_EQ("{ \"result\": 2, \"text\": \"'bogus' command not supported.\" }", + response); + + sendUnixCommand("utter nonsense", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"invalid first character u\" }", + response); +} + +// Tests that the server properly responds to shutdown command. +TEST_F(CtrlChannelD2Test, shutdown) { + EXPECT_NO_THROW(createUnixChannelServer()); + string response; + + sendUnixCommand("{ \"command\": \"shutdown\" }", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"Shutdown initiated, type is: normal\" }", + response); + EXPECT_EQ(EXIT_SUCCESS, server_->getExitValue()); +} + +// Tests that the server sets exit value supplied as argument +// to shutdown command. +TEST_F(CtrlChannelD2Test, shutdownExitValue) { + EXPECT_NO_THROW(createUnixChannelServer()); + string response; + + sendUnixCommand("{ \"command\": \"shutdown\", " + "\"arguments\": { \"exit-value\": 77 }}", + response); + + EXPECT_EQ("{ \"result\": 0, \"text\": \"Shutdown initiated, type is: normal\" }", + response); + + EXPECT_EQ(77, server_->getExitValue()); +} + +// This test verifies that the DHCP server handles version-get commands. +TEST_F(CtrlChannelD2Test, getversion) { + EXPECT_NO_THROW(createUnixChannelServer()); + string response; + + // Send the version-get command. + sendUnixCommand("{ \"command\": \"version-get\" }", response); + EXPECT_TRUE(response.find("\"result\": 0") != string::npos); + EXPECT_TRUE(response.find("log4cplus") != string::npos); + EXPECT_FALSE(response.find("GTEST_VERSION") != string::npos); + + // Send the build-report command. + sendUnixCommand("{ \"command\": \"build-report\" }", response); + EXPECT_TRUE(response.find("\"result\": 0") != string::npos); + EXPECT_TRUE(response.find("GTEST_VERSION") != string::npos); +} + +// Tests that the server properly responds to list-commands command. +TEST_F(CtrlChannelD2Test, listCommands) { + EXPECT_NO_THROW(createUnixChannelServer()); + string response; + + sendUnixCommand("{ \"command\": \"list-commands\" }", response); + + ConstElementPtr rsp; + EXPECT_NO_THROW(rsp = Element::fromJSON(response)); + + // We expect the server to report at least the following commands: + checkListCommands(rsp, "build-report"); + checkListCommands(rsp, "config-get"); + checkListCommands(rsp, "config-reload"); + checkListCommands(rsp, "config-set"); + checkListCommands(rsp, "config-test"); + checkListCommands(rsp, "config-write"); + checkListCommands(rsp, "list-commands"); + checkListCommands(rsp, "statistic-get"); + checkListCommands(rsp, "statistic-get-all"); + checkListCommands(rsp, "statistic-reset"); + checkListCommands(rsp, "statistic-reset-all"); + checkListCommands(rsp, "status-get"); + checkListCommands(rsp, "shutdown"); + checkListCommands(rsp, "version-get"); +} + +// This test verifies that the D2 server handles status-get commands +TEST_F(CtrlChannelD2Test, statusGet) { + EXPECT_NO_THROW(createUnixChannelServer()); + + std::string response_txt; + + // Send the version-get command + sendUnixCommand("{ \"command\": \"status-get\" }", response_txt); + ConstElementPtr response; + ASSERT_NO_THROW(response = Element::fromJSON(response_txt)); + ASSERT_TRUE(response); + ASSERT_EQ(Element::map, response->getType()); + EXPECT_EQ(2, response->size()); + ConstElementPtr result = response->get("result"); + ASSERT_TRUE(result); + ASSERT_EQ(Element::integer, result->getType()); + EXPECT_EQ(0, result->intValue()); + ConstElementPtr arguments = response->get("arguments"); + ASSERT_EQ(Element::map, arguments->getType()); + + // The returned pid should be the pid of our process. + auto found_pid = arguments->get("pid"); + ASSERT_TRUE(found_pid); + EXPECT_EQ(static_cast<int64_t>(getpid()), found_pid->intValue()); + + // It is hard to check the actual reload time as it is based + // on current time. Let's just make sure it is within a reasonable + // range. + auto found_reload = arguments->get("reload"); + ASSERT_TRUE(found_reload); + EXPECT_LE(found_reload->intValue(), 5); + EXPECT_GE(found_reload->intValue(), 0); + + /// @todo uptime is not available in this test, because the launch() + /// function is not called. This is not critical to test here, + /// because the same logic is tested for CA and in that case the + /// uptime is tested. +} + +// Tests if the server returns its configuration using config-get. +// Note there are separate tests that verify if toElement() called by the +// config-get handler are actually converting the configuration correctly. +TEST_F(CtrlChannelD2Test, configGet) { + EXPECT_NO_THROW(createUnixChannelServer()); + string response; + + sendUnixCommand("{ \"command\": \"config-get\" }", response); + ConstElementPtr rsp; + + // The response should be a valid JSON. + EXPECT_NO_THROW(rsp = Element::fromJSON(response)); + ASSERT_TRUE(rsp); + + int status; + ConstElementPtr cfg = parseAnswer(status, rsp); + EXPECT_EQ(CONTROL_RESULT_SUCCESS, status); + + // Ok, now roughly check if the response seems legit. + ASSERT_TRUE(cfg); + ASSERT_EQ(Element::map, cfg->getType()); + EXPECT_TRUE(cfg->get("DhcpDdns")); +} + +// Verify that the "config-test" command will do what we expect. +TEST_F(CtrlChannelD2Test, configTest) { + + // Define strings to permutate the config arguments. + // (Note the line feeds makes errors easy to find) + string config_test_txt = "{ \"command\": \"config-test\" \n"; + string args_txt = " \"arguments\": { \n"; + string d2_header = + " \"DhcpDdns\": \n"; + string d2_cfg_txt = + " { \n" + " \"ip-address\": \"192.168.77.1\", \n" + " \"port\": 777, \n" + " \"forward-ddns\" : {}, \n" + " \"reverse-ddns\" : {}, \n" + " \"tsig-keys\": [ \n"; + string key1 = + " {\"name\": \"d2_key.example.com\", \n" + " \"algorithm\": \"hmac-md5\", \n" + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"; + string key2 = + " {\"name\": \"d2_key.billcat.net\", \n" + " \"algorithm\": \"hmac-md5\", \n" + " \"digest-bits\": 120, \n" + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"; + string bad_key = + " {\"BOGUS\": \"d2_key.example.com\", \n" + " \"algorithm\": \"hmac-md5\", \n" + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"; + string key_footer = + " ] \n"; + string control_socket_header = + " ,\"control-socket\": { \n" + " \"socket-type\": \"unix\", \n" + " \"socket-name\": \""; + string control_socket_footer = + "\" \n} \n"; + + ostringstream os; + // Create a valid config with all the parts should parse. + os << d2_cfg_txt + << key1 + << key_footer + << control_socket_header + << socket_path_ + << control_socket_footer + << "}\n"; + + ASSERT_TRUE(server_); + + ConstElementPtr config; + ASSERT_NO_THROW(config = parseDHCPDDNS(os.str(), true)); + ASSERT_NO_THROW(d2Controller()->initProcess()); + D2ProcessPtr proc = d2Controller()->getProcess(); + ASSERT_TRUE(proc); + ConstElementPtr answer = proc->configure(config, false); + ASSERT_TRUE(answer); + EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration applied successfully.\" }", + answer->str()); + ASSERT_NO_THROW(d2Controller()->registerCommands()); + + // Check that the config was indeed applied. + D2CfgMgrPtr cfg_mgr = proc->getD2CfgMgr(); + ASSERT_TRUE(cfg_mgr); + D2CfgContextPtr d2_context = cfg_mgr->getD2CfgContext(); + ASSERT_TRUE(d2_context); + TSIGKeyInfoMapPtr keys = d2_context->getKeys(); + ASSERT_TRUE(keys); + EXPECT_EQ(1, keys->size()); + + ASSERT_GT(CommandMgr::instance().getControlSocketFD(), -1); + + // Create a config with malformed subnet that should fail to parse. + os.str(""); + os << config_test_txt << "," + << args_txt + << d2_header + << d2_cfg_txt + << bad_key + << key_footer + << control_socket_header + << socket_path_ + << control_socket_footer + << "}\n" // close DhcpDdns. + << "}}"; + + // Send the config-test command. + string response; + sendUnixCommand(os.str(), response); + + // Should fail with a syntax error. + EXPECT_EQ("{ \"result\": 1, \"text\": \"missing parameter 'name' (<wire>:9:14)\" }", + response); + + // Check that the config was not lost (fix: reacquire the context). + d2_context = cfg_mgr->getD2CfgContext(); + keys = d2_context->getKeys(); + ASSERT_TRUE(keys); + EXPECT_EQ(1, keys->size()); + + // Create a valid config with two keys and no command channel. + os.str(""); + os << config_test_txt << "," + << args_txt + << d2_header + << d2_cfg_txt + << key1 + << ",\n" + << key2 + << key_footer + << "}\n" // close DhcpDdns. + << "}}"; + + // Verify the control channel socket exists. + ASSERT_TRUE(test::fileExists(socket_path_)); + + // Send the config-test command. + sendUnixCommand(os.str(), response); + + // Verify the control channel socket still exists. + EXPECT_TRUE(test::fileExists(socket_path_)); + + // Verify the configuration was successful. + EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration check successful\" }", + response); + + // Check that the config was not applied. + d2_context = cfg_mgr->getD2CfgContext(); + keys = d2_context->getKeys(); + ASSERT_TRUE(keys); + EXPECT_EQ(1, keys->size()); +} + +// Verify that the "config-set" command will do what we expect. +TEST_F(CtrlChannelD2Test, configSet) { + + // Define strings to permutate the config arguments. + // (Note the line feeds makes errors easy to find) + string config_set_txt = "{ \"command\": \"config-set\" \n"; + string args_txt = " \"arguments\": { \n"; + string d2_header = + " \"DhcpDdns\": \n"; + string d2_cfg_txt = + " { \n" + " \"ip-address\": \"192.168.77.1\", \n" + " \"port\": 777, \n" + " \"forward-ddns\" : {}, \n" + " \"reverse-ddns\" : {}, \n" + " \"tsig-keys\": [ \n"; + string key1 = + " {\"name\": \"d2_key.example.com\", \n" + " \"algorithm\": \"hmac-md5\", \n" + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"; + string key2 = + " {\"name\": \"d2_key.billcat.net\", \n" + " \"algorithm\": \"hmac-md5\", \n" + " \"digest-bits\": 120, \n" + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"; + string bad_key = + " {\"BOGUS\": \"d2_key.example.com\", \n" + " \"algorithm\": \"hmac-md5\", \n" + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\"} \n"; + string key_footer = + " ] \n"; + string control_socket_header = + " ,\"control-socket\": { \n" + " \"socket-type\": \"unix\", \n" + " \"socket-name\": \""; + string control_socket_footer = + "\" \n} \n"; + + ostringstream os; + // Create a valid config with all the parts should parse. + os << d2_cfg_txt + << key1 + << key_footer + << control_socket_header + << socket_path_ + << control_socket_footer + << "}\n"; + + ASSERT_TRUE(server_); + + ConstElementPtr config; + ASSERT_NO_THROW(config = parseDHCPDDNS(os.str(), true)); + ASSERT_NO_THROW(d2Controller()->initProcess()); + D2ProcessPtr proc = d2Controller()->getProcess(); + ASSERT_TRUE(proc); + ConstElementPtr answer = proc->configure(config, false); + ASSERT_TRUE(answer); + EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration applied successfully.\" }", + answer->str()); + ASSERT_NO_THROW(d2Controller()->registerCommands()); + + // Check that the config was indeed applied. + D2CfgMgrPtr cfg_mgr = proc->getD2CfgMgr(); + ASSERT_TRUE(cfg_mgr); + D2CfgContextPtr d2_context = cfg_mgr->getD2CfgContext(); + ASSERT_TRUE(d2_context); + TSIGKeyInfoMapPtr keys = d2_context->getKeys(); + ASSERT_TRUE(keys); + EXPECT_EQ(1, keys->size()); + + ASSERT_GT(CommandMgr::instance().getControlSocketFD(), -1); + + // Create a config with malformed subnet that should fail to parse. + os.str(""); + os << config_set_txt << "," + << args_txt + << d2_header + << d2_cfg_txt + << bad_key + << key_footer + << control_socket_header + << socket_path_ + << control_socket_footer + << "}\n" // close DhcpDdns. + << "}}"; + + // Send the config-set command. + string response; + sendUnixCommand(os.str(), response); + + // Should fail with a syntax error. + EXPECT_EQ("{ \"result\": 1, \"text\": \"missing parameter 'name' (<wire>:9:14)\" }", + response); + + // Check that the config was not lost (fix: reacquire the context). + d2_context = cfg_mgr->getD2CfgContext(); + keys = d2_context->getKeys(); + ASSERT_TRUE(keys); + EXPECT_EQ(1, keys->size()); + + // Create a valid config with two keys and no command channel. + os.str(""); + os << config_set_txt << "," + << args_txt + << d2_header + << d2_cfg_txt + << key1 + << ",\n" + << key2 + << key_footer + << "}\n" // close DhcpDdns. + << "}}"; + + // Verify the control channel socket exists. + ASSERT_TRUE(test::fileExists(socket_path_)); + + // Send the config-set command. + sendUnixCommand(os.str(), response); + + // Verify the control channel socket no longer exists. + EXPECT_FALSE(test::fileExists(socket_path_)); + + // Verify the configuration was successful. + EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration applied successfully.\" }", + response); + + // Check that the config was applied. + d2_context = cfg_mgr->getD2CfgContext(); + keys = d2_context->getKeys(); + ASSERT_TRUE(keys); + EXPECT_EQ(2, keys->size()); +} + +// Tests if config-write can be called without any parameters. +TEST_F(CtrlChannelD2Test, writeConfigNoFilename) { + EXPECT_NO_THROW(createUnixChannelServer()); + string response; + + // This is normally set by the command line -c parameter. + server_->setConfigFile("test1.json"); + + // If the filename is not explicitly specified, the name used + // in -c command line switch is used. + sendUnixCommand("{ \"command\": \"config-write\" }", response); + + checkConfigWrite(response, CONTROL_RESULT_SUCCESS, "test1.json"); + ::remove("test1.json"); +} + +// Tests if config-write can be called with a valid filename as parameter. +TEST_F(CtrlChannelD2Test, writeConfigFilename) { + EXPECT_NO_THROW(createUnixChannelServer()); + string response; + + sendUnixCommand("{ \"command\": \"config-write\", " + "\"arguments\": { \"filename\": \"test2.json\" } }", + response); + checkConfigWrite(response, CONTROL_RESULT_SUCCESS, "test2.json"); + ::remove("test2.json"); +} + +// Tests if config-reload attempts to reload a file and reports that the +// file is missing. +TEST_F(CtrlChannelD2Test, configReloadMissingFile) { + EXPECT_NO_THROW(createUnixChannelServer()); + string response; + + // This is normally set to whatever value is passed to -c when the server is + // started, but we're not starting it that way, so need to set it by hand. + server_->setConfigFile("does-not-exist.json"); + + // Tell the server to reload its configuration. It should attempt to load + // does-not-exist.json (and fail, because the file is not there). + sendUnixCommand("{ \"command\": \"config-reload\" }", response); + + // Verify the reload was rejected. + string expected = "{ \"result\": 1, \"text\": " + "\"Configuration parsing failed: " + "Unable to open file does-not-exist.json\" }"; + EXPECT_EQ(expected, response); +} + +// Tests if config-reload attempts to reload a file and reports that the +// file is not a valid JSON. +TEST_F(CtrlChannelD2Test, configReloadBrokenFile) { + EXPECT_NO_THROW(createUnixChannelServer()); + string response; + + // This is normally set to whatever value is passed to -c when the server is + // started, but we're not starting it that way, so need to set it by hand. + server_->setConfigFile("testbad.json"); + + // Although Kea is smart, its AI routines are not smart enough to handle + // this one... at least not yet. + ofstream f("testbad.json", ios::trunc); + f << "bla bla bla..."; + f.close(); + + // Tell the server to reload its configuration. It should attempt to load + // testbad.json (and fail, because the file is not valid JSON). + // does-not-exist.json (and fail, because the file is not there). + sendUnixCommand("{ \"command\": \"config-reload\" }", response); + + // Verify the reload was rejected. + string expected = "{ \"result\": 1, \"text\": " + "\"Configuration parsing failed: " + "testbad.json:1.1: Invalid character: b\" }"; + EXPECT_EQ(expected, response); + + // Remove the file. + ::remove("testbad.json"); +} + +// Tests if config-reload attempts to reload a file and reports that the +// file is missing. +TEST_F(CtrlChannelD2Test, configReloadFileValid) { + EXPECT_NO_THROW(createUnixChannelServer()); + string response; + + // This is normally set to whatever value is passed to -c when the server is + // started, but we're not starting it that way, so need to set it by hand. + server_->setConfigFile("testvalid.json"); + + // Ok, enough fooling around. Let's create a valid config. + ofstream f("testvalid.json", ios::trunc); + f << "{ \"DhcpDdns\": " + << "{" + << " \"ip-address\": \"192.168.77.1\" , " + << " \"port\": 777 , " + << "\"tsig-keys\": [], " + << "\"forward-ddns\" : {}, " + << "\"reverse-ddns\" : {} " + << "}" + << " }" << endl; + f.close(); + + // Tell the server to reload its configuration. It should attempt to load + // testvalid.json (and succeed). + sendUnixCommand("{ \"command\": \"config-reload\" }", response); + + // Verify the reload was successful. + string expected = "{ \"result\": 0, \"text\": " + "\"Configuration applied successfully.\" }"; + EXPECT_EQ(expected, response); + + // Check that the config was indeed applied. + D2ProcessPtr proc = d2Controller()->getProcess(); + ASSERT_TRUE(proc); + D2CfgMgrPtr d2_cfg_mgr = proc->getD2CfgMgr(); + ASSERT_TRUE(d2_cfg_mgr); + D2ParamsPtr d2_params = d2_cfg_mgr->getD2Params(); + ASSERT_TRUE(d2_params); + + EXPECT_EQ("192.168.77.1", d2_params->getIpAddress().toText()); + EXPECT_EQ(777, d2_params->getPort()); + EXPECT_FALSE(d2_cfg_mgr->forwardUpdatesEnabled()); + EXPECT_FALSE(d2_cfg_mgr->reverseUpdatesEnabled()); + + // Remove the file. + ::remove("testvalid.json"); +} + +/// Verify that concurrent connections over the control channel can be +/// established. (@todo change when response will be sent in multiple chunks) +TEST_F(CtrlChannelD2Test, concurrentConnections) { + EXPECT_NO_THROW(createUnixChannelServer()); + + boost::scoped_ptr<UnixControlClient> client1(new UnixControlClient()); + ASSERT_TRUE(client1); + + boost::scoped_ptr<UnixControlClient> client2(new UnixControlClient()); + ASSERT_TRUE(client2); + + // Client 1 connects. + ASSERT_TRUE(client1->connectToServer(socket_path_)); + ASSERT_NO_THROW(getIOService()->poll()); + + // Client 2 connects. + ASSERT_TRUE(client2->connectToServer(socket_path_)); + ASSERT_NO_THROW(getIOService()->poll()); + + // Send the command while another client is connected. + ASSERT_TRUE(client2->sendCommand("{ \"command\": \"list-commands\" }")); + ASSERT_NO_THROW(getIOService()->poll()); + + string response; + // The server should respond ok. + ASSERT_TRUE(client2->getResponse(response)); + EXPECT_TRUE(response.find("\"result\": 0") != std::string::npos); + + // Disconnect the servers. + client1->disconnectFromServer(); + client2->disconnectFromServer(); + ASSERT_NO_THROW(getIOService()->poll()); +} + +// This test verifies that the server can receive and process a large command. +TEST_F(CtrlChannelD2Test, longCommand) { + + ostringstream command; + + // This is the desired size of the command sent to the server (1MB). + // The actual size sent will be slightly greater than that. + const size_t command_size = 1024 * 1000; + + while (command.tellp() < command_size) { + + // We're sending command 'foo' with arguments being a list of + // strings. If this is the first transmission, send command name + // and open the arguments list. Also insert the first argument + // so as all subsequent arguments can be prefixed with a comma. + if (command.tellp() == 0) { + command << "{ \"command\": \"foo\", \"arguments\": [ \"begin\""; + + } else { + // Generate a random number and insert it into the stream as + // 10 digits long string. + ostringstream arg; + arg << setw(10) << std::rand(); + // Append the argument in the command. + command << ", \"" << arg.str() << "\"\n"; + + // If we have hit the limit of the command size, close braces to + // get appropriate JSON. + if (command.tellp() > command_size) { + command << "] }"; + } + } + } + + ASSERT_NO_THROW( + CommandMgr::instance().registerCommand("foo", + std::bind(&CtrlChannelD2Test::longCommandHandler, + command.str(), ph::_1, ph::_2)); + ); + + createUnixChannelServer(); + + string response; + std::thread th([this, &response, &command]() { + + // IO service will be stopped automatically when this object goes + // out of scope and is destroyed. This is useful because we use + // asserts which may break the thread in various exit points. + IOServiceWork work(getIOService()); + + // Create client which we will use to send command to the server. + boost::scoped_ptr<UnixControlClient> client(new UnixControlClient()); + ASSERT_TRUE(client); + + // Connect to the server. This will trigger acceptor handler on the + // server side and create a new connection. + ASSERT_TRUE(client->connectToServer(socket_path_)); + + // Initially the remaining_string holds the entire command and we + // will be erasing the portions that we have sent. + string remaining_data = command.str(); + while (!remaining_data.empty()) { + // Send the command in chunks of 1024 bytes. + const size_t l = remaining_data.size() < 1024 ? remaining_data.size() : 1024; + ASSERT_TRUE(client->sendCommand(remaining_data.substr(0, l))); + remaining_data.erase(0, l); + } + + // Set timeout to 5 seconds to allow the time for the server to send + // a response. + const unsigned int timeout = 5; + ASSERT_TRUE(client->getResponse(response, timeout)); + + // We're done. Close the connection to the server. + client->disconnectFromServer(); + }); + + // Run the server until the command has been processed and response + // received. + getIOService()->run(); + + // Wait for the thread to complete. + th.join(); + + EXPECT_EQ("{ \"result\": 0, \"text\": \"long command received ok\" }", + response); +} + +// This test verifies that the server can send long response to the client. +TEST_F(CtrlChannelD2Test, longResponse) { + // We need to generate large response. The simplest way is to create + // a command and a handler which will generate some static response + // of a desired size + ASSERT_NO_THROW( + CommandMgr::instance().registerCommand("foo", + std::bind(&CtrlChannelD2Test::longResponseHandler, ph::_1, ph::_2)); + ); + + createUnixChannelServer(); + + // The UnixControlClient doesn't have any means to check that the entire + // response has been received. What we want to do is to generate a + // reference response using our command handler and then compare + // what we have received over the unix domain socket with this reference + // response to figure out when to stop receiving. + string reference_response = longResponseHandler("foo", ConstElementPtr())->str(); + + // In this stream we're going to collect out partial responses. + ostringstream response; + + // The client is synchronous so it is useful to run it in a thread. + std::thread th([this, &response, reference_response]() { + + // IO service will be stopped automatically when this object goes + // out of scope and is destroyed. This is useful because we use + // asserts which may break the thread in various exit points. + IOServiceWork work(getIOService()); + + // Remember the response size so as we know when we should stop + // receiving. + const size_t long_response_size = reference_response.size(); + + // Create the client and connect it to the server. + boost::scoped_ptr<UnixControlClient> client(new UnixControlClient()); + ASSERT_TRUE(client); + ASSERT_TRUE(client->connectToServer(socket_path_)); + + // Send the stub command. + std::string command = "{ \"command\": \"foo\", \"arguments\": { } }"; + ASSERT_TRUE(client->sendCommand(command)); + + // Keep receiving response data until we have received the full answer. + while (response.tellp() < long_response_size) { + std::string partial; + const unsigned int timeout = 5; + ASSERT_TRUE(client->getResponse(partial, timeout)); + response << partial; + } + + // We have received the entire response, so close the connection and + // stop the IO service. + client->disconnectFromServer(); + }); + + // Run the server until the entire response has been received. + getIOService()->run(); + + // Wait for the thread to complete. + th.join(); + + // Make sure we have received correct response. + EXPECT_EQ(reference_response, response.str()); +} + +// This test verifies that the server signals timeout if the transmission +// takes too long, after receiving a partial command +TEST_F(CtrlChannelD2Test, connectionTimeoutPartialCommand) { + createUnixChannelServer(); + + // Set connection timeout to 2s to prevent long waiting time for the + // timeout during this test. + const unsigned short timeout = 2000; + CommandMgr::instance().setConnectionTimeout(timeout); + + // Server's response will be assigned to this variable. + string response; + + // It is useful to create a thread and run the server and the client + // at the same time and independently. + std::thread th([this, &response]() { + + // IO service will be stopped automatically when this object goes + // out of scope and is destroyed. This is useful because we use + // asserts which may break the thread in various exit points. + IOServiceWork work(getIOService()); + + // Create the client and connect it to the server. + boost::scoped_ptr<UnixControlClient> client(new UnixControlClient()); + ASSERT_TRUE(client); + ASSERT_TRUE(client->connectToServer(socket_path_)); + + // Send partial command. The server will be waiting for the remaining + // part to be sent and will eventually signal a timeout. + string command = "{ \"command\": \"foo\" "; + ASSERT_TRUE(client->sendCommand(command)); + + // Let's wait up to 15s for the server's response. The response + // should arrive sooner assuming that the timeout mechanism for + // the server is working properly. + const unsigned int timeout = 15; + ASSERT_TRUE(client->getResponse(response, timeout)); + + // Explicitly close the client's connection. + client->disconnectFromServer(); + }); + + // Run the server until stopped. + getIOService()->run(); + + // Wait for the thread to return. + th.join(); + + // Check that the server has signalled a timeout. + EXPECT_EQ("{ \"result\": 1, \"text\": \"Connection over control channel timed out, discarded partial command of 19 bytes\" }" , + response); +} + +// This test verifies that the server signals timeout if the transmission +// takes too long, having received no data from the client. +TEST_F(CtrlChannelD2Test, connectionTimeoutNoData) { + createUnixChannelServer(); + + // Set connection timeout to 2s to prevent long waiting time for the + // timeout during this test. + const unsigned short timeout = 2000; + CommandMgr::instance().setConnectionTimeout(timeout); + + // Server's response will be assigned to this variable. + string response; + + // It is useful to create a thread and run the server and the client + // at the same time and independently. + std::thread th([this, &response]() { + + // IO service will be stopped automatically when this object goes + // out of scope and is destroyed. This is useful because we use + // asserts which may break the thread in various exit points. + IOServiceWork work(getIOService()); + + // Create the client and connect it to the server. + boost::scoped_ptr<UnixControlClient> client(new UnixControlClient()); + ASSERT_TRUE(client); + ASSERT_TRUE(client->connectToServer(socket_path_)); + + // Let's wait up to 15s for the server's response. The response + // should arrive sooner assuming that the timeout mechanism for + // the server is working properly. + const unsigned int timeout = 15; + ASSERT_TRUE(client->getResponse(response, timeout)); + + // Explicitly close the client's connection. + client->disconnectFromServer(); + }); + + // Run the server until stopped. + getIOService()->run(); + + // Wait for the thread to return. + th.join(); + + // Check that the server has signalled a timeout. + EXPECT_EQ("{ \"result\": 1, \"text\": \"Connection over control channel timed out\" }", + response); +} + +} // End of anonymous namespace diff --git a/src/bin/d2/tests/d2_controller_unittests.cc b/src/bin/d2/tests/d2_controller_unittests.cc new file mode 100644 index 0000000..0c6e5ec --- /dev/null +++ b/src/bin/d2/tests/d2_controller_unittests.cc @@ -0,0 +1,303 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <asiolink/testutils/timed_signal.h> +#include <cc/command_interpreter.h> +#include <d2srv/testutils/nc_test_utils.h> +#include <d2/d2_controller.h> +#include <d2/d2_process.h> +#include <process/testutils/d_test_stubs.h> + +#include <boost/pointer_cast.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <gtest/gtest.h> + +#include <sstream> + +using namespace isc::asiolink::test; +using namespace isc::process; +using namespace boost::posix_time; + +namespace isc { +namespace d2 { + +/// @brief Test fixture class for testing D2Controller class. +/// +/// This class derives from DControllerTest and wraps a D2Controller. Much of +/// the underlying functionality is in the DControllerBase class which has an +/// extensive set of unit tests that are independent of DHCP-DDNS. +/// @TODO Currently These tests are relatively light and duplicate some of +/// the testing done on the base class. These tests are sufficient to ensure +/// that D2Controller properly derives from its base class and to test the +/// logic that is unique to D2Controller. These tests will be augmented and +/// or new tests added as additional functionality evolves. +/// Unlike the stub testing, there is no use of SimFailure to induce error +/// conditions as this is production code. +class D2ControllerTest : public DControllerTest { +public: + /// @brief Constructor + /// Note the constructor passes in the static D2Controller instance + /// method. + D2ControllerTest() : DControllerTest(D2Controller::instance) { + } + + /// @brief Fetches the D2Controller's D2Process + /// + /// @return A pointer to the process which may be null if it has not yet + /// been instantiated. + D2ProcessPtr getD2Process() { + return (boost::dynamic_pointer_cast<D2Process>(getProcess())); + } + + /// @brief Fetches the D2Process's D2Configuration manager + /// + /// @return A pointer to the manager which may be null if it has not yet + /// been instantiated. + D2CfgMgrPtr getD2CfgMgr() { + D2CfgMgrPtr p; + if (getD2Process()) { + p = getD2Process()->getD2CfgMgr(); + } + + return (p); + } + + /// @brief Fetches the D2Configuration manager's D2CfgContext + /// + /// @return A pointer to the context which may be null if it has not yet + /// been instantiated. + D2CfgContextPtr getD2CfgContext() { + D2CfgContextPtr p; + if (getD2CfgMgr()) { + p = getD2CfgMgr()->getD2CfgContext(); + } + + return (p); + } +}; + +/// @brief Basic Controller instantiation testing. +/// Verifies that the controller singleton gets created and that the +/// basic derivation from the base class is intact. +TEST_F(D2ControllerTest, basicInstanceTesting) { + // Verify the singleton instance can be fetched and that + // it has the correct type. + DControllerBasePtr& controller = DControllerTest::getController(); + ASSERT_TRUE(controller); + ASSERT_NO_THROW(boost::dynamic_pointer_cast<D2Controller>(controller)); + + // Verify that controller's app name is correct. + EXPECT_TRUE(checkAppName(D2Controller::d2_app_name_)); + + // Verify that controller's bin name is correct. + EXPECT_TRUE(checkBinName(D2Controller::d2_bin_name_)); + + // Verify that controller's IOService exists. + EXPECT_TRUE(checkIOService()); + + // Verify that the Process does NOT exist. + EXPECT_FALSE(checkProcess()); +} + +/// @brief Tests basic command line processing. +/// Verifies that: +/// 1. Standard command line options are supported. +/// 2. Invalid options are detected. +TEST_F(D2ControllerTest, commandLineArgs) { + char* argv[] = { const_cast<char*>("progName"), + const_cast<char*>("-c"), + const_cast<char*>(DControllerTest::CFG_TEST_FILE), + const_cast<char*>("-d") }; + int argc = 4; + + // Verify that verbose flag is false initially. + EXPECT_TRUE(checkVerbose(false)); + + // Verify that standard options can be parsed without error. + EXPECT_NO_THROW(parseArgs(argc, argv)); + + // Verify that verbose flag is true. + EXPECT_TRUE(checkVerbose(true)); + + // Verify configuration file name is correct. + EXPECT_TRUE(checkConfigFileName(DControllerTest::CFG_TEST_FILE)); + + // Verify that an unknown option is detected. + char* argv2[] = { const_cast<char*>("progName"), + const_cast<char*>("-x") }; + argc = 2; + EXPECT_THROW(parseArgs(argc, argv2), InvalidUsage); +} + +/// @brief Tests application process creation and initialization. +/// Verifies that the process can be successfully created and initialized. +TEST_F(D2ControllerTest, initProcessTesting) { + ASSERT_NO_THROW(initProcess()); + EXPECT_TRUE(checkProcess()); +} + +/// @brief Tests launch and normal shutdown (stand alone mode). +/// This creates an interval timer to generate a normal shutdown and then +/// launches with a valid, stand-alone command line and no simulated errors. +TEST_F(D2ControllerTest, launchNormalShutdown) { + // Write valid_d2_config and then run launch() for 1000 ms. + time_duration elapsed_time; + runWithConfig(valid_d2_config, 1000, elapsed_time); + + // Give a generous margin to accommodate slower test environs. + EXPECT_TRUE(elapsed_time.total_milliseconds() >= 800 && + elapsed_time.total_milliseconds() <= 1300); +} + +/// @brief Configuration update event testing. +/// This really tests just the ability of the handlers to invoke the necessary +/// chain of methods and handle error conditions. Configuration parsing and +/// retrieval should be tested as part of the d2 configuration management +/// implementation. +/// This test verifies that: +/// 1. A valid configuration yields a successful parse result. +/// 2. That an application process error in configuration updating is handled +/// properly. +TEST_F(D2ControllerTest, configUpdateTests) { + int rcode = -1; + isc::data::ConstElementPtr answer; + + // Initialize the application process. + ASSERT_NO_THROW(initProcess()); + EXPECT_TRUE(checkProcess()); + + // Create a configuration set using a small, valid D2 configuration. + isc::data::ElementPtr config_set = + isc::data::Element::fromJSON(valid_d2_config); + + // Verify that given a valid config we get a successful update result. + answer = updateConfig(config_set); + isc::config::parseAnswer(rcode, answer); + EXPECT_EQ(0, rcode); + + // Verify that given a valid config we get a successful check result. + answer = checkConfig(config_set); + isc::config::parseAnswer(rcode, answer); + EXPECT_EQ(0, rcode); + + // Use an invalid configuration to verify parsing error return. + std::string config = "{ \"ip-address\": 1000 } "; + config_set = isc::data::Element::fromJSON(config); + answer = updateConfig(config_set); + isc::config::parseAnswer(rcode, answer); + EXPECT_EQ(1, rcode); + + // Use an invalid configuration to verify checking error return. + answer = checkConfig(config_set); + isc::config::parseAnswer(rcode, answer); + EXPECT_EQ(1, rcode); +} + +// Tests that the original configuration is retained after a SIGHUP triggered +// reconfiguration fails due to invalid config content. +TEST_F(D2ControllerTest, invalidConfigReload) { + // Schedule to replace the configuration file after launch. This way the + // file is updated after we have done the initial configuration. + scheduleTimedWrite("{ \"string_test\": BOGUS JSON }", 100); + + // Setup to raise SIGHUP in 200 ms. + TimedSignal sighup(*getIOService(), SIGHUP, 200); + + // Write valid_d2_config and then run launch() for a maximum of 500 ms. + time_duration elapsed_time; + runWithConfig(valid_d2_config, 500, elapsed_time); + + // Context is still available post launch. + // Check to see that our configuration matches the original per + // valid_d2_config (see src/lib/process/testutils/d_test_stubs.cc) + D2CfgMgrPtr d2_cfg_mgr = getD2CfgMgr(); + D2ParamsPtr d2_params = d2_cfg_mgr->getD2Params(); + ASSERT_TRUE(d2_params); + + EXPECT_EQ("127.0.0.1", d2_params->getIpAddress().toText()); + EXPECT_EQ(5031, d2_params->getPort()); + EXPECT_TRUE(d2_cfg_mgr->forwardUpdatesEnabled()); + EXPECT_TRUE(d2_cfg_mgr->reverseUpdatesEnabled()); + + /// @todo add a way to trap log file and search it +} + +// Tests that the original configuration is replaced after a SIGHUP triggered +// reconfiguration succeeds. +TEST_F(D2ControllerTest, validConfigReload) { + // Define a replacement config. + const char* second_cfg = + "{" + " \"ip-address\": \"192.168.77.1\" , " + " \"port\": 777 , " + "\"tsig-keys\": [], " + "\"forward-ddns\" : {}, " + "\"reverse-ddns\" : {} " + "}"; + + // Schedule to replace the configuration file after launch. This way the + // file is updated after we have done the initial configuration. + scheduleTimedWrite(second_cfg, 100); + + // Setup to raise SIGHUP in 200 ms. + TimedSignal sighup(*getIOService(), SIGHUP, 200); + + // Write valid_d2_config and then run launch() for a maximum of 500ms. + time_duration elapsed_time; + runWithConfig(valid_d2_config, 500, elapsed_time); + + // Context is still available post launch. + // Check to see that our configuration matches the replacement config. + D2CfgMgrPtr d2_cfg_mgr = getD2CfgMgr(); + D2ParamsPtr d2_params = d2_cfg_mgr->getD2Params(); + ASSERT_TRUE(d2_params); + + EXPECT_EQ("192.168.77.1", d2_params->getIpAddress().toText()); + EXPECT_EQ(777, d2_params->getPort()); + EXPECT_FALSE(d2_cfg_mgr->forwardUpdatesEnabled()); + EXPECT_FALSE(d2_cfg_mgr->reverseUpdatesEnabled()); + + /// @todo add a way to trap log file and search it +} + +// Tests that the SIGINT triggers a normal shutdown. +TEST_F(D2ControllerTest, sigintShutdown) { + // Setup to raise SIGINT in 1 ms. + TimedSignal sighup(*getIOService(), SIGINT, 1); + + // Write valid_d2_config and then run launch() for a maximum of 1000 ms. + time_duration elapsed_time; + runWithConfig(valid_d2_config, 1000, elapsed_time); + + // Signaled shutdown should make our elapsed time much smaller than + // the maximum run time. Give generous margin to accommodate slow + // test environs. + EXPECT_TRUE(elapsed_time.total_milliseconds() < 300); + + /// @todo add a way to trap log file and search it +} + +// Tests that the SIGTERM triggers a normal shutdown. +TEST_F(D2ControllerTest, sigtermShutdown) { + // Setup to raise SIGTERM in 1 ms. + TimedSignal sighup(*getIOService(), SIGTERM, 1); + + // Write valid_d2_config and then run launch() for a maximum of 1 s. + time_duration elapsed_time; + runWithConfig(valid_d2_config, 1000, elapsed_time); + + // Signaled shutdown should make our elapsed time much smaller than + // the maximum run time. Give generous margin to accommodate slow + // test environs. + EXPECT_TRUE(elapsed_time.total_milliseconds() < 300); + + /// @todo add a way to trap log file and search it +} + +}; // end of isc::d2 namespace +}; // end of isc namespace diff --git a/src/bin/d2/tests/d2_process_tests.sh.in b/src/bin/d2/tests/d2_process_tests.sh.in new file mode 100644 index 0000000..895b9a2 --- /dev/null +++ b/src/bin/d2/tests/d2_process_tests.sh.in @@ -0,0 +1,332 @@ +#!/bin/sh + +# 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/. + +# shellcheck disable=SC1091 +# SC1091: Not following: ... was not specified as input (see shellcheck -x). + +# shellcheck disable=SC2039 +# SC2039: In POSIX sh, 'local' is undefined. + +# Exit with error if commands exit with non-zero and if undefined variables are +# used. +set -eu + +# Path to the temporary configuration file. +CFG_FILE="@abs_top_builddir@/src/bin/d2/tests/test_config.json" +# Path to the D2 log file. +LOG_FILE="@abs_top_builddir@/src/bin/d2/tests/test.log" +# D2 configuration to be stored in the configuration file. +CONFIG="{ + \"DhcpDdns\": + { + \"ip-address\": \"127.0.0.1\", + \"port\": 53001, + \"tsig-keys\": [], + \"forward-ddns\" : {}, + \"reverse-ddns\" : {}, + \"loggers\": [ + { + \"name\": \"kea-dhcp-ddns\", + \"output_options\": [ + { + \"output\": \"$LOG_FILE\" + } + ], + \"severity\": \"DEBUG\" + } + ] + } +}" + +# Invalid configuration (syntax error) to check that Kea can check syntax. +CONFIG_BAD_SYNTAX="{ + \"DhcpDdns\": + { + \"ip-address\": \"127.0.0.1\", + \"port\": BOGUS, + \"tsig-keys\": [], + \"forward-ddns\" : {}, + \"reverse-ddns\" : {}, + \"loggers\": [ + { + \"name\": \"kea-dhcp-ddns\", + \"output_options\": [ + { + \"output\": \"$LOG_FILE\" + } + ], + \"severity\": \"INFO\" + } + ] + } +}" + +# Invalid configuration (out of range port) to check that Kea can check syntax. +CONFIG_BAD_VALUE="{ + \"DhcpDdns\": + { + \"ip-address\": \"127.0.0.1\", + \"port\": 80000, + \"tsig-keys\": [], + \"forward-ddns\" : {}, + \"reverse-ddns\" : {}, + \"loggers\": [ + { + \"name\": \"kea-dhcp-ddns\", + \"output_options\": [ + { + \"output\": \"$LOG_FILE\" + } + ], + \"severity\": \"INFO\" + } + ] + } +}" + +# Invalid value configuration (invalid port) to check that D2 +# gracefully handles reconfiguration errors. +CONFIG_INVALID="{ + \"DhcpDdns\": + { + \"ip-address\": \"127.0.0.1\", + \"port\": BOGUS, + \"tsig-keys\": [], + \"forward-ddns\" : {}, + \"reverse-ddns\" : {}, + \"loggers\": [ + { + \"name\": \"kea-dhcp-ddns\", + \"output_options\": [ + { + \"output\": \"$LOG_FILE\" + } + ], + \"severity\": \"INFO\" + } + ] + } +}" + +CONFIG_WITH_SECRET=' +{ + "DhcpDdns": { + "tsig-keys": [ + { + "algorithm": "HMAC-MD5", + "name": "d2.md5.key", + "secret": "sensitivejdPJI5QxlpnfQ==" + } + ], + "user-context": { + "password": "superadmin", + "secret": "superadmin", + "shared-info": { + "password": "superadmin", + "secret": "superadmin" + } + } + } +} +' + +# Set the location of the executable. +bin="kea-dhcp-ddns" +bin_path="@abs_top_builddir@/src/bin/d2" + +# Import common test library. +. "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh" + +# This test verifies that syntax checking works properly. This function +# requires 3 parameters: +# test_name +# config - string with a content of the config (will be written to a file) +# expected_code - expected exit code returned by kea (0 - success, 1 - failure) +syntax_check_test() { + local test_name="${1}" + local config="${2}" + local expected_code="${3}" + + # Log the start of the test and print test name. + test_start "${test_name}" + # Create correct configuration file. + create_config "${config}" + # Check it + printf "Running command %s.\n" "\"${bin_path}/${bin} -t ${CFG_FILE}\"" + run_command \ + "${bin_path}/${bin}" -t "${CFG_FILE}" + if [ "${EXIT_CODE}" -ne "${expected_code}" ]; then + printf 'ERROR: expected exit code %s, got %s\n' "${expected_code}" "${EXIT_CODE}" + clean_exit 1 + fi + test_finish 0 +} + +# This test verifies that D2 can be reconfigured with a SIGHUP signal. +dynamic_reconfiguration_test() { + # Log the start of the test and print test name. + test_start "dhcp_ddns.dynamic_reconfiguration" + # Create new configuration file. + create_config "${CONFIG}" + # Instruct D2 to log to the specific file. + set_logger + # Start D2. + start_kea ${bin_path}/${bin} + # Wait up to 20s for D2 to start. + wait_for_kea 20 + if [ "${_WAIT_FOR_KEA}" -eq 0 ]; then + printf "ERROR: timeout waiting for D2 to start.\n" + clean_exit 1 + fi + + # Check if it is still running. It could have terminated (e.g. as a result + # of configuration failure). + get_pid ${bin} + if [ "${_GET_PIDS_NUM}" -ne 1 ]; then + printf "ERROR: expected one D2 process to be started. Found %d processes\ + started.\n" "${_GET_PIDS_NUM}" + clean_exit 1 + fi + + # Check in the log file, how many times server has been configured. + # It should be just once on startup. + get_reconfigs + if [ "${_GET_RECONFIGS}" -ne 1 ]; then + printf "ERROR: D2 hasn't been configured.\n" + clean_exit 1 + else + printf "D2 successfully configured.\n" + fi + + # Now use invalid configuration. + create_config "${CONFIG_INVALID}" + + # Try to reconfigure by sending SIGHUP + send_signal 1 ${bin} + + # Wait up to 10s for the D2Controller to log reload signal received. + wait_for_message 10 "DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD" 1 + if [ "${_WAIT_FOR_MESSAGE}" -eq 0 ]; then + printf "ERROR: D2 did report the reload signal receipt.\n" + clean_exit 1 + fi + + # After receiving SIGHUP the server should try to reconfigure itself. + # The configuration provided is invalid so it should result in + # reconfiguration failure but the server should still be running. + wait_for_message 10 "DCTL_CFG_FILE_RELOAD_ERROR" 1 + if [ "${_WAIT_FOR_MESSAGE}" -eq 0 ]; then + printf "ERROR: D2 did not report reload error.\n" + clean_exit 1 + fi + + # Make sure the server is still operational. + get_pid ${bin} + if [ "${_GET_PIDS_NUM}" -ne 1 ]; then + printf "ERROR: D2 was killed when attempting reconfiguration.\n" + clean_exit 1 + fi + + # Restore the good configuration. + create_config "${CONFIG}" + + # Reconfigure the server with SIGHUP. + send_signal 1 ${bin} + + # There should be two occurrences of the DHCP4_CONFIG_COMPLETE messages. + # Wait for it up to 10s. + wait_for_message 10 "DCTL_CONFIG_COMPLETE" 2 + + # After receiving SIGHUP the server should get reconfigured and the + # reconfiguration should be noted in the log file. We should now + # have two configurations logged in the log file. + if [ "${_WAIT_FOR_MESSAGE}" -eq 0 ]; then + printf "ERROR: D2 hasn't been reconfigured.\n" + clean_exit 1 + else + printf "D2 successfully reconfigured.\n" + fi + + # Make sure the server is still operational. + get_pid ${bin} + if [ "${_GET_PIDS_NUM}" -ne 1 ]; then + printf "ERROR: D2 was killed when attempting reconfiguration.\n" + clean_exit 1 + fi + + # All ok. Shut down D2 and exit. + test_finish 0 +} + +# This test verifies that DHCPv4 server is shut down gracefully when it +# receives a SIGINT or SIGTERM signal. +shutdown_test() { + local test_name="${1}" # Test name + local signum="${2}" # Signal number + # Log the start of the test and print test name. + test_start "${test_name}" + # Create new configuration file. + create_config "${CONFIG}" + # Instruct D2 to log to the specific file. + set_logger + # Start D2. + start_kea ${bin_path}/${bin} + # Wait up to 20s for D2 to start. + wait_for_kea 20 + if [ "${_WAIT_FOR_KEA}" -eq 0 ]; then + printf "ERROR: timeout waiting for D2 to start.\n" + clean_exit 1 + fi + + # Check if it is still running. It could have terminated (e.g. as a result + # of configuration failure). + get_pid ${bin} + if [ "${_GET_PIDS_NUM}" -ne 1 ]; then + printf "ERROR: expected one D2 process to be started. Found %d processes\ + started.\n" "${_GET_PIDS_NUM}" + clean_exit 1 + fi + + # Check in the log file, how many times server has been configured. + # It should be just once on startup. + get_reconfigs + if [ "${_GET_RECONFIGS}" -ne 1 ]; then + printf "ERROR: server hasn't been configured.\n" + clean_exit 1 + else + printf "Server successfully configured.\n" + fi + + # Send signal to D2 (SIGTERM, SIGINT etc.) + send_signal "${signum}" "${bin}" + + # Now wait for process to log that it is exiting. + wait_for_message 10 "DCTL_SHUTDOWN" 1 + if [ "${_WAIT_FOR_MESSAGE}" -eq 0 ]; then + printf "ERROR: DHCP-DDNS did not log shutdown.\n" + clean_exit 1 + fi + + # Make sure the server is down. + wait_for_server_down 5 ${bin} + assert_eq 1 "${_WAIT_FOR_SERVER_DOWN}" \ + "Expected wait_for_server_down return %d, returned %d" + + test_finish 0 +} + +server_pid_file_test "${CONFIG}" DCTL_ALREADY_RUNNING +dynamic_reconfiguration_test +shutdown_test "dhcp-ddns.sigterm_test" 15 +shutdown_test "dhcp-ddns.sigint_test" 2 +version_test "dhcp-ddns.version" +logger_vars_test "dhcp-ddns.variables" +syntax_check_test "dhcp-ddns.syntax_check_success" "${CONFIG}" 0 +syntax_check_test "dhcp-ddns.syntax_check_bad_syntax" "${CONFIG_BAD_SYNTAX}" 1 +syntax_check_test "dhcp-ddns.syntax_check_bad_values" "${CONFIG_BAD_VALUE}" 1 +password_redact_test "dhcp-ddns.password_redact_test" "${CONFIG_WITH_SECRET}" 0 diff --git a/src/bin/d2/tests/d2_process_unittests.cc b/src/bin/d2/tests/d2_process_unittests.cc new file mode 100644 index 0000000..4dda275 --- /dev/null +++ b/src/bin/d2/tests/d2_process_unittests.cc @@ -0,0 +1,693 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <asiolink/io_service.h> +#include <cc/command_interpreter.h> +#include <d2srv/testutils/nc_test_utils.h> +#include <d2/d2_process.h> +#include <d2/tests/test_configured_libraries.h> +#include <dhcp_ddns/ncr_io.h> +#include <process/testutils/d_test_stubs.h> + +#include <boost/date_time/posix_time/posix_time.hpp> +#include <gtest/gtest.h> + +#include <functional> +#include <sstream> + +using namespace std; +using namespace isc; +using namespace isc::config; +using namespace isc::d2; +using namespace isc::data; +using namespace isc::process; +using namespace boost::posix_time; + +namespace { + +/// @brief Valid configuration containing an unavailable IP address. +const char* bad_ip_d2_config = "{ " + "\"ip-address\" : \"1.1.1.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 } ] } " + "] } }"; + +/// @brief D2Process test fixture class +//class D2ProcessTest : public D2Process, public ::testing::Test { +class D2ProcessTest : public D2Process, public ConfigParseTest { +public: + + /// @brief Constructor + D2ProcessTest() : + D2Process("d2test", + asiolink::IOServicePtr(new isc::asiolink::IOService())) { + } + + /// @brief Destructor + virtual ~D2ProcessTest() { + } + + /// @brief Callback that will invoke shutdown method. + void genShutdownCallback() { + shutdown(ConstElementPtr()); + } + + /// @brief Callback that throws an exception. + void genFatalErrorCallback() { + isc_throw (DProcessBaseError, "simulated fatal error"); + } + + /// @brief Reconfigures and starts the queue manager given a configuration. + /// + /// This method emulates the reception of a new configuration and should + /// conclude with the Queue manager placed in the RUNNING state. + /// + /// @param config is the configuration to use + /// + /// @return Returns AssertionSuccess if the queue manager was successfully + /// reconfigured, AssertionFailure otherwise. + ::testing::AssertionResult runWithConfig(const char* config) { + int rcode = -1; + // Convert the string configuration into an Element set. + ::testing::AssertionResult res = fromJSON(config); + if (res != ::testing::AssertionSuccess()) { + return res; + } + + ConstElementPtr answer = configure(config_set_, false); + ConstElementPtr comment; + comment = isc::config::parseAnswer(rcode, answer); + + if (rcode) { + return (::testing::AssertionFailure(::testing::Message() << + "configure() failed: " + << comment)); + } + + // Must call checkQueueStatus, to cause queue manager to reconfigure + // and start. + checkQueueStatus(); + const D2QueueMgrPtr& queue_mgr = getD2QueueMgr(); + + // If queue manager isn't in the RUNNING state, return failure. + if (D2QueueMgr::RUNNING != queue_mgr->getMgrState()) { + return (::testing::AssertionFailure(::testing::Message() << + "queue manager did not start")); + } + + // Good to go. + return (::testing::AssertionSuccess()); + } + + /// @brief Checks if shutdown criteria would be met given a shutdown type. + /// + /// This method sets the D2Process shutdown type to the given value, and + /// calls the canShutdown() method, returning its return value. + /// + /// @return Returns the boolean result canShutdown. + bool checkCanShutdown(ShutdownType shutdown_type) { + setShutdownType(shutdown_type); + return (canShutdown()); + } + + /// @brief Replaces %LIBRARY% with specified library name. + /// + /// @param config input config text (should contain "%LIBRARY%" string). + /// @param lib_name %LIBRARY% will be replaced with that name. + /// @return configuration text with library name replaced. + string pathReplacer(const char* config, const char* lib_name) { + string txt(config); + txt.replace(txt.find("%LIBRARY%"), strlen("%LIBRARY%"), string(lib_name)); + return (txt); + } +}; + +/// @brief Verifies D2Process construction behavior. +/// 1. Verifies that constructor fails with an invalid IOService +/// 2. Verifies that constructor succeeds with a valid IOService +/// 3. Verifies that all managers are accessible +TEST(D2Process, construction) { + // Verify that the constructor will fail if given an empty + // io service. + asiolink::IOServicePtr lcl_io_service; + EXPECT_THROW (D2Process("TestProcess", lcl_io_service), DProcessBaseError); + + // Verify that the constructor succeeds with a valid io_service + lcl_io_service.reset(new isc::asiolink::IOService()); + ASSERT_NO_THROW (D2Process("TestProcess", lcl_io_service)); + + // Verify that the configuration, queue, and update managers + // are all accessible after construction. + D2Process d2process("TestProcess", lcl_io_service); + + D2CfgMgrPtr cfg_mgr = d2process.getD2CfgMgr(); + ASSERT_TRUE(cfg_mgr); + + D2QueueMgrPtr queue_mgr = d2process.getD2QueueMgr(); + ASSERT_TRUE(queue_mgr); + + const D2UpdateMgrPtr& update_mgr = d2process.getD2UpdateMgr(); + ASSERT_TRUE(update_mgr); +} + +/// @brief Verifies basic configure method behavior. +/// This test primarily verifies that upon receipt of a new configuration, +/// D2Process will reconfigure the queue manager if the configuration is valid, +/// or leave queue manager unaffected if not. Currently, the queue manager is +/// only D2 component that must adapt to new configurations. Other components, +/// such as Transactions will be unaffected as they are transient and use +/// whatever configuration was in play at the time they were created. +/// If other components need to provide "dynamic" configuration responses, +/// those tests would need to be added. +TEST_F(D2ProcessTest, configure) { + // Verify the queue manager is not yet initialized. + D2QueueMgrPtr queue_mgr = getD2QueueMgr(); + ASSERT_TRUE(queue_mgr); + ASSERT_EQ(D2QueueMgr::NOT_INITTED, queue_mgr->getMgrState()); + + // Verify that reconfigure queue manager flag is false. + ASSERT_FALSE(getReconfQueueFlag()); + + // Create a valid configuration set from text config. + ASSERT_TRUE(fromJSON(valid_d2_config)); + + // Invoke configure() with a valid D2 configuration. + ConstElementPtr answer = configure(config_set_, false); + + // Verify that configure result is success and reconfigure queue manager + // flag is true. + ASSERT_TRUE(checkAnswer(answer, 0)); + ASSERT_TRUE(getReconfQueueFlag()); + + // Call checkQueueStatus, to cause queue manager to reconfigure and start. + checkQueueStatus(); + + // Verify that queue manager is now in the RUNNING state, and flag is false. + ASSERT_EQ(D2QueueMgr::RUNNING, queue_mgr->getMgrState()); + ASSERT_FALSE(getReconfQueueFlag()); + + // Create an invalid configuration set from text config. + ASSERT_TRUE(fromJSON("{ \"bogus\": 1000 } ")); + + // Invoke configure() with the invalid configuration. + answer = configure(config_set_, false); + + // Verify that configure result is a success, as extra parameters are + // ignored. the reconfigure flag is false, and that the queue manager is + // still running. + ASSERT_TRUE(checkAnswer(answer, 0)); + EXPECT_TRUE(getReconfQueueFlag()); + EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr->getMgrState()); + + // Finally, try with an invalid configuration. + // Create an invalid configuration set from text config. + ASSERT_TRUE(fromJSON("{ \"ip-address\": \"950 Charter St.\" } ")); + answer = configure(config_set_, false); + ASSERT_TRUE(checkAnswer(answer, 1)); + EXPECT_FALSE(getReconfQueueFlag()); + EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr->getMgrState()); +} + +/// @brief Tests checkQueueStatus() logic for stopping the queue on shutdown +/// This test manually sets shutdown flag and verifies that queue manager +/// stop is initiated. +TEST_F(D2ProcessTest, queueStopOnShutdown) { + ASSERT_TRUE(runWithConfig(valid_d2_config)); + const D2QueueMgrPtr& queue_mgr = getD2QueueMgr(); + + setShutdownFlag(true); + + // Calling checkQueueStatus restart queue manager + checkQueueStatus(); + + // Verify that the queue manager is stopping. + EXPECT_EQ(D2QueueMgr::STOPPING, queue_mgr->getMgrState()); + + // Verify that a subsequent call with no events occurring in between, + // results in no change to queue manager + checkQueueStatus(); + + // Verify that the queue manager is still stopping. + EXPECT_EQ(D2QueueMgr::STOPPING, queue_mgr->getMgrState()); + + // Call runIO so the IO cancel event occurs and verify that queue manager + // has stopped. + runIO(); + ASSERT_EQ(D2QueueMgr::STOPPED, queue_mgr->getMgrState()); +} + +/// @brief Tests checkQueueStatus() logic for stopping the queue on reconfigure. +/// This test manually sets queue reconfiguration flag and verifies that queue +/// manager stop is initiated. +TEST_F(D2ProcessTest, queueStopOnReconf) { + ASSERT_TRUE(runWithConfig(valid_d2_config)); + const D2QueueMgrPtr& queue_mgr = getD2QueueMgr(); + + // Manually set the reconfigure indicator. + setReconfQueueFlag(true); + + // Calling checkQueueStatus should initiate stopping the queue manager. + checkQueueStatus(); + + // Verify that the queue manager is stopping. + EXPECT_EQ(D2QueueMgr::STOPPING, queue_mgr->getMgrState()); + + // Call runIO so the IO cancel event occurs and verify that queue manager + // has stopped. + runIO(); + ASSERT_EQ(D2QueueMgr::STOPPED, queue_mgr->getMgrState()); +} + +/// @brief Tests checkQueueStatus() logic for recovering from queue full +/// This test manually creates a receive queue full condition and then +/// "drains" the queue until the queue manager resumes listening. This +/// verifies D2Process's ability to recover from a queue full condition. +TEST_F(D2ProcessTest, queueFullRecovery) { + // Valid test message, contents are unimportant. + const char* test_msg = + "{" + " \"change-type\" : 0 , " + " \"forward-change\" : true , " + " \"reverse-change\" : false , " + " \"fqdn\" : \"walah.walah.com\" , " + " \"ip-address\" : \"192.168.2.1\" , " + " \"dhcid\" : \"010203040A7F8E3D\" , " + " \"lease-expires-on\" : \"20130121132405\" , " + " \"lease-length\" : 1300, " + " \"use-conflict-resolution\" : true " + "}"; + + // Start queue manager with known good config. + ASSERT_TRUE(runWithConfig(valid_d2_config)); + const D2QueueMgrPtr& queue_mgr = getD2QueueMgr(); + + // Set the maximum queue size to manageable number. + size_t max_queue_size = 5; + queue_mgr->setMaxQueueSize(max_queue_size); + + // Manually enqueue max requests. + dhcp_ddns::NameChangeRequestPtr ncr; + ASSERT_NO_THROW(ncr = dhcp_ddns::NameChangeRequest::fromJSON(test_msg)); + for (int i = 0; i < max_queue_size; i++) { + // Verify that the request can be added to the queue and queue + // size increments accordingly. + ASSERT_NO_THROW(queue_mgr->enqueue(ncr)); + ASSERT_EQ(i+1, queue_mgr->getQueueSize()); + } + + // Since we are not really receiving, we will simulate QUEUE FULL + // detection. + queue_mgr->stopListening(D2QueueMgr::STOPPED_QUEUE_FULL); + ASSERT_EQ(D2QueueMgr::STOPPING, queue_mgr->getMgrState()); + + // Call runIO so the IO cancel event occurs and verify that queue manager + // has stopped. + runIO(); + ASSERT_EQ(D2QueueMgr::STOPPED_QUEUE_FULL, queue_mgr->getMgrState()); + + // Dequeue requests one at a time, calling checkQueueStatus after each + // dequeue, until we reach the resume threshold. This simulates update + // manager consuming jobs. Queue manager should remain stopped during + // this loop. + int resume_threshold = (max_queue_size * QUEUE_RESTART_PERCENT); + while (queue_mgr->getQueueSize() > resume_threshold) { + checkQueueStatus(); + ASSERT_EQ(D2QueueMgr::STOPPED_QUEUE_FULL, queue_mgr->getMgrState()); + ASSERT_NO_THROW(queue_mgr->dequeue()); + } + + // Dequeue one more, which brings us under the threshold and call + // checkQueueStatus. + // Verify that the queue manager is again running. + ASSERT_NO_THROW(queue_mgr->dequeue()); + checkQueueStatus(); + EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr->getMgrState()); +} + +/// @brief Tests checkQueueStatus() logic for queue receive error recovery +/// This test manually creates a queue receive error condition and tests +/// verifies that checkQueueStatus reacts properly to recover. +TEST_F(D2ProcessTest, queueErrorRecovery) { + ASSERT_TRUE(runWithConfig(valid_d2_config)); + const D2QueueMgrPtr& queue_mgr = getD2QueueMgr(); + + // Since we are not really receiving, we have to stage an error. + queue_mgr->stopListening(D2QueueMgr::STOPPED_RECV_ERROR); + ASSERT_EQ(D2QueueMgr::STOPPING, queue_mgr->getMgrState()); + + // Call runIO so the IO cancel event occurs and verify that queue manager + // has stopped. + runIO(); + ASSERT_EQ(D2QueueMgr::STOPPED_RECV_ERROR, queue_mgr->getMgrState()); + + // Calling checkQueueStatus should restart queue manager + checkQueueStatus(); + + // Verify that queue manager is again running. + EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr->getMgrState()); +} + +/// @brief Verifies queue manager recovery from unusable configuration +/// This test checks D2Process's gracefully handle a configuration which +/// while valid is not operationally usable (i.e. IP address is unavailable), +/// and to subsequently recover given a usable configuration. +TEST_F(D2ProcessTest, badConfigureRecovery) { + D2QueueMgrPtr queue_mgr = getD2QueueMgr(); + ASSERT_TRUE(queue_mgr); + + // Verify the queue manager is not initialized. + EXPECT_EQ(D2QueueMgr::NOT_INITTED, queue_mgr->getMgrState()); + + // Invoke configure() with a valid config that contains an unusable IP + ASSERT_TRUE(fromJSON(bad_ip_d2_config)); + ConstElementPtr answer = configure(config_set_, false); + + // Verify that configure result is success and reconfigure queue manager + // flag is true. + ASSERT_TRUE(checkAnswer(answer, 0)); + ASSERT_TRUE(getReconfQueueFlag()); + + // Call checkQueueStatus to cause queue manager to attempt to reconfigure. + checkQueueStatus(); + + // Verify that queue manager failed to start, (i.e. is in INITTED state), + // and the reconfigure flag is false. + ASSERT_EQ(D2QueueMgr::INITTED, queue_mgr->getMgrState()); + ASSERT_FALSE(getReconfQueueFlag()); + + // Verify we can recover given a valid config with an usable IP address. + ASSERT_TRUE(fromJSON(valid_d2_config)); + answer = configure(config_set_, false); + + // Verify that configure result is success and reconfigure queue manager + // flag is true. + ASSERT_TRUE(checkAnswer(answer, 0)); + ASSERT_TRUE(getReconfQueueFlag()); + + // Call checkQueueStatus to cause queue manager to reconfigure and start. + checkQueueStatus(); + + // Verify that queue manager is now in the RUNNING state, and reconfigure + // flag is false. + EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr->getMgrState()); + EXPECT_FALSE(getReconfQueueFlag()); +} + +/// @brief Tests shutdown command argument parsing +/// The shutdown command supports an optional "type" argument. This test +/// checks that for valid values, the shutdown() method: sets the shutdown +/// type to correct value, set the shutdown flag to true, and returns a +/// success response; and for invalid values: sets the shutdown flag to false +/// and returns a failure response. +TEST_F(D2ProcessTest, shutdownArgs) { + ElementPtr args; + ConstElementPtr answer; + const char* default_args = "{}"; + const char* normal_args = "{ \"type\" : \"normal\" }"; + const char* drain_args = "{ \"type\" : \"drain_first\" }"; + const char* now_args = "{ \"type\" : \"now\" }"; + const char* bogus_args = "{ \"type\" : \"bogus\" }"; + + // Verify defaulting to SD_NORMAL if no argument is given. + ASSERT_NO_THROW(args = Element::fromJSON(default_args)); + EXPECT_NO_THROW(answer = shutdown(args)); + ASSERT_TRUE(checkAnswer(answer, 0)); + EXPECT_EQ(SD_NORMAL, getShutdownType()); + EXPECT_TRUE(shouldShutdown()); + + // Verify argument value "normal". + ASSERT_NO_THROW(args = Element::fromJSON(normal_args)); + EXPECT_NO_THROW(answer = shutdown(args)); + ASSERT_TRUE(checkAnswer(answer, 0)); + EXPECT_EQ(SD_NORMAL, getShutdownType()); + EXPECT_TRUE(shouldShutdown()); + + // Verify argument value "drain_first". + ASSERT_NO_THROW(args = Element::fromJSON(drain_args)); + EXPECT_NO_THROW(answer = shutdown(args)); + ASSERT_TRUE(checkAnswer(answer, 0)); + EXPECT_EQ(SD_DRAIN_FIRST, getShutdownType()); + EXPECT_TRUE(shouldShutdown()); + + // Verify argument value "now". + ASSERT_NO_THROW(args = Element::fromJSON(now_args)); + EXPECT_NO_THROW(answer = shutdown(args)); + ASSERT_TRUE(checkAnswer(answer, 0)); + EXPECT_EQ(SD_NOW, getShutdownType()); + EXPECT_TRUE(shouldShutdown()); + + // Verify correct handling of an invalid value. + ASSERT_NO_THROW(args = Element::fromJSON(bogus_args)); + EXPECT_NO_THROW(answer = shutdown(args)); + ASSERT_TRUE(checkAnswer(answer, 1)); + EXPECT_FALSE(shouldShutdown()); +} + +/// @brief Tests shutdown criteria logic +/// D2Process using the method canShutdown() to determine if a shutdown +/// can be performed given the value of the shutdown flag and the type of +/// shutdown requested. For each shutdown type certain criteria must be met +/// before the shutdown is permitted. This method is invoked once each pass +/// through the main event loop. This test checks the operation of the +/// canShutdown method. It uses a convenience method, checkCanShutdown(), +/// which sets the shutdown type to the given value and invokes canShutdown(), +/// returning its result. +TEST_F(D2ProcessTest, canShutdown) { + ASSERT_TRUE(runWithConfig(valid_d2_config)); + const D2QueueMgrPtr& queue_mgr = getD2QueueMgr(); + + // Shutdown flag is false. Method should return false for all types. + EXPECT_FALSE(checkCanShutdown(SD_NORMAL)); + EXPECT_FALSE(checkCanShutdown(SD_DRAIN_FIRST)); + EXPECT_FALSE(checkCanShutdown(SD_NOW)); + + // Set shutdown flag to true. + setShutdownFlag(true); + + // Queue Manager is running, queue is empty, no transactions. + // Only SD_NOW should return true. + EXPECT_FALSE(checkCanShutdown(SD_NORMAL)); + EXPECT_FALSE(checkCanShutdown(SD_DRAIN_FIRST)); + EXPECT_TRUE(checkCanShutdown(SD_NOW)); + + // Tell queue manager to stop. + queue_mgr->stopListening(); + // Verify that the queue manager is stopping. + EXPECT_EQ(D2QueueMgr::STOPPING, queue_mgr->getMgrState()); + + // Queue Manager is stopping, queue is empty, no transactions. + // Only SD_NOW should return true. + EXPECT_FALSE(checkCanShutdown(SD_NORMAL)); + EXPECT_FALSE(checkCanShutdown(SD_DRAIN_FIRST)); + EXPECT_TRUE(checkCanShutdown(SD_NOW)); + + // Allow cancel event to process. + ASSERT_NO_THROW(runIO()); + // Verify that queue manager is stopped. + EXPECT_EQ(D2QueueMgr::STOPPED, queue_mgr->getMgrState()); + + // Queue Manager is stopped, queue is empty, no transactions. + // All types should return true. + EXPECT_TRUE(checkCanShutdown(SD_NORMAL)); + EXPECT_TRUE(checkCanShutdown(SD_DRAIN_FIRST)); + EXPECT_TRUE(checkCanShutdown(SD_NOW)); + + const char* test_msg = + "{" + " \"change-type\" : 0 , " + " \"forward-change\" : true , " + " \"reverse-change\" : false , " + " \"fqdn\" : \"fish.example.com\" , " + " \"ip-address\" : \"192.168.2.1\" , " + " \"dhcid\" : \"010203040A7F8E3D\" , " + " \"lease-expires-on\" : \"20130121132405\" , " + " \"lease-length\" : 1300, " + " \"use-conflict-resolution\" : true " + "}"; + + // Manually enqueue a request. This lets us test logic with queue + // not empty. + dhcp_ddns::NameChangeRequestPtr ncr; + ASSERT_NO_THROW(ncr = dhcp_ddns::NameChangeRequest::fromJSON(test_msg)); + ASSERT_NO_THROW(queue_mgr->enqueue(ncr)); + ASSERT_EQ(1, queue_mgr->getQueueSize()); + + // Queue Manager is stopped. Queue is not empty, no transactions. + // SD_DRAIN_FIRST should be false, SD_NORMAL and SD_NOW should be true. + EXPECT_TRUE(checkCanShutdown(SD_NORMAL)); + EXPECT_FALSE(checkCanShutdown(SD_DRAIN_FIRST)); + EXPECT_TRUE(checkCanShutdown(SD_NOW)); + + // Now use update manager to dequeue the request and make a transaction. + // This lets us verify transaction list not empty logic. + const D2UpdateMgrPtr& update_mgr = getD2UpdateMgr(); + ASSERT_TRUE(update_mgr); + ASSERT_NO_THROW(update_mgr->sweep()); + ASSERT_EQ(0, queue_mgr->getQueueSize()); + ASSERT_EQ(1, update_mgr->getTransactionCount()); + + // Queue Manager is stopped. Queue is empty, one transaction. + // Only SD_NOW should be true. + EXPECT_FALSE(checkCanShutdown(SD_NORMAL)); + EXPECT_FALSE(checkCanShutdown(SD_DRAIN_FIRST)); + EXPECT_TRUE(checkCanShutdown(SD_NOW)); +} + +/// @brief Verifies that an "external" call to shutdown causes the run method +/// to exit gracefully. +TEST_F(D2ProcessTest, normalShutdown) { + // Use an asiolink IntervalTimer and callback to generate the + // shutdown invocation. (Note IntervalTimer setup is in milliseconds). + isc::asiolink::IntervalTimer timer(*getIoService()); + timer.setup(std::bind(&D2ProcessTest::genShutdownCallback, this), + 2 * 1000); + + // Record start time, and invoke run(). + ptime start = microsec_clock::universal_time(); + EXPECT_NO_THROW(run()); + + // Record stop time. + ptime stop = microsec_clock::universal_time(); + + // Verify that duration of the run invocation is the same as the + // timer duration. This demonstrates that the shutdown was driven + // by an io_service event and callback. + time_duration elapsed = stop - start; + EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 && + elapsed.total_milliseconds() <= 2200); +} + +/// @brief Verifies that an "uncaught" exception thrown during event loop +/// execution is treated as a fatal error. +TEST_F(D2ProcessTest, fatalErrorShutdown) { + // Use an asiolink IntervalTimer and callback to generate the + // the exception. (Note IntervalTimer setup is in milliseconds). + isc::asiolink::IntervalTimer timer(*getIoService()); + timer.setup(std::bind(&D2ProcessTest::genFatalErrorCallback, this), + 2 * 1000); + + // Record start time, and invoke run(). + ptime start = microsec_clock::universal_time(); + EXPECT_THROW(run(), DProcessBaseError); + + // Record stop time. + ptime stop = microsec_clock::universal_time(); + + // Verify that duration of the run invocation is the same as the + // timer duration. This demonstrates that the anomaly occurred + // during io callback processing. + time_duration elapsed = stop - start; + EXPECT_TRUE(elapsed.total_milliseconds() >= 1900 && + elapsed.total_milliseconds() <= 2200); +} + +/// @brief Used to permit visual inspection of logs to ensure +/// DHCP_DDNS_NOT_ON_LOOPBACK is issued when ip_address is not +/// loopback. +TEST_F(D2ProcessTest, notLoopbackTest) { + const char* config = "{ " + "\"ip-address\" : \"0.0.0.0\" , " + "\"port\" : 53001, " + "\"tsig-keys\": []," + "\"forward-ddns\" : {}," + "\"reverse-ddns\" : {}" + "}"; + + // Note we don't care nor can we predict if this + // succeeds or fails. The address and port may or may + // not be valid on the test host. + runWithConfig(config); +} + +/// @brief Used to permit visual inspection of logs to ensure +/// DHCP_DDNS_NOT_ON_LOOPBACK is not issued. +TEST_F(D2ProcessTest, v4LoopbackTest) { + const char* config = "{ " + "\"ip-address\" : \"127.0.0.1\" , " + "\"port\" : 53001, " + "\"tsig-keys\": []," + "\"forward-ddns\" : {}," + "\"reverse-ddns\" : {}" + "}"; + ASSERT_TRUE(runWithConfig(config)); +} + +/// @brief Used to permit visual inspection of logs to ensure +/// DHCP_DDNS_NOT_ON_LOOPBACK is not issued. +TEST_F(D2ProcessTest, v6LoopbackTest) { + const char* config = "{ " + "\"ip-address\" : \"::1\" , " + "\"port\" : 53001, " + "\"tsig-keys\": []," + "\"forward-ddns\" : {}," + "\"reverse-ddns\" : {}" + "}"; + ASSERT_TRUE(runWithConfig(config)); +} + +/// @brief Check the configured callout (positive case). +TEST_F(D2ProcessTest, configuredNoFail) { + const char* config = "{\n" + "\"hooks-libraries\": [ {\n" + " \"library\": \"%LIBRARY%\",\n" + " \"parameters\": {\n" + " } } ] }\n"; + string cfg = pathReplacer(config, CONFIGURED_LIBRARY); + + ConstElementPtr json; + ASSERT_NO_THROW(json = Element::fromJSON(cfg)); + ConstElementPtr answer; + ASSERT_NO_THROW(answer = configure(json, false)); + int rcode = -1; + ConstElementPtr comment; + comment = isc::config::parseAnswer(rcode, answer); + EXPECT_EQ(0, rcode) << comment->str(); +} + +/// @brief Check the configured callout (negative case). +TEST_F(D2ProcessTest, configuredFail) { + const char* config = "{\n" + "\"user-context\": { \"error\": \"Fail!\" },\n" + "\"hooks-libraries\": [ {\n" + " \"library\": \"%LIBRARY%\",\n" + " \"parameters\": {\n" + " } } ] }\n"; + string cfg = pathReplacer(config, CONFIGURED_LIBRARY); + + ConstElementPtr json; + ASSERT_NO_THROW(json = Element::fromJSON(cfg)); + ConstElementPtr answer; + ASSERT_NO_THROW(answer = configure(json, false)); + int rcode = -1; + ConstElementPtr comment; + comment = isc::config::parseAnswer(rcode, answer); + EXPECT_EQ(1, rcode); + ASSERT_TRUE(comment); + ASSERT_EQ(Element::string, comment->getType()); + EXPECT_EQ("Fail!", comment->stringValue()); +} + +} // end of anonymous namespace diff --git a/src/bin/d2/tests/d2_queue_mgr_unittests.cc b/src/bin/d2/tests/d2_queue_mgr_unittests.cc new file mode 100644 index 0000000..42177e6 --- /dev/null +++ b/src/bin/d2/tests/d2_queue_mgr_unittests.cc @@ -0,0 +1,457 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <asiolink/io_service.h> +#include <asiolink/interval_timer.h> +#include <d2/d2_queue_mgr.h> +#include <d2srv/testutils/stats_test_utils.h> +#include <dhcp_ddns/ncr_udp.h> +#include <util/time_utilities.h> + +#include <gtest/gtest.h> +#include <algorithm> +#include <functional> +#include <vector> + +using namespace std; +using namespace isc; +using namespace isc::dhcp_ddns; +using namespace isc::d2; +using namespace isc::d2::test; + +namespace { + +/// @brief Defines a list of valid JSON NameChangeRequest test messages. +const char *valid_msgs[] = +{ + // Valid Add. + "{" + " \"change-type\" : 0 , " + " \"forward-change\" : true , " + " \"reverse-change\" : false , " + " \"fqdn\" : \"walah.walah.com\" , " + " \"ip-address\" : \"192.168.2.1\" , " + " \"dhcid\" : \"010203040A7F8E3D\" , " + " \"lease-expires-on\" : \"20130121132405\" , " + " \"lease-length\" : 1300, " + " \"use-conflict-resolution\" : true " + "}", + // Valid Remove. + "{" + " \"change-type\" : 1 , " + " \"forward-change\" : true , " + " \"reverse-change\" : false , " + " \"fqdn\" : \"walah.walah.com\" , " + " \"ip-address\" : \"192.168.2.1\" , " + " \"dhcid\" : \"010203040A7F8E3D\" , " + " \"lease-expires-on\" : \"20130121132405\" , " + " \"lease-length\" : 1300, " + " \"use-conflict-resolution\" : true " + "}", + // Valid Add with IPv6 address + "{" + " \"change-type\" : 0 , " + " \"forward-change\" : true , " + " \"reverse-change\" : false , " + " \"fqdn\" : \"walah.walah.com\" , " + " \"ip-address\" : \"fe80::2acf:e9ff:fe12:e56f\" , " + " \"dhcid\" : \"010203040A7F8E3D\" , " + " \"lease-expires-on\" : \"20130121132405\" , " + " \"lease-length\" : 1300, " + " \"use-conflict-resolution\" : true " + "}" +}; + +static const int VALID_MSG_CNT = sizeof(valid_msgs)/sizeof(char*); + +const char* TEST_ADDRESS = "127.0.0.1"; +const uint32_t LISTENER_PORT = 5301; +const uint32_t SENDER_PORT = LISTENER_PORT+1; +const long TEST_TIMEOUT = 5 * 1000; + +/// @brief Tests that construction with max queue size of zero is not allowed. +TEST(D2QueueMgrBasicTest, construction1) { + asiolink::IOServicePtr io_service; + + // Verify that constructing with null IOServicePtr is not allowed. + EXPECT_THROW((D2QueueMgr(io_service)), D2QueueMgrError); + + io_service.reset(new isc::asiolink::IOService()); + // Verify that constructing with max queue size of zero is not allowed. + EXPECT_THROW(D2QueueMgr(io_service, 0), D2QueueMgrError); +} + +/// @brief Tests default construction works. +TEST(D2QueueMgrBasicTest, construction2) { + asiolink::IOServicePtr io_service(new isc::asiolink::IOService()); + + // Verify that valid constructor works. + D2QueueMgrPtr queue_mgr; + ASSERT_NO_THROW(queue_mgr.reset(new D2QueueMgr(io_service))); + // Verify queue max is defaulted correctly. + EXPECT_EQ(D2QueueMgr::MAX_QUEUE_DEFAULT, queue_mgr->getMaxQueueSize()); +} + +/// @brief Tests construction with custom queue size works properly +TEST(D2QueueMgrBasicTest, construction3) { + asiolink::IOServicePtr io_service(new isc::asiolink::IOService()); + + // Verify that custom queue size constructor works. + D2QueueMgrPtr queue_mgr; + ASSERT_NO_THROW(queue_mgr.reset(new D2QueueMgr(io_service, 100))); + // Verify queue max is the custom value. + EXPECT_EQ(100, queue_mgr->getMaxQueueSize()); +} + +/// @brief Tests QueueMgr's basic queue functions +/// This test verifies that: +/// 1. Following construction queue is empty +/// 2. Attempting to peek at an empty queue is not allowed +/// 3. Attempting to dequeue an empty queue is not allowed +/// 4. Peek returns the first entry on the queue without altering queue content +/// 5. Dequeue removes the first entry on the queue +TEST(D2QueueMgrBasicTest, basicQueue) { + asiolink::IOServicePtr io_service(new isc::asiolink::IOService()); + + // Construct the manager with max queue size set to number of messages + // we'll use. + D2QueueMgrPtr queue_mgr; + ASSERT_NO_THROW(queue_mgr.reset(new D2QueueMgr(io_service, VALID_MSG_CNT))); + ASSERT_EQ(VALID_MSG_CNT, queue_mgr->getMaxQueueSize()); + + // Verify queue is empty after construction. + EXPECT_EQ(0, queue_mgr->getQueueSize()); + + // Verify that peek and dequeue both throw when queue is empty. + EXPECT_THROW(queue_mgr->peek(), D2QueueMgrQueueEmpty); + EXPECT_THROW(queue_mgr->dequeue(), D2QueueMgrQueueEmpty); + + // Vector to keep track of the NCRs we que. + std::vector<NameChangeRequestPtr>ref_msgs; + NameChangeRequestPtr ncr; + + // Iterate over the list of requests and add each to the queue. + for (int i = 0; i < VALID_MSG_CNT; i++) { + // Create the ncr and add to our reference list. + ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i])); + ref_msgs.push_back(ncr); + + // Verify that the request can be added to the queue and queue + // size increments accordingly. + EXPECT_NO_THROW(queue_mgr->enqueue(ncr)); + EXPECT_EQ(i+1, queue_mgr->getQueueSize()); + } + + // Loop through and verify that the queue contents match the + // reference list. + for (int i = 0; i < VALID_MSG_CNT; i++) { + // Verify that peek on a non-empty queue returns first entry + // without altering queue content. + EXPECT_NO_THROW(ncr = queue_mgr->peek()); + + // Verify the peeked entry is the one it should be. + ASSERT_TRUE(ncr); + EXPECT_TRUE (*(ref_msgs[i]) == *ncr); + + // Verify that peek did not alter the queue size. + EXPECT_EQ(VALID_MSG_CNT - i, queue_mgr->getQueueSize()); + + // Verify the dequeuing from non-empty queue works + EXPECT_NO_THROW(queue_mgr->dequeue()); + + // Verify queue size decrements following dequeue. + EXPECT_EQ(VALID_MSG_CNT - (i + 1), queue_mgr->getQueueSize()); + } + + // Iterate over the list of requests and add each to the queue. + for (int i = 0; i < VALID_MSG_CNT; i++) { + // Create the ncr and add to our reference list. + ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i])); + ref_msgs.push_back(ncr); + EXPECT_NO_THROW(queue_mgr->enqueue(ncr)); + } + + // Verify queue count is correct. + EXPECT_EQ(VALID_MSG_CNT, queue_mgr->getQueueSize()); + + // Verify that peekAt returns the correct entry. + EXPECT_NO_THROW(ncr = queue_mgr->peekAt(1)); + EXPECT_TRUE (*(ref_msgs[1]) == *ncr); + + // Verify that dequeueAt removes the correct entry. + // Removing it, this should shift the queued entries forward by one. + EXPECT_NO_THROW(queue_mgr->dequeueAt(1)); + EXPECT_NO_THROW(ncr = queue_mgr->peekAt(1)); + EXPECT_TRUE (*(ref_msgs[2]) == *ncr); + + // Verify the peekAt and dequeueAt throw when given indexes beyond the end. + EXPECT_THROW(queue_mgr->peekAt(VALID_MSG_CNT + 1), D2QueueMgrInvalidIndex); + EXPECT_THROW(queue_mgr->dequeueAt(VALID_MSG_CNT + 1), + D2QueueMgrInvalidIndex); +} + +/// @brief Compares two NameChangeRequests for equality. +bool checkSendVsReceived(NameChangeRequestPtr sent_ncr, + NameChangeRequestPtr received_ncr) { + return ((sent_ncr && received_ncr) && + (*sent_ncr == *received_ncr)); +} + +/// @brief Text fixture that allows testing a listener and sender together +/// It derives from both the receive and send handler classes and contains +/// and instance of UDP listener and UDP sender. +class QueueMgrUDPTest : public virtual ::testing::Test, public D2StatTest, + NameChangeSender::RequestSendHandler { +public: + asiolink::IOServicePtr io_service_; + NameChangeSenderPtr sender_; + isc::asiolink::IntervalTimer test_timer_; + D2QueueMgrPtr queue_mgr_; + + NameChangeSender::Result send_result_; + std::vector<NameChangeRequestPtr> sent_ncrs_; + std::vector<NameChangeRequestPtr> received_ncrs_; + + QueueMgrUDPTest() : io_service_(new isc::asiolink::IOService()), + test_timer_(*io_service_), + send_result_(NameChangeSender::SUCCESS) { + isc::asiolink::IOAddress addr(TEST_ADDRESS); + // Create our sender instance. Note that reuse_address is true. + sender_.reset(new NameChangeUDPSender(addr, SENDER_PORT, + addr, LISTENER_PORT, + FMT_JSON, *this, 100, true)); + + // Set the test timeout to break any running tasks if they hang. + test_timer_.setup(std::bind(&QueueMgrUDPTest::testTimeoutHandler, + this), + TEST_TIMEOUT); + } + + void reset_results() { + sent_ncrs_.clear(); + received_ncrs_.clear(); + } + + /// @brief Implements the send completion handler. + virtual void operator ()(const NameChangeSender::Result result, + NameChangeRequestPtr& ncr) { + // save the result and the NCR sent. + send_result_ = result; + sent_ncrs_.push_back(ncr); + } + + /// @brief Handler invoked when test timeout is hit. + /// + /// This callback stops all running (hanging) tasks on IO service. + void testTimeoutHandler() { + io_service_->stop(); + FAIL() << "Test timeout hit."; + } +}; + +/// @brief Tests D2QueueMgr's state model. +/// This test verifies that: +/// 1. Upon construction, initial state is NOT_INITTED. +/// 2. Cannot start listening from while state is NOT_INITTED. +/// 3. Successful listener initialization transitions from NOT_INITTED +/// to INITTED. +/// 4. Attempting to initialize the listener from INITTED state is not +/// allowed. +/// 5. Starting listener from INITTED transitions to RUNNING. +/// 6. Stopping the listener transitions from RUNNING to STOPPED. +/// 7. Starting listener from STOPPED transitions to RUNNING. +TEST_F (QueueMgrUDPTest, stateModel) { + // Create the queue manager. + ASSERT_NO_THROW(queue_mgr_.reset(new D2QueueMgr(io_service_, + VALID_MSG_CNT))); + + // Verify that the initial state is NOT_INITTED. + EXPECT_EQ(D2QueueMgr::NOT_INITTED, queue_mgr_->getMgrState()); + + // Verify that trying to listen before when not initialized fails. + EXPECT_THROW(queue_mgr_->startListening(), D2QueueMgrError); + + // Verify that initializing the listener moves us to INITTED state. + isc::asiolink::IOAddress addr(TEST_ADDRESS); + EXPECT_NO_THROW(queue_mgr_->initUDPListener(addr, LISTENER_PORT, + FMT_JSON, true)); + EXPECT_EQ(D2QueueMgr::INITTED, queue_mgr_->getMgrState()); + + // Verify that attempting to initialize the listener, from INITTED + // is not allowed. + EXPECT_THROW(queue_mgr_->initUDPListener(addr, LISTENER_PORT, + FMT_JSON, true), + D2QueueMgrError); + + // Verify that we can enter the RUNNING from INITTED by starting the + // listener. + EXPECT_NO_THROW(queue_mgr_->startListening()); + EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr_->getMgrState()); + + // Verify that we can move from RUNNING to STOPPING by stopping the + // listener. + EXPECT_NO_THROW(queue_mgr_->stopListening()); + EXPECT_EQ(D2QueueMgr::STOPPING, queue_mgr_->getMgrState()); + + // Stopping requires IO cancel, which result in a callback. + // So process one event and verify we are STOPPED. + io_service_->run_one(); + EXPECT_EQ(D2QueueMgr::STOPPED, queue_mgr_->getMgrState()); + + // Verify that we can re-enter the RUNNING from STOPPED by starting the + // listener. + EXPECT_NO_THROW(queue_mgr_->startListening()); + EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr_->getMgrState()); + + // Verify that we cannot remove the listener in the RUNNING state + EXPECT_THROW(queue_mgr_->removeListener(), D2QueueMgrError); + + // Stop the listener. + EXPECT_NO_THROW(queue_mgr_->stopListening()); + EXPECT_EQ(D2QueueMgr::STOPPING, queue_mgr_->getMgrState()); + + // Stopping requires IO cancel, which result in a callback. + // So process one event and verify we are STOPPED. + io_service_->run_one(); + EXPECT_EQ(D2QueueMgr::STOPPED, queue_mgr_->getMgrState()); + + // Verify that we can remove the listener in the STOPPED state and + // end up back in NOT_INITTED. + EXPECT_NO_THROW(queue_mgr_->removeListener()); + EXPECT_EQ(D2QueueMgr::NOT_INITTED, queue_mgr_->getMgrState()); +} + +/// @brief Tests D2QueueMgr's ability to manage received requests +/// This test verifies that: +/// 1. Requests can be received, queued, and dequeued +/// 2. Once the queue is full, a subsequent request transitions +/// manager to STOPPED_QUEUE_FULL state. +/// 3. Starting listener returns manager to the RUNNING state. +/// 4. Queue contents are preserved across state transitions. +/// 5. Clearing the queue via the clearQueue() method works. +/// 6. Requests can be received and queued normally after the queue +/// has been emptied. +/// 7. setQueueMax disallows values of 0 or less than current queue size. +TEST_F (QueueMgrUDPTest, liveFeed) { + NameChangeRequestPtr send_ncr; + NameChangeRequestPtr received_ncr; + + // Create the queue manager and start listening.. + ASSERT_NO_THROW(queue_mgr_.reset(new D2QueueMgr(io_service_, + VALID_MSG_CNT))); + ASSERT_EQ(D2QueueMgr::NOT_INITTED, queue_mgr_->getMgrState()); + + // Verify that setting max queue size to 0 is not allowed. + EXPECT_THROW(queue_mgr_->setMaxQueueSize(0), D2QueueMgrError); + EXPECT_EQ(VALID_MSG_CNT, queue_mgr_->getMaxQueueSize()); + + isc::asiolink::IOAddress addr(TEST_ADDRESS); + ASSERT_NO_THROW(queue_mgr_->initUDPListener(addr, LISTENER_PORT, + FMT_JSON, true)); + ASSERT_EQ(D2QueueMgr::INITTED, queue_mgr_->getMgrState()); + + ASSERT_NO_THROW(queue_mgr_->startListening()); + ASSERT_EQ(D2QueueMgr::RUNNING, queue_mgr_->getMgrState()); + + // Place the sender into sending state. + ASSERT_NO_THROW(sender_->startSending(*io_service_)); + ASSERT_TRUE(sender_->amSending()); + + // Iterate over the list of requests sending and receiving + // each one. Verify and dequeue as they arrive. + for (int i = 0; i < VALID_MSG_CNT; i++) { + // Create the ncr and add to our reference list. + ASSERT_NO_THROW(send_ncr = NameChangeRequest::fromJSON(valid_msgs[i])); + ASSERT_NO_THROW(sender_->sendRequest(send_ncr)); + + // running two should do the send then the receive + io_service_->run_one(); + io_service_->run_one(); + + // Verify that the request can be added to the queue and queue + // size increments accordingly. + EXPECT_EQ(1, queue_mgr_->getQueueSize()); + + // Verify that peek shows the NCR we just sent + EXPECT_NO_THROW(received_ncr = queue_mgr_->peek()); + EXPECT_TRUE(checkSendVsReceived(send_ncr, received_ncr)); + + // Verify that we and dequeue the request. + EXPECT_NO_THROW(queue_mgr_->dequeue()); + EXPECT_EQ(0, queue_mgr_->getQueueSize()); + } + + StatMap stats_ncr = { + { "ncr-received", 3}, + { "ncr-invalid", 0}, + { "ncr-error", 0} + }; + checkStats(stats_ncr); + + // Iterate over the list of requests, sending and receiving + // each one. Allow them to accumulate in the queue. + for (int i = 0; i < VALID_MSG_CNT; i++) { + // Create the ncr and add to our reference list. + ASSERT_NO_THROW(send_ncr = NameChangeRequest::fromJSON(valid_msgs[i])); + ASSERT_NO_THROW(sender_->sendRequest(send_ncr)); + + // running two should do the send then the receive + EXPECT_NO_THROW(io_service_->run_one()); + EXPECT_NO_THROW(io_service_->run_one()); + EXPECT_EQ(i+1, queue_mgr_->getQueueSize()); + } + + StatMap stats_ncr_new = { + { "ncr-received", 6}, + { "ncr-invalid", 0}, + { "ncr-error", 0} + }; + checkStats(stats_ncr_new); + + // Verify that the queue is at max capacity. + EXPECT_EQ(queue_mgr_->getMaxQueueSize(), queue_mgr_->getQueueSize()); + + // Send another. The send should succeed. + ASSERT_NO_THROW(sender_->sendRequest(send_ncr)); + EXPECT_NO_THROW(io_service_->run_one()); + + // Now execute the receive which should not throw but should move us + // to STOPPED_QUEUE_FULL state. + EXPECT_NO_THROW(io_service_->run_one()); + EXPECT_EQ(D2QueueMgr::STOPPED_QUEUE_FULL, queue_mgr_->getMgrState()); + + // Verify queue size did not increase beyond max. + EXPECT_EQ(VALID_MSG_CNT, queue_mgr_->getQueueSize()); + + // Verify that setting max queue size to a value less than current size of + // the queue is not allowed. + EXPECT_THROW(queue_mgr_->setMaxQueueSize(VALID_MSG_CNT-1), D2QueueMgrError); + EXPECT_EQ(VALID_MSG_CNT, queue_mgr_->getQueueSize()); + + // Verify that we can re-enter RUNNING from STOPPED_QUEUE_FULL. + EXPECT_NO_THROW(queue_mgr_->startListening()); + EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr_->getMgrState()); + + // Verify that the queue contents were preserved. + EXPECT_EQ(queue_mgr_->getMaxQueueSize(), queue_mgr_->getQueueSize()); + + // Verify that clearQueue works. + EXPECT_NO_THROW(queue_mgr_->clearQueue()); + EXPECT_EQ(0, queue_mgr_->getQueueSize()); + + // Verify that we can again receive requests. + // Send should be fine. + ASSERT_NO_THROW(sender_->sendRequest(send_ncr)); + EXPECT_NO_THROW(io_service_->run_one()); + + // Receive should succeed. + EXPECT_NO_THROW(io_service_->run_one()); + EXPECT_EQ(1, queue_mgr_->getQueueSize()); +} + +} // end of anonymous namespace diff --git a/src/bin/d2/tests/d2_simple_parser_unittest.cc b/src/bin/d2/tests/d2_simple_parser_unittest.cc new file mode 100644 index 0000000..b236469 --- /dev/null +++ b/src/bin/d2/tests/d2_simple_parser_unittest.cc @@ -0,0 +1,1194 @@ +// 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.com/MPL/2.0/. + +#include <config.h> +#include <gtest/gtest.h> +#include <cc/data.h> +#include <d2/tests/parser_unittest.h> +#include <d2srv/d2_simple_parser.h> +#include <testutils/test_to_element.h> + +#include <boost/lexical_cast.hpp> + +using namespace isc; +using namespace isc::data; +using namespace isc::d2; +using namespace isc::test; + +namespace { + +/// @brief Checks if specified element matches the given integer default +/// +/// @param element defaulted element to check +/// @param deflt SimpleDefault which supplied the default value +void checkIntegerValue(const ConstElementPtr& element, + const SimpleDefault& deflt) { + ASSERT_TRUE(element); + + // Verify it is an integer. + ASSERT_EQ(Element::integer, element->getType()); + + // Turn default value string into an int. + int64_t default_value = 0; + ASSERT_NO_THROW(default_value = boost::lexical_cast<int64_t>(deflt.value_)); + + // Verify it has the expected value. + EXPECT_EQ(default_value, element->intValue()); +} + +/// @brief Checks if specified element matches the given boolean default +/// +/// @param element defaulted element to check +/// @param deflt SimpleDefault which supplied the default value +void checkBooleanValue(const ConstElementPtr& element, + const SimpleDefault& deflt) { + ASSERT_TRUE(element); + + // Verify it is a bool. + ASSERT_EQ(Element::boolean, element->getType()); + + // Turn default value string into a bool. + bool default_value = false; + ASSERT_NO_THROW(boost::lexical_cast<bool>(deflt.value_)); + + // Verify it has the expected value. + EXPECT_EQ(default_value, element->boolValue()); +} + +/// @brief Checks if specified element matches the given string default +/// +/// @param element defaulted element to check +/// @param deflt SimpleDefault which supplied the default value +void checkStringValue(const ConstElementPtr& element, + const SimpleDefault& deflt) { + ASSERT_TRUE(element); + + // Verify it's a string + ASSERT_EQ(Element::string, element->getType()); + + // Verify it has the expected value + EXPECT_EQ(deflt.value_, element->stringValue()); + } + +/// TSIGKeyInfo against the given set of values, and that the TSIGKey +/// member points to a key. +/// +/// @param key is a pointer to the TSIGKeyInfo instance to verify +/// @param name is the value to compare against key's name_. +/// @param algorithm is the string value to compare against key's algorithm. +/// @param secret is the value to compare against key's secret. +/// +/// @return returns true if there is a match across the board, otherwise it +/// returns false. +bool checkKey(TSIGKeyInfoPtr key, const std::string& name, + const std::string& algorithm, const std::string& secret, + uint32_t digestbits = 0) { + // Return value, assume its a match. + return (((key) && + (key->getName() == name) && + (key->getAlgorithm() == algorithm) && + (key->getDigestbits() == digestbits) && + (key->getSecret() == secret) && + (key->getTSIGKey()))); +} + +/// @brief Convenience function which compares the contents of the given +/// DnsServerInfo against the given set of values. +/// +/// It is structured in such a way that each value is checked, and output +/// is generated for all that do not match. +/// +/// @param server is a pointer to the server to check against. +/// @param hostname is the value to compare against server's hostname_. +/// @param ip_address is the string value to compare against server's +/// ip_address_. +/// @param port is the value to compare against server's port. +/// +/// @return returns true if there is a match across the board, otherwise it +/// returns false. +bool checkServer(DnsServerInfoPtr server, const char* hostname, + const char *ip_address, uint32_t port) +{ + // Return value, assume its a match. + bool result = true; + + if (!server) { + EXPECT_TRUE(server); + return false; + } + + // Check hostname. + if (server->getHostname() != hostname) { + EXPECT_EQ(hostname, server->getHostname()); + result = false; + } + + // Check IP address. + if (server->getIpAddress().toText() != ip_address) { + EXPECT_EQ(ip_address, server->getIpAddress().toText()); + result = false; + } + + // Check port. + if (server->getPort() != port) { + EXPECT_EQ (port, server->getPort()); + result = false; + } + + return (result); +} + +/// @brief Base class test fixture for testing JSON and element parsing +/// for D2 configuration elements. It combines the three phases of +/// configuration parsing normally orchestrated by D2CfgMgr: +/// 1. Submit the JSON text to the JSON parser +/// 2. Add defaults to the element tree produced by the JSON parser +/// 3. Pass the element tree into the appropriate SimpleParser derivation +/// to parse the element tree into D2 objects. +class D2SimpleParserTest : public ::testing::Test { +public: + /// @brief Constructor + /// + /// @param parser_type specifies the parsing starting point at which + /// the JSON parser should begin. It defaults to PARSER_JSON. See @c + /// D2ParserContext::ParserType for all possible values. + D2SimpleParserTest(const D2ParserContext::ParserType& + parser_type = D2ParserContext::PARSER_JSON) + : parser_type_(parser_type) { + reset(); + } + + /// @brief Destructor + virtual ~D2SimpleParserTest() { + reset(); + } + + /// @brief Parses JSON text and compares the results against an expected + /// outcome. + /// + /// The JSON text is submitted to the D2ParserContext for parsing. Any + /// errors emitted here are caught and compared against the expected + /// error or flagged as unexpected. + /// Next, the virtual method, setDefaults()is invoked. his method should + /// be used by derivations to add default values to the element tree + /// produced by the JSON parser. + /// Lastly, it passes the element tree into the virtual method, + /// parseElement(). This method should be used by derivations to create + /// the appropriate element parser to parse the element tree into the + /// appropriate D2 object(s). + /// + /// @param json JSON text to parse + /// @param exp_error exact text of the error message expected or "" + /// if parsing should succeed. + ::testing::AssertionResult parseOrFail(const std::string& json, + const std::string& exp_error) { + try { + // Free up objects created by previous invocation + reset(); + + // Submit JSON text to JSON parser. We convert the result to + // a mutable element tree to allow defaults to be added. + D2ParserContext context; + data::ElementPtr elem = boost::const_pointer_cast<Element> + (context.parseString(json, parser_type_)); + // Add any defaults + setDefaults(elem); + + // Now parse the element tree into object(s). + parseElement(elem); + } catch (const std::exception& ex) { + std::string caught_error = ex.what(); + if (exp_error.empty()) { + return ::testing::AssertionFailure() + << "Unexpected error: " << caught_error + << "\n json: [" << json << "]"; + } + + if (exp_error != caught_error) { + return ::testing::AssertionFailure() + << "Wrong error detected, expected: " + << exp_error << ", got: " << caught_error + << "\n json: [" << json << "]"; + } + return ::testing::AssertionSuccess(); + } + + if (!exp_error.empty()) { + return ::testing::AssertionFailure() + << "Unexpected parsing success " + << exp_error << "\n json: [" << json << "]"; + } + + return ::testing::AssertionSuccess(); + } + + +protected: + /// @brief Free up objects created by element parsing + /// This method is invoked at the beginning of @c parseOrFail() to + /// ensure any D2 object(s) that were created by a prior invocation are + /// destroyed. This permits parsing to be conducted more than once + /// in the same test. + virtual void reset(){}; + + /// @brief Adds default values to the given element tree + /// + /// Derivations are expected to use the appropriate methods in + /// D2SimpleParser to add defaults values. + /// + /// @param config element tree in which defaults should be added + /// @return the number of default items added to the tree + virtual size_t setDefaults(data::ElementPtr config) { + static_cast<void>(config); + return (0); + } + + /// @brief Parses a given element tree into D2 object(s) + /// + /// Derivations are expected to create the appropriate element + /// parser and pass it the element tree for parsing. Any object(s) + /// created should likely be saved for content verification + /// outside of this method. + /// + /// @param config element tree to parse + virtual void parseElement(data::ConstElementPtr config) { + static_cast<void>(config); + } + + D2ParserContext::ParserType parser_type_; +}; + +/// @brief Convenience macros for calling parseOrFail +#define PARSE_OK(a) EXPECT_TRUE((parseOrFail(a, ""))) +#define PARSE_FAIL(a,b) EXPECT_TRUE((parseOrFail(a, b))) + +// This test checks if global defaults are properly set for D2. +TEST_F(D2SimpleParserTest, globalD2Defaults) { + + ElementPtr empty = isc::d2::test::parseJSON("{ }"); + size_t num = 0; + + EXPECT_NO_THROW(num = D2SimpleParser::setAllDefaults(empty)); + + // We expect 5 parameters to be inserted. + EXPECT_EQ(num, 8); + + // Let's go over all parameters we have defaults for. + BOOST_FOREACH(SimpleDefault deflt, D2SimpleParser::D2_GLOBAL_DEFAULTS) { + ConstElementPtr x; + ASSERT_NO_THROW(x = empty->get(deflt.name_)); + + EXPECT_TRUE(x); + if (x) { + if (deflt.type_ == Element::integer) { + checkIntegerValue(x, deflt); + } else if (deflt.type_ == Element::boolean) { + checkBooleanValue(x, deflt); + } else if (deflt.type_ == Element::string) { + checkStringValue(x, deflt); + } else { + // add them if we need to. Like what do you if it's a map? + ADD_FAILURE() << "default type not supported:" << deflt.name_ + << " ,type: " << deflt.type_; + } + } + } +} + +/// @brief Test fixture class for testing TSIGKeyInfo parsing. +class TSIGKeyInfoParserTest : public D2SimpleParserTest { +public: + /// @brief Constructor + TSIGKeyInfoParserTest() + : D2SimpleParserTest(D2ParserContext::PARSER_TSIG_KEY) { + } + + /// @brief Free up the keys created by parsing + virtual void reset() { + key_.reset(); + }; + + /// @brief Destructor + virtual ~TSIGKeyInfoParserTest() { + reset(); + }; + + /// @brief Adds TSIG Key default values to the given TSIG Key element + /// + /// @param config TSIG Key element to which defaults should be added + /// + /// @return the number of default items added to the tree + size_t setDefaults(data::ElementPtr config) { + return (SimpleParser::setDefaults(config, D2SimpleParser:: + TSIG_KEY_DEFAULTS)); + } + + /// @brief Attempts to parse the given element into a TSIGKeyInfo + /// + /// Assumes the given element is a Map containing the attributes for + /// a TSIG Key. If parsing is successful the new TSIGKeyInfo instance + /// is retained in the member, key_; + /// + /// @param config element to parse + void parseElement(data::ConstElementPtr config) { + TSIGKeyInfoParser parser; + key_ = parser.parse(config); + } + + /// @brief Retains the TSIGKeyInfo created by a successful parsing + TSIGKeyInfoPtr key_; +}; + + +/// @brief Test fixture class for testing TSIGKeyInfo list parsing. +class TSIGKeyInfoListParserTest : public D2SimpleParserTest { +public: + /// @brief Constructor + TSIGKeyInfoListParserTest() + : D2SimpleParserTest(D2ParserContext::PARSER_TSIG_KEYS) { + } + + /// @brief Destructor + virtual ~TSIGKeyInfoListParserTest() { + reset(); + } + + /// @brief Free up the keys created by parsing + virtual void reset() { + keys_.reset(); + }; + + /// @brief Adds TSIG Key default values to a list of TSIG Key elements + /// + /// @param config list of TSIG Key elements to which defaults should be + /// added + /// + /// @return the number of default items added to the tree + size_t setDefaults(data::ElementPtr config) { + return (SimpleParser::setListDefaults(config, D2SimpleParser:: + TSIG_KEY_DEFAULTS)); + } + + /// @brief Attempts to parse the given element into a list of TSIGKeyInfos + /// + /// Assumes the given element is a list containing one or more TSIG Keys + /// elements. If parsing is successful the list of TSIGKeyInfo instances + /// is retained in the member, keys_; + /// + /// @param config element to parse + void parseElement(data::ConstElementPtr config) { + TSIGKeyInfoListParser parser; + keys_ = parser.parse(config); + } + + /// @brief Retains the TSIGKeyInfos created by a successful parsing + TSIGKeyInfoMapPtr keys_; +}; + +/// @brief Test fixture class for testing DnsServerInfo parsing. +class DnsServerInfoParserTest : public D2SimpleParserTest { +public: + /// @brief Constructor + DnsServerInfoParserTest() + : D2SimpleParserTest(D2ParserContext::PARSER_DNS_SERVER) { + } + + /// @brief Destructor + virtual ~DnsServerInfoParserTest() { + reset(); + } + + /// @brief Free up the server created by parsing + virtual void reset() { + server_.reset(); + } + + /// @brief Adds DNS Server default values to the given DNS Server element + /// + /// @param config DNS Server element to which defaults should be added + /// + /// @return the number of default items added to the tree + virtual size_t setDefaults(data::ElementPtr config) { + return (SimpleParser::setDefaults(config, D2SimpleParser:: + DNS_SERVER_DEFAULTS)); + } + + /// @brief Attempts to parse the given element into a DnsServerInfo + /// + /// Assumes the given element is a map containing the attributes for + /// a DNS Server. If parsing is successful the new DnsServerInfo instance + /// is retained in the member, server_; + /// + /// @param config element to parse + virtual void parseElement(data::ConstElementPtr config) { + DnsServerInfoParser parser; + std::string domain = "{ \"key-name\": \"\" }"; + server_ = parser.parse(config, Element::fromJSON(domain), {}); + } + + /// @brief Retains the DnsServerInfo created by a successful parsing + DnsServerInfoPtr server_; +}; + +/// @brief Test fixture class for testing DnsServerInfoList parsing. +class DnsServerInfoListParserTest : public D2SimpleParserTest { +public: + /// @brief Constructor + DnsServerInfoListParserTest() + : D2SimpleParserTest(D2ParserContext::PARSER_DNS_SERVERS) { + } + + /// @brief Destructor + virtual ~DnsServerInfoListParserTest() { + reset(); + } + + /// @brief Free up the servers created by parsing + virtual void reset() { + servers_.reset(); + } + + /// @brief Adds DNS Server default values to a list of DNS Server elements + /// + /// @param config list of DNS Server elements to which defaults should be + /// added + /// + /// @return the number of default items added to the tree + virtual size_t setDefaults(data::ElementPtr config) { + return (SimpleParser::setListDefaults(config, D2SimpleParser:: + DNS_SERVER_DEFAULTS)); + } + + /// @brief Attempts to parse the given element into a list of DnsServerInfos + /// + /// Assumes the given element is a list containing one or more DNS Servers + /// elements. If parsing is successful the list of DnsServerInfo instances + /// is retained in the member, keys_; + /// + /// @param config element to parse + virtual void parseElement(data::ConstElementPtr config) { + DnsServerInfoListParser parser; + std::string domain = "{ \"key-name\": \"\" }"; + servers_ = parser.parse(config, Element::fromJSON(domain), {}); + } + + /// @brief Retains the DnsServerInfos created by a successful parsing + DnsServerInfoStoragePtr servers_; +}; + + +/// @brief Test fixture class for testing DDnsDomain parsing. +class DdnsDomainParserTest : public D2SimpleParserTest { +public: + + /// @brief Constructor + DdnsDomainParserTest(const D2ParserContext::ParserType& parser_type + = D2ParserContext::PARSER_DDNS_DOMAIN) + : D2SimpleParserTest(parser_type), keys_(new TSIGKeyInfoMap()) { + } + + /// @brief Destructor + virtual ~DdnsDomainParserTest() { + reset(); + } + + /// @brief Free up the domain created by parsing + virtual void reset() { + domain_.reset(); + } + + /// @brief Add TSIGKeyInfos to the key map + /// + /// @param name the name of the key + /// @param algorithm the algorithm of the key + /// @param secret the secret value of the key + void addKey(const std::string& name, const std::string& algorithm, + const std::string& secret) { + TSIGKeyInfoPtr key_info(new TSIGKeyInfo(name, algorithm, secret)); + (*keys_)[name]=key_info; + } + + /// @brief Adds DDNS Domain values to the given DDNS Domain element + /// + /// @param config DDNS Domain element to which defaults should be added + /// + /// @return the number of default items added to the tree + virtual size_t setDefaults(data::ElementPtr config) { + return (D2SimpleParser::setDdnsDomainDefaults(config, D2SimpleParser:: + DDNS_DOMAIN_DEFAULTS)); + } + + /// @brief Attempts to parse the given element into a DdnsDomain + /// + /// Assumes the given element is a map containing the attributes for + /// a DDNS Domain. If parsing is successful the new DdnsDomain instance + /// is retained in the member, server_; + /// + /// @param config element to parse + virtual void parseElement(data::ConstElementPtr config) { + DdnsDomainParser parser; + domain_ = parser.parse(config, keys_); + } + + /// @brief Retains the DdnsDomain created by a successful parsing + DdnsDomainPtr domain_; + + /// @brief Storage for TSIGKeys, used by DdnsDomainParser to validate + /// domain keys + TSIGKeyInfoMapPtr keys_; +}; + +class DdnsDomainListParserTest : public DdnsDomainParserTest { +public: + /// @brief Constructor + DdnsDomainListParserTest() + // We need the list context type to parse lists correctly + : DdnsDomainParserTest(D2ParserContext::PARSER_DDNS_DOMAINS) { + } + + /// @brief Destructor + virtual ~DdnsDomainListParserTest() { + reset(); + } + + /// @brief Free up domains created by parsing + virtual void reset() { + domains_.reset(); + } + + /// @brief Adds DDNS Domain default values to a list of DDNS Domain elements + /// + /// @param config list of DDNS Domain elements to which defaults should be + /// added + /// + /// @return the number of default items added to the tree + virtual size_t setDefaults(data::ElementPtr config) { + size_t cnt = 0; + // We don't use SimpleParser::setListDefaults() as this does + // not handle sub-lists or sub-maps + BOOST_FOREACH(ElementPtr domain, config->listValue()) { + cnt += D2SimpleParser:: + setDdnsDomainDefaults(domain, D2SimpleParser:: + DDNS_DOMAIN_DEFAULTS); + } + + return (cnt); + } + + /// @brief Attempts to parse the given element into a list of DdnsDomains + /// + /// Assumes the given element is a list containing one or more DDNS Domains + /// elements. If parsing is successful the list of DdnsDomain instances + /// is retained in the member, keys_; + /// + /// @param config element to parse + virtual void parseElement(data::ConstElementPtr config) { + DdnsDomainListParser parser; + domains_ = parser.parse(config, keys_); + } + + /// @brief Retains the DdnsDomains created by a successful parsing + DdnsDomainMapPtr domains_; +}; + +/// @brief Tests the enforcement of data validation when parsing TSIGKeyInfos. +/// It verifies that: +/// 1. Name cannot be blank. +/// 2. Algorithm cannot be blank. +/// 3. Secret cannot be blank. +TEST_F(TSIGKeyInfoParserTest, invalidEntry) { + + // Name cannot be blank. + std::string config = "{" + " \"name\": \"\" , " + " \"algorithm\": \"HMAC-MD5\" , " + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " + "}"; + PARSE_FAIL(config, "<string>:1.9: TSIG key name cannot be blank"); + + // Algorithm cannot be be blank. + config = "{" + " \"name\": \"d2_key_one\" , " + " \"algorithm\": \"\" , " + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " + "}"; + PARSE_FAIL(config, "<string>:1.38: TSIG key algorithm cannot be blank"); + + // Algorithm must be a valid algorithm + config = "{" + " \"name\": \"d2_key_one\" , " + " \"algorithm\": \"bogus\" , " + " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " + "}"; + PARSE_FAIL(config, "tsig-key : Unknown TSIG Key algorithm:" + " bogus (<string>:1:40)"); + + // Secret cannot be blank + config = "{" + " \"name\": \"d2_key_one\" , " + " \"algorithm\": \"HMAC-MD5\" , " + " \"secret\": \"\" " + "}"; + PARSE_FAIL(config, "<string>:1.62: TSIG key secret cannot be blank"); + + // Secret must be valid for algorithm + config = "{" + " \"name\": \"d2_key_one\" , " + " \"algorithm\": \"HMAC-MD5\" , " + " \"digest-bits\": 120 , " + " \"secret\": \"bogus\" " + "}"; + PARSE_FAIL(config, "Cannot make D2TsigKey: Incomplete input for base64:" + " bogus (<string>:1:1)"); +} + + +/// @brief Verifies that TSIGKeyInfo parsing creates a proper TSIGKeyInfo +/// when given a valid combination of entries. +TEST_F(TSIGKeyInfoParserTest, validEntry) { + // Valid entries for TSIG key, all items are required. + std::string config = "{" + " \"name\": \"d2_key_one\" , " + " \"algorithm\": \"HMAC-MD5\" , " + " \"digest-bits\": 120 , " + " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " + "}"; + // Verify that it parses. + PARSE_OK(config); + ASSERT_TRUE(key_); + + // Verify the key contents. + EXPECT_TRUE(checkKey(key_, "d2_key_one", "HMAC-MD5", + "dGhpcyBrZXkgd2lsbCBtYXRjaA==", 120)); + + // Verify unparsing. + runToElementTest<TSIGKeyInfo>(config, *key_); +} + +/// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo +/// entries is detected. +TEST_F(TSIGKeyInfoListParserTest, invalidTSIGKeyList) { + // Construct a list of keys with an invalid key entry. + std::string config = "[" + " { \"name\": \"key1\" , " + " \"algorithm\": \"HMAC-MD5\" ," + " \"digest-bits\": 120 , " + " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " + " }," + // this entry has an invalid algorithm + " { \"name\": \"key2\" , " + " \"algorithm\": \"\" ," + " \"digest-bits\": 120 , " + " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " + " }," + " { \"name\": \"key3\" , " + " \"algorithm\": \"HMAC-MD5\" ," + " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " + " }" + " ]"; + + PARSE_FAIL(config, "<string>:1.151: TSIG key algorithm cannot be blank"); +} + +/// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo +/// entries is detected. +TEST_F(TSIGKeyInfoListParserTest, duplicateTSIGKey) { + // Construct a list of keys with an invalid key entry. + std::string config = "[" + " { \"name\": \"key1\" , " + " \"algorithm\": \"HMAC-MD5\" ," + " \"digest-bits\": 120 , " + " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " + " }," + " { \"name\": \"key2\" , " + " \"algorithm\": \"HMAC-MD5\" ," + " \"digest-bits\": 120 , " + " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " + " }," + " { \"name\": \"key1\" , " + " \"algorithm\": \"HMAC-MD5\" ," + " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " + " }" + " ]"; + + PARSE_FAIL(config, + "Duplicate TSIG key name specified : key1 (<string>:1:239)"); +} + +/// @brief Verifies a valid list of TSIG Keys parses correctly. +/// Also verifies that all of the supported algorithm names work. +TEST_F(TSIGKeyInfoListParserTest, validTSIGKeyList) { + // Construct a valid list of keys. + std::string config = "[" + " { \"name\": \"key1\" , " + " \"algorithm\": \"HMAC-MD5\" ," + " \"digest-bits\": 80 , " + " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " + " }," + " { \"name\": \"key2\" , " + " \"algorithm\": \"HMAC-SHA1\" ," + " \"digest-bits\": 80 , " + " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " + " }," + " { \"name\": \"key3\" , " + " \"algorithm\": \"HMAC-SHA256\" ," + " \"digest-bits\": 128 , " + " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " + " }," + " { \"name\": \"key4\" , " + " \"algorithm\": \"HMAC-SHA224\" ," + " \"digest-bits\": 112 , " + " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " + " }," + " { \"name\": \"key5\" , " + " \"algorithm\": \"HMAC-SHA384\" ," + " \"digest-bits\": 192 , " + " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " + " }," + " { \"name\": \"key6\" , " + " \"algorithm\": \"HMAC-SHA512\" ," + " \"digest-bits\": 256 , " + " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " + " }" + " ]"; + + PARSE_OK(config); + ASSERT_TRUE(keys_); + + std::string ref_secret = "dGhpcyBrZXkgd2lsbCBtYXRjaA=="; + // Verify the correct number of keys are present + int count = keys_->size(); + ASSERT_EQ(6, count); + + // Find the 1st key and retrieve it. + TSIGKeyInfoMap::iterator gotit = keys_->find("key1"); + ASSERT_TRUE(gotit != keys_->end()); + TSIGKeyInfoPtr& key = gotit->second; + + // Verify the key contents. + EXPECT_TRUE(checkKey(key, "key1", TSIGKeyInfo::HMAC_MD5_STR, + ref_secret, 80)); + + // Find the 2nd key and retrieve it. + gotit = keys_->find("key2"); + ASSERT_TRUE(gotit != keys_->end()); + key = gotit->second; + + // Verify the key contents. + EXPECT_TRUE(checkKey(key, "key2", TSIGKeyInfo::HMAC_SHA1_STR, + ref_secret, 80)); + + // Find the 3rd key and retrieve it. + gotit = keys_->find("key3"); + ASSERT_TRUE(gotit != keys_->end()); + key = gotit->second; + + // Verify the key contents. + EXPECT_TRUE(checkKey(key, "key3", TSIGKeyInfo::HMAC_SHA256_STR, + ref_secret, 128)); + + // Find the 4th key and retrieve it. + gotit = keys_->find("key4"); + ASSERT_TRUE(gotit != keys_->end()); + key = gotit->second; + + // Verify the key contents. + EXPECT_TRUE(checkKey(key, "key4", TSIGKeyInfo::HMAC_SHA224_STR, + ref_secret, 112)); + + // Find the 5th key and retrieve it. + gotit = keys_->find("key5"); + ASSERT_TRUE(gotit != keys_->end()); + key = gotit->second; + + // Verify the key contents. + EXPECT_TRUE(checkKey(key, "key5", TSIGKeyInfo::HMAC_SHA384_STR, + ref_secret, 192)); + + // Find the 6th key and retrieve it. + gotit = keys_->find("key6"); + ASSERT_TRUE(gotit != keys_->end()); + key = gotit->second; + + // Verify the key contents. + EXPECT_TRUE(checkKey(key, "key6", TSIGKeyInfo::HMAC_SHA512_STR, + ref_secret, 256)); +} + +/// @brief Tests the enforcement of data validation when parsing DnsServerInfos. +/// It verifies that: +/// 1. Specifying both a hostname and an ip address is not allowed. +/// 2. Specifying both blank a hostname and blank ip address is not allowed. +/// 3. Specifying a negative port number is not allowed. + +TEST_F(DnsServerInfoParserTest, invalidEntry) { + // Create a config in which both host and ip address are supplied. + // Verify that parsing fails. + std::string config = "{ \"hostname\": \"pegasus.example\", " + " \"ip-address\": \"127.0.0.1\", " + " \"port\": 100} "; + PARSE_FAIL(config, "<string>:1.13: hostname is not yet supported"); + + + // Neither host nor ip address supplied + // Verify that builds fails. + config = "{ \"hostname\": \"\", " + " \"ip-address\": \"\", " + " \"port\": 100} "; + PARSE_FAIL(config, "Dns Server must specify one or the other" + " of hostname or IP address (<string>:1:1)"); + + // Create a config with a negative port number. + // Verify that build fails. + config = "{ \"hostname\": \"\", " + " \"ip-address\": \"192.168.5.6\" ," + " \"port\": -100 }"; + PARSE_FAIL(config, "<string>:1.60-63: port must be greater than zero but less than 65536"); +} + + +/// @brief Verifies that DnsServerInfo parsing creates a proper DnsServerInfo +/// when given a valid combination of entries. +/// It verifies that: +/// 1. A DnsServerInfo entry is correctly made, when given only a hostname. +/// 2. A DnsServerInfo entry is correctly made, when given ip address and port. +/// 3. A DnsServerInfo entry is correctly made, when given only an ip address. +TEST_F(DnsServerInfoParserTest, validEntry) { + /// @todo When resolvable hostname is supported you'll need this test. + /// // Valid entries for dynamic host + /// std::string config = "{ \"hostname\": \"pegasus.example\" }"; + /// ASSERT_TRUE(fromJSON(config)); + + /// // Verify that it builds and commits without throwing. + /// ASSERT_NO_THROW(parser_->build(config_set_)); + /// ASSERT_NO_THROW(parser_->commit()); + + /// //Verify the correct number of servers are present + /// int count = servers_->size(); + /// EXPECT_EQ(1, count); + + /// Verify the server exists and has the correct values. + /// DnsServerInfoPtr server = (*servers_)[0]; + /// EXPECT_TRUE(checkServer(server, "pegasus.example", + /// DnsServerInfo::EMPTY_IP_STR, + /// DnsServerInfo::STANDARD_DNS_PORT)); + + /// // Start over for a new test. + /// reset(); + + // Valid entries for static ip + std::string config = " { \"hostname\" : \"\", " + " \"ip-address\": \"127.0.0.1\" , " + " \"port\": 100 }"; + PARSE_OK(config); + ASSERT_TRUE(server_); + EXPECT_TRUE(checkServer(server_, "", "127.0.0.1", 100)); + + // Verify unparsing. + runToElementTest<DnsServerInfo>(config, *server_); + + // Valid entries for static ip, no port + // This will fail without invoking set defaults + config = " { \"ip-address\": \"192.168.2.5\" }"; + PARSE_OK(config); + ASSERT_TRUE(server_); + EXPECT_TRUE(checkServer(server_, "", "192.168.2.5", + DnsServerInfo::STANDARD_DNS_PORT)); +} + +/// @brief Verifies that attempting to parse an invalid list of DnsServerInfo +/// entries is detected. +TEST_F(DnsServerInfoListParserTest, invalidServerList) { + // Construct a list of servers with an invalid server entry. + std::string config = "[ { \"ip-address\": \"127.0.0.1\" }, " + "{ \"ip-address\": \"\" }, " + "{ \"ip-address\": \"127.0.0.2\" } ]"; + PARSE_FAIL(config, "Dns Server must specify one or the other" + " of hostname or IP address (<string>:1:34)"); + ASSERT_FALSE(servers_); +} + +/// @brief Verifies that a list of DnsServerInfo entries parses correctly given +/// a valid configuration. +TEST_F(DnsServerInfoListParserTest, validServerList) { + // Create a valid list of servers. + std::string config = "[ { \"ip-address\": \"127.0.0.1\" }, " + "{ \"ip-address\": \"127.0.0.2\" }, " + "{ \"ip-address\": \"127.0.0.3\" } ]"; + PARSE_OK(config); + + // Verify that the server storage contains the correct number of servers. + ASSERT_EQ(3, servers_->size()); + + // Verify the first server exists and has the correct values. + DnsServerInfoPtr server = (*servers_)[0]; + EXPECT_TRUE(checkServer(server, "", "127.0.0.1", + DnsServerInfo::STANDARD_DNS_PORT)); + + // Verify the second server exists and has the correct values. + server = (*servers_)[1]; + EXPECT_TRUE(checkServer(server, "", "127.0.0.2", + DnsServerInfo::STANDARD_DNS_PORT)); + + // Verify the third server exists and has the correct values. + server = (*servers_)[2]; + EXPECT_TRUE(checkServer(server, "", "127.0.0.3", + DnsServerInfo::STANDARD_DNS_PORT)); +} + +/// @brief Tests the enforcement of data validation when parsing DdnsDomains. +/// It verifies that: +/// 1. Domain storage cannot be null when constructing a DdnsDomainParser. +/// 2. The name entry is not optional. +/// 3. The server list may not be empty. +/// 4. That a mal-formed server entry is detected. +/// 5. That an undefined key name is detected. +TEST_F(DdnsDomainParserTest, invalidDomain) { + // Create a domain configuration without a name + std::string config = "{ \"key-name\": \"d2_key.example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" , " + " \"port\": 100 }," + " { \"ip-address\": \"127.0.0.2\" , " + " \"port\": 200 }," + " { \"ip-address\": \"127.0.0.3\" , " + " \"port\": 300 } ] } "; + PARSE_FAIL(config, "missing parameter 'name' (<string>:1:1)"); + + // Create a domain configuration with an empty server list. + config = "{ \"name\": \"example.com\" , " + " \"key-name\": \"\" , " + " \"dns-servers\" : [ " + " ] } "; + PARSE_FAIL(config, "<string>:1.69: syntax error, unexpected ], expecting {"); + + // Create a domain configuration with a mal-formed server entry. + config = "{ \"name\": \"example.com\" , " + " \"key-name\": \"\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.3\" , " + " \"port\": -1 } ] } "; + PARSE_FAIL(config, "<string>:1.111-112: port must be greater than zero but less than 65536"); + + // Create a domain configuration without an defined key name + config = "{ \"name\": \"example.com\" , " + " \"key-name\": \"d2_key.example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.3\" , " + " \"port\": 300 } ] } "; + PARSE_FAIL(config, "DdnsDomain : specifies" + " an undefined key: d2_key.example.com (<string>:1:41)"); +} + +/// @brief Verifies the basics of parsing of a DdnsDomain. +TEST_F(DdnsDomainParserTest, validDomain) { + // Add a TSIG key to the test key map, so key validation will pass. + addKey("d2_key.example.com", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ=="); + + // Create a valid domain configuration entry containing three valid + // servers. + std::string config = + "{ \"name\": \"example.com\" , " + " \"key-name\": \"d2_key.example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" , " + " \"port\": 100 }," + " { \"ip-address\": \"127.0.0.2\" , " + " \"port\": 200 }," + " { \"ip-address\": \"127.0.0.3\" , " + " \"port\": 300 } ] } "; + PARSE_OK(config); + + // Domain should exist + ASSERT_TRUE(domain_); + + // Verify the name and key_name values. + EXPECT_EQ("example.com", domain_->getName()); + EXPECT_EQ("d2_key.example.com", domain_->getKeyName()); + + // Verify that the server list exists and contains the correct number of + // servers. + const DnsServerInfoStoragePtr& servers = domain_->getServers(); + ASSERT_TRUE(servers); + EXPECT_EQ(3, servers->size()); + + // Fetch each server and verify its contents. + DnsServerInfoPtr server = (*servers)[0]; + ASSERT_TRUE(server); + + EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100)); + ASSERT_TRUE(server->getTSIGKeyInfo()); + EXPECT_TRUE(server->getTSIGKeyInfo()->getTSIGKey()); + + server = (*servers)[1]; + ASSERT_TRUE(server); + + EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200)); + ASSERT_TRUE(server->getTSIGKeyInfo()); + EXPECT_TRUE(server->getTSIGKeyInfo()->getTSIGKey()); + + server = (*servers)[2]; + ASSERT_TRUE(server); + + EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300)); + ASSERT_TRUE(server->getTSIGKeyInfo()); + EXPECT_TRUE(server->getTSIGKeyInfo()->getTSIGKey()); + + // Verify unparsing. + ElementPtr json; + ASSERT_NO_THROW(json = Element::fromJSON(config)); + ConstElementPtr servers_json; + ASSERT_NO_THROW(servers_json = json->get("dns-servers")); + ASSERT_TRUE(servers_json); + ASSERT_EQ(Element::list, servers_json->getType()); + for (size_t i = 0; i < servers_json->size(); ++i) { + ElementPtr server_json; + ASSERT_NO_THROW(server_json = servers_json->getNonConst(i)); + ASSERT_NO_THROW(server_json->set("hostname", + Element::create(std::string()))); + } + runToElementTest<DdnsDomain>(json, *domain_); +} + +/// @brief Tests the fundamentals of parsing DdnsDomain lists. +/// This test verifies that given a valid domain list configuration +/// it will accurately parse and populate each domain in the list. +TEST_F(DdnsDomainListParserTest, validList) { + // Add keys to key map so key validation passes. + addKey("d2_key.example.com", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ=="); + addKey("d2_key.billcat.net", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ=="); + + // Create a valid domain list configuration, with two domains + // that have three servers each. + std::string config = + "[ " + "{ \"name\": \"example.com\" , " + " \"key-name\": \"d2_key.example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" , " + " \"port\": 100 }," + " { \"ip-address\": \"127.0.0.2\" , " + " \"port\": 200 }," + " { \"ip-address\": \"127.0.0.3\" , " + " \"port\": 300 } ] } " + ", " + "{ \"name\": \"billcat.net\" , " + " \"key-name\": \"d2_key.billcat.net\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.4\" , " + " \"port\": 400 }," + " { \"ip-address\": \"127.0.0.5\" , " + " \"port\": 500 }," + " { \"ip-address\": \"127.0.0.6\" , " + " \"port\": 600 } ] } " + "] "; + + // Verify that the domain list parses without error. + PARSE_OK(config); + ASSERT_TRUE(domains_); + EXPECT_EQ(2, domains_->size()); + + // Verify that the first domain exists and can be retrieved. + DdnsDomainMap::iterator gotit = domains_->find("example.com"); + ASSERT_TRUE(gotit != domains_->end()); + DdnsDomainPtr& domain = gotit->second; + + // Verify the name and key_name values of the first domain. + EXPECT_EQ("example.com", domain->getName()); + EXPECT_EQ("d2_key.example.com", domain->getKeyName()); + + // Verify the each of the first domain's servers + DnsServerInfoStoragePtr servers = domain->getServers(); + ASSERT_TRUE(servers); + EXPECT_EQ(3, servers->size()); + + DnsServerInfoPtr server = (*servers)[0]; + ASSERT_TRUE(server); + EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100)); + + // Verify the TSIGKeyInfo name and that the actual key was created + ASSERT_TRUE(server->getTSIGKeyInfo()); + EXPECT_EQ(domain->getKeyName(), server->getKeyName()); + EXPECT_EQ(domain->getKeyName(), server->getTSIGKeyInfo()->getName()); + EXPECT_TRUE(server->getTSIGKeyInfo()->getTSIGKey()); + + server = (*servers)[1]; + ASSERT_TRUE(server); + EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200)); + ASSERT_TRUE(server->getTSIGKeyInfo()); + EXPECT_EQ(domain->getKeyName(), server->getKeyName()); + EXPECT_EQ(domain->getKeyName(), server->getTSIGKeyInfo()->getName()); + EXPECT_TRUE(server->getTSIGKeyInfo()->getTSIGKey()); + + server = (*servers)[2]; + ASSERT_TRUE(server); + EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300)); + ASSERT_TRUE(server->getTSIGKeyInfo()); + EXPECT_EQ(domain->getKeyName(), server->getKeyName()); + EXPECT_EQ(domain->getKeyName(), server->getTSIGKeyInfo()->getName()); + EXPECT_TRUE(server->getTSIGKeyInfo()->getTSIGKey()); + + // Verify second domain + gotit = domains_->find("billcat.net"); + ASSERT_TRUE(gotit != domains_->end()); + domain = gotit->second; + + // Verify the name and key_name values of the second domain. + EXPECT_EQ("billcat.net", domain->getName()); + EXPECT_EQ("d2_key.billcat.net", domain->getKeyName()); + + // Verify the each of second domain's servers + servers = domain->getServers(); + ASSERT_TRUE(servers); + EXPECT_EQ(3, servers->size()); + + server = (*servers)[0]; + ASSERT_TRUE(server); + EXPECT_TRUE(checkServer(server, "", "127.0.0.4", 400)); + ASSERT_TRUE(server->getTSIGKeyInfo()); + EXPECT_EQ(domain->getKeyName(), server->getKeyName()); + EXPECT_EQ(domain->getKeyName(), server->getTSIGKeyInfo()->getName()); + EXPECT_TRUE(server->getTSIGKeyInfo()->getTSIGKey()); + + server = (*servers)[1]; + ASSERT_TRUE(server); + EXPECT_TRUE(checkServer(server, "", "127.0.0.5", 500)); + ASSERT_TRUE(server->getTSIGKeyInfo()); + EXPECT_EQ(domain->getKeyName(), server->getKeyName()); + EXPECT_EQ(domain->getKeyName(), server->getTSIGKeyInfo()->getName()); + EXPECT_TRUE(server->getTSIGKeyInfo()->getTSIGKey()); + + server = (*servers)[2]; + ASSERT_TRUE(server); + EXPECT_TRUE(checkServer(server, "", "127.0.0.6", 600)); + ASSERT_TRUE(server->getTSIGKeyInfo()); + EXPECT_EQ(domain->getKeyName(), server->getKeyName()); + EXPECT_EQ(domain->getKeyName(), server->getTSIGKeyInfo()->getName()); + EXPECT_TRUE(server->getTSIGKeyInfo()->getTSIGKey()); +} + +/// @brief Tests that a domain list configuration cannot contain duplicates. +TEST_F(DdnsDomainListParserTest, duplicateDomain) { + // Create a domain list configuration that contains two domains with + // the same name. + std::string config = + "[ " + "{ \"name\": \"example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.3\" , " + " \"port\": 300 } ] } " + ", " + "{ \"name\": \"example.com\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.3\" , " + " \"port\": 300 } ] } " + "] "; + // Verify that the parsing fails. + PARSE_FAIL(config, + "Duplicate domain specified:example.com (<string>:1:115)"); +} + +} diff --git a/src/bin/d2/tests/d2_unittests.cc b/src/bin/d2/tests/d2_unittests.cc new file mode 100644 index 0000000..4607550 --- /dev/null +++ b/src/bin/d2/tests/d2_unittests.cc @@ -0,0 +1,28 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <d2srv/d2_log.h> +#include <log/logger_support.h> +#include <gtest/gtest.h> + +int +main(int argc, char* argv[]) { + + ::testing::InitGoogleTest(&argc, argv); + + // See the documentation of the KEA_* environment variables in + // src/lib/log/README for info on how to tweak logging + isc::log::initLogger(); + + // Override --localstatedir value for PID files + setenv("KEA_PIDFILE_DIR", TEST_DATA_BUILDDIR, 1); + + int result = RUN_ALL_TESTS(); + + return (result); +} diff --git a/src/bin/d2/tests/d2_update_mgr_unittests.cc b/src/bin/d2/tests/d2_update_mgr_unittests.cc new file mode 100644 index 0000000..d79b453 --- /dev/null +++ b/src/bin/d2/tests/d2_update_mgr_unittests.cc @@ -0,0 +1,989 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <asiolink/io_service.h> +#include <d2srv/testutils/nc_test_utils.h> +#include <d2/d2_update_mgr.h> +#include <d2/nc_add.h> +#include <d2/nc_remove.h> +#include <d2/simple_add.h> +#include <d2/simple_remove.h> +#include <process/testutils/d_test_stubs.h> +#include <util/time_utilities.h> + +#include <gtest/gtest.h> +#include <algorithm> +#include <vector> + +using namespace std; +using namespace isc; +using namespace isc::dhcp_ddns; +using namespace isc::d2; +using namespace isc::process; +using namespace isc::util; + +namespace { + +/// @brief Wrapper class for D2UpdateMgr providing access to non-public methods. +/// +/// This class facilitates testing by making non-public methods accessible so +/// they can be invoked directly in test routines. +class D2UpdateMgrWrapper : public D2UpdateMgr { +public: + /// @brief Constructor + /// + /// Parameters match those needed by D2UpdateMgr. + D2UpdateMgrWrapper(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr, + asiolink::IOServicePtr& io_service, + const size_t max_transactions = MAX_TRANSACTIONS_DEFAULT) + : D2UpdateMgr(queue_mgr, cfg_mgr, io_service, max_transactions) { + } + + /// @brief Destructor + virtual ~D2UpdateMgrWrapper() { + } + + // Expose the protected methods to be tested. + using D2UpdateMgr::checkFinishedTransactions; + using D2UpdateMgr::pickNextJob; + using D2UpdateMgr::makeTransaction; +}; + +/// @brief Defines a pointer to a D2UpdateMgr instance. +typedef boost::shared_ptr<D2UpdateMgrWrapper> D2UpdateMgrWrapperPtr; + +/// @brief Test fixture for testing D2UpdateMgr. +/// +/// Note this class uses D2UpdateMgrWrapper class to exercise non-public +/// aspects of D2UpdateMgr. D2UpdateMgr depends on both D2QueueMgr and +/// D2CfgMgr. This fixture provides an instance of each, plus a canned, +/// valid DHCP_DDNS configuration sufficient to test D2UpdateMgr's basic +/// functions. +class D2UpdateMgrTest : public TimedIO, public ConfigParseTest { +public: + D2QueueMgrPtr queue_mgr_; + D2CfgMgrPtr cfg_mgr_; + D2UpdateMgrWrapperPtr update_mgr_; + std::vector<NameChangeRequestPtr> canned_ncrs_; + size_t canned_count_; + + D2UpdateMgrTest() { + queue_mgr_.reset(new D2QueueMgr(io_service_)); + cfg_mgr_.reset(new D2CfgMgr()); + update_mgr_.reset(new D2UpdateMgrWrapper(queue_mgr_, cfg_mgr_, + io_service_)); + makeCannedNcrs(); + makeCannedConfig(); + } + + ~D2UpdateMgrTest() { + } + + /// @brief Creates a list of valid NameChangeRequest. + /// + /// This method builds a list of NameChangeRequests from a single + /// JSON string request. Each request is assigned a unique DHCID. + void makeCannedNcrs() { + const char* msg_str = + "{" + " \"change-type\" : 0 , " + " \"forward-change\" : true , " + " \"reverse-change\" : false , " + " \"fqdn\" : \"my.example.com.\" , " + " \"ip-address\" : \"192.168.1.2\" , " + " \"dhcid\" : \"0102030405060708\" , " + " \"lease-expires-on\" : \"20130121132405\" , " + " \"lease-length\" : 1300, " + " \"use-conflict-resolution\" : true " + "}"; + + const char* dhcids[] = { "111111", "222222", "333333", "444444" }; + canned_count_ = 4; + for (int i = 0; i < canned_count_; i++) { + dhcp_ddns::NameChangeRequestPtr ncr = NameChangeRequest:: + fromJSON(msg_str); + ncr->setDhcid(dhcids[i]); + ncr->setChangeType(i % 2 == 0 ? + dhcp_ddns::CHG_ADD : dhcp_ddns::CHG_REMOVE); + canned_ncrs_.push_back(ncr); + } + } + + /// @brief Seeds configuration manager with a valid DHCP_DDNS configuration. + void makeCannedConfig() { + std::string canned_config_ = + "{ " + "\"ip-address\" : \"192.168.1.33\" , " + "\"port\" : 88 , " + "\"tsig-keys\": [] ," + "\"forward-ddns\" : {" + " \"ddns-domains\": [ " + " { \"name\": \"example.com.\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\", \"port\" : 5301 } " + " ] }," + " { \"name\": \"org.\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" } " + " ] }" + " ] }, " + "\"reverse-ddns\" : { " + " \"ddns-domains\": [ " + " { \"name\": \"1.168.192.in-addr.arpa.\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\", \"port\" : 5301 } " + " ] }, " + " { \"name\": \"2.0.3.0.8.B.D.0.1.0.0.2.ip6.arpa.\" , " + " \"dns-servers\" : [ " + " { \"ip-address\": \"127.0.0.1\" } " + " ] } " + " ] } }"; + + // If this configuration fails to parse most tests will fail. + ASSERT_TRUE(fromJSON(canned_config_)); + answer_ = cfg_mgr_->simpleParseConfig(config_set_); + ASSERT_TRUE(checkAnswer(0)); + } + + /// @brief Fakes the completion of a given transaction. + /// + /// @param index index of the request from which the transaction was formed. + /// @param status completion status to assign to the request + void completeTransaction(const size_t index, + const dhcp_ddns::NameChangeStatus& status) { + // add test on index + if (index >= canned_count_) { + ADD_FAILURE() << "request index is out of range: " << index; + } + + const dhcp_ddns::D2Dhcid key = canned_ncrs_[index]->getDhcid(); + + // locate the transaction based on the request DHCID + TransactionList::iterator pos = update_mgr_->findTransaction(key); + if (pos == update_mgr_->transactionListEnd()) { + ADD_FAILURE() << "cannot find transaction for key: " << key.toStr(); + } + + NameChangeTransactionPtr trans = (*pos).second; + // Update the status of the request + trans->getNcr()->setStatus(status); + // End the model. + trans->endModel(); + } + + /// @brief Determines if any transactions are waiting for IO completion. + /// + /// @returns True if isModelWaiting() is true for at least one of the current + /// transactions. + bool anyoneWaiting() { + TransactionList::iterator it = update_mgr_->transactionListBegin(); + while (it != update_mgr_->transactionListEnd()) { + if (((*it).second)->isModelWaiting()) { + return true; + } + } + + return false; + } + + /// @brief Process events until all requests have been completed. + /// + /// This method iteratively calls D2UpdateMgr::sweep and executes + /// IOService calls until both the request queue and transaction list + /// are empty or a timeout occurs. Note that in addition to the safety + /// timer, the number of passes through the loop is also limited to + /// a given number. This is a failsafe to guard against an infinite loop + /// in the test. + void processAll(size_t max_passes = 100) { + // Loop until all the transactions have been dequeued and run through to + // completion. + size_t passes = 0; + size_t handlers = 0; + + // Set the timeout to slightly more than DNSClient timeout to allow + // timeout processing to occur naturally. + size_t timeout = cfg_mgr_->getD2Params()->getDnsServerTimeout() + 100; + while (update_mgr_->getQueueCount() || + update_mgr_->getTransactionCount()) { + ++passes; + update_mgr_->sweep(); + // If any transactions are waiting on IO, run the service. + if (anyoneWaiting()) { + int cnt = runTimedIO(timeout); + + // If cnt is zero then the service stopped unexpectedly. + if (cnt == 0) { + ADD_FAILURE() + << "processALL: IO service stopped unexpectedly," + << " passes: " << passes << ", handlers executed: " + << handlers; + } + + handlers += cnt; + } + + // This is a last resort fail safe to ensure we don't go around + // forever. We cut it off the number of passes at 100 (default + // value). This is roughly ten times the number for the longest + // test (currently, multiTransactionTimeout). + if (passes > max_passes) { + FAIL() << "processALL failed, too many passes: " + << passes << ", total handlers executed: " << handlers; + } + } + } + +}; + +/// @brief Tests the D2UpdateMgr construction. +/// This test verifies that: +/// 1. Construction with invalid queue manager is not allowed +/// 2. Construction with invalid configuration manager is not allowed +/// 3. Construction with max transactions of zero is not allowed +/// 4. Default construction works and max transactions is defaulted properly +/// 5. Construction with custom max transactions works properly +TEST(D2UpdateMgr, construction) { + asiolink::IOServicePtr io_service(new isc::asiolink::IOService()); + D2QueueMgrPtr queue_mgr; + D2CfgMgrPtr cfg_mgr; + D2UpdateMgrPtr update_mgr; + + // Verify that constructor fails if given an invalid queue manager. + ASSERT_NO_THROW(cfg_mgr.reset(new D2CfgMgr())); + EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service), + D2UpdateMgrError); + + // Verify that constructor fails if given an invalid config manager. + ASSERT_NO_THROW(queue_mgr.reset(new D2QueueMgr(io_service))); + ASSERT_NO_THROW(cfg_mgr.reset()); + EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service), + D2UpdateMgrError); + + ASSERT_NO_THROW(cfg_mgr.reset(new D2CfgMgr())); + + // Verify that constructor fails with invalid io_service. + io_service.reset(); + EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service), + D2UpdateMgrError); + io_service.reset(new isc::asiolink::IOService()); + + // Verify that max transactions cannot be zero. + EXPECT_THROW(D2UpdateMgr(queue_mgr, cfg_mgr, io_service, 0), + D2UpdateMgrError); + + // Verify that given valid values, constructor works. + ASSERT_NO_THROW(update_mgr.reset(new D2UpdateMgr(queue_mgr, cfg_mgr, + io_service))); + + // Verify that max transactions defaults properly. + EXPECT_EQ(D2UpdateMgr::MAX_TRANSACTIONS_DEFAULT, + update_mgr->getMaxTransactions()); + + + // Verify that constructor permits custom max transactions. + ASSERT_NO_THROW(update_mgr.reset(new D2UpdateMgr(queue_mgr, cfg_mgr, + io_service, 100))); + + // Verify that max transactions is correct. + EXPECT_EQ(100, update_mgr->getMaxTransactions()); +} + +/// @brief Tests the D2UpdateManager's transaction list services +/// This test verifies that: +/// 1. A transaction can be added to the list. +/// 2. Finding a transaction in the list by key works correctly. +/// 3. Looking for a non-existent transaction works properly. +/// 4. Attempting to add a transaction for a DHCID already in the list fails. +/// 5. Removing a transaction by key works properly. +/// 6. Attempting to remove an non-existent transaction does no harm. +TEST_F(D2UpdateMgrTest, transactionList) { + // Grab a canned request for test purposes. + NameChangeRequestPtr& ncr = canned_ncrs_[0]; + TransactionList::iterator pos; + + // Verify that we can add a transaction. + EXPECT_NO_THROW(update_mgr_->makeTransaction(ncr)); + EXPECT_EQ(1, update_mgr_->getTransactionCount()); + + // Verify that we can find a transaction by key. + EXPECT_NO_THROW(pos = update_mgr_->findTransaction(ncr->getDhcid())); + EXPECT_TRUE(pos != update_mgr_->transactionListEnd()); + + // Verify that convenience method has same result. + EXPECT_TRUE(update_mgr_->hasTransaction(ncr->getDhcid())); + + // Verify that we will not find a transaction that isn't there. + dhcp_ddns::D2Dhcid bogus_id("FFFF"); + EXPECT_NO_THROW(pos = update_mgr_->findTransaction(bogus_id)); + EXPECT_TRUE(pos == update_mgr_->transactionListEnd()); + + // Verify that convenience method has same result. + EXPECT_FALSE(update_mgr_->hasTransaction(bogus_id)); + + // Verify that adding a transaction for the same key fails. + EXPECT_THROW(update_mgr_->makeTransaction(ncr), D2UpdateMgrError); + EXPECT_EQ(1, update_mgr_->getTransactionCount()); + + // Verify the we can remove a transaction by key. + EXPECT_NO_THROW(update_mgr_->removeTransaction(ncr->getDhcid())); + EXPECT_EQ(0, update_mgr_->getTransactionCount()); + + // Verify the we can try to remove a non-existent transaction without harm. + EXPECT_NO_THROW(update_mgr_->removeTransaction(ncr->getDhcid())); +} + +/// @brief Checks transaction creation when both update directions are enabled. +/// Verifies that when both directions are enabled and servers are matched to +/// the request, that the transaction is created with both directions turned on. +TEST_F(D2UpdateMgrTest, bothEnabled) { + // Grab a canned request for test purposes. + NameChangeRequestPtr& ncr = canned_ncrs_[0]; + ncr->setReverseChange(true); + + // Verify we are requesting both directions. + ASSERT_TRUE(ncr->isForwardChange()); + ASSERT_TRUE(ncr->isReverseChange()); + + // Verify both both directions are enabled. + ASSERT_TRUE(cfg_mgr_->forwardUpdatesEnabled()); + ASSERT_TRUE(cfg_mgr_->reverseUpdatesEnabled()); + + // Attempt to make a transaction. + ASSERT_NO_THROW(update_mgr_->makeTransaction(ncr)); + + // Verify we create a transaction with both directions turned on. + EXPECT_EQ(1, update_mgr_->getTransactionCount()); + EXPECT_TRUE(ncr->isForwardChange()); + EXPECT_TRUE(ncr->isReverseChange()); +} + +/// @brief Checks transaction creation when reverse updates are disabled. +/// Verifies that when reverse updates are disabled, and there matching forward +/// servers, that the transaction is still created but with only the forward +/// direction turned on. +TEST_F(D2UpdateMgrTest, reverseDisable) { + // Make a NCR which requests both directions. + NameChangeRequestPtr& ncr = canned_ncrs_[0]; + ncr->setReverseChange(true); + + // Wipe out forward domain list. + DdnsDomainMapPtr emptyDomains(new DdnsDomainMap()); + cfg_mgr_->getD2CfgContext()->getReverseMgr()->setDomains(emptyDomains); + + // Verify enable methods are correct. + ASSERT_TRUE(cfg_mgr_->forwardUpdatesEnabled()); + ASSERT_FALSE(cfg_mgr_->reverseUpdatesEnabled()); + + // Attempt to make a transaction. + ASSERT_NO_THROW(update_mgr_->makeTransaction(ncr)); + + // Verify we create a transaction with only forward turned on. + EXPECT_EQ(1, update_mgr_->getTransactionCount()); + EXPECT_TRUE(ncr->isForwardChange()); + EXPECT_FALSE(ncr->isReverseChange()); +} + +/// @brief Checks transaction creation when forward updates are disabled. +/// Verifies that when forward updates are disabled, and there matching reverse +/// servers, that the transaction is still created but with only the reverse +/// direction turned on. +TEST_F(D2UpdateMgrTest, forwardDisabled) { + // Make a NCR which requests both directions. + NameChangeRequestPtr& ncr = canned_ncrs_[0]; + ncr->setReverseChange(true); + + // Wipe out forward domain list. + DdnsDomainMapPtr emptyDomains(new DdnsDomainMap()); + cfg_mgr_->getD2CfgContext()->getForwardMgr()->setDomains(emptyDomains); + + // Verify enable methods are correct. + ASSERT_FALSE(cfg_mgr_->forwardUpdatesEnabled()); + ASSERT_TRUE(cfg_mgr_->reverseUpdatesEnabled()); + + // Attempt to make a transaction. + ASSERT_NO_THROW(update_mgr_->makeTransaction(ncr)); + + // Verify we create a transaction with only reverse turned on. + EXPECT_EQ(1, update_mgr_->getTransactionCount()); + EXPECT_FALSE(ncr->isForwardChange()); + EXPECT_TRUE(ncr->isReverseChange()); +} + + +/// @brief Checks transaction creation when neither update direction is enabled. +/// Verifies that transactions are not created when both forward and reverse +/// directions are disabled. +TEST_F(D2UpdateMgrTest, bothDisabled) { + // Grab a canned request for test purposes. + NameChangeRequestPtr& ncr = canned_ncrs_[0]; + ncr->setReverseChange(true); + TransactionList::iterator pos; + + // Wipe out both forward and reverse domain lists. + DdnsDomainMapPtr emptyDomains(new DdnsDomainMap()); + cfg_mgr_->getD2CfgContext()->getForwardMgr()->setDomains(emptyDomains); + cfg_mgr_->getD2CfgContext()->getReverseMgr()->setDomains(emptyDomains); + + // Verify enable methods are correct. + EXPECT_FALSE(cfg_mgr_->forwardUpdatesEnabled()); + EXPECT_FALSE(cfg_mgr_->reverseUpdatesEnabled()); + + // Attempt to make a transaction. + ASSERT_NO_THROW(update_mgr_->makeTransaction(ncr)); + + // Verify that do not create a transaction. + EXPECT_EQ(0, update_mgr_->getTransactionCount()); +} + +/// @brief Tests D2UpdateManager's checkFinishedTransactions method. +/// This test verifies that: +/// 1. Completed transactions are removed from the transaction list. +/// 2. Failed transactions are removed from the transaction list. +/// @todo This test will need to expand if and when checkFinishedTransactions +/// method expands to do more than remove them from the list. +TEST_F(D2UpdateMgrTest, checkFinishedTransaction) { + // Ensure we have at least 4 canned requests with which to work. + ASSERT_TRUE(canned_count_ >= 4); + + // Create a transaction for each canned request. + for (int i = 0; i < canned_count_; i++) { + EXPECT_NO_THROW(update_mgr_->makeTransaction(canned_ncrs_[i])); + } + // Verify we have that the transaction count is correct. + EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount()); + + // Verify that all four transactions have been started. + TransactionList::iterator pos; + EXPECT_NO_THROW(pos = update_mgr_->transactionListBegin()); + while (pos != update_mgr_->transactionListEnd()) { + NameChangeTransactionPtr trans = (*pos).second; + ASSERT_EQ(dhcp_ddns::ST_PENDING, trans->getNcrStatus()); + ASSERT_TRUE(trans->isModelRunning()); + ++pos; + } + + // Verify that invoking checkFinishedTransactions does not throw. + EXPECT_NO_THROW(update_mgr_->checkFinishedTransactions()); + + // Since nothing is running IOService, the all four transactions should + // still be in the list. + EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount()); + + // Now "complete" two of the four. + // Simulate a successful completion. + completeTransaction(1, dhcp_ddns::ST_COMPLETED); + + // Simulate a failed completion. + completeTransaction(3, dhcp_ddns::ST_FAILED); + + // Verify that invoking checkFinishedTransactions does not throw. + EXPECT_NO_THROW(update_mgr_->checkFinishedTransactions()); + + // Verify that the list of transactions has decreased by two. + EXPECT_EQ(canned_count_ - 2, update_mgr_->getTransactionCount()); + + // Verify that the transaction list is correct. + EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[0]->getDhcid())); + EXPECT_FALSE(update_mgr_->hasTransaction(canned_ncrs_[1]->getDhcid())); + EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[2]->getDhcid())); + EXPECT_FALSE(update_mgr_->hasTransaction(canned_ncrs_[3]->getDhcid())); +} + +/// @brief Tests D2UpdateManager's pickNextJob method. +/// This test verifies that: +/// 1. pickNextJob will select and make transactions from NCR queue. +/// 2. Requests are removed from the queue once selected +/// 3. Requests for DHCIDs with transactions already in progress are not +/// selected. +/// 4. Requests with no matching servers are removed from the queue and +/// discarded. +TEST_F(D2UpdateMgrTest, pickNextJob) { + // Ensure we have at least 4 canned requests with which to work. + ASSERT_TRUE(canned_count_ >= 4); + + // Put each transaction on the queue. + for (int i = 0; i < canned_count_; i++) { + ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[i])); + } + + // Invoke pickNextJob canned_count_ times which should create a + // transaction for each canned ncr. + for (int i = 0; i < canned_count_; i++) { + EXPECT_NO_THROW(update_mgr_->pickNextJob()); + EXPECT_EQ(i + 1, update_mgr_->getTransactionCount()); + EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[i]->getDhcid())); + } + + // Verify that the queue has been drained. + EXPECT_EQ(0, update_mgr_->getQueueCount()); + + // Now verify that a subsequent request for a DCHID for which a + // transaction is in progress, is not dequeued. + // First add the "subsequent" request. + dhcp_ddns::NameChangeRequestPtr + subsequent_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[2]))); + EXPECT_NO_THROW(queue_mgr_->enqueue(subsequent_ncr)); + EXPECT_EQ(1, update_mgr_->getQueueCount()); + + // Verify that invoking pickNextJob: + // 1. Does not throw + // 2. Does not make a new transaction + // 3. Does not dequeue the entry + EXPECT_NO_THROW(update_mgr_->pickNextJob()); + EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount()); + EXPECT_EQ(1, update_mgr_->getQueueCount()); + + // Clear out the queue and transaction list. + queue_mgr_->clearQueue(); + update_mgr_->clearTransactionList(); + + // Make a forward change NCR with an FQDN that has no forward match. + dhcp_ddns::NameChangeRequestPtr + bogus_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[0]))); + bogus_ncr->setForwardChange(true); + bogus_ncr->setReverseChange(false); + bogus_ncr->setFqdn("bogus.forward.domain.com"); + + // Put it on the queue up + ASSERT_NO_THROW(queue_mgr_->enqueue(bogus_ncr)); + + // Verify that invoking pickNextJob: + // 1. Does not throw + // 2. Does not make a new transaction + // 3. Does dequeue the entry + EXPECT_NO_THROW(update_mgr_->pickNextJob()); + EXPECT_EQ(0, update_mgr_->getTransactionCount()); + EXPECT_EQ(0, update_mgr_->getQueueCount()); + + // Make a reverse change NCR with an FQDN that has no reverse match. + bogus_ncr.reset(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[0]))); + bogus_ncr->setForwardChange(false); + bogus_ncr->setReverseChange(true); + bogus_ncr->setIpAddress("77.77.77.77"); + + // Verify that invoking pickNextJob: + // 1. does not throw + // 2. Does not make a new transaction + // 3. Does dequeue the entry + EXPECT_NO_THROW(update_mgr_->pickNextJob()); + EXPECT_EQ(0, update_mgr_->getTransactionCount()); + EXPECT_EQ(0, update_mgr_->getQueueCount()); +} + +/// @brief Tests D2UpdateManager's sweep method. +/// Since sweep is primarily a wrapper around checkFinishedTransactions and +/// pickNextJob, along with checks on maximum transaction limits, it mostly +/// verifies that these three pieces work together to move process jobs. +/// Most of what is tested here is tested above. +TEST_F(D2UpdateMgrTest, sweep) { + // Ensure we have at least 4 canned requests with which to work. + ASSERT_TRUE(canned_count_ >= 4); + + // Set max transactions to same as current transaction count. + EXPECT_NO_THROW(update_mgr_->setMaxTransactions(canned_count_)); + EXPECT_EQ(canned_count_, update_mgr_->getMaxTransactions()); + + // Put each transaction on the queue. + for (int i = 0; i < canned_count_; i++) { + EXPECT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[i])); + } + + // Invoke sweep canned_count_ times which should create a + // transaction for each canned ncr. + for (int i = 0; i < canned_count_; i++) { + EXPECT_NO_THROW(update_mgr_->sweep()); + EXPECT_EQ(i + 1, update_mgr_->getTransactionCount()); + EXPECT_TRUE(update_mgr_->hasTransaction(canned_ncrs_[i]->getDhcid())); + } + + // Verify that the queue has been drained. + EXPECT_EQ(0, update_mgr_->getQueueCount()); + + // Verify max transactions can't be less than current transaction count. + EXPECT_THROW(update_mgr_->setMaxTransactions(1), D2UpdateMgrError); + + // Queue up a request for a DHCID which has a transaction in progress. + dhcp_ddns::NameChangeRequestPtr + subsequent_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[2]))); + EXPECT_NO_THROW(queue_mgr_->enqueue(subsequent_ncr)); + EXPECT_EQ(1, update_mgr_->getQueueCount()); + + // Verify that invoking sweep, does not dequeue the job nor make a + // transaction for it. + EXPECT_NO_THROW(update_mgr_->sweep()); + EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount()); + EXPECT_EQ(1, update_mgr_->getQueueCount()); + + // Mark the transaction complete. + completeTransaction(2, dhcp_ddns::ST_COMPLETED); + + // Verify that invoking sweep, cleans up the completed transaction, + // dequeues the queued job and adds its transaction to the list. + EXPECT_NO_THROW(update_mgr_->sweep()); + EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount()); + EXPECT_EQ(0, update_mgr_->getQueueCount()); + + // Queue up a request from a new DHCID. + dhcp_ddns::NameChangeRequestPtr + another_ncr(new dhcp_ddns::NameChangeRequest(*(canned_ncrs_[0]))); + another_ncr->setDhcid("AABBCCDDEEFF"); + EXPECT_NO_THROW(queue_mgr_->enqueue(another_ncr)); + EXPECT_EQ(1, update_mgr_->getQueueCount()); + + // Verify that sweep does not dequeue the new request as we are at + // maximum transaction count. + EXPECT_NO_THROW(update_mgr_->sweep()); + EXPECT_EQ(canned_count_, update_mgr_->getTransactionCount()); + EXPECT_EQ(1, update_mgr_->getQueueCount()); + + // Set max transactions to same as current transaction count. + EXPECT_NO_THROW(update_mgr_->setMaxTransactions(canned_count_ + 1)); + + // Verify that invoking sweep, dequeues the request and creates + // a transaction for it. + EXPECT_NO_THROW(update_mgr_->sweep()); + EXPECT_EQ(canned_count_ + 1, update_mgr_->getTransactionCount()); + EXPECT_EQ(0, update_mgr_->getQueueCount()); + + // Verify that clearing transaction list works. + EXPECT_NO_THROW(update_mgr_->clearTransactionList()); + EXPECT_EQ(0, update_mgr_->getTransactionCount()); +} + +/// @brief Tests integration of NameAddTransaction +/// This test verifies that update manager can create and manage a +/// NameAddTransaction from start to finish. It utilizes a fake server +/// which responds to all requests sent with NOERROR, simulating a +/// successful addition. The transaction processes both forward and +/// reverse changes. +TEST_F(D2UpdateMgrTest, addTransaction) { + // Put each transaction on the queue. + canned_ncrs_[0]->setChangeType(dhcp_ddns::CHG_ADD); + canned_ncrs_[0]->setReverseChange(true); + ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0])); + + // Call sweep once, this should: + // 1. Dequeue the request + // 2. Create the transaction + // 3. Start the transaction + ASSERT_NO_THROW(update_mgr_->sweep()); + + // Get a copy of the transaction. + TransactionList::iterator pos = update_mgr_->transactionListBegin(); + ASSERT_TRUE (pos != update_mgr_->transactionListEnd()); + NameChangeTransactionPtr trans = (*pos).second; + ASSERT_TRUE(trans); + + // Verify the correct type of transaction was created. + NameAddTransaction* t = dynamic_cast<NameAddTransaction*>(trans.get()); + ASSERT_TRUE(t); + + // At this point the transaction should have constructed + // and sent the DNS request. + ASSERT_TRUE(trans->getCurrentServer()); + ASSERT_TRUE(trans->isModelRunning()); + ASSERT_EQ(1, trans->getUpdateAttempts()); + ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent()); + + // Create a server based on the transaction's current server, and + // start it listening. + FauxServer server(*io_service_, *(trans->getCurrentServer())); + server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR()); + + // Run sweep and IO until everything is done. + processAll(); + + // Verify that model succeeded. + EXPECT_FALSE(trans->didModelFail()); + + // Both completion flags should be true. + EXPECT_TRUE(trans->getForwardChangeCompleted()); + EXPECT_TRUE(trans->getReverseChangeCompleted()); + + // Verify that we went through success state. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + trans->getPrevState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + trans->getLastEvent()); +} + +/// @brief Tests integration of NameRemoveTransaction +/// This test verifies that update manager can create and manage a +/// NameRemoveTransaction from start to finish. It utilizes a fake server +/// which responds to all requests sent with NOERROR, simulating a +/// successful addition. The transaction processes both forward and +/// reverse changes. +TEST_F(D2UpdateMgrTest, removeTransaction) { + // Put each transaction on the queue. + canned_ncrs_[0]->setChangeType(dhcp_ddns::CHG_REMOVE); + canned_ncrs_[0]->setReverseChange(true); + ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0])); + + // Call sweep once, this should: + // 1. Dequeue the request + // 2. Create the transaction + // 3. Start the transaction + ASSERT_NO_THROW(update_mgr_->sweep()); + + // Get a copy of the transaction. + TransactionList::iterator pos = update_mgr_->transactionListBegin(); + ASSERT_TRUE (pos != update_mgr_->transactionListEnd()); + NameChangeTransactionPtr trans = (*pos).second; + ASSERT_TRUE(trans); + + // Verify the correct type of transaction was created. + NameRemoveTransaction* t = dynamic_cast<NameRemoveTransaction*>(trans.get()); + ASSERT_TRUE(t); + + // At this point the transaction should have constructed + // and sent the DNS request. + ASSERT_TRUE(trans->getCurrentServer()); + ASSERT_TRUE(trans->isModelRunning()); + ASSERT_EQ(1, trans->getUpdateAttempts()); + ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent()); + + // Create a server based on the transaction's current server, + // and start it listening. + FauxServer server(*io_service_, *(trans->getCurrentServer())); + server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR()); + + // Run sweep and IO until everything is done. + processAll(); + + // Verify that model succeeded. + EXPECT_FALSE(trans->didModelFail()); + + // Both completion flags should be true. + EXPECT_TRUE(trans->getForwardChangeCompleted()); + EXPECT_TRUE(trans->getReverseChangeCompleted()); + + // Verify that we went through success state. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + trans->getPrevState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + trans->getLastEvent()); +} + + +/// @brief Tests handling of a transaction which fails. +/// This test verifies that update manager correctly concludes a transaction +/// which fails to complete successfully. The failure simulated is repeated +/// corrupt responses from the server, which causes an exhaustion of the +/// available servers. +TEST_F(D2UpdateMgrTest, errorTransaction) { + // Put each transaction on the queue. + ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0])); + + // Call sweep once, this should: + // 1. Dequeue the request + // 2. Create the transaction + // 3. Start the transaction + ASSERT_NO_THROW(update_mgr_->sweep()); + + // Get a copy of the transaction. + TransactionList::iterator pos = update_mgr_->transactionListBegin(); + ASSERT_TRUE (pos != update_mgr_->transactionListEnd()); + NameChangeTransactionPtr trans = (*pos).second; + ASSERT_TRUE(trans); + + ASSERT_TRUE(trans->isModelRunning()); + ASSERT_EQ(1, trans->getUpdateAttempts()); + ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent()); + ASSERT_TRUE(trans->getCurrentServer()); + + // Create a server and start it listening. + FauxServer server(*io_service_, *(trans->getCurrentServer())); + server.receive(FauxServer::CORRUPT_RESP); + + // Run sweep and IO until everything is done. + processAll(); + + // Verify that model succeeded. + EXPECT_FALSE(trans->didModelFail()); + + // Both completion flags should be false. + EXPECT_FALSE(trans->getForwardChangeCompleted()); + EXPECT_FALSE(trans->getReverseChangeCompleted()); + + // Verify that we went through success state. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + trans->getPrevState()); + EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT, + trans->getLastEvent()); + + +} + +/// @brief Tests processing of multiple transactions. +/// This test verifies that update manager can create and manage a multiple +/// transactions, concurrently. It uses a fake server that responds to all +/// requests sent with NOERROR, simulating successful DNS updates. The +/// transactions are a mix of both adds and removes. +TEST_F(D2UpdateMgrTest, multiTransaction) { + // Queue up all the requests. + int test_count = canned_count_; + for (int i = test_count; i > 0; i--) { + canned_ncrs_[i-1]->setReverseChange(true); + ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[i-1])); + } + + // Create a server and start it listening. Note this relies on the fact + // that all of configured servers have the same address. + // and start it listening. + asiolink::IOAddress server_ip("127.0.0.1"); + FauxServer server(*io_service_, server_ip, 5301); + server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR()); + + // Run sweep and IO until everything is done. + processAll(); + + for (int i = 0; i < test_count; i++) { + EXPECT_EQ(dhcp_ddns::ST_COMPLETED, canned_ncrs_[i]->getStatus()); + } +} + +/// @brief Tests processing of multiple transactions. +/// This test verifies that update manager can create and manage a multiple +/// transactions, concurrently. It uses a fake server that responds to all +/// requests sent with NOERROR, simulating successful DNS updates. The +/// transactions are a mix of both adds and removes. +TEST_F(D2UpdateMgrTest, multiTransactionTimeout) { + // Queue up all the requests. + int test_count = canned_count_; + for (int i = test_count; i > 0; i--) { + canned_ncrs_[i-1]->setReverseChange(true); + ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[i-1])); + } + + // No server is running, so everything will time out. + + // Run sweep and IO until everything is done. + processAll(); + + for (int i = 0; i < test_count; i++) { + EXPECT_EQ(dhcp_ddns::ST_FAILED, canned_ncrs_[i]->getStatus()); + } +} + +/// @brief Tests integration of SimpleAddTransaction +/// This test verifies that update manager can create and manage a +/// SimpleAddTransaction from start to finish. It utilizes a fake server +/// which responds to all requests sent with NOERROR, simulating a +/// successful addition. The transaction processes both forward and +/// reverse changes. +TEST_F(D2UpdateMgrTest, simpleAddTransaction) { + // Put each transaction on the queue. + canned_ncrs_[0]->setChangeType(dhcp_ddns::CHG_ADD); + canned_ncrs_[0]->setReverseChange(true); + canned_ncrs_[0]->setConflictResolution(false); + ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0])); + + // Call sweep once, this should: + // 1. Dequeue the request + // 2. Create the transaction + // 3. Start the transaction + ASSERT_NO_THROW(update_mgr_->sweep()); + + // Get a copy of the transaction. + TransactionList::iterator pos = update_mgr_->transactionListBegin(); + ASSERT_TRUE (pos != update_mgr_->transactionListEnd()); + NameChangeTransactionPtr trans = (*pos).second; + ASSERT_TRUE(trans); + + // Verify the correct type of transaction was created. + SimpleAddTransaction* t = dynamic_cast<SimpleAddTransaction*>(trans.get()); + ASSERT_TRUE(t); + + // At this point the transaction should have constructed + // and sent the DNS request. + ASSERT_TRUE(trans->getCurrentServer()); + ASSERT_TRUE(trans->isModelRunning()); + ASSERT_EQ(1, trans->getUpdateAttempts()); + ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent()); + + // Create a server based on the transaction's current server, and + // start it listening. + FauxServer server(*io_service_, *(trans->getCurrentServer())); + server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR()); + + // Run sweep and IO until everything is done. + processAll(); + + // Verify that model succeeded. + EXPECT_FALSE(trans->didModelFail()); + + // Both completion flags should be true. + EXPECT_TRUE(trans->getForwardChangeCompleted()); + EXPECT_TRUE(trans->getReverseChangeCompleted()); + + // Verify that we went through success state. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + trans->getPrevState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + trans->getLastEvent()); +} + +/// @brief Tests integration of SimpleRemoveTransaction +/// This test verifies that update manager can create and manage a +/// SimpleRemoveTransaction from start to finish. It utilizes a fake server +/// which responds to all requests sent with NOERROR, simulating a +/// successful addition. The transaction processes both forward and +/// reverse changes. +TEST_F(D2UpdateMgrTest, simpleRemoveTransaction) { + // Put each transaction on the queue. + canned_ncrs_[0]->setChangeType(dhcp_ddns::CHG_REMOVE); + canned_ncrs_[0]->setReverseChange(true); + canned_ncrs_[0]->setConflictResolution(false); + ASSERT_NO_THROW(queue_mgr_->enqueue(canned_ncrs_[0])); + + // Call sweep once, this should: + // 1. Dequeue the request + // 2. Create the transaction + // 3. Start the transaction + ASSERT_NO_THROW(update_mgr_->sweep()); + + // Get a copy of the transaction. + TransactionList::iterator pos = update_mgr_->transactionListBegin(); + ASSERT_TRUE (pos != update_mgr_->transactionListEnd()); + NameChangeTransactionPtr trans = (*pos).second; + ASSERT_TRUE(trans); + + // Verify the correct type of transaction was created. + SimpleRemoveTransaction* t = dynamic_cast<SimpleRemoveTransaction*>(trans.get()); + ASSERT_TRUE(t); + + // At this point the transaction should have constructed + // and sent the DNS request. + ASSERT_TRUE(trans->getCurrentServer()); + ASSERT_TRUE(trans->isModelRunning()); + ASSERT_EQ(1, trans->getUpdateAttempts()); + ASSERT_EQ(StateModel::NOP_EVT, trans->getNextEvent()); + + // Create a server based on the transaction's current server, + // and start it listening. + FauxServer server(*io_service_, *(trans->getCurrentServer())); + server.receive(FauxServer::USE_RCODE, dns::Rcode::NOERROR()); + + // Run sweep and IO until everything is done. + processAll(); + + // Verify that model succeeded. + EXPECT_FALSE(trans->didModelFail()); + + // Both completion flags should be true. + EXPECT_TRUE(trans->getForwardChangeCompleted()); + EXPECT_TRUE(trans->getReverseChangeCompleted()); + + // Verify that we went through success state. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + trans->getPrevState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + trans->getLastEvent()); +} + +} diff --git a/src/bin/d2/tests/get_config_unittest.cc b/src/bin/d2/tests/get_config_unittest.cc new file mode 100644 index 0000000..0936e82 --- /dev/null +++ b/src/bin/d2/tests/get_config_unittest.cc @@ -0,0 +1,293 @@ +// 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 <config.h> + +#include <cc/command_interpreter.h> +#include <cc/data.h> +#include <d2/parser_context.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/d2_config.h> +#include <process/testutils/d_test_stubs.h> +#include <testutils/user_context_utils.h> +#include <gtest/gtest.h> + +#include <iostream> +#include <fstream> +#include <string> +#include <sstream> + +#include "test_data_files_config.h" +#include "test_callout_libraries.h" + +using namespace isc::config; +using namespace isc::d2; +using namespace isc::data; +using namespace isc::process; +using namespace isc::test; + +namespace { + +/// @name How to generate the testdata/get_config.json file +/// +/// Define GENERATE_ACTION and recompile. Run d2_unittests on +/// D2GetConfigTest redirecting the standard error to a temporary +/// file, e.g. by +/// @code +/// ./d2_unittests --gtest_filter="D2GetConfig*" > /dev/null 2> u +/// @endcode +/// +/// Update testdata/get_config.json using the temporary file content, +/// recompile without GENERATE_ACTION. + +/// @brief the generate action +/// false means do nothing, true means unparse extracted configurations +#ifdef GENERATE_ACTION +const bool generate_action = true; +#else +const bool generate_action = false; +#endif + +/// @brief Read a file into a string +std::string +readFile(const std::string& file_path) { + std::ifstream ifs(file_path); + if (!ifs.is_open()) { + ADD_FAILURE() << "readFile cannot open " << file_path; + isc_throw(isc::Unexpected, "readFile cannot open " << file_path); + } + std::string lines; + std::string line; + while (std::getline(ifs, line)) { + lines += line + "\n"; + } + ifs.close(); + return (lines); +} + +/// @brief Runs parser in JSON mode +ElementPtr +parseJSON(const std::string& in, bool verbose = false) { + try { + D2ParserContext ctx; + return (ctx.parseString(in, D2ParserContext::PARSER_JSON)); + } catch (const std::exception& ex) { + if (verbose) { + std::cout << "EXCEPTION: " << ex.what() << std::endl; + } + throw; + } +} + +/// @brief Runs parser in DHCPDDNS mode +ElementPtr +parseDHCPDDNS(const std::string& in, bool verbose = false) { + try { + D2ParserContext ctx; + return (ctx.parseString(in, D2ParserContext::PARSER_DHCPDDNS)); + } catch (const std::exception& ex) { + if (verbose) { + std::cout << "EXCEPTION: " << ex.what() << std::endl; + } + throw; + } +} + +/// @brief Replace the library path +void pathReplacer(ConstElementPtr d2_cfg) { + ConstElementPtr hooks_libs = d2_cfg->get("hooks-libraries"); + if (!hooks_libs || hooks_libs->empty()) { + return; + } + ElementPtr first_lib = hooks_libs->getNonConst(0); + std::string lib_path(CALLOUT_LIBRARY); + first_lib->set("library", Element::create(lib_path)); +} + +} + +/// Test fixture class +class D2GetConfigTest : public ConfigParseTest { +public: + D2GetConfigTest() + : rcode_(-1) { + srv_.reset(new D2CfgMgr()); + // Enforce not verbose mode. + Daemon::setVerbose(false); + // Create fresh context. + resetConfiguration(); + } + + ~D2GetConfigTest() { + resetConfiguration(); + } + + /// @brief Parse and Execute configuration + /// + /// Parses a configuration and executes a configuration of the server. + /// If the operation fails, the current test will register a failure. + /// + /// @param config Configuration to parse + /// @param operation Operation being performed. In the case of an error, + /// the error text will include the string "unable to <operation>.". + /// + /// @return true if the configuration succeeded, false if not. + bool + executeConfiguration(const std::string& config, const char* operation) { + // try JSON parser + ConstElementPtr json; + try { + json = parseJSON(config, true); + } catch (const std::exception& ex) { + ADD_FAILURE() << "invalid JSON for " << operation + << " failed with " << ex.what() + << " on\n" << config << "\n"; + return (false); + } + + // try DHCPDDNS parser + try { + json = parseDHCPDDNS(config, true); + } catch (...) { + ADD_FAILURE() << "parsing failed for " << operation + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // get DhcpDdns element + ConstElementPtr d2 = json->get("DhcpDdns"); + if (!d2) { + ADD_FAILURE() << "cannot get DhcpDdns for " << operation + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // update hooks-libraries + pathReplacer(d2); + + // try DHCPDDNS configure + ConstElementPtr status; + try { + status = srv_->simpleParseConfig(d2, false); + } catch (const std::exception& ex) { + ADD_FAILURE() << "configure for " << operation + << " failed with " << ex.what() + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // The status object must not be NULL + if (!status) { + ADD_FAILURE() << "configure for " << operation + << " returned null on\n" + << prettyPrint(json) << "\n"; + return (false); + } + + // Returned value should be 0 (configuration success) + comment_ = parseAnswer(rcode_, status); + if (rcode_ != 0) { + string reason = ""; + if (comment_) { + reason = string(" (") + comment_->stringValue() + string(")"); + } + ADD_FAILURE() << "configure for " << operation + << " returned error code " + << rcode_ << reason << " on\n" + << prettyPrint(json) << "\n"; + return (false); + } + return (true); + } + + /// @brief Reset configuration database. + /// + /// This function resets configuration data base by + /// removing control sockets, hooks, etc. Reset must + /// be performed after each test to make sure that + /// contents of the database do not affect result of + /// subsequent tests. + void resetConfiguration() { + string config = "{ \"DhcpDdns\": {" + " \"ip-address\": \"127.0.0.1\"," + " \"port\": 53001," + " \"dns-server-timeout\": 100," + " \"ncr-protocol\": \"UDP\"," + " \"ncr-format\": \"JSON\"," + " \"tsig-keys\": [ ]," + " \"forward-ddns\": { }," + " \"reverse-ddns\": { } } }"; + EXPECT_TRUE(executeConfiguration(config, "reset config")); + } + + boost::scoped_ptr<D2CfgMgr> srv_; ///< D2 server under test + int rcode_; ///< Return code from element parsing + ConstElementPtr comment_; ///< Reason for parse fail +}; + +/// Test a configuration +TEST_F(D2GetConfigTest, sample1) { + + // get the sample1 configuration + std::string sample1_file = string(CFG_EXAMPLES) + "/" + "sample1.json"; + std::string config; + ASSERT_NO_THROW(config = readFile(sample1_file)); + + // get the expected configuration + std::string expected_file = + std::string(D2_TEST_DATA_DIR) + "/" + "get_config.json"; + std::string expected; + ASSERT_NO_THROW(expected = readFile(expected_file)); + + // execute the sample configuration + ASSERT_TRUE(executeConfiguration(config, "sample1 config")); + + // unparse it + D2CfgContextPtr context = srv_->getD2CfgContext(); + ConstElementPtr unparsed; + ASSERT_NO_THROW(unparsed = context->toElement()); + + // dump if wanted else check + if (generate_action) { + std::cerr << "/ Generated Configuration (remove this line)\n"; + ASSERT_NO_THROW(expected = prettyPrint(unparsed)); + prettyPrint(unparsed, std::cerr, 0, 4); + std::cerr << "\n"; + } else { + // get the expected config using the d2 syntax parser + ElementPtr jsond; + ASSERT_NO_THROW(jsond = parseDHCPDDNS(expected, true)); + // get the expected config using the generic JSON syntax parser + ElementPtr jsonj; + ASSERT_NO_THROW(jsonj = parseJSON(expected)); + // the generic JSON parser does not handle comments + EXPECT_TRUE(isEquivalent(jsond, moveComments(jsonj))); + // replace the path by its actual value + ConstElementPtr d2; + ASSERT_NO_THROW(d2 = jsonj->get("DhcpDdns")); + ASSERT_TRUE(d2); + pathReplacer(d2); + // check that unparsed and expected values match + EXPECT_TRUE(isEquivalent(unparsed, jsonj)); + // check on pretty prints too + std::string current = prettyPrint(unparsed, 0, 4); + std::string expected2 = prettyPrint(jsonj, 0, 4); + EXPECT_EQ(expected2, current); + if (expected2 != current) { + expected = current + "\n"; + } + } + + // execute the d2 configuration + EXPECT_TRUE(executeConfiguration(expected, "unparsed config")); + + // is it a fixed point? + D2CfgContextPtr context2 = srv_->getD2CfgContext(); + ConstElementPtr unparsed2; + ASSERT_NO_THROW(unparsed2 = context2->toElement()); + ASSERT_TRUE(unparsed2); + EXPECT_TRUE(isEquivalent(unparsed, unparsed2)); +} diff --git a/src/bin/d2/tests/nc_add_unittests.cc b/src/bin/d2/tests/nc_add_unittests.cc new file mode 100644 index 0000000..3f36b46 --- /dev/null +++ b/src/bin/d2/tests/nc_add_unittests.cc @@ -0,0 +1,1713 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <asiolink/io_service.h> +#include <d2/nc_add.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/testutils/nc_test_utils.h> +#include <dns/messagerenderer.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc; +using namespace isc::d2; +using namespace isc::util; + +namespace { + +/// @brief Test class derived from NameAddTransaction to provide visibility +// to protected methods. +class NameAddStub : public NameAddTransaction { +public: + NameAddStub(asiolink::IOServicePtr& io_service, + dhcp_ddns::NameChangeRequestPtr& ncr, + DdnsDomainPtr& forward_domain, + DdnsDomainPtr& reverse_domain, + D2CfgMgrPtr& cfg_mgr) + : NameAddTransaction(io_service, ncr, forward_domain, reverse_domain, + cfg_mgr), + simulate_send_exception_(false), + simulate_build_request_exception_(false) { + } + + virtual ~NameAddStub() { + } + + /// @brief Simulates sending update requests to the DNS server + /// + /// This method simulates the initiation of an asynchronous send of + /// a DNS update request. It overrides the actual sendUpdate method in + /// the base class, thus avoiding an actual send, yet still increments + /// the update attempt count and posts a next event of NOP_EVT. + /// + /// It will also simulate an exception-based failure of sendUpdate, if + /// the simulate_send_exception_ flag is true. + /// + /// @param comment Parameter is unused, but present in base class method. + /// + virtual void sendUpdate(const std::string& /*comment*/) { + if (simulate_send_exception_) { + // Make the flag a one-shot by resetting it. + simulate_send_exception_ = false; + // Transition to failed. + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + return; + } + + // Update send attempt count and post a NOP_EVT. + setUpdateAttempts(getUpdateAttempts() + 1); + postNextEvent(StateModel::NOP_EVT); + } + + /// @brief Prepares the initial D2UpdateMessage + /// + /// This method overrides the NameChangeTransaction implementation to + /// provide the ability to simulate an exception throw in the build + /// request logic. + /// If the one-shot flag, simulate_build_request_exception_ is true, + /// this method will throw an exception, otherwise it will invoke the + /// base class method, providing normal functionality. + /// + /// For parameter description see the NameChangeTransaction implementation. + virtual D2UpdateMessagePtr prepNewRequest(DdnsDomainPtr domain) { + if (simulate_build_request_exception_) { + simulate_build_request_exception_ = false; + isc_throw (NameAddTransactionError, + "Simulated build requests exception"); + } + + return (NameChangeTransaction::prepNewRequest(domain)); + } + + /// @brief Simulates receiving a response + /// + /// This method simulates the completion of a DNSClient send. This allows + /// the state handler logic devoted to dealing with IO completion to be + /// fully exercised without requiring any actual IO. The two primary + /// pieces of information gleaned from IO completion are the DNSClient + /// status which indicates whether or not the IO exchange was successful + /// and the rcode, which indicates the server's reaction to the request. + /// + /// This method updates the transaction's DNS status value to that of the + /// given parameter, and then constructs and DNS update response message + /// with the given rcode value. To complete the simulation it then posts + /// a next event of IO_COMPLETED_EVT. + /// + /// @param status simulated DNSClient status + /// @param rcode simulated server response code + void fakeResponse(const DNSClient::Status& status, + const dns::Rcode& rcode) { + // Set the DNS update status. This is normally set in + // DNSClient IO completion handler. + setDnsUpdateStatus(status); + + // Construct an empty message with the given Rcode. + D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)); + msg->setRcode(rcode); + + // Set the update response to the message. + setDnsUpdateResponse(msg); + + // Post the IO completion event. + postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + } + + /// @brief Selects the first forward server. + /// Some state handlers require a server to have been selected. + /// This selects a server without going through the state + /// transition(s) to do so. + bool selectFwdServer() { + if (getForwardDomain()) { + initServerSelection(getForwardDomain()); + selectNextServer(); + return (getCurrentServer().get() != 0); + } + + return (false); + } + + /// @brief Selects the first reverse server. + /// Some state handlers require a server to have been selected. + /// This selects a server without going through the state + /// transition(s) to do so. + bool selectRevServer() { + if (getReverseDomain()) { + initServerSelection(getReverseDomain()); + selectNextServer(); + return (getCurrentServer().get() != 0); + } + + return (false); + } + + /// @brief One-shot flag which will simulate sendUpdate failure if true. + bool simulate_send_exception_; + + /// @brief One-shot flag which will simulate an exception when sendUpdate + /// failure if true. + bool simulate_build_request_exception_; + + using StateModel::postNextEvent; + using StateModel::setState; + using StateModel::initDictionaries; + using NameAddTransaction::defineEvents; + using NameAddTransaction::verifyEvents; + using NameAddTransaction::defineStates; + using NameAddTransaction::verifyStates; + using NameAddTransaction::readyHandler; + using NameAddTransaction::selectingFwdServerHandler; + using NameAddTransaction::getCurrentServer; + using NameAddTransaction::addingFwdAddrsHandler; + using NameAddTransaction::setDnsUpdateStatus; + using NameAddTransaction::replacingFwdAddrsHandler; + using NameAddTransaction::selectingRevServerHandler; + using NameAddTransaction::replacingRevPtrsHandler; + using NameAddTransaction::processAddOkHandler; + using NameAddTransaction::processAddFailedHandler; + using NameAddTransaction::buildAddFwdAddressRequest; + using NameAddTransaction::buildReplaceFwdAddressRequest; + using NameAddTransaction::buildReplaceRevPtrsRequest; +}; + +typedef boost::shared_ptr<NameAddStub> NameAddStubPtr; + +/// @brief Test fixture for testing NameAddTransaction +/// +/// Note this class uses NameAddStub class to exercise non-public +/// aspects of NameAddTransaction. +class NameAddTransactionTest : public TransactionTest { +public: + + NameAddTransactionTest() { + } + + virtual ~NameAddTransactionTest() { + } + + /// @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_mask determines which change directions are requested + NameAddStubPtr makeTransaction4(int change_mask = FWD_AND_REV_CHG) { + // Creates IPv4 remove request, forward, and reverse domains. + setupForIPv4Transaction(dhcp_ddns::CHG_ADD, change_mask); + + // Now create the test transaction as would occur in update manager. + return (NameAddStubPtr(new NameAddStub(io_service_, ncr_, + forward_domain_, + reverse_domain_, cfg_mgr_))); + } + + /// @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_mask determines which change directions are requested + NameAddStubPtr makeTransaction6(int change_mask = FWD_AND_REV_CHG) { + // Creates IPv6 remove request, forward, and reverse domains. + setupForIPv6Transaction(dhcp_ddns::CHG_ADD, change_mask); + + // Now create the test transaction as would occur in update manager. + return (NameAddStubPtr(new NameAddStub(io_service_, ncr_, + forward_domain_, + reverse_domain_, + cfg_mgr_))); + } + + /// @brief Create a test transaction at a known point in the state model. + /// + /// Method prepares a new test transaction and sets its state and next + /// event values to those given. This makes the transaction appear to + /// be at that point in the state model without having to transition it + /// through prerequisite states. It also provides the ability to set + /// which change directions are requested: forward change only, reverse + /// change only, or both. + /// + /// @param state value to set as the current state + /// @param event value to post as the next event + /// @param change_mask determines which change directions are requested + /// @param family selects between an IPv4 (AF_INET) and IPv6 (AF_INET6) + /// transaction. + NameAddStubPtr prepHandlerTest(unsigned int state, unsigned int event, + unsigned int change_mask = FWD_AND_REV_CHG, + short family = AF_INET) { + NameAddStubPtr name_add = (family == AF_INET ? + makeTransaction4(change_mask) : + makeTransaction4(change_mask)); + name_add->initDictionaries(); + name_add->postNextEvent(event); + name_add->setState(state); + return (name_add); + } +}; + +/// @brief Tests NameAddTransaction construction. +/// This test verifies that: +/// 1. Construction with invalid type of request +/// 2. Valid construction functions properly +TEST(NameAddTransaction, construction) { + asiolink::IOServicePtr io_service(new isc::asiolink::IOService()); + D2CfgMgrPtr cfg_mgr(new D2CfgMgr()); + + const char* msg_str = + "{" + " \"change-type\" : 1 , " + " \"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; + 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 wrong change type fails. + EXPECT_THROW(NameAddTransaction(io_service, ncr, + forward_domain, reverse_domain, cfg_mgr), + NameAddTransactionError); + + // Verify that a valid construction attempt works. + ncr->setChangeType(isc::dhcp_ddns::CHG_ADD); + EXPECT_NO_THROW(NameAddTransaction(io_service, ncr, + forward_domain, reverse_domain, + cfg_mgr)); +} + +/// @brief Tests event and state dictionary construction and verification. +TEST_F(NameAddTransactionTest, dictionaryCheck) { + NameAddStubPtr name_add; + ASSERT_NO_THROW(name_add = makeTransaction4()); + // Verify that the event and state dictionary validation fails prior + // dictionary construction. + ASSERT_THROW(name_add->verifyEvents(), StateModelError); + ASSERT_THROW(name_add->verifyStates(), StateModelError); + + // Construct both dictionaries. + ASSERT_NO_THROW(name_add->defineEvents()); + ASSERT_NO_THROW(name_add->defineStates()); + + // Verify both event and state dictionaries now pass validation. + ASSERT_NO_THROW(name_add->verifyEvents()); + ASSERT_NO_THROW(name_add->verifyStates()); +} + +/// @brief Tests construction of a DNS update request for adding a forward +/// dns entry. +TEST_F(NameAddTransactionTest, buildForwardAdd) { + // Create a IPv4 forward add transaction. + // Verify the request builds without error. + // and then verify the request contents. + NameAddStubPtr name_add; + ASSERT_NO_THROW(name_add = makeTransaction4()); + ASSERT_NO_THROW(name_add->buildAddFwdAddressRequest()); + checkAddFwdAddressRequest(*name_add); + + // Create a IPv6 forward add transaction. + // Verify the request builds without error. + // and then verify the request contents. + ASSERT_NO_THROW(name_add = makeTransaction6()); + ASSERT_NO_THROW(name_add->buildAddFwdAddressRequest()); + checkAddFwdAddressRequest(*name_add); +} + +/// @brief Tests construction of a DNS update request for replacing a forward +/// dns entry. +TEST_F(NameAddTransactionTest, buildReplaceFwdAddressRequest) { + // Create a IPv4 forward replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + NameAddStubPtr name_add; + ASSERT_NO_THROW(name_add = makeTransaction4()); + ASSERT_NO_THROW(name_add->buildReplaceFwdAddressRequest()); + checkReplaceFwdAddressRequest(*name_add); + + // Create a IPv6 forward replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + ASSERT_NO_THROW(name_add = makeTransaction6()); + ASSERT_NO_THROW(name_add->buildReplaceFwdAddressRequest()); + checkReplaceFwdAddressRequest(*name_add); +} + +/// @brief Tests the construction of a DNS update request for replacing a +/// reverse dns entry. +TEST_F(NameAddTransactionTest, buildReplaceRevPtrsRequest) { + // Create a IPv4 reverse replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + NameAddStubPtr name_add; + ASSERT_NO_THROW(name_add = makeTransaction4()); + ASSERT_NO_THROW(name_add->buildReplaceRevPtrsRequest()); + checkReplaceRevPtrsRequest(*name_add); + + // Create a IPv6 reverse replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + ASSERT_NO_THROW(name_add = makeTransaction6()); + ASSERT_NO_THROW(name_add->buildReplaceRevPtrsRequest()); + checkReplaceRevPtrsRequest(*name_add); +} + +// Tests the readyHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is START_EVT and request includes only a forward change +// 2. Posted event is START_EVT and request includes both a forward and a +// reverse change +// 3. Posted event is START_EVT and request includes only a reverse change +// 4. Posted event is invalid +// +TEST_F(NameAddTransactionTest, readyHandler) { + NameAddStubPtr name_add; + + // Create a transaction which includes only a forward change. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, FORWARD_CHG)); + // Run readyHandler. + EXPECT_NO_THROW(name_add->readyHandler()); + + // Verify that a request requiring only a forward change, transitions to + // selecting a forward server. + EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, + name_add->getNextEvent()); + + + // Create a transaction which includes both a forward and a reverse change. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, FWD_AND_REV_CHG)); + // Run readyHandler. + EXPECT_NO_THROW(name_add->readyHandler()); + + // Verify that a request requiring both forward and reverse, starts with + // the forward change by transitioning to selecting a forward server. + EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, + name_add->getNextEvent()); + + + // Create and prep a reverse only transaction. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, REVERSE_CHG)); + // Run readyHandler. + EXPECT_NO_THROW(name_add->readyHandler()); + + // Verify that a request requiring only a reverse change, transitions to + // selecting a reverse server. + EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, + name_add->getNextEvent()); + + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::NOP_EVT)); + + // Running the readyHandler should throw. + EXPECT_THROW(name_add->readyHandler(), NameAddTransactionError); +} + +// Tests the selectingFwdServerHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is SELECT_SERVER_EVT +// 2. Posted event is SERVER_IO_ERROR_EVT +// 3. Posted event is invalid +// +TEST_F(NameAddTransactionTest, selectingFwdServerHandler) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction:: + SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT)); + + // Call selectingFwdServerHandler enough times to select all of the + // servers in it's current domain. The first time, it will be with + // next event of SELECT_SERVER_EVT. Thereafter it will be with a next + // event of SERVER_IO_ERROR_EVT. + int num_servers = name_add->getForwardDomain()->getServers()->size(); + for (int i = 0; i < num_servers; ++i) { + // Run selectingFwdServerHandler. + ASSERT_NO_THROW(name_add->selectingFwdServerHandler()) + << " num_servers: " << num_servers + << " selections: " << i; + + // Verify that a server was selected. + ASSERT_TRUE(name_add->getCurrentServer()) + << " num_servers: " << num_servers << " selections: " << i; + + // Verify that we transitioned correctly. + ASSERT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST, + name_add->getCurrState()) + << " num_servers: " << num_servers << " selections: " << i; + ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_add->getNextEvent()) + << " num_servers: " << num_servers << " selections: " << i; + + // Post a server IO error event. This simulates an IO error occurring + // and a need to select the new server. + ASSERT_NO_THROW(name_add->postNextEvent(NameChangeTransaction:: + SERVER_IO_ERROR_EVT)) + << " num_servers: " << num_servers + << " selections: " << i; + } + + // We should have exhausted the list of servers. Processing another + // SERVER_IO_ERROR_EVT should transition us to failure. + EXPECT_NO_THROW(name_add->selectingFwdServerHandler()); + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT, + name_add->getNextEvent()); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction:: + SELECTING_FWD_SERVER_ST, + StateModel::NOP_EVT)); + + // Running the handler should throw. + EXPECT_THROW(name_add->selectingFwdServerHandler(), + NameAddTransactionError); +} + +// ************************ addingFwdAddrHandler Tests ***************** + +// Tests that addingFwdAddrsHandler rejects invalid events. +TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_InvalidEvent) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler but with + // an invalid event. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, + NameChangeTransaction:: + StateModel::NOP_EVT)); + + // Running the handler should throw. + EXPECT_THROW(name_add->addingFwdAddrsHandler(), + NameAddTransactionError); +} + +// Tests addingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update +// +TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_FwdOnlyAddOK) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Run addingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Verify that an update message was constructed properly. + checkAddFwdAddressRequest(*name_add); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::NOP_EVT, + name_add->getNextEvent()); + + // Simulate receiving a successful update response. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run addingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Forward completion should be true, reverse should be false. + EXPECT_TRUE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since it is a forward only change, we should be done. + // Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + name_add->getNextEvent()); +} + +// Tests addingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_fwdAndRevAddOK) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FWD_AND_REV_CHG)); + + // Run addingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Simulate receiving a successful update response. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run addingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Forward change completion should be true, reverse flag should be false. + EXPECT_TRUE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since the request also includes a reverse change we should + // be poised to start it. Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, + name_add->getNextEvent()); +} + +// Tests addingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates the FQDN is in use. +// +TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_FqdnInUse) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT)); + + // Run addingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Simulate receiving a FQDN in use response. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::YXDOMAIN()); + + // Run addingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since the FQDN is in use, per the RFC we must attempt to replace it. + // Verify that we transitioned correctly. + EXPECT_EQ(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + name_add->getCurrState()); + EXPECT_EQ(NameAddTransaction::FQDN_IN_USE_EVT, + name_add->getNextEvent()); +} + +// Tests addingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates the update was rejected. +// +TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_OtherRcode) { + NameAddStubPtr name_add; + + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectFwdServer()); + + // Run addingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Simulate receiving server rejection response. Per RFC, anything other + // than no error or FQDN in use is failure. Arbitrarily choosing refused. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED()); + + // Run addingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // We should have failed the transaction. Verify that we transitioned + // correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_add->getNextEvent()); +} + +// Tests addingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_Timeout) { + NameAddStubPtr name_add; + + // Create and prep a transaction, poised to run the handler. + // The log message issued when this test succeeds, displays the + // selected server, so we need to select a server before running this + // test. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectFwdServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run addingFwdAddrsHandler to send the request. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Simulate a server IO timeout. + name_add->setDnsUpdateStatus(DNSClient::TIMEOUT); + name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run addingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_add->getNextEvent()); + } else { + // Server retries should be exhausted, time for a new server. + EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_add->getNextEvent()); + } + } +} + +// Tests addingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent but a corrupt response is received, this occurs +// MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_InvalidResponse) { + NameAddStubPtr name_add; + + // Create and prep a transaction, poised to run the handler. + // The log message issued when this test succeeds, displays the + // selected server, so we need to select a server before running this + // test. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectFwdServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run addingFwdAddrsHandler to construct send the request. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Simulate a corrupt server response. + name_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE); + name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run addingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_add->getNextEvent()); + } else { + // Server retries should be exhausted, time for a new server. + EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_add->getNextEvent()); + } + } + +} + +// ************************ replacingFwdAddrHandler Tests ***************** + +// Tests that replacingFwdAddrsHandler rejects invalid events. +TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_InvalidEvent) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler but with + // an invalid event. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + NameChangeTransaction:: + StateModel::NOP_EVT)); + + // Running the handler should throw. + EXPECT_THROW(name_add->replacingFwdAddrsHandler(), + NameAddTransactionError); +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is FQDN_IN_USE_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_FwdOnlyAddOK) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + NameAddTransaction:: + FQDN_IN_USE_EVT, FORWARD_CHG)); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Run replacingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Verify that an update message was constructed properly. + checkReplaceFwdAddressRequest(*name_add); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + EXPECT_EQ(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::NOP_EVT, + name_add->getNextEvent()); + + // Simulate receiving a successful update response. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run replacingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Forward completion should be true, reverse should be false. + EXPECT_TRUE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since it is a forward only change, we should be done. + // Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + name_add->getNextEvent()); +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_FwdOnlyAddOK2) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + // Run replacingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Simulate receiving a successful update response. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run replacingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Forward completion should be true, reverse should be false. + EXPECT_TRUE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since it is a forward only change, we should be done. + // Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + name_add->getNextEvent()); +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is FQDN_IN_USE_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_FwdAndRevAddOK) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + NameAddTransaction:: + FQDN_IN_USE_EVT, FWD_AND_REV_CHG)); + + // Run replacingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Simulate receiving a successful update response. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run replacingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Forward change completion should be true, reverse flag should be false. + EXPECT_TRUE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since the request also includes a reverse change we should + // be poised to start it. Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, + name_add->getNextEvent()); +} + + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is FQDN_IN_USE_EVT. +// The update request is sent without error. +// A server response is received which indicates the FQDN is NOT in use. +// +TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_FqdnNotInUse) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + NameAddTransaction:: + FQDN_IN_USE_EVT, FWD_AND_REV_CHG)); + + // Run replacingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Simulate receiving a FQDN not in use response. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXDOMAIN()); + + // Run replacingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since the FQDN is no longer in use, per the RFC, try to add it. + // Verify that we transitioned correctly. + EXPECT_EQ(NameAddTransaction::ADDING_FWD_ADDRS_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_add->getNextEvent()); +} + + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// The update request is sent without error. +// A server response is received which indicates the update was rejected. +// +TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_OtherRcode) { + NameAddStubPtr name_add; + // Create the transaction. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + NameAddTransaction:: + FQDN_IN_USE_EVT, FWD_AND_REV_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectFwdServer()); + + // Run replacingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Simulate receiving server rejection response. Per RFC, anything other + // than no error or FQDN in use is failure. Arbitrarily choosing refused. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED()); + + // Run replacingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // We should have failed the transaction. Verify that we transitioned + // correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_add->getNextEvent()); +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is FQDN_IN_USE_EVT. +// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_Timeout) { + NameAddStubPtr name_add; + + // Create the transaction. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + NameAddTransaction:: + FQDN_IN_USE_EVT, FWD_AND_REV_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectFwdServer()); + + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run replacingFwdAddrsHandler to send the request. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Simulate a server IO timeout. + name_add->setDnsUpdateStatus(DNSClient::TIMEOUT); + name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run replacingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_add->getNextEvent()); + } else { + // Server retries should be exhausted, time for a new server. + EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_add->getNextEvent()); + } + } +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is FQDN_IN_USE_EVT. +// The update request is sent but a corrupt response is received, this occurs +// MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_CorruptResponse) { + NameAddStubPtr name_add; + + // Create the transaction. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + NameAddTransaction:: + FQDN_IN_USE_EVT, FWD_AND_REV_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectFwdServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run replacingFwdAddrsHandler to send the request. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Simulate a corrupt server response. + name_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE); + name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run replacingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_add->getNextEvent()); + } else { + // Server retries should be exhausted, time for a new server. + EXPECT_EQ(NameAddTransaction::SELECTING_FWD_SERVER_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_add->getNextEvent()); + } + } +} + +// Tests the selectingRevServerHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is SELECT_SERVER_EVT +// 2. Posted event is SERVER_IO_ERROR_EVT +// 3. Posted event is invalid +// +TEST_F(NameAddTransactionTest, selectingRevServerHandler) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction:: + SELECTING_REV_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT)); + + // Call selectingRevServerHandler enough times to select all of the + // servers in it's current domain. The first time, it will be with + // next event of SELECT_SERVER_EVT. Thereafter it will be with a next + // event of SERVER_IO_ERROR_EVT. + int num_servers = name_add->getReverseDomain()->getServers()->size(); + for (int i = 0; i < num_servers; ++i) { + // Run selectingRevServerHandler. + ASSERT_NO_THROW(name_add->selectingRevServerHandler()) + << " num_servers: " << num_servers + << " selections: " << i; + + // Verify that a server was selected. + ASSERT_TRUE(name_add->getCurrentServer()) + << " num_servers: " << num_servers + << " selections: " << i; + + // Verify that we transitioned correctly. + ASSERT_EQ(NameAddTransaction::REPLACING_REV_PTRS_ST, + name_add->getCurrState()) + << " num_servers: " << num_servers << " selections: " << i; + ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_add->getNextEvent()) + << " num_servers: " << num_servers << " selections: " << i; + + // Post a server IO error event. This simulates an IO error occurring + // and a need to select the new server. + ASSERT_NO_THROW(name_add->postNextEvent(NameChangeTransaction:: + SERVER_IO_ERROR_EVT)) + << " num_servers: " << num_servers + << " selections: " << i; + } + + // We should have exhausted the list of servers. Processing another + // SERVER_IO_ERROR_EVT should transition us to failure. + EXPECT_NO_THROW(name_add->selectingRevServerHandler()); + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT, + name_add->getNextEvent()); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction:: + SELECTING_REV_SERVER_ST, + StateModel::NOP_EVT)); + + // Running the handler should throw. + EXPECT_THROW(name_add->selectingRevServerHandler(), + NameAddTransactionError); +} + +//************************** replacingRevPtrsHandler tests ***************** + +// Tests that replacingRevPtrsHandler rejects invalid events. +TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_InvalidEvent) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler but with + // an invalid event. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, + NameChangeTransaction:: + StateModel::NOP_EVT)); + + // Running the handler should throw. + EXPECT_THROW(name_add->replacingRevPtrsHandler(), + NameAddTransactionError); +} + +// Tests replacingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_FwdOnlyAddOK) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, + NameAddTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Run replacingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Verify that an update message was constructed properly. + checkReplaceRevPtrsRequest(*name_add); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + EXPECT_EQ(NameAddTransaction::REPLACING_REV_PTRS_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::NOP_EVT, + name_add->getNextEvent()); + + // Simulate receiving a successful update response. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run replacingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Forward completion should be false, reverse should be true. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_TRUE(name_add->getReverseChangeCompleted()); + + // Since it is a reverse change, we should be done. + // Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + name_add->getNextEvent()); +} + +// Tests replacingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates the update was rejected. +// +TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_OtherRcode) { + NameAddStubPtr name_add; + // Create the transaction. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, + NameAddTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectRevServer()); + + // Run replacingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Simulate receiving server rejection response. Per RFC, anything other + // than no error is failure. Arbitrarily choosing refused. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED()); + + // Run replacingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // We should have failed the transaction. Verify that we transitioned + // correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_add->getNextEvent()); +} + +// Tests replacingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_Timeout) { + NameAddStubPtr name_add; + // Create the transaction. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, + NameAddTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectRevServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run replacingRevPtrsHandler to send the request. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Simulate a server IO timeout. + name_add->setDnsUpdateStatus(DNSClient::TIMEOUT); + name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run replacingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameAddTransaction::REPLACING_REV_PTRS_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_add->getNextEvent()); + } else { + // Server retries should be exhausted, time for a new server. + EXPECT_EQ(NameAddTransaction::SELECTING_REV_SERVER_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_add->getNextEvent()); + } + } +} + +// Tests replacingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent but a corrupt response is received, this occurs +// MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_CorruptResponse) { + NameAddStubPtr name_add; + // Create the transaction. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, + NameAddTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectRevServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run replacingRevPtrsHandler to send the request. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Simulate a server corrupt response. + name_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE); + name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run replacingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameAddTransaction::REPLACING_REV_PTRS_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_add->getNextEvent()); + } else { + // Server retries should be exhausted, time for a new server. + EXPECT_EQ(NameAddTransaction::SELECTING_REV_SERVER_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_add->getNextEvent()); + } + } +} + +// Tests the processAddOkHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is UPDATE_OK_EVT +// 2. Posted event is invalid +// +TEST_F(NameAddTransactionTest, processAddOkHandler) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST, + NameChangeTransaction::UPDATE_OK_EVT)); + // Run processAddOkHandler. + EXPECT_NO_THROW(name_add->processAddOkHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_add->getNcrStatus()); + + // Verify that the model has ended. + EXPECT_EQ(StateModel::END_ST, name_add->getCurrState()); + EXPECT_EQ(StateModel::END_EVT, name_add->getNextEvent()); + + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST, + StateModel::NOP_EVT)); + // Running the handler should throw. + EXPECT_THROW(name_add->processAddOkHandler(), NameAddTransactionError); +} + +// Tests the processAddFailedHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is UPDATE_FAILED_EVT +// 2. Posted event is invalid +// +TEST_F(NameAddTransactionTest, processAddFailedHandler) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction:: + PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT)); + // Run processAddFailedHandler. + EXPECT_NO_THROW(name_add->processAddFailedHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_FAILED, name_add->getNcrStatus()); + + // Verify that the model has ended. (Remember, the transaction failed NOT + // the model. The model should have ended normally.) + EXPECT_EQ(StateModel::END_ST, name_add->getCurrState()); + EXPECT_EQ(StateModel::END_EVT, name_add->getNextEvent()); + + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameChangeTransaction:: + PROCESS_TRANS_FAILED_ST, + StateModel::NOP_EVT)); + // Running the handler should throw. + EXPECT_THROW(name_add->processAddFailedHandler(), NameAddTransactionError); +} + +// Tests the processAddFailedHandler functionality. +// It verifies behavior for posted event of NO_MORE_SERVERS_EVT. +TEST_F(NameAddTransactionTest, processAddFailedHandler_NoMoreServers) { + NameAddStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction:: + PROCESS_TRANS_FAILED_ST, + NameChangeTransaction:: + NO_MORE_SERVERS_EVT)); + + // Run processAddFailedHandler. + EXPECT_NO_THROW(name_remove->processAddFailedHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus()); + + // Verify that the model has ended. (Remember, the transaction failed NOT + // the model. The model should have ended normally.) + EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState()); + EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent()); +} + +// Tests addingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The send update request fails due to an unexpected exception. +// +TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_sendUpdateException) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + name_add->simulate_send_exception_ = true; + + // Run replacingFwdAddrsHandler to construct and send the request. + ASSERT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_add->getNextEvent()); +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The send update request fails due to an unexpected exception. +// +TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_SendUpdateException) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + name_add->simulate_send_exception_ = true; + + // Run replacingFwdAddrsHandler to construct and send the request. + ASSERT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_add->getNextEvent()); +} + +// Tests replacingRevPtrHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The send update request fails due to an unexpected exception. +// +TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_SendUpdateException) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + name_add->simulate_send_exception_ = true; + + // Run replacingRevPtrsHandler to construct and send the request. + ASSERT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_add->getNextEvent()); +} + +// Tests addingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The request build fails due to an unexpected exception. +// +TEST_F(NameAddTransactionTest, addingFwdAddrsHandler_BuildRequestException) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::ADDING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + // Set the one-shot exception simulation flag. + name_add->simulate_build_request_exception_ = true; + + // Run replacingRevPtrsHandler to construct and send the request. + // This should fail with a build request throw which should be caught + // in the state handler. + ASSERT_NO_THROW(name_add->addingFwdAddrsHandler()); + + // Verify we did not attempt to send anything. + EXPECT_EQ(0, name_add->getUpdateAttempts()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_add->getNextEvent()); +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The request build fails due to an unexpected exception. +// +TEST_F(NameAddTransactionTest, replacingFwdAddrsHandler_BuildRequestException) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + // Set the one-shot exception simulation flag. + name_add->simulate_build_request_exception_ = true; + + // Run replacingFwdAddrsHandler to construct and send the request. + // This should fail with a build request throw which should be caught + // in the state handler. + ASSERT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Verify we did not attempt to send anything. + EXPECT_EQ(0, name_add->getUpdateAttempts()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_add->getNextEvent()); +} + + +// Tests replacingRevPtrHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The request build fails due to an unexpected exception. +// +TEST_F(NameAddTransactionTest, replacingRevPtrsHandler_BuildRequestException) { + NameAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_add = + prepHandlerTest(NameAddTransaction::REPLACING_REV_PTRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + // Set the one-shot exception simulation flag. + name_add->simulate_build_request_exception_ = true; + + // Run replacingRevPtrsHandler to construct and send the request. + // This should fail with a build request throw which should be caught + // in the state handler. + ASSERT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Verify we did not attempt to send anything. + EXPECT_EQ(0, name_add->getUpdateAttempts()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_add->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_add->getNextEvent()); +} + + +} diff --git a/src/bin/d2/tests/nc_remove_unittests.cc b/src/bin/d2/tests/nc_remove_unittests.cc new file mode 100644 index 0000000..4373811 --- /dev/null +++ b/src/bin/d2/tests/nc_remove_unittests.cc @@ -0,0 +1,1796 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <asiolink/io_service.h> +#include <d2/nc_remove.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/testutils/nc_test_utils.h> +#include <dns/messagerenderer.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc; +using namespace isc::d2; +using namespace isc::util; + + +namespace { + +/// @brief Test class derived from NameRemoveTransaction to provide visibility +// to protected methods. +class NameRemoveStub : public NameRemoveTransaction { +public: + NameRemoveStub(asiolink::IOServicePtr& io_service, + dhcp_ddns::NameChangeRequestPtr& ncr, + DdnsDomainPtr& forward_domain, + DdnsDomainPtr& reverse_domain, + D2CfgMgrPtr& cfg_mgr) + : NameRemoveTransaction(io_service, ncr, forward_domain, + reverse_domain, cfg_mgr), + simulate_send_exception_(false), + simulate_build_request_exception_(false) { + } + + virtual ~NameRemoveStub() { + } + + /// @brief Simulates sending update requests to the DNS server + /// + /// This method simulates the initiation of an asynchronous send of + /// a DNS update request. It overrides the actual sendUpdate method in + /// the base class, thus avoiding an actual send, yet still increments + /// the update attempt count and posts a next event of NOP_EVT. + /// + /// It will also simulate an exception-based failure of sendUpdate, if + /// the simulate_send_exception_ flag is true. + /// + /// @param comment Parameter is unused, but present in base class method + /// + virtual void sendUpdate(const std::string& /* comment */) { + if (simulate_send_exception_) { + // Make the flag a one-shot by resetting it. + simulate_send_exception_ = false; + // Transition to failed. + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + return; + } + + // Update send attempt count and post a NOP_EVT. + setUpdateAttempts(getUpdateAttempts() + 1); + postNextEvent(StateModel::NOP_EVT); + } + + /// @brief Prepares the initial D2UpdateMessage + /// + /// This method overrides the NameChangeTransaction implementation to + /// provide the ability to simulate an exception throw in the build + /// request logic. + /// If the one-shot flag, simulate_build_request_exception_ is true, + /// this method will throw an exception, otherwise it will invoke the + /// base class method, providing normal functionality. + /// + /// For parameter description see the NameChangeTransaction implementation. + virtual D2UpdateMessagePtr prepNewRequest(DdnsDomainPtr domain) { + if (simulate_build_request_exception_) { + simulate_build_request_exception_ = false; + isc_throw (NameRemoveTransactionError, + "Simulated build requests exception"); + } + + return (NameChangeTransaction::prepNewRequest(domain)); + } + + /// @brief Simulates receiving a response + /// + /// This method simulates the completion of a DNSClient send. This allows + /// the state handler logic devoted to dealing with IO completion to be + /// fully exercised without requiring any actual IO. The two primary + /// pieces of information gleaned from IO completion are the DNSClient + /// status which indicates whether or not the IO exchange was successful + /// and the rcode, which indicates the server's reaction to the request. + /// + /// This method updates the transaction's DNS status value to that of the + /// given parameter, and then constructs and DNS update response message + /// with the given rcode value. To complete the simulation it then posts + /// a next event of IO_COMPLETED_EVT. + /// + /// @param status simulated DNSClient status + /// @param rcode simulated server response code + void fakeResponse(const DNSClient::Status& status, + const dns::Rcode& rcode) { + // Set the DNS update status. This is normally set in + // DNSClient IO completion handler. + setDnsUpdateStatus(status); + + // Construct an empty message with the given Rcode. + D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)); + msg->setRcode(rcode); + + // Set the update response to the message. + setDnsUpdateResponse(msg); + + // Post the IO completion event. + postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + } + + /// @brief Selects the first forward server. + /// Some state handlers require a server to have been selected. + /// This selects a server without going through the state + /// transition(s) to do so. + bool selectFwdServer() { + if (getForwardDomain()) { + initServerSelection(getForwardDomain()); + selectNextServer(); + return (getCurrentServer().get() != 0); + } + + return (false); + } + + /// @brief Selects the first reverse server. + /// Some state handlers require a server to have been selected. + /// This selects a server without going through the state + /// transition(s) to do so. + bool selectRevServer() { + if (getReverseDomain()) { + initServerSelection(getReverseDomain()); + selectNextServer(); + return (getCurrentServer().get() != 0); + } + + return (false); + } + + /// @brief One-shot flag which will simulate sendUpdate failure if true. + bool simulate_send_exception_; + + /// @brief One-shot flag which will simulate an exception when sendUpdate + /// failure if true. + bool simulate_build_request_exception_; + + using StateModel::postNextEvent; + using StateModel::setState; + using StateModel::initDictionaries; + using NameRemoveTransaction::defineEvents; + using NameRemoveTransaction::verifyEvents; + using NameRemoveTransaction::defineStates; + using NameRemoveTransaction::verifyStates; + using NameRemoveTransaction::readyHandler; + using NameRemoveTransaction::selectingFwdServerHandler; + using NameRemoveTransaction::getCurrentServer; + using NameRemoveTransaction::removingFwdAddrsHandler; + using NameRemoveTransaction::setDnsUpdateStatus; + using NameRemoveTransaction::removingFwdRRsHandler; + using NameRemoveTransaction::selectingRevServerHandler; + using NameRemoveTransaction::removingRevPtrsHandler; + using NameRemoveTransaction::processRemoveOkHandler; + using NameRemoveTransaction::processRemoveFailedHandler; + using NameRemoveTransaction::buildRemoveFwdAddressRequest; + using NameRemoveTransaction::buildRemoveFwdRRsRequest; + using NameRemoveTransaction::buildRemoveRevPtrsRequest; +}; + +typedef boost::shared_ptr<NameRemoveStub> NameRemoveStubPtr; + +/// @brief Test fixture for testing NameRemoveTransaction +/// +/// Note this class uses NameRemoveStub class to exercise non-public +/// aspects of NameRemoveTransaction. +class NameRemoveTransactionTest : public TransactionTest { +public: + NameRemoveTransactionTest() { + } + + virtual ~NameRemoveTransactionTest() { + } + + /// @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_mask determines which change directions are requested + NameRemoveStubPtr makeTransaction4(int change_mask) { + // Creates IPv4 remove request, forward, and reverse domains. + setupForIPv4Transaction(dhcp_ddns::CHG_REMOVE, change_mask); + + // Now create the test transaction as would occur in update manager. + return (NameRemoveStubPtr(new NameRemoveStub(io_service_, ncr_, + forward_domain_, + reverse_domain_, + cfg_mgr_))); + } + + /// @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_mask determines which change directions are requested + NameRemoveStubPtr makeTransaction6(int change_mask) { + // Creates IPv6 remove request, forward, and reverse domains. + setupForIPv6Transaction(dhcp_ddns::CHG_REMOVE, change_mask); + + // Now create the test transaction as would occur in update manager. + return (NameRemoveStubPtr(new NameRemoveStub(io_service_, ncr_, + forward_domain_, + reverse_domain_, + cfg_mgr_))); + } + + /// @brief Create a test transaction at a known point in the state model. + /// + /// Method prepares a new test transaction and sets its state and next + /// event values to those given. This makes the transaction appear to + /// be at that point in the state model without having to transition it + /// through prerequisite states. It also provides the ability to set + /// which change directions are requested: forward change only, reverse + /// change only, or both. + /// + /// @param state value to set as the current state + /// @param event value to post as the next event + /// @param change_mask determines which change directions are requested + /// @param family selects between an IPv4 (AF_INET) and IPv6 (AF_INET6) + /// transaction. + NameRemoveStubPtr prepHandlerTest(unsigned int state, unsigned int event, + unsigned int change_mask + = FWD_AND_REV_CHG, + short family = AF_INET) { + NameRemoveStubPtr name_remove = (family == AF_INET ? + makeTransaction4(change_mask) : + makeTransaction6(change_mask)); + name_remove->initDictionaries(); + name_remove->postNextEvent(event); + name_remove->setState(state); + return (name_remove); + } + +}; + +/// @brief Tests NameRemoveTransaction construction. +/// This test verifies that: +/// 1. Construction with invalid type of request +/// 2. Valid construction functions properly +TEST(NameRemoveTransaction, 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; + 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 wrong change type fails. + EXPECT_THROW(NameRemoveTransaction(io_service, ncr, + forward_domain, reverse_domain, cfg_mgr), + NameRemoveTransactionError); + + // Verify that a valid construction attempt works. + ncr->setChangeType(isc::dhcp_ddns::CHG_REMOVE); + EXPECT_NO_THROW(NameRemoveTransaction(io_service, ncr, + forward_domain, reverse_domain, + cfg_mgr)); +} + +/// @brief Tests event and state dictionary construction and verification. +TEST_F(NameRemoveTransactionTest, dictionaryCheck) { + NameRemoveStubPtr name_remove; + ASSERT_NO_THROW(name_remove = makeTransaction4(FWD_AND_REV_CHG)); + // Verify that the event and state dictionary validation fails prior + // dictionary construction. + ASSERT_THROW(name_remove->verifyEvents(), StateModelError); + ASSERT_THROW(name_remove->verifyStates(), StateModelError); + + // Construct both dictionaries. + ASSERT_NO_THROW(name_remove->defineEvents()); + ASSERT_NO_THROW(name_remove->defineStates()); + + // Verify both event and state dictionaries now pass validation. + ASSERT_NO_THROW(name_remove->verifyEvents()); + ASSERT_NO_THROW(name_remove->verifyStates()); +} + + +/// @brief Tests construction of a DNS update request for removing forward +/// DNS address RRs. +TEST_F(NameRemoveTransactionTest, buildRemoveFwdAddressRequest) { + // Create a IPv4 forward add transaction. + // Verify the request builds without error. + // and then verify the request contents. + NameRemoveStubPtr name_remove; + ASSERT_NO_THROW(name_remove = makeTransaction4(FORWARD_CHG)); + ASSERT_NO_THROW(name_remove->buildRemoveFwdAddressRequest()); + checkRemoveFwdAddressRequest(*name_remove); + + // Create a IPv6 forward add transaction. + // Verify the request builds without error. + // and then verify the request contents. + ASSERT_NO_THROW(name_remove = makeTransaction6(FORWARD_CHG)); + ASSERT_NO_THROW(name_remove->buildRemoveFwdAddressRequest()); + checkRemoveFwdAddressRequest(*name_remove); +} + +/// @brief Tests construction of a DNS update request for removing forward +/// dns RR entries. +TEST_F(NameRemoveTransactionTest, buildRemoveFwdRRsRequest) { + // Create a IPv4 forward replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + NameRemoveStubPtr name_remove; + ASSERT_NO_THROW(name_remove = makeTransaction4(FORWARD_CHG)); + ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest()); + checkRemoveFwdRRsRequest(*name_remove); + + // Create a IPv6 forward replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + ASSERT_NO_THROW(name_remove = makeTransaction6(FORWARD_CHG)); + ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest()); + checkRemoveFwdRRsRequest(*name_remove); +} + +/// @brief Tests the construction of a DNS update request for removing a +/// reverse dns entry. +TEST_F(NameRemoveTransactionTest, buildRemoveRevPtrsRequest) { + // Create a IPv4 reverse replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + NameRemoveStubPtr name_remove; + ASSERT_NO_THROW(name_remove = makeTransaction4(REVERSE_CHG)); + ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest()); + checkRemoveRevPtrsRequest(*name_remove); + + // Create a IPv6 reverse replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + ASSERT_NO_THROW(name_remove = makeTransaction6(REVERSE_CHG)); + ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest()); + checkRemoveRevPtrsRequest(*name_remove); +} + +// Tests the readyHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is START_EVT and request includes only a forward change +// 2. Posted event is START_EVT and request includes both a forward and a +// reverse change +// 3. Posted event is START_EVT and request includes only a reverse change +// 4. Posted event is invalid +// +TEST_F(NameRemoveTransactionTest, readyHandler) { + NameRemoveStubPtr name_remove; + + // Create a transaction which includes only a forward change. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, FORWARD_CHG)); + // Run readyHandler. + EXPECT_NO_THROW(name_remove->readyHandler()); + + // Verify that a request requiring only a forward change, transitions to + // selecting a forward server. + EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, + name_remove->getNextEvent()); + + // Create a transaction which includes both a forward and a reverse change. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, FWD_AND_REV_CHG)); + // Run readyHandler. + EXPECT_NO_THROW(name_remove->readyHandler()); + + // Verify that a request requiring both forward and reverse, starts with + // the forward change by transitioning to selecting a forward server. + EXPECT_EQ(NameChangeTransaction::SELECTING_FWD_SERVER_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, + name_remove->getNextEvent()); + + // Create and prep a reverse only transaction. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, REVERSE_CHG)); + // Run readyHandler. + EXPECT_NO_THROW(name_remove->readyHandler()); + + // Verify that a request requiring only a reverse change, transitions to + // selecting a reverse server. + EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, + name_remove->getNextEvent()); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::NOP_EVT)); + + // Running the readyHandler should throw. + EXPECT_THROW(name_remove->readyHandler(), NameRemoveTransactionError); +} + + +// Tests the selectingFwdServerHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is SELECT_SERVER_EVT +// 2. Posted event is SERVER_IO_ERROR_EVT +// 3. Posted event is invalid +// +TEST_F(NameRemoveTransactionTest, selectingFwdServerHandler) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction:: + SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT)); + + // Call selectingFwdServerHandler enough times to select all of the + // servers in it's current domain. The first time, it will be with + // next event of SELECT_SERVER_EVT. Thereafter it will be with a next + // event of SERVER_IO_ERROR_EVT. + int num_servers = name_remove->getForwardDomain()->getServers()->size(); + for (int i = 0; i < num_servers; ++i) { + // Run selectingFwdServerHandler. + ASSERT_NO_THROW(name_remove->selectingFwdServerHandler()) + << " num_servers: " << num_servers + << " selections: " << i; + + // Verify that a server was selected. + ASSERT_TRUE(name_remove->getCurrentServer()) + << " num_servers: " << num_servers << " selections: " << i; + + // Verify that we transitioned correctly. + ASSERT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST, + name_remove->getCurrState()) + << " num_servers: " << num_servers << " selections: " << i; + ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_remove->getNextEvent()) + << " num_servers: " << num_servers << " selections: " << i; + + // Post a server IO error event. This simulates an IO error occurring + // and a need to select the new server. + ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction:: + SERVER_IO_ERROR_EVT)) + << " num_servers: " << num_servers + << " selections: " << i; + } + + // We should have exhausted the list of servers. Processing another + // SERVER_IO_ERROR_EVT should transition us to failure. + EXPECT_NO_THROW(name_remove->selectingFwdServerHandler()); + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT, + name_remove->getNextEvent()); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction:: + SELECTING_FWD_SERVER_ST, + StateModel::NOP_EVT)); + + // Running the handler should throw. + EXPECT_THROW(name_remove->selectingFwdServerHandler(), + NameRemoveTransactionError); +} + +// ************************ addingFwdAddrHandler Tests ***************** + +// Tests that removingFwdAddrsHandler rejects invalid events. +TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_InvalidEvent) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler but with + // an invalid event. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + StateModel::NOP_EVT)); + + // Running the handler should throw. + EXPECT_THROW(name_remove->removingFwdAddrsHandler(), + NameRemoveTransactionError); +} + + +// Tests addingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update +// +TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_FwdOnlyOK) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Run removingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Verify that an update message was constructed properly. + checkRemoveFwdAddressRequest(*name_remove); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::NOP_EVT, + name_remove->getNextEvent()); + + // Simulate receiving a successful update response. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run removingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Completion flags should both still be false, as we are only partly + // done with forward updates. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since we succeeded, we should now attempt to remove any remaining + // forward RRs. + // Verify that we transitioned correctly. + EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameRemoveTransaction::UPDATE_OK_EVT, + name_remove->getNextEvent()); +} + +// Tests removingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates FQDN is not in use. +// +TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_FqdnNotInUse) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + // Run removingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Simulate receiving a RRSET does not exist. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXRRSET()); + + // Run removingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Completion flags should both still be false, as we are only partly + // done with forward updates. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // There was no address RR to remove, but we will still make sure there + // are no other RRs for this FQDN. + // Verify that we transitioned correctly. + EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameRemoveTransaction::UPDATE_OK_EVT, + name_remove->getNextEvent()); +} + + +// Tests removingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates the update was rejected. +// +TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_OtherRcode) { + NameRemoveStubPtr name_remove; + + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectFwdServer()); + + // Run removingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Simulate receiving server rejection response. Per RFC, anything other + // than no error or FQDN not in use is failure. Arbitrarily choosing + // refused. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED()); + + // Run removingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // We should have failed the transaction. Verify that we transitioned + // correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_remove->getNextEvent()); +} + + +// Tests removingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_Timeout) { + NameRemoveStubPtr name_remove; + + // Create and prep a transaction, poised to run the handler. + // The log message issued when this test succeeds, displays the + // selected server, so we need to select a server before running this + // test. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectFwdServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run removingFwdAddrsHandler to send the request. + EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Simulate a server IO timeout. + name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT); + name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run removingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_remove->getNextEvent()); + } else { + // Server retries should be exhausted, time for a new server. + EXPECT_EQ(NameRemoveTransaction::SELECTING_FWD_SERVER_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_remove->getNextEvent()); + } + } +} + +// Tests removingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent but a corrupt response is received, this occurs +// MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_InvalidResponse) { + NameRemoveStubPtr name_remove; + + // Create and prep a transaction, poised to run the handler. + // The log message issued when this test succeeds, displays the + // selected server, so we need to select a server before running this + // test. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectFwdServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run removingFwdAddrsHandler to construct send the request. + EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Simulate a corrupt server response. + name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE); + name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run removingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_remove->getNextEvent()); + } else { + // Server retries should be exhausted, time for a new server. + EXPECT_EQ(NameRemoveTransaction::SELECTING_FWD_SERVER_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_remove->getNextEvent()); + } + } + +} + +// ************************ removingFwdRRsHandler Tests ***************** + +// Tests that removingFwdRRsHandler rejects invalid events. +TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_InvalidEvent) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler but with + // an invalid event. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_RRS_ST, + StateModel::NOP_EVT)); + + // Running the handler should throw. + EXPECT_THROW(name_remove->removingFwdRRsHandler(), + NameRemoveTransactionError); +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is UPDATE_OK_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + UPDATE_OK_EVT, FORWARD_CHG)); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Run removingFwdRRsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Verify that an update message was constructed properly. + checkRemoveFwdRRsRequest(*name_remove); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_ADDRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::NOP_EVT, + name_remove->getNextEvent()); + + // Simulate receiving a successful update response. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run removingFwdRRsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Forward completion should be true, reverse should be false. + EXPECT_TRUE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since it is a forward only change, we should be done. + // Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + name_remove->getNextEvent()); +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK2) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + // Run removingFwdRRsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Simulate receiving a successful update response. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run removingFwdRRsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Forward completion should be true, reverse should be false. + EXPECT_TRUE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since it is a forward only change, we should be done. + // Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + name_remove->getNextEvent()); +} + + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is UPDATE_OK_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FwdAndRevOK) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + UPDATE_OK_EVT, FWD_AND_REV_CHG)); + + // Run removingFwdRRsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Simulate receiving a successful update response. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run removingFwdRRsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Forward change completion should be true, reverse flag should be false. + EXPECT_TRUE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since the request also includes a reverse change we should + // be poised to start it. Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SELECT_SERVER_EVT, + name_remove->getNextEvent()); +} + +// Tests removingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is UPDATE_OK_EVT. +// The update request is sent without error. +// A server response is received which indicates the FQDN is NOT in use. +// +TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_FqdnNotInUse) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + UPDATE_OK_EVT, FORWARD_CHG)); + + // Run removingFwdRRsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Simulate receiving a RRSET does not exist response. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXRRSET()); + + // Run removingFwdRRsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Forward completion flag should be true, reverse should still be false. + EXPECT_TRUE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // The FQDN is no longer in use, RFC is unclear about this, + // but we will treat this as success. + // Since it is a forward only change, we should be done. + // Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + name_remove->getNextEvent()); +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// The update request is sent without error. +// A server response is received which indicates the update was rejected. +// +TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_OtherRcode) { + NameRemoveStubPtr name_remove; + // Create the transaction. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + UPDATE_OK_EVT, FWD_AND_REV_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectFwdServer()); + + // Run removingFwdRRsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Simulate receiving server rejection response. Per RFC, anything other + // than no error is failure (we are also treating FQDN not in use is + // success). Arbitrarily choosing refused. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED()); + + // Run removingFwdRRsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // We should have failed the transaction. Verify that we transitioned + // correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_remove->getNextEvent()); +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is UPDATE_OK_EVT. +// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_Timeout) { + NameRemoveStubPtr name_remove; + + // Create the transaction. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_RRS_ST, + NameChangeTransaction:: + UPDATE_OK_EVT, FWD_AND_REV_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectFwdServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run removingFwdRRsHandler to send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Simulate a server IO timeout. + name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT); + name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run removingFwdRRsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_remove->getNextEvent()); + } else { + // Server retries should be exhausted. + // We should abandon the transaction. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_remove->getNextEvent()); + } + } +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is UPDATE_OK_EVT. +// The update request is sent but a corrupt response is received, this occurs +// MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_InvalidResponse) { + NameRemoveStubPtr name_remove; + + // Create the transaction. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_RRS_ST, + NameChangeTransaction:: + UPDATE_OK_EVT, FWD_AND_REV_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectFwdServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run removingFwdRRsHandler to send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Simulate a corrupt server response. + name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE); + name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run removingFwdRRsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameRemoveTransaction::REMOVING_FWD_RRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_remove->getNextEvent()); + } else { + // Server retries should be exhausted. + // We should abandon the transaction. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_remove->getNextEvent()); + } + } +} + + +// Tests the selectingRevServerHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is SELECT_SERVER_EVT +// 2. Posted event is SERVER_IO_ERROR_EVT +// 3. Posted event is invalid +// +TEST_F(NameRemoveTransactionTest, selectingRevServerHandler) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction:: + SELECTING_REV_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT)); + + // Call selectingRevServerHandler enough times to select all of the + // servers in it's current domain. The first time, it will be with + // next event of SELECT_SERVER_EVT. Thereafter it will be with a next + // event of SERVER_IO_ERROR_EVT. + int num_servers = name_remove->getReverseDomain()->getServers()->size(); + for (int i = 0; i < num_servers; ++i) { + // Run selectingRevServerHandler. + ASSERT_NO_THROW(name_remove->selectingRevServerHandler()) + << " num_servers: " << num_servers + << " selections: " << i; + + // Verify that a server was selected. + ASSERT_TRUE(name_remove->getCurrentServer()) + << " num_servers: " << num_servers + << " selections: " << i; + + // Verify that we transitioned correctly. + ASSERT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST, + name_remove->getCurrState()) + << " num_servers: " << num_servers << " selections: " << i; + ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_remove->getNextEvent()) + << " num_servers: " << num_servers << " selections: " << i; + + // Post a server IO error event. This simulates an IO error occurring + // and a need to select the new server. + ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction:: + SERVER_IO_ERROR_EVT)) + << " num_servers: " << num_servers + << " selections: " << i; + } + + // We should have exhausted the list of servers. Processing another + // SERVER_IO_ERROR_EVT should transition us to failure. + EXPECT_NO_THROW(name_remove->selectingRevServerHandler()); + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::NO_MORE_SERVERS_EVT, + name_remove->getNextEvent()); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction:: + SELECTING_REV_SERVER_ST, + StateModel::NOP_EVT)); + + // Running the handler should throw. + EXPECT_THROW(name_remove->selectingRevServerHandler(), + NameRemoveTransactionError); +} + +//************************** removingRevPtrsHandler tests ***************** + +// Tests that removingRevPtrsHandler rejects invalid events. +TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_InvalidEvent) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler but with + // an invalid event. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_REV_PTRS_ST, + StateModel::NOP_EVT)); + + // Running the handler should throw. + EXPECT_THROW(name_remove->removingRevPtrsHandler(), + NameRemoveTransactionError); +} + +// Tests removingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_RevOnlyOK) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_REV_PTRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Run removingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Verify that an update message was constructed properly. + checkRemoveRevPtrsRequest(*name_remove); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(StateModel::NOP_EVT, + name_remove->getNextEvent()); + + // Simulate receiving a successful update response. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run removingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Forward completion should be false, reverse should be true. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_TRUE(name_remove->getReverseChangeCompleted()); + + // Since it is a reverse change, we should be done. + // Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + name_remove->getNextEvent()); +} + +// Tests removingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates FQDN is NOT in use. +// +TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_FqdnNotInUse) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_REV_PTRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Run removingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Verify that an update message was constructed properly. + checkRemoveRevPtrsRequest(*name_remove); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(StateModel::NOP_EVT, + name_remove->getNextEvent()); + + // Simulate receiving a RRSET does not exist. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXRRSET()); + + // Run removingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Forward completion should be false, reverse should be true. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_TRUE(name_remove->getReverseChangeCompleted()); + + // Since it is a reverse change, we should be done. + // Verify that we transitioned correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_OK_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_OK_EVT, + name_remove->getNextEvent()); +} + +// Tests removingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates the update was rejected. +// +TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_OtherRcode) { + NameRemoveStubPtr name_remove; + // Create the transaction. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_REV_PTRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectRevServer()); + + // Run removingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Simulate receiving server rejection response. Per RFC, anything other + // than no error is failure. Arbitrarily choosing refused. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED()); + + // Run removingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // We should have failed the transaction. Verify that we transitioned + // correctly. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_remove->getNextEvent()); +} + +// Tests removingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_Timeout) { + NameRemoveStubPtr name_remove; + // Create the transaction. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_REV_PTRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectRevServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run removingRevPtrsHandler to send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Simulate a server IO timeout. + name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT); + name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run removingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_remove->getNextEvent()); + } else { + // Server retries should be exhausted, time for a new server. + EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_remove->getNextEvent()); + } + } +} + + +// Tests removingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent but a corrupt response is received, this occurs +// MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_CorruptResponse) { + NameRemoveStubPtr name_remove; + // Create the transaction. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_REV_PTRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectRevServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run removingRevPtrsHandler to send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Simulate a server corrupt response. + name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE); + name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run removingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + EXPECT_EQ(NameRemoveTransaction::REMOVING_REV_PTRS_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT, + name_remove->getNextEvent()); + } else { + // Server retries should be exhausted, time for a new server. + EXPECT_EQ(NameChangeTransaction::SELECTING_REV_SERVER_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT, + name_remove->getNextEvent()); + } + } +} + +// Tests the processRemoveOkHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is UPDATE_OK_EVT +// 2. Posted event is invalid +// +TEST_F(NameRemoveTransactionTest, processRemoveOkHandler) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST, + NameChangeTransaction::UPDATE_OK_EVT)); + // Run processRemoveOkHandler. + EXPECT_NO_THROW(name_remove->processRemoveOkHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_remove->getNcrStatus()); + + // Verify that the model has ended. + EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState()); + EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent()); + + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST, + StateModel::NOP_EVT)); + // Running the handler should throw. + EXPECT_THROW(name_remove->processRemoveOkHandler(), + NameRemoveTransactionError); +} + +// Tests the processRemoveFailedHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is UPDATE_FAILED_EVT +// 2. Posted event is invalid +// +TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction:: + PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT)); + // Run processRemoveFailedHandler. + EXPECT_NO_THROW(name_remove->processRemoveFailedHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus()); + + // Verify that the model has ended. (Remember, the transaction failed NOT + // the model. The model should have ended normally.) + EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState()); + EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent()); + + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction:: + PROCESS_TRANS_FAILED_ST, + StateModel::NOP_EVT)); + // Running the handler should throw. + EXPECT_THROW(name_remove->processRemoveFailedHandler(), + NameRemoveTransactionError); +} + +// Tests the processRemoveFailedHandler functionality. +// It verifies behavior for posted event of NO_MORE_SERVERS_EVT. +TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler_NoMoreServers) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction:: + PROCESS_TRANS_FAILED_ST, + NameChangeTransaction:: + NO_MORE_SERVERS_EVT)); + + // Run processRemoveFailedHandler. + EXPECT_NO_THROW(name_remove->processRemoveFailedHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus()); + + // Verify that the model has ended. (Remember, the transaction failed NOT + // the model. The model should have ended normally.) + EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState()); + EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent()); +} + +// Tests the processRemoveFailedHandler functionality. +// It verifies behavior for posted event of SERVER_IO_ERROR_EVT. +TEST_F(NameRemoveTransactionTest, processRemoveFailedHandler_ServerIOError) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameChangeTransaction:: + PROCESS_TRANS_FAILED_ST, + NameChangeTransaction:: + SERVER_IO_ERROR_EVT)); + + // Run processRemoveFailedHandler. + EXPECT_NO_THROW(name_remove->processRemoveFailedHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus()); + + // Verify that the model has ended. (Remember, the transaction failed NOT + // the model. The model should have ended normally.) + EXPECT_EQ(StateModel::END_ST, name_remove->getCurrState()); + EXPECT_EQ(StateModel::END_EVT, name_remove->getNextEvent()); +} + +// Tests removingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The send update request fails due to an unexpected exception. +// +TEST_F(NameRemoveTransactionTest, removingFwdAddrsHandler_sendUpdateException) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + name_remove->simulate_send_exception_ = true; + + // Run removingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_remove->getNextEvent()); +} + + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The send update request fails due to an unexpected exception. +// +TEST_F(NameRemoveTransactionTest, removingFwdRRsHandler_SendUpdateException) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_RRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + name_remove->simulate_send_exception_ = true; + + // Run removingFwdRRsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_remove->getNextEvent()); +} + +// Tests removingRevPtrHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The send update request fails due to an unexpected exception. +// +TEST_F(NameRemoveTransactionTest, removingRevPtrsHandler_SendUpdateException) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_REV_PTRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, REVERSE_CHG)); + + name_remove->simulate_send_exception_ = true; + + // Run removingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_remove->getNextEvent()); +} + +// Tests removingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The request build fails due to an unexpected exception. +// +TEST_F(NameRemoveTransactionTest, + removingFwdAddrsHandler_BuildRequestException) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_ADDRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + // Set the one-shot exception simulation flag. + name_remove->simulate_build_request_exception_ = true; + + // Run removingFwdAddrsHandler to construct and send the request. + // This should fail with a build request throw which should be caught + // in the state handler. + ASSERT_NO_THROW(name_remove->removingFwdAddrsHandler()); + + // Verify we did not attempt to send anything. + EXPECT_EQ(0, name_remove->getUpdateAttempts()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_remove->getNextEvent()); +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The request build fails due to an unexpected exception. +// +TEST_F(NameRemoveTransactionTest, + removingFwdRRsHandler_BuildRequestException) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_FWD_RRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + // Set the one-shot exception simulation flag. + name_remove->simulate_build_request_exception_ = true; + + // Run removingFwdRRsHandler to construct and send the request. + // This should fail with a build request throw which should be caught + // in the state handler. + ASSERT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Verify we did not attempt to send anything. + EXPECT_EQ(0, name_remove->getUpdateAttempts()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_remove->getNextEvent()); +} + +// Tests removingRevPTRsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The request build fails due to an unexpected exception. +// +TEST_F(NameRemoveTransactionTest, + removingRevPTRsHandler_BuildRequestException) { + NameRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW(name_remove = + prepHandlerTest(NameRemoveTransaction:: + REMOVING_REV_PTRS_ST, + NameChangeTransaction:: + SERVER_SELECTED_EVT, FORWARD_CHG)); + + // Set the one-shot exception simulation flag. + name_remove->simulate_build_request_exception_ = true; + + // Run removingRevPtrsHandler to construct and send the request. + // This should fail with a build request throw which should be caught + // in the state handler. + ASSERT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Verify we did not attempt to send anything. + EXPECT_EQ(0, name_remove->getUpdateAttempts()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + EXPECT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + name_remove->getCurrState()); + EXPECT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT, + name_remove->getNextEvent()); +} + +} diff --git a/src/bin/d2/tests/parser_unittest.cc b/src/bin/d2/tests/parser_unittest.cc new file mode 100644 index 0000000..9100511 --- /dev/null +++ b/src/bin/d2/tests/parser_unittest.cc @@ -0,0 +1,871 @@ +// 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 <config.h> + +#include <cc/data.h> +#include <d2/parser_context.h> +#include <d2/tests/parser_unittest.h> +#include <testutils/io_utils.h> +#include <testutils/log_utils.h> +#include <testutils/user_context_utils.h> + +#include <gtest/gtest.h> + +#include <fstream> +#include <set> + +#include <boost/algorithm/string.hpp> + +#include "test_data_files_config.h" + +using namespace isc::data; +using namespace isc::test; +using namespace std; + +namespace isc { +namespace d2 { +namespace test { + +/// @brief compares two JSON trees +/// +/// If differences are discovered, gtest failure is reported (using EXPECT_EQ) +/// +/// @param a first to be compared +/// @param b second to be compared +void compareJSON(ConstElementPtr a, ConstElementPtr b) { + ASSERT_TRUE(a); + ASSERT_TRUE(b); + EXPECT_EQ(a->str(), b->str()); +} + +/// @brief Tests if the input string can be parsed with specific parser +/// +/// The input text will be passed to bison parser of specified type. +/// Then the same input text is passed to legacy JSON parser and outputs +/// from both parsers are compared. The legacy comparison can be disabled, +/// if the feature tested is not supported by the old parser (e.g. +/// new comment styles) +/// +/// @param txt text to be compared +/// @param parser_type bison parser type to be instantiated +/// @param compare whether to compare the output with legacy JSON parser +void testParser(const std::string& txt, D2ParserContext::ParserType parser_type, + bool compare = true) { + SCOPED_TRACE("\n=== tested config ===\n" + txt + "====================="); + + ConstElementPtr test_json; + ASSERT_NO_THROW({ + try { + D2ParserContext ctx; + test_json = ctx.parseString(txt, parser_type); + } catch (const std::exception &e) { + cout << "EXCEPTION: " << e.what() << endl; + throw; + } + + }); + + if (!compare) { + return; + } + + // Now compare if both representations are the same. + ElementPtr reference_json; + ASSERT_NO_THROW(reference_json = Element::fromJSON(txt, true)); + compareJSON(reference_json, test_json); +} + +// Generic JSON parsing tests +TEST(ParserTest, mapInMap) { + string txt = "{ \"xyzzy\": { \"foo\": 123, \"baz\": 456 } }"; + testParser(txt, D2ParserContext::PARSER_JSON); +} + +TEST(ParserTest, listInList) { + string txt = "[ [ \"Britain\", \"Wales\", \"Scotland\" ], " + "[ \"Pomorze\", \"Wielkopolska\", \"Tatry\"] ]"; + testParser(txt, D2ParserContext::PARSER_JSON); +} + +TEST(ParserTest, nestedMaps) { + string txt = "{ \"europe\": { \"UK\": { \"London\": { \"street\": \"221B Baker\" }}}}"; + testParser(txt, D2ParserContext::PARSER_JSON); +} + +TEST(ParserTest, nestedLists) { + string txt = "[ \"half\", [ \"quarter\", [ \"eighth\", [ \"sixteenth\" ]]]]"; + testParser(txt, D2ParserContext::PARSER_JSON); +} + +TEST(ParserTest, listsInMaps) { + string txt = "{ \"constellations\": { \"orion\": [ \"rigel\", \"betelgeuse\" ], " + "\"cygnus\": [ \"deneb\", \"albireo\"] } }"; + testParser(txt, D2ParserContext::PARSER_JSON); +} + +TEST(ParserTest, mapsInLists) { + string txt = "[ { \"body\": \"earth\", \"gravity\": 1.0 }," + " { \"body\": \"mars\", \"gravity\": 0.376 } ]"; + testParser(txt, D2ParserContext::PARSER_JSON); +} + +TEST(ParserTest, types) { + string txt = "{ \"string\": \"foo\"," + "\"integer\": 42," + "\"boolean\": true," + "\"map\": { \"foo\": \"bar\" }," + "\"list\": [ 1, 2, 3 ]," + "\"null\": null }"; + testParser(txt, D2ParserContext::PARSER_JSON); +} + +TEST(ParserTest, keywordJSON) { + string txt = "{ \"name\": \"user\"," + "\"type\": \"password\"," + "\"user\": \"name\"," + "\"password\": \"type\" }"; + testParser(txt, D2ParserContext::PARSER_JSON); +} + +// PARSER_DHCPDDNS parser tests +TEST(ParserTest, keywordDhcpDdns) { + string txt = + "{ \"DhcpDdns\" : \n" + "{ \n" + " \"ip-address\": \"192.168.77.1\", \n" + " \"port\": 777 , \n " + " \"ncr-protocol\": \"UDP\", \n" + " \"tsig-keys\": [], \n" + " \"forward-ddns\" : {}, \n" + " \"reverse-ddns\" : {} \n" + "} \n" + "} \n"; + testParser(txt, D2ParserContext::PARSER_DHCPDDNS); +} + +// Tests if bash (#) comments are supported. That's the only comment type that +// was supported by the old parser. +TEST(ParserTest, bashComments) { + string txt = + "{ \"DhcpDdns\" : \n" + "{ \n" + " \"ip-address\": \"192.168.77.1\", \n" + "# this is a comment\n" + " \"port\": 777, \n " + " \"ncr-protocol\": \"UDP\", \n" + "# lots of comments here\n" + "# and here\n" + "\"tsig-keys\": [], \n" + "\"forward-ddns\" : {}, \n" + "\"reverse-ddns\" : {} \n" + "} \n" + "} \n"; + testParser(txt, D2ParserContext::PARSER_DHCPDDNS); +} + +// Tests if C++ (//) comments can start anywhere, not just in the first line. +TEST(ParserTest, cppComments) { + string txt = + "{ \"DhcpDdns\" : \n" + "{ \n" + " \"ip-address\": \"192.168.77.1\", \n" + " \"port\": 777, // this is a comment \n" + " \"ncr-protocol\": \"UDP\", // everything after // is ignored\n" + "\"tsig-keys\": [], // this will be ignored, too\n" + "\"forward-ddns\" : {}, \n" + "\"reverse-ddns\" : {} \n" + "} \n" + "} \n"; + testParser(txt, D2ParserContext::PARSER_DHCPDDNS, false); +} + +// Tests if bash (#) comments can start anywhere, not just in the first line. +TEST(ParserTest, bashCommentsInline) { + string txt = + "{ \"DhcpDdns\" : \n" + "{ \n" + " \"ip-address\": \"192.168.77.1\", \n" + " \"port\": 777, # this is a comment \n" + " \"ncr-protocol\": \"UDP\", # everything after # is ignored\n" + "\"tsig-keys\": [], # this will be ignored, too\n" + "\"forward-ddns\" : {}, \n" + "\"reverse-ddns\" : {} \n" + "} \n" + "} \n"; + testParser(txt, D2ParserContext::PARSER_DHCPDDNS, false); +} + +// Tests if multi-line C style comments are handled correctly. +TEST(ParserTest, multilineComments) { + string txt = + "{ \"DhcpDdns\" : \n" + "{ \n" + " \"ip-address\": \"192.168.77.1\", \n" + " \"port\": 777, /* this is a C style comment\n" + "that\n can \n span \n multiple \n lines */ \n" + " \"ncr-protocol\": \"UDP\", \n" + "\"tsig-keys\": [], \n" + "\"forward-ddns\" : {}, \n" + "\"reverse-ddns\" : {} \n" + "} \n" + "} \n"; + testParser(txt, D2ParserContext::PARSER_DHCPDDNS, false); +} + +// Tests if embedded comments are handled correctly. +TEST(ParserTest, embbededComments) { + string txt = + "{ \"DhcpDdns\" : \n" + "{ \n" + "\"comment\": \"a comment\",\n" + " \"ip-address\": \"192.168.77.1\", \n" + " \"port\": 777, \n " + " \"ncr-protocol\": \"UDP\", \n" + "\"tsig-keys\" : [ { \n" + " \"name\" : \"d2.md5.key\", \n" + " \"user-context\" : { \"comment\" : \"indirect\" } } ], \n" + "\"forward-ddns\" : {}, \n" + "\"reverse-ddns\" : {}, \n" + "\"user-context\": { \"compatible\": true }" + "} \n" + "} \n"; + testParser(txt, D2ParserContext::PARSER_DHCPDDNS, false); +} + +/// @brief Loads specified example config file +/// +/// This test loads specified example file twice: first, using the legacy +/// JSON file and then second time using bison parser. Two created Element +/// trees are then compared. The input is decommented before it is passed +/// to legacy parser (as legacy support for comments is very limited). +/// +/// @param fname name of the file to be loaded +void testFile(const std::string& fname) { + ElementPtr json; + ElementPtr reference_json; + ConstElementPtr test_json; + + string decommented = decommentJSONfile(fname); + + cout << "Parsing file " << fname << " (" << decommented << ")" << endl; + + ASSERT_NO_THROW(json = Element::fromJSONFile(decommented, true)); + reference_json = moveComments(json); + + // remove the temporary file + EXPECT_NO_THROW(::remove(decommented.c_str())); + + EXPECT_NO_THROW( + try { + D2ParserContext ctx; + test_json = ctx.parseFile(fname, D2ParserContext::PARSER_DHCPDDNS); + } catch (const std::exception &x) { + cout << "EXCEPTION: " << x.what() << endl; + throw; + }); + + ASSERT_TRUE(reference_json); + ASSERT_TRUE(test_json); + + compareJSON(reference_json, test_json); +} + +// This test loads all available existing files. Each config is loaded +// twice: first with the existing Element::fromJSONFile() and then +// the second time with D2Parser. Both JSON trees are then compared. +TEST(ParserTest, file) { + vector<string> configs; + configs.push_back("all-keys.json"); + configs.push_back("all-keys-netconf.json"); + configs.push_back("comments.json"); + configs.push_back("gss-tsig.json"); + configs.push_back("sample1.json"); + configs.push_back("template.json"); + + for (int i = 0; i<configs.size(); i++) { + testFile(string(CFG_EXAMPLES) + "/" + configs[i]); + } +} + +/// @brief Tests error conditions in D2Parser +/// +/// @param txt text to be parsed +/// @param parser_type type of the parser to be used in the test +/// @param msg expected content of the exception +void testError(const std::string& txt, + D2ParserContext::ParserType parser_type, + const std::string& msg) { + SCOPED_TRACE("\n=== tested config ===\n" + txt + "====================="); + + try { + D2ParserContext ctx; + ConstElementPtr parsed = ctx.parseString(txt, parser_type); + FAIL() << "Expected D2ParseError but nothing was raised (expected: " + << msg << ")"; + } + catch (const D2ParseError& ex) { + EXPECT_EQ(msg, ex.what()); + } + catch (...) { + FAIL() << "Expected D2ParseError but something else was raised"; + } +} + +// Verify that error conditions are handled correctly. +TEST(ParserTest, errors) { + // no input + testError("", D2ParserContext::PARSER_JSON, + "<string>:1.1: syntax error, unexpected end of file"); + testError(" ", D2ParserContext::PARSER_JSON, + "<string>:1.2: syntax error, unexpected end of file"); + testError("\n", D2ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file"); + testError("\t", D2ParserContext::PARSER_JSON, + "<string>:1.2: syntax error, unexpected end of file"); + testError("\r", D2ParserContext::PARSER_JSON, + "<string>:1.2: syntax error, unexpected end of file"); + + // comments + testError("# nothing\n", + D2ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file"); + testError(" #\n", + D2ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file"); + testError("// nothing\n", + D2ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file"); + testError("/* nothing */\n", + D2ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file"); + testError("/* no\nthing */\n", + D2ParserContext::PARSER_JSON, + "<string>:3.1: syntax error, unexpected end of file"); + testError("/* no\nthing */\n\n", + D2ParserContext::PARSER_JSON, + "<string>:4.1: syntax error, unexpected end of file"); + testError("/* nothing\n", + D2ParserContext::PARSER_JSON, + "Comment not closed. (/* in line 1"); + testError("\n\n\n/* nothing\n", + D2ParserContext::PARSER_JSON, + "Comment not closed. (/* in line 4"); + testError("{ /* */*/ }\n", + D2ParserContext::PARSER_JSON, + "<string>:1.3-8: Invalid character: *"); + testError("{ /* // *// }\n", + D2ParserContext::PARSER_JSON, + "<string>:1.3-11: Invalid character: /"); + testError("{ /* // */// }\n", + D2ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file, " + "expecting }"); + + // includes + testError("<?\n", + D2ParserContext::PARSER_JSON, + "Directive not closed."); + testError("<?include\n", + D2ParserContext::PARSER_JSON, + "Directive not closed."); + string file = string(CFG_EXAMPLES) + "/" + "sample1.json"; + testError("<?include \"" + file + "\"\n", + D2ParserContext::PARSER_JSON, + "Directive not closed."); + testError("<?include \"/foo/bar\" ?>/n", + D2ParserContext::PARSER_JSON, + "Can't open include file /foo/bar"); + + // JSON keywords + testError("{ \"foo\": True }", + D2ParserContext::PARSER_JSON, + "<string>:1.10-13: JSON true reserved keyword is lower case only"); + testError("{ \"foo\": False }", + D2ParserContext::PARSER_JSON, + "<string>:1.10-14: JSON false reserved keyword is lower case only"); + testError("{ \"foo\": NULL }", + D2ParserContext::PARSER_JSON, + "<string>:1.10-13: JSON null reserved keyword is lower case only"); + testError("{ \"foo\": Tru }", + D2ParserContext::PARSER_JSON, + "<string>:1.10: Invalid character: T"); + testError("{ \"foo\": nul }", + D2ParserContext::PARSER_JSON, + "<string>:1.10: Invalid character: n"); + + // numbers + testError("123", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.1-3: syntax error, unexpected integer, " + "expecting {"); + testError("-456", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.1-4: syntax error, unexpected integer, " + "expecting {"); + testError("-0001", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.1-5: syntax error, unexpected integer, " + "expecting {"); + testError("1234567890123456789012345678901234567890", + D2ParserContext::PARSER_JSON, + "<string>:1.1-40: Failed to convert " + "1234567890123456789012345678901234567890" + " to an integer."); + testError("-3.14e+0", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.1-8: syntax error, unexpected floating point, " + "expecting {"); + testError("1e50000", + D2ParserContext::PARSER_JSON, + "<string>:1.1-7: Failed to convert 1e50000 " + "to a floating point."); + + // strings + testError("\"aabb\"", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.1-6: syntax error, unexpected constant string, " + "expecting {"); + testError("{ \"aabb\"err", + D2ParserContext::PARSER_JSON, + "<string>:1.9: Invalid character: e"); + testError("{ err\"aabb\"", + D2ParserContext::PARSER_JSON, + "<string>:1.3: Invalid character: e"); + testError("\"a\n\tb\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1-6 (near 2): Invalid control in \"a\n\tb\""); + testError("\"a\n\\u12\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1-8 (near 2): Invalid control in \"a\n\\u12\""); + testError("\"a\\n\\tb\"", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.1-8: syntax error, unexpected constant string, " + "expecting {"); + testError("\"a\\x01b\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1-8 (near 3): Bad escape in \"a\\x01b\""); + testError("\"a\\u0162\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1-9 (near 4): Unsupported unicode escape " + "in \"a\\u0162\""); + testError("\"a\\u062z\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1-9 (near 3): Bad escape in \"a\\u062z\""); + testError("\"abc\\\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1-6 (near 6): Overflow escape in \"abc\\\""); + testError("\"a\\u006\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1-8 (near 3): Overflow unicode escape " + "in \"a\\u006\""); + testError("\"\\u\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1-4 (near 2): Overflow unicode escape in \"\\u\""); + testError("\"\\u\x02\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1-5 (near 2): Bad escape in \"\\u\x02\""); + testError("\"\\u\\\"foo\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1-5 (near 2): Bad escape in \"\\u\\\"..."); + testError("\"\x02\\u\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1-5 (near 1): Invalid control in \"\x02\\u\""); + + // from data_unittest.c + testError("\\a", + D2ParserContext::PARSER_JSON, + "<string>:1.1: Invalid character: \\"); + testError("\\", + D2ParserContext::PARSER_JSON, + "<string>:1.1: Invalid character: \\"); + testError("\\\"\\\"", + D2ParserContext::PARSER_JSON, + "<string>:1.1: Invalid character: \\"); + + // want a map + testError("[]\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.1: syntax error, unexpected [, " + "expecting {"); + testError("[]\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.1: syntax error, unexpected [, " + "expecting {"); + testError("{ 123 }\n", + D2ParserContext::PARSER_JSON, + "<string>:1.3-5: syntax error, unexpected integer, " + "expecting }"); + testError("{ 123 }\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.3-5: syntax error, unexpected integer, " + "expecting DhcpDdns"); + testError("{ \"foo\" }\n", + D2ParserContext::PARSER_JSON, + "<string>:1.9: syntax error, unexpected }, " + "expecting :"); + testError("{ \"foo\" }\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.3-7: syntax error, unexpected constant string, " + "expecting DhcpDdns"); + testError("{ \"foo\":null }\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.3-7: syntax error, unexpected constant string, " + "expecting DhcpDdns"); + testError("{ \"Logging\":null }\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:1.3-11: syntax error, unexpected constant string, " + "expecting DhcpDdns"); + testError("{}{}\n", + D2ParserContext::PARSER_JSON, + "<string>:1.3: syntax error, unexpected {, " + "expecting end of file"); + + // duplicate in map + testError("{ \"foo\": 1, \"foo\": true }\n", + D2ParserContext::PARSER_JSON, + "<string>:1:13: duplicate foo entries in " + "JSON map (previous at <string>:1:10)"); + + // bad commas + testError("{ , }\n", + D2ParserContext::PARSER_JSON, + "<string>:1.3: syntax error, unexpected \",\", " + "expecting }"); + testError("{ , \"foo\":true }\n", + D2ParserContext::PARSER_JSON, + "<string>:1.3: syntax error, unexpected \",\", " + "expecting }"); + + // bad type + testError("{ \"DhcpDdns\":{\n" + " \"dns-server-timeout\":false }}\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:2.24-28: syntax error, unexpected boolean, " + "expecting integer"); + + // unknown keyword + testError("{ \"DhcpDdns\":{\n" + " \"totally-bogus\":600 }}\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:2.2-16: got unexpected keyword " + "\"totally-bogus\" in DhcpDdns map."); + + // user context and embedded comments + testError("{ \"DhcpDdns\":{\n" + " \"comment\": true,\n" + " \"dns-server-timeout\": 1000 }}\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:2.14-17: syntax error, unexpected boolean, " + "expecting constant string"); + + testError("{ \"DhcpDdns\":{\n" + " \"user-context\": \"a comment\",\n" + " \"dns-server-timeout\": 1000 }}\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:2.19-29: syntax error, unexpected constant string, " + "expecting {"); + + testError("{ \"DhcpDdns\":{\n" + " \"comment\": \"a comment\",\n" + " \"comment\": \"another one\",\n" + " \"dns-server-timeout\": 1000 }}\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:3.3-11: duplicate user-context/comment entries " + "(previous at <string>:2:3)"); + + testError("{ \"DhcpDdns\":{\n" + " \"user-context\": { \"version\": 1 },\n" + " \"user-context\": { \"one\": \"only\" },\n" + " \"dns-server-timeout\": 1000 }}\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:3.3-16: duplicate user-context entries " + "(previous at <string>:2:19)"); + + testError("{ \"DhcpDdns\":{\n" + " \"user-context\": { \"comment\": \"indirect\" },\n" + " \"comment\": \"a comment\",\n" + " \"dns-server-timeout\": 1000 }}\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:3.3-11: duplicate user-context/comment entries " + "(previous at <string>:2:19)"); + + // duplicate DhcpDdns entries + testError("{ \"DhcpDdns\":{\n" + " \"comment\": \"first\" },\n" + " \"DhcpDdns\":{\n" + " \"comment\": \"second\" }}\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:3.3-12: syntax error, unexpected DhcpDdns, expecting \",\" or }"); + + // duplicate of not string entries + testError("{ \"DhcpDdns\":{\n" + " \"port\": 53001,\n" + " \"port\": 53002 }}\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:3:3: duplicate port entries in " + "DhcpDdns map (previous at <string>:2:11)"); + + // duplicate of string entries + testError("{ \"DhcpDdns\":{\n" + " \"ip-address\": \"127.0.0.1\",\n" + " \"ip-address\": \"::1\" }}\n", + D2ParserContext::PARSER_DHCPDDNS, + "<string>:3:3: duplicate ip-address entries in " + "DhcpDdns map (previous at <string>:2:17)"); +} + +// Check unicode escapes +TEST(ParserTest, unicodeEscapes) { + ConstElementPtr result; + string json; + + // check we can reread output + for (char c = -128; c < 127; ++c) { + string ins(" "); + ins[1] = c; + ConstElementPtr e(new StringElement(ins)); + json = e->str(); + ASSERT_NO_THROW( + try { + D2ParserContext ctx; + result = ctx.parseString(json, D2ParserContext::PARSER_JSON); + } catch (const std::exception &x) { + cout << "EXCEPTION: " << x.what() << endl; + throw; + }); + ASSERT_EQ(Element::string, result->getType()); + EXPECT_EQ(ins, result->stringValue()); + } +} + +// This test checks that all representations of a slash is recognized properly. +TEST(ParserTest, unicodeSlash) { + // check the 4 possible encodings of solidus '/' + ConstElementPtr result; + string json = "\"/\\/\\u002f\\u002F\""; + ASSERT_NO_THROW( + try { + D2ParserContext ctx; + result = ctx.parseString(json, D2ParserContext::PARSER_JSON); + } catch (const std::exception &x) { + cout << "EXCEPTION: " << x.what() << endl; + throw; + }); + ASSERT_EQ(Element::string, result->getType()); + EXPECT_EQ("////", result->stringValue()); +} + +/// @brief Load a file into a JSON element. +/// +/// @param fname The name of the file to load. +/// @param list The JSON element list to add the parsing result to. +void loadFile(const string& fname, ElementPtr list) { + D2ParserContext ctx; + ElementPtr json; + EXPECT_NO_THROW(json = ctx.parseFile(fname, D2ParserContext::PARSER_DHCPDDNS)); + ASSERT_TRUE(json); + list->add(json); +} + +// This test checks that all map entries are in the sample file. +TEST(ParserTest, mapEntries) { + // Type of keyword set. + typedef set<string> KeywordSet; + + // Get keywords from the syntax file (d2_parser.yy). + ifstream syntax_file(SYNTAX_FILE); + EXPECT_TRUE(syntax_file.is_open()); + string line; + KeywordSet syntax_keys = { "user-context" }; + // Code setting the map entry. + const string pattern = "ctx.stack_.back()->set(\""; + while (getline(syntax_file, line)) { + // Skip comments. + size_t comment = line.find("//"); + if (comment <= pattern.size()) { + continue; + } + if (comment != string::npos) { + line.resize(comment); + } + // Search for the code pattern. + size_t key_begin = line.find(pattern); + if (key_begin == string::npos) { + continue; + } + // Extract keywords. + line = line.substr(key_begin + pattern.size()); + size_t key_end = line.find_first_of('"'); + EXPECT_NE(string::npos, key_end); + string keyword = line.substr(0, key_end); + // Ignore result when adding the keyword to the syntax keyword set. + static_cast<void>(syntax_keys.insert(keyword)); + } + syntax_file.close(); + + // Get keywords from the example files. + string sample_dir(CFG_EXAMPLES); + sample_dir += "/"; + ElementPtr sample_json = Element::createList(); + loadFile(sample_dir + "all-keys.json", sample_json); + loadFile(sample_dir + "all-keys-netconf.json", sample_json); + KeywordSet sample_keys = { + "hostname" + }; + // Recursively extract keywords. + static void (*extract)(ConstElementPtr, KeywordSet&) = + [] (ConstElementPtr json, KeywordSet& set) { + if (json->getType() == Element::list) { + // Handle lists. + for (auto elem : json->listValue()) { + extract(elem, set); + } + } else if (json->getType() == Element::map) { + // Handle maps. + for (auto elem : json->mapValue()) { + static_cast<void>(set.insert(elem.first)); + // Skip entries with free content. + if ((elem.first != "user-context") && + (elem.first != "parameters")) { + extract(elem.second, set); + } + } + } + }; + extract(sample_json, sample_keys); + + // Compare. + EXPECT_EQ(syntax_keys, sample_keys); +} + +/// @brief Tests a duplicate entry. +/// +/// The entry was duplicated by adding a new <name>DDDD entry. +/// An error is expected, usually it is a duplicate but there are +/// a few syntax errors when the syntax allows only one parameter. +/// +/// @param json the JSON configuration with the duplicate entry. +void testDuplicate(ConstElementPtr json) { + string config = json->str(); + size_t where = config.find("DDDD"); + ASSERT_NE(string::npos, where); + string before = config.substr(0, where); + string after = config.substr(where + 4, string::npos); + D2ParserContext ctx; + EXPECT_THROW(ctx.parseString(before + after, + D2ParserContext::PARSER_DHCPDDNS), + D2ParseError) << "config: " << config; +} + +// This test checks that duplicate entries make parsing to fail. +TEST(ParserTest, duplicateMapEntries) { + // Get the config to work with from the sample file. + string sample_fname(CFG_EXAMPLES); + sample_fname += "/all-keys.json"; + D2ParserContext ctx; + ElementPtr sample_json; + EXPECT_NO_THROW(sample_json = + ctx.parseFile(sample_fname, D2ParserContext::PARSER_DHCPDDNS)); + ASSERT_TRUE(sample_json); + + // Recursively check duplicates. + static void (*test)(ElementPtr, ElementPtr, size_t&) = + [] (ElementPtr config, ElementPtr json, size_t& cnt) { + if (json->getType() == Element::list) { + // Handle lists. + for (auto elem : json->listValue()) { + test(config, elem, cnt); + } + } else if (json->getType() == Element::map) { + // Handle maps. + for (auto elem : json->mapValue()) { + // Skip entries with free content. + if ((elem.first == "user-context") || + (elem.first == "parameters")) { + continue; + } + + // Perform tests. + string dup = elem.first + "DDDD"; + json->set(dup, elem.second); + testDuplicate(config); + json->remove(dup); + ++cnt; + + // Recursive call. + ElementPtr mutable_json = + boost::const_pointer_cast<Element>(elem.second); + ASSERT_TRUE(mutable_json); + test(config, mutable_json, cnt); + } + } + }; + size_t cnt = 0; + test(sample_json, sample_json, cnt); + cout << "checked " << cnt << " duplicated map entries\n"; +} + +/// @brief Test fixture for trailing commas. +class TrailingCommasTest : public isc::dhcp::test::LogContentTest { +public: + /// @brief Add a log entry. + /// + /// @param loc Location of the trailing comma. + void addLog(const string& loc) { + string log = "DHCP_DDNS_CONFIG_SYNTAX_WARNING DHCP-DDNS server "; + log += "configuration syntax warning: " + loc; + log += ": Extraneous comma. "; + log += "A piece of configuration may have been omitted."; + addString(log); + } +}; + +// Test that trailing commas are allowed. +TEST_F(TrailingCommasTest, tests) { + string txt(R"({ + "DhcpDdns": { + "forward-ddns": {}, + "ip-address": "127.0.0.1", + "loggers": [ + { + "name": "kea-dhcp-ddns", + "output_options": [ + { + "output": "stdout" + }, + ], + "severity": "DEBUG", + }, + ], + "port": 53001, + "reverse-ddns": {}, + "tsig-keys": [ + { + "algorithm": "HMAC-MD5", + "name": "d2.md5.key", + "secret": "sensitivejdPJI5QxlpnfQ==", + }, + ] + }, +})"); + testParser(txt, D2ParserContext::PARSER_DHCPDDNS, false); + + addLog("<string>:11.12"); + addLog("<string>:13.28"); + addLog("<string>:14.8"); + addLog("<string>:22.45"); + addLog("<string>:23.8"); + addLog("<string>:25.4"); + EXPECT_TRUE(checkFile()); + + // Test with many consecutive commas. + boost::replace_all(txt, ",", ",,,,"); + testParser(txt, D2ParserContext::PARSER_DHCPDDNS, false); +} + +} // namespace test +} // namespace d2 +} // namespace isc diff --git a/src/bin/d2/tests/parser_unittest.h b/src/bin/d2/tests/parser_unittest.h new file mode 100644 index 0000000..bcefcb7 --- /dev/null +++ b/src/bin/d2/tests/parser_unittest.h @@ -0,0 +1,36 @@ +// Copyright (C) 2017-2020 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 PARSER_UNITTEST_H +#define PARSER_UNITTEST_H + +#include <gtest/gtest.h> +#include <cc/data.h> +#include <d2/parser_context.h> + +using namespace isc::data; +using namespace std; + +namespace isc { +namespace d2 { +namespace test { + +/// @brief Runs parser in JSON mode, useful for parser testing +/// +/// @param in string to be parsed +/// @return ElementPtr structure representing parsed JSON +inline isc::data::ElementPtr +parseJSON(const std::string& in) +{ + isc::d2::D2ParserContext ctx; + return (ctx.parseString(in, isc::d2::D2ParserContext::PARSER_JSON)); +} + +} +} +} + +#endif // PARSER_UNITTEST_H diff --git a/src/bin/d2/tests/simple_add_unittests.cc b/src/bin/d2/tests/simple_add_unittests.cc new file mode 100644 index 0000000..c47a2b6 --- /dev/null +++ b/src/bin/d2/tests/simple_add_unittests.cc @@ -0,0 +1,1155 @@ +// 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/. + +#include <config.h> + +#include <asiolink/io_service.h> +#include <d2/simple_add.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/testutils/nc_test_utils.h> +#include <dns/messagerenderer.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc; +using namespace isc::d2; +using namespace isc::util; + +namespace { + +/// @brief Test class derived from SimpleAddTransaction to provide visibility +// to protected methods. +class SimpleAddStub : public SimpleAddTransaction { +public: + SimpleAddStub(asiolink::IOServicePtr& io_service, + dhcp_ddns::NameChangeRequestPtr& ncr, + DdnsDomainPtr& forward_domain, + DdnsDomainPtr& reverse_domain, + D2CfgMgrPtr& cfg_mgr) + : SimpleAddTransaction(io_service, ncr, forward_domain, reverse_domain, + cfg_mgr), + simulate_send_exception_(false), + simulate_build_request_exception_(false) { + } + + virtual ~SimpleAddStub() { + } + + /// @brief Simulates sending update requests to the DNS server + /// + /// This method simulates the initiation of an asynchronous send of + /// a DNS update request. It overrides the actual sendUpdate method in + /// the base class, thus avoiding an actual send, yet still increments + /// the update attempt count and posts a next event of NOP_EVT. + /// + /// It will also simulate an exception-based failure of sendUpdate, if + /// the simulate_send_exception_ flag is true. + /// + /// @param comment Parameter is unused, but present in base class method. + /// + virtual void sendUpdate(const std::string& /*comment*/) { + if (simulate_send_exception_) { + // Make the flag a one-shot by resetting it. + simulate_send_exception_ = false; + // Transition to failed. + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + return; + } + + // Update send attempt count and post a NOP_EVT. + setUpdateAttempts(getUpdateAttempts() + 1); + postNextEvent(StateModel::NOP_EVT); + } + + /// @brief Prepares the initial D2UpdateMessage + /// + /// This method overrides the NameChangeTransaction implementation to + /// provide the ability to simulate an exception throw in the build + /// request logic. + /// If the one-shot flag, simulate_build_request_exception_ is true, + /// this method will throw an exception, otherwise it will invoke the + /// base class method, providing normal functionality. + /// + /// For parameter description see the NameChangeTransaction implementation. + virtual D2UpdateMessagePtr prepNewRequest(DdnsDomainPtr domain) { + if (simulate_build_request_exception_) { + simulate_build_request_exception_ = false; + isc_throw (SimpleAddTransactionError, + "Simulated build requests exception"); + } + + return (NameChangeTransaction::prepNewRequest(domain)); + } + + /// @brief Simulates receiving a response + /// + /// This method simulates the completion of a DNSClient send. This allows + /// the state handler logic devoted to dealing with IO completion to be + /// fully exercised without requiring any actual IO. The two primary + /// pieces of information gleaned from IO completion are the DNSClient + /// status which indicates whether or not the IO exchange was successful + /// and the rcode, which indicates the server's reaction to the request. + /// + /// This method updates the transaction's DNS status value to that of the + /// given parameter, and then constructs and DNS update response message + /// with the given rcode value. To complete the simulation it then posts + /// a next event of IO_COMPLETED_EVT. + /// + /// @param status simulated DNSClient status + /// @param rcode simulated server response code + void fakeResponse(const DNSClient::Status& status, + const dns::Rcode& rcode) { + // Set the DNS update status. This is normally set in + // DNSClient IO completion handler. + setDnsUpdateStatus(status); + + // Construct an empty message with the given Rcode. + D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)); + msg->setRcode(rcode); + + // Set the update response to the message. + setDnsUpdateResponse(msg); + + // Post the IO completion event. + postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + } + + /// @brief Selects the first forward server. + /// Some state handlers require a server to have been selected. + /// This selects a server without going through the state + /// transition(s) to do so. + bool selectFwdServer() { + if (getForwardDomain()) { + initServerSelection(getForwardDomain()); + selectNextServer(); + return (getCurrentServer().get() != 0); + } + + return (false); + } + + /// @brief Selects the first reverse server. + /// Some state handlers require a server to have been selected. + /// This selects a server without going through the state + /// transition(s) to do so. + bool selectRevServer() { + if (getReverseDomain()) { + initServerSelection(getReverseDomain()); + selectNextServer(); + return (getCurrentServer().get() != 0); + } + + return (false); + } + + /// @brief One-shot flag which will simulate sendUpdate failure if true. + bool simulate_send_exception_; + + /// @brief One-shot flag which will simulate an exception when sendUpdate + /// failure if true. + bool simulate_build_request_exception_; + + using StateModel::postNextEvent; + using StateModel::setState; + using StateModel::initDictionaries; + using SimpleAddTransaction::defineEvents; + using SimpleAddTransaction::verifyEvents; + using SimpleAddTransaction::defineStates; + using SimpleAddTransaction::verifyStates; + using SimpleAddTransaction::readyHandler; + using SimpleAddTransaction::selectingFwdServerHandler; + using SimpleAddTransaction::getCurrentServer; + using SimpleAddTransaction::setDnsUpdateStatus; + using SimpleAddTransaction::replacingFwdAddrsHandler; + using SimpleAddTransaction::selectingRevServerHandler; + using SimpleAddTransaction::replacingRevPtrsHandler; + using SimpleAddTransaction::processAddOkHandler; + using SimpleAddTransaction::processAddFailedHandler; + using SimpleAddTransaction::buildReplaceFwdAddressRequest; + using SimpleAddTransaction::buildReplaceRevPtrsRequest; +}; + +typedef boost::shared_ptr<SimpleAddStub> SimpleAddStubPtr; + +/// @brief Test fixture for testing SimpleAddTransaction +/// +/// Note this class uses SimpleAddStub class to exercise non-public +/// aspects of SimpleAddTransaction. +class SimpleAddTransactionTest : public TransactionTest { +public: + + SimpleAddTransactionTest() { + } + + virtual ~SimpleAddTransactionTest() { + } + + /// @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_mask determines which change directions are requested + SimpleAddStubPtr makeTransaction4(int change_mask = FWD_AND_REV_CHG) { + // Creates IPv4 remove request, forward, and reverse domains. + setupForIPv4Transaction(dhcp_ddns::CHG_ADD, change_mask); + + // Now create the test transaction as would occur in update manager. + return (SimpleAddStubPtr(new SimpleAddStub(io_service_, ncr_, + forward_domain_, + reverse_domain_, cfg_mgr_))); + } + + /// @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_mask determines which change directions are requested + SimpleAddStubPtr makeTransaction6(int change_mask = FWD_AND_REV_CHG) { + // Creates IPv6 remove request, forward, and reverse domains. + setupForIPv6Transaction(dhcp_ddns::CHG_ADD, change_mask); + + // Now create the test transaction as would occur in update manager. + return (SimpleAddStubPtr(new SimpleAddStub(io_service_, ncr_, + forward_domain_, + reverse_domain_, + cfg_mgr_))); + } + + /// @brief Create a test transaction at a known point in the state model. + /// + /// Method prepares a new test transaction and sets its state and next + /// event values to those given. This makes the transaction appear to + /// be at that point in the state model without having to transition it + /// through prerequisite states. It also provides the ability to set + /// which change directions are requested: forward change only, reverse + /// change only, or both. + /// + /// @param state value to set as the current state + /// @param event value to post as the next event + /// @param change_mask determines which change directions are requested + /// @param family selects between an IPv4 (AF_INET) and IPv6 (AF_INET6) + /// transaction. + SimpleAddStubPtr prepHandlerTest(unsigned int state, unsigned int event, + unsigned int change_mask = FWD_AND_REV_CHG, + short family = AF_INET) { + SimpleAddStubPtr name_add = (family == AF_INET ? + makeTransaction4(change_mask) : + makeTransaction6(change_mask)); + name_add->initDictionaries(); + name_add->postNextEvent(event); + name_add->setState(state); + return (name_add); + } +}; + +/// @brief Tests SimpleAddTransaction construction. +/// This test verifies that: +/// 1. Construction with invalid type of request +/// 2. Valid construction functions properly +TEST(SimpleAddTransaction, construction) { + asiolink::IOServicePtr io_service(new isc::asiolink::IOService()); + D2CfgMgrPtr cfg_mgr(new D2CfgMgr()); + + const char* msg_str = + "{" + " \"change-type\" : 1 , " + " \"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; + 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 wrong change type fails. + EXPECT_THROW(SimpleAddTransaction(io_service, ncr, + forward_domain, reverse_domain, cfg_mgr), + SimpleAddTransactionError); + + // Verify that a valid construction attempt works. + ncr->setChangeType(isc::dhcp_ddns::CHG_ADD); + EXPECT_NO_THROW(SimpleAddTransaction(io_service, ncr, + forward_domain, reverse_domain, + cfg_mgr)); +} + +/// @brief Tests event and state dictionary construction and verification. +TEST_F(SimpleAddTransactionTest, dictionaryCheck) { + SimpleAddStubPtr name_add; + ASSERT_NO_THROW(name_add = makeTransaction4()); + // Verify that the event and state dictionary validation fails prior + // dictionary construction. + ASSERT_THROW(name_add->verifyEvents(), StateModelError); + ASSERT_THROW(name_add->verifyStates(), StateModelError); + + // Construct both dictionaries. + ASSERT_NO_THROW(name_add->defineEvents()); + ASSERT_NO_THROW(name_add->defineStates()); + + // Verify both event and state dictionaries now pass validation. + ASSERT_NO_THROW(name_add->verifyEvents()); + ASSERT_NO_THROW(name_add->verifyStates()); +} + +/// @brief Tests construction of a DNS update request for replacing a forward +/// dns entry. +TEST_F(SimpleAddTransactionTest, buildReplaceFwdAddressRequest) { + // Create a IPv4 forward replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + SimpleAddStubPtr name_add; + ASSERT_NO_THROW(name_add = makeTransaction4()); + ASSERT_NO_THROW(name_add->buildReplaceFwdAddressRequest()); + checkSimpleReplaceFwdAddressRequest(*name_add); + + // Create a IPv6 forward replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + ASSERT_NO_THROW(name_add = makeTransaction6()); + ASSERT_NO_THROW(name_add->buildReplaceFwdAddressRequest()); + checkSimpleReplaceFwdAddressRequest(*name_add); +} + +/// @brief Tests the construction of a DNS update request for replacing a +/// reverse dns entry. +TEST_F(SimpleAddTransactionTest, buildReplaceRevPtrsRequest) { + // Create a IPv4 reverse replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + SimpleAddStubPtr name_add; + ASSERT_NO_THROW(name_add = makeTransaction4()); + ASSERT_NO_THROW(name_add->buildReplaceRevPtrsRequest()); + checkReplaceRevPtrsRequest(*name_add); + + // Create a IPv6 reverse replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + ASSERT_NO_THROW(name_add = makeTransaction6()); + ASSERT_NO_THROW(name_add->buildReplaceRevPtrsRequest()); + checkReplaceRevPtrsRequest(*name_add); +} + +// Tests the readyHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is START_EVT and request includes only a forward change +// 2. Posted event is START_EVT and request includes both a forward and a +// reverse change +// 3. Posted event is START_EVT and request includes only a reverse change +// 4. Posted event is invalid +// +TEST_F(SimpleAddTransactionTest, readyHandler) { + SimpleAddStubPtr name_add; + + // Create a transaction which includes only a forward change. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, FORWARD_CHG) + ); + + // Run readyHandler. + EXPECT_NO_THROW(name_add->readyHandler()); + + // Verify that a request requiring only a forward change, transitions to + // selecting a forward server. + CHECK_CONTEXT(name_add, NameChangeTransaction::SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT); + + // Create a transaction which includes both a forward and a reverse change. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, FWD_AND_REV_CHG) + ); + + // Run readyHandler. + EXPECT_NO_THROW(name_add->readyHandler()); + + // Verify that a request requiring both forward and reverse, starts with + // the forward change by transitioning to selecting a forward server. + CHECK_CONTEXT(name_add, NameChangeTransaction::SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT); + + // Create and prep a reverse only transaction. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, REVERSE_CHG) + ); + + // Run readyHandler. + EXPECT_NO_THROW(name_add->readyHandler()); + + // Verify that a request requiring only a reverse change, transitions to + // selecting a reverse server. + CHECK_CONTEXT(name_add, NameChangeTransaction::SELECTING_REV_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::READY_ST, StateModel::NOP_EVT) + ); + + // Running the readyHandler should throw. + EXPECT_THROW(name_add->readyHandler(), SimpleAddTransactionError); +} + +// Tests the selectingFwdServerHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is SELECT_SERVER_EVT +// 2. Posted event is SERVER_IO_ERROR_EVT +// 3. Posted event is invalid +// +TEST_F(SimpleAddTransactionTest, selectingFwdServerHandler) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT) + ); + + // Call selectingFwdServerHandler enough times to select all of the + // servers in it's current domain. The first time, it will be with + // next event of SELECT_SERVER_EVT. Thereafter it will be with a next + // event of SERVER_IO_ERROR_EVT. + int num_servers = name_add->getForwardDomain()->getServers()->size(); + for (int i = 0; i < num_servers; ++i) { + SCOPED_TRACE(testing::Message() << " num_servers: " << num_servers + << " selections: " << i); + // Run selectingFwdServerHandler. + ASSERT_NO_THROW(name_add->selectingFwdServerHandler()); + + // Verify that a server was selected. + ASSERT_TRUE(name_add->getCurrentServer()); + + // Verify that we transitioned correctly. + CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_FWD_ADDRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + + // Post a server IO error event. This simulates an IO error occurring + // and a need to select the new server. + ASSERT_NO_THROW(name_add->postNextEvent(NameChangeTransaction:: + SERVER_IO_ERROR_EVT)); + } + + // We should have exhausted the list of servers. Processing another + // SERVER_IO_ERROR_EVT should transition us to failure. + EXPECT_NO_THROW(name_add->selectingFwdServerHandler()); + CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::NO_MORE_SERVERS_EVT); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::SELECTING_FWD_SERVER_ST, + StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_add->selectingFwdServerHandler(), + SimpleAddTransactionError); +} + +// ************************ replacingFwdAddrHandler Tests ***************** + +// Tests that replacingFwdAddrsHandler rejects invalid events. +TEST_F(SimpleAddTransactionTest, replacingFwdAddrsHandler_InvalidEvent) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler but with + // an invalid event. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST, + NameChangeTransaction::StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_add->replacingFwdAddrsHandler(), SimpleAddTransactionError); +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(SimpleAddTransactionTest, replacingFwdAddrsHandler_FwdOnlyAddOK) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST, + SimpleAddTransaction::SERVER_SELECTED_EVT, FORWARD_CHG) + ); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Run replacingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Verify that an update message was constructed properly. + checkSimpleReplaceFwdAddressRequest(*name_add); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_FWD_ADDRS_ST, + NameChangeTransaction::NOP_EVT); + + // Simulate receiving a successful update response. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run replacingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Forward completion should be true, reverse should be false. + EXPECT_TRUE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since it is a forward only change, we should be done. + // Verify that we transitioned correctly. + CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_OK_ST, + NameChangeTransaction::UPDATE_OK_EVT); +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// The update request is sent without error. +// A server response is received which indicates the update was rejected. +// +TEST_F(SimpleAddTransactionTest, replacingFwdAddrsHandler_OtherRcode) { + SimpleAddStubPtr name_add; + // Create the transaction. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST, + SimpleAddTransaction::SERVER_SELECTED_EVT, FWD_AND_REV_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectFwdServer()); + + // Run replacingFwdAddrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Simulate receiving server rejection response. Per RFC, anything other + // than no error or FQDN in use is failure. Arbitrarily choosing refused. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED()); + + // Run replacingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // We should have failed the transaction. Verify that we transitioned + // correctly. + CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(SimpleAddTransactionTest, replacingFwdAddrsHandler_Timeout) { + SimpleAddStubPtr name_add; + + // Create the transaction. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST, + SimpleAddTransaction::SERVER_SELECTED_EVT, FWD_AND_REV_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectFwdServer()); + + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run replacingFwdAddrsHandler to send the request. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Simulate a server IO timeout. + name_add->setDnsUpdateStatus(DNSClient::TIMEOUT); + name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run replacingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_FWD_ADDRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + } else { + // Server retries should be exhausted, time for a new server. + CHECK_CONTEXT(name_add, SimpleAddTransaction::SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SERVER_IO_ERROR_EVT); + } + } +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent but a corrupt response is received, this occurs +// MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(SimpleAddTransactionTest, replacingFwdAddrsHandler_CorruptResponse) { + SimpleAddStubPtr name_add; + + // Create the transaction. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST, + SimpleAddTransaction::SERVER_SELECTED_EVT, FWD_AND_REV_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectFwdServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run replacingFwdAddrsHandler to send the request. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Simulate a corrupt server response. + name_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE); + name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run replacingFwdAddrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_FWD_ADDRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + } else { + // Server retries should be exhausted, time for a new server. + CHECK_CONTEXT(name_add, SimpleAddTransaction::SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SERVER_IO_ERROR_EVT); + } + } +} + +// Tests the selectingRevServerHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is SELECT_SERVER_EVT +// 2. Posted event is SERVER_IO_ERROR_EVT +// 3. Posted event is invalid +// +TEST_F(SimpleAddTransactionTest, selectingRevServerHandler) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::SELECTING_REV_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT) + ); + + // Call selectingRevServerHandler enough times to select all of the + // servers in it's current domain. The first time, it will be with + // next event of SELECT_SERVER_EVT. Thereafter it will be with a next + // event of SERVER_IO_ERROR_EVT. + int num_servers = name_add->getReverseDomain()->getServers()->size(); + for (int i = 0; i < num_servers; ++i) { + SCOPED_TRACE(testing::Message() << " num_servers: " << num_servers + << " selections: " << i); + // Run selectingRevServerHandler. + ASSERT_NO_THROW(name_add->selectingRevServerHandler()); + + // Verify that a server was selected. + ASSERT_TRUE(name_add->getCurrentServer()); + + // Verify that we transitioned correctly. + CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + + // Post a server IO error event. This simulates an IO error occurring + // and a need to select the new server. + ASSERT_NO_THROW(name_add->postNextEvent(NameChangeTransaction:: + SERVER_IO_ERROR_EVT)); + } + + // We should have exhausted the list of servers. Processing another + // SERVER_IO_ERROR_EVT should transition us to failure. + EXPECT_NO_THROW(name_add->selectingRevServerHandler()); + CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::NO_MORE_SERVERS_EVT); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::SELECTING_REV_SERVER_ST, + StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_add->selectingRevServerHandler(), + SimpleAddTransactionError); +} + +//************************** replacingRevPtrsHandler tests ***************** + +// Tests that replacingRevPtrsHandler rejects invalid events. +TEST_F(SimpleAddTransactionTest, replacingRevPtrsHandler_InvalidEvent) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler but with + // an invalid event. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST, + NameChangeTransaction::StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_add->replacingRevPtrsHandler(), + SimpleAddTransactionError); +} + +// Tests replacingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(SimpleAddTransactionTest, replacingRevPtrsHandler_FwdOnlyAddOK) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST, + SimpleAddTransaction::SERVER_SELECTED_EVT, REVERSE_CHG) + ); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_add->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Run replacingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Verify that an update message was constructed properly. + checkReplaceRevPtrsRequest(*name_add); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_REV_PTRS_ST, + NameChangeTransaction::NOP_EVT); + + // Simulate receiving a successful update response. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run replacingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Forward completion should be false, reverse should be true. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_TRUE(name_add->getReverseChangeCompleted()); + + // Since it is a reverse change, we should be done. + // Verify that we transitioned correctly. + CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_OK_ST, + NameChangeTransaction::UPDATE_OK_EVT); +} + +// Tests replacingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates the update was rejected. +// +TEST_F(SimpleAddTransactionTest, replacingRevPtrsHandler_OtherRcode) { + SimpleAddStubPtr name_add; + // Create the transaction. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST, + SimpleAddTransaction::SERVER_SELECTED_EVT, REVERSE_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectRevServer()); + + // Run replacingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Simulate receiving server rejection response. Per RFC, anything other + // than no error is failure. Arbitrarily choosing refused. + name_add->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED()); + + // Run replacingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // We should have failed the transaction. Verify that we transitioned + // correctly. + CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +// Tests replacingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(SimpleAddTransactionTest, replacingRevPtrsHandler_Timeout) { + SimpleAddStubPtr name_add; + // Create the transaction. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST, + SimpleAddTransaction::SERVER_SELECTED_EVT, REVERSE_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectRevServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run replacingRevPtrsHandler to send the request. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Simulate a server IO timeout. + name_add->setDnsUpdateStatus(DNSClient::TIMEOUT); + name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run replacingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + } else { + // Server retries should be exhausted, time for a new server. + CHECK_CONTEXT(name_add, SimpleAddTransaction::SELECTING_REV_SERVER_ST, + NameChangeTransaction::SERVER_IO_ERROR_EVT); + } + } +} + +// Tests replacingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent but a corrupt response is received, this occurs +// MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(SimpleAddTransactionTest, replacingRevPtrsHandler_CorruptResponse) { + SimpleAddStubPtr name_add; + // Create the transaction. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST, + SimpleAddTransaction::SERVER_SELECTED_EVT, REVERSE_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_add->selectRevServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run replacingRevPtrsHandler to send the request. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Simulate a server corrupt response. + name_add->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE); + name_add->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run replacingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + CHECK_CONTEXT(name_add, SimpleAddTransaction::REPLACING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + } else { + // Server retries should be exhausted, time for a new server. + CHECK_CONTEXT(name_add, SimpleAddTransaction::SELECTING_REV_SERVER_ST, + NameChangeTransaction::SERVER_IO_ERROR_EVT); + } + } +} + +// Tests the processAddOkHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is UPDATE_OK_EVT +// 2. Posted event is invalid +// +TEST_F(SimpleAddTransactionTest, processAddOkHandler) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST, + NameChangeTransaction::UPDATE_OK_EVT) + ); + + // Run processAddOkHandler. + EXPECT_NO_THROW(name_add->processAddOkHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_add->getNcrStatus()); + + // Verify that the model has ended. + CHECK_CONTEXT(name_add, StateModel::END_ST, StateModel::END_EVT); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST, + StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_add->processAddOkHandler(), SimpleAddTransactionError); +} + +// Tests the processAddFailedHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is UPDATE_FAILED_EVT +// 2. Posted event is invalid +// +TEST_F(SimpleAddTransactionTest, processAddFailedHandler) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT) + ); + + // Run processAddFailedHandler. + EXPECT_NO_THROW(name_add->processAddFailedHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_FAILED, name_add->getNcrStatus()); + + // Verify that the model has ended. (Remember, the transaction failed NOT + // the model. The model should have ended normally.) + CHECK_CONTEXT(name_add, StateModel::END_ST, StateModel::END_EVT); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_add->processAddFailedHandler(), SimpleAddTransactionError); +} + +// Tests the processAddFailedHandler functionality. +// It verifies behavior for posted event of NO_MORE_SERVERS_EVT. +TEST_F(SimpleAddTransactionTest, processAddFailedHandler_NoMoreServers) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_add = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::NO_MORE_SERVERS_EVT) + ); + + // Run processAddFailedHandler. + EXPECT_NO_THROW(name_add->processAddFailedHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_FAILED, name_add->getNcrStatus()); + + // Verify that the model has ended. (Remember, the transaction failed NOT + // the model. The model should have ended normally.) + CHECK_CONTEXT(name_add, StateModel::END_ST, StateModel::END_EVT); +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The send update request fails due to an unexpected exception. +// +TEST_F(SimpleAddTransactionTest, replacingFwdAddrsHandler_SendUpdateException) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, FORWARD_CHG) + ); + + name_add->simulate_send_exception_ = true; + + // Run replacingFwdAddrsHandler to construct and send the request. + ASSERT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +// Tests replacingRevPtrHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The send update request fails due to an unexpected exception. +// +TEST_F(SimpleAddTransactionTest, replacingRevPtrsHandler_SendUpdateException) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, REVERSE_CHG) + ); + + name_add->simulate_send_exception_ = true; + + // Run replacingRevPtrsHandler to construct and send the request. + ASSERT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +// Tests replacingFwdAddrsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The request build fails due to an unexpected exception. +// +TEST_F(SimpleAddTransactionTest, replacingFwdAddrsHandler_BuildRequestException) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_FWD_ADDRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, FORWARD_CHG) + ); + + // Set the one-shot exception simulation flag. + name_add->simulate_build_request_exception_ = true; + + // Run replacingFwdAddrsHandler to construct and send the request. + // This should fail with a build request throw which should be caught + // in the state handler. + ASSERT_NO_THROW(name_add->replacingFwdAddrsHandler()); + + // Verify we did not attempt to send anything. + EXPECT_EQ(0, name_add->getUpdateAttempts()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +// Tests replacingRevPtrHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The request build fails due to an unexpected exception. +// +TEST_F(SimpleAddTransactionTest, replacingRevPtrsHandler_BuildRequestException) { + SimpleAddStubPtr name_add; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_add = prepHandlerTest(SimpleAddTransaction::REPLACING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, REVERSE_CHG) + ); + + // Set the one-shot exception simulation flag. + name_add->simulate_build_request_exception_ = true; + + // Run replacingRevPtrsHandler to construct and send the request. + // This should fail with a build request throw which should be caught + // in the state handler. + ASSERT_NO_THROW(name_add->replacingRevPtrsHandler()); + + // Verify we did not attempt to send anything. + EXPECT_EQ(0, name_add->getUpdateAttempts()); + + // Completion flags should be false. + EXPECT_FALSE(name_add->getForwardChangeCompleted()); + EXPECT_FALSE(name_add->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + CHECK_CONTEXT(name_add, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +} diff --git a/src/bin/d2/tests/simple_remove_unittests.cc b/src/bin/d2/tests/simple_remove_unittests.cc new file mode 100644 index 0000000..1d53400 --- /dev/null +++ b/src/bin/d2/tests/simple_remove_unittests.cc @@ -0,0 +1,1238 @@ +// 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/. + +#include <config.h> + +#include <asiolink/io_service.h> +#include <d2/simple_remove.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/testutils/nc_test_utils.h> +#include <dns/messagerenderer.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc; +using namespace isc::d2; +using namespace isc::util; + + +namespace { + +/// @brief Test class derived from SimpleRemoveTransaction to provide visibility +// to protected methods. +class SimpleRemoveStub : public SimpleRemoveTransaction { +public: + SimpleRemoveStub(asiolink::IOServicePtr& io_service, + dhcp_ddns::NameChangeRequestPtr& ncr, + DdnsDomainPtr& forward_domain, + DdnsDomainPtr& reverse_domain, + D2CfgMgrPtr& cfg_mgr) + : SimpleRemoveTransaction(io_service, ncr, forward_domain, + reverse_domain, cfg_mgr), + simulate_send_exception_(false), + simulate_build_request_exception_(false) { + } + + virtual ~SimpleRemoveStub() { + } + + /// @brief Simulates sending update requests to the DNS server + /// + /// This method simulates the initiation of an asynchronous send of + /// a DNS update request. It overrides the actual sendUpdate method in + /// the base class, thus avoiding an actual send, yet still increments + /// the update attempt count and posts a next event of NOP_EVT. + /// + /// It will also simulate an exception-based failure of sendUpdate, if + /// the simulate_send_exception_ flag is true. + /// + /// @param comment Parameter is unused, but present in base class method + /// + virtual void sendUpdate(const std::string& /* comment */) { + if (simulate_send_exception_) { + // Make the flag a one-shot by resetting it. + simulate_send_exception_ = false; + // Transition to failed. + transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT); + return; + } + + // Update send attempt count and post a NOP_EVT. + setUpdateAttempts(getUpdateAttempts() + 1); + postNextEvent(StateModel::NOP_EVT); + } + + /// @brief Prepares the initial D2UpdateMessage + /// + /// This method overrides the NameChangeTransaction implementation to + /// provide the ability to simulate an exception throw in the build + /// request logic. + /// If the one-shot flag, simulate_build_request_exception_ is true, + /// this method will throw an exception, otherwise it will invoke the + /// base class method, providing normal functionality. + /// + /// For parameter description see the NameChangeTransaction implementation. + virtual D2UpdateMessagePtr prepNewRequest(DdnsDomainPtr domain) { + if (simulate_build_request_exception_) { + simulate_build_request_exception_ = false; + isc_throw (SimpleRemoveTransactionError, + "Simulated build requests exception"); + } + + return (NameChangeTransaction::prepNewRequest(domain)); + } + + /// @brief Simulates receiving a response + /// + /// This method simulates the completion of a DNSClient send. This allows + /// the state handler logic devoted to dealing with IO completion to be + /// fully exercised without requiring any actual IO. The two primary + /// pieces of information gleaned from IO completion are the DNSClient + /// status which indicates whether or not the IO exchange was successful + /// and the rcode, which indicates the server's reaction to the request. + /// + /// This method updates the transaction's DNS status value to that of the + /// given parameter, and then constructs and DNS update response message + /// with the given rcode value. To complete the simulation it then posts + /// a next event of IO_COMPLETED_EVT. + /// + /// @param status simulated DNSClient status + /// @param rcode simulated server response code + void fakeResponse(const DNSClient::Status& status, + const dns::Rcode& rcode) { + // Set the DNS update status. This is normally set in + // DNSClient IO completion handler. + setDnsUpdateStatus(status); + + // Construct an empty message with the given Rcode. + D2UpdateMessagePtr msg(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)); + msg->setRcode(rcode); + + // Set the update response to the message. + setDnsUpdateResponse(msg); + + // Post the IO completion event. + postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + } + + /// @brief Selects the first forward server. + /// Some state handlers require a server to have been selected. + /// This selects a server without going through the state + /// transition(s) to do so. + bool selectFwdServer() { + if (getForwardDomain()) { + initServerSelection(getForwardDomain()); + selectNextServer(); + return (getCurrentServer().get() != 0); + } + + return (false); + } + + /// @brief Selects the first reverse server. + /// Some state handlers require a server to have been selected. + /// This selects a server without going through the state + /// transition(s) to do so. + bool selectRevServer() { + if (getReverseDomain()) { + initServerSelection(getReverseDomain()); + selectNextServer(); + return (getCurrentServer().get() != 0); + } + + return (false); + } + + /// @brief One-shot flag which will simulate sendUpdate failure if true. + bool simulate_send_exception_; + + /// @brief One-shot flag which will simulate an exception when sendUpdate + /// failure if true. + bool simulate_build_request_exception_; + + using StateModel::postNextEvent; + using StateModel::setState; + using StateModel::initDictionaries; + using SimpleRemoveTransaction::defineEvents; + using SimpleRemoveTransaction::verifyEvents; + using SimpleRemoveTransaction::defineStates; + using SimpleRemoveTransaction::verifyStates; + using SimpleRemoveTransaction::readyHandler; + using SimpleRemoveTransaction::selectingFwdServerHandler; + using SimpleRemoveTransaction::getCurrentServer; + using SimpleRemoveTransaction::setDnsUpdateStatus; + using SimpleRemoveTransaction::removingFwdRRsHandler; + using SimpleRemoveTransaction::selectingRevServerHandler; + using SimpleRemoveTransaction::removingRevPtrsHandler; + using SimpleRemoveTransaction::processRemoveOkHandler; + using SimpleRemoveTransaction::processRemoveFailedHandler; + using SimpleRemoveTransaction::buildRemoveFwdRRsRequest; + using SimpleRemoveTransaction::buildRemoveRevPtrsRequest; +}; + +typedef boost::shared_ptr<SimpleRemoveStub> SimpleRemoveStubPtr; + +/// @brief Test fixture for testing SimpleRemoveTransaction +/// +/// Note this class uses SimpleRemoveStub class to exercise non-public +/// aspects of SimpleRemoveTransaction. +class SimpleRemoveTransactionTest : public TransactionTest { +public: + SimpleRemoveTransactionTest() { + } + + virtual ~SimpleRemoveTransactionTest() { + } + + /// @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_mask determines which change directions are requested + SimpleRemoveStubPtr makeTransaction4(int change_mask) { + // Creates IPv4 remove request, forward, and reverse domains. + setupForIPv4Transaction(dhcp_ddns::CHG_REMOVE, change_mask); + + // Now create the test transaction as would occur in update manager. + return (SimpleRemoveStubPtr(new SimpleRemoveStub(io_service_, ncr_, + forward_domain_, + reverse_domain_, + cfg_mgr_))); + } + + /// @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_mask determines which change directions are requested + SimpleRemoveStubPtr makeTransaction6(int change_mask) { + // Creates IPv6 remove request, forward, and reverse domains. + setupForIPv6Transaction(dhcp_ddns::CHG_REMOVE, change_mask); + + // Now create the test transaction as would occur in update manager. + return (SimpleRemoveStubPtr(new SimpleRemoveStub(io_service_, ncr_, + forward_domain_, + reverse_domain_, + cfg_mgr_))); + } + + /// @brief Create a test transaction at a known point in the state model. + /// + /// Method prepares a new test transaction and sets its state and next + /// event values to those given. This makes the transaction appear to + /// be at that point in the state model without having to transition it + /// through prerequisite states. It also provides the ability to set + /// which change directions are requested: forward change only, reverse + /// change only, or both. + /// + /// @param state value to set as the current state + /// @param event value to post as the next event + /// @param change_mask determines which change directions are requested + /// @param family selects between an IPv4 (AF_INET) and IPv6 (AF_INET6) + /// transaction. + SimpleRemoveStubPtr prepHandlerTest(unsigned int state, unsigned int event, + unsigned int change_mask = FWD_AND_REV_CHG, + short family = AF_INET) { + SimpleRemoveStubPtr name_remove = (family == AF_INET ? + makeTransaction4(change_mask) : + makeTransaction6(change_mask)); + name_remove->initDictionaries(); + name_remove->postNextEvent(event); + name_remove->setState(state); + return (name_remove); + } + +}; + +/// @brief Tests SimpleRemoveTransaction construction. +/// This test verifies that: +/// 1. Construction with invalid type of request +/// 2. Valid construction functions properly +TEST(SimpleRemoveTransaction, 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; + 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 wrong change type fails. + EXPECT_THROW(SimpleRemoveTransaction(io_service, ncr, + forward_domain, reverse_domain, cfg_mgr), + SimpleRemoveTransactionError); + + // Verify that a valid construction attempt works. + ncr->setChangeType(isc::dhcp_ddns::CHG_REMOVE); + EXPECT_NO_THROW(SimpleRemoveTransaction(io_service, ncr, + forward_domain, reverse_domain, + cfg_mgr)); +} + +/// @brief Tests event and state dictionary construction and verification. +TEST_F(SimpleRemoveTransactionTest, dictionaryCheck) { + SimpleRemoveStubPtr name_remove; + ASSERT_NO_THROW(name_remove = makeTransaction4(FWD_AND_REV_CHG)); + // Verify that the event and state dictionary validation fails prior + // dictionary construction. + ASSERT_THROW(name_remove->verifyEvents(), StateModelError); + ASSERT_THROW(name_remove->verifyStates(), StateModelError); + + // Construct both dictionaries. + ASSERT_NO_THROW(name_remove->defineEvents()); + ASSERT_NO_THROW(name_remove->defineStates()); + + // Verify both event and state dictionaries now pass validation. + ASSERT_NO_THROW(name_remove->verifyEvents()); + ASSERT_NO_THROW(name_remove->verifyStates()); +} + +/// @brief Tests construction of a DNS update request for removing forward +/// dns RR entries. +TEST_F(SimpleRemoveTransactionTest, buildRemoveFwdRRsRequest) { + // Create a IPv4 forward replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + SimpleRemoveStubPtr name_remove; + ASSERT_NO_THROW(name_remove = makeTransaction4(FORWARD_CHG)); + ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest()); + checkSimpleRemoveFwdRRsRequest(*name_remove); + + // Create a IPv6 forward replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + ASSERT_NO_THROW(name_remove = makeTransaction6(FORWARD_CHG)); + ASSERT_NO_THROW(name_remove->buildRemoveFwdRRsRequest()); + checkSimpleRemoveFwdRRsRequest(*name_remove); +} + +/// @brief Tests the construction of a DNS update request for removing a +/// reverse dns entry. +TEST_F(SimpleRemoveTransactionTest, buildRemoveRevPtrsRequest) { + // Create a IPv4 reverse replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + SimpleRemoveStubPtr name_remove; + ASSERT_NO_THROW(name_remove = makeTransaction4(REVERSE_CHG)); + ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest()); + checkSimpleRemoveRevPtrsRequest(*name_remove); + + // Create a IPv6 reverse replace transaction. + // Verify the request builds without error. + // and then verify the request contents. + ASSERT_NO_THROW(name_remove = makeTransaction6(REVERSE_CHG)); + ASSERT_NO_THROW(name_remove->buildRemoveRevPtrsRequest()); + checkSimpleRemoveRevPtrsRequest(*name_remove); +} + +// Tests the readyHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is START_EVT and request includes only a forward change +// 2. Posted event is START_EVT and request includes both a forward and a +// reverse change +// 3. Posted event is START_EVT and request includes only a reverse change +// 4. Posted event is invalid +// +TEST_F(SimpleRemoveTransactionTest, readyHandler) { + SimpleRemoveStubPtr name_remove; + + // Create a transaction which includes only a forward change. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, FORWARD_CHG) + ); + + // Run readyHandler. + EXPECT_NO_THROW(name_remove->readyHandler()); + + // Verify that a request requiring only a forward change, transitions to + // selecting a forward server. + CHECK_CONTEXT(name_remove, NameChangeTransaction::SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT); + + // Create a transaction which includes both a forward and a reverse change. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, FWD_AND_REV_CHG) + ); + + // Run readyHandler. + EXPECT_NO_THROW(name_remove->readyHandler()); + + // Verify that a request requiring both forward and reverse, starts with + // the forward change by transitioning to selecting a forward server. + CHECK_CONTEXT(name_remove, NameChangeTransaction::SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT); + + // Create and prep a reverse only transaction. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::START_EVT, REVERSE_CHG) + ); + + // Run readyHandler. + EXPECT_NO_THROW(name_remove->readyHandler()); + + // Verify that a request requiring only a reverse change, transitions to + // selecting a reverse server. + CHECK_CONTEXT(name_remove, NameChangeTransaction::SELECTING_REV_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::READY_ST, + StateModel::NOP_EVT) + ); + + // Running the readyHandler should throw. + EXPECT_THROW(name_remove->readyHandler(), SimpleRemoveTransactionError); +} + + +// Tests the selectingFwdServerHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is SELECT_SERVER_EVT +// 2. Posted event is SERVER_IO_ERROR_EVT +// 3. Posted event is invalid +// +TEST_F(SimpleRemoveTransactionTest, selectingFwdServerHandler) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT) + ); + + // Call selectingFwdServerHandler enough times to select all of the + // servers in it's current domain. The first time, it will be with + // next event of SELECT_SERVER_EVT. Thereafter it will be with a next + // event of SERVER_IO_ERROR_EVT. + int num_servers = name_remove->getForwardDomain()->getServers()->size(); + for (int i = 0; i < num_servers; ++i) { + SCOPED_TRACE(testing::Message() << " num_servers: " << num_servers + << " selections:" << i); + // Run selectingFwdServerHandler. + ASSERT_NO_THROW(name_remove->selectingFwdServerHandler()); + + // Verify that a server was selected. + ASSERT_TRUE(name_remove->getCurrentServer()); + + // Verify that we transitioned correctly. + CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_FWD_RRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + + // Post a server IO error event. This simulates an IO error occurring + // and a need to select the new server. + ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction:: + SERVER_IO_ERROR_EVT)); + } + + // We should have exhausted the list of servers. Processing another + // SERVER_IO_ERROR_EVT should transition us to failure. + EXPECT_NO_THROW(name_remove->selectingFwdServerHandler()); + CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::NO_MORE_SERVERS_EVT); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::SELECTING_FWD_SERVER_ST, + StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_remove->selectingFwdServerHandler(), + SimpleRemoveTransactionError); +} + +// ************************ removingFwdRRsHandler Tests ***************** + +// Tests that removingFwdRRsHandler rejects invalid events. +TEST_F(SimpleRemoveTransactionTest, removingFwdRRsHandler_InvalidEvent) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler but with + // an invalid event. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_FWD_RRS_ST, + StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_remove->removingFwdRRsHandler(), + SimpleRemoveTransactionError); +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(SimpleRemoveTransactionTest, removingFwdRRsHandler_FwdOnlyOK) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_FWD_RRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, FORWARD_CHG) + ); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Run removingFwdRRsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Verify that an update message was constructed properly. + checkSimpleRemoveFwdRRsRequest(*name_remove); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_FWD_RRS_ST, + NameChangeTransaction::NOP_EVT); + + // Simulate receiving a successful update response. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run removingFwdRRsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Forward completion should be true, reverse should be false. + EXPECT_TRUE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since it is a forward only change, we should be done. + // Verify that we transitioned correctly. + CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_OK_ST, + NameChangeTransaction::UPDATE_OK_EVT); +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// The update request is sent without error. +// A server response is received which indicates the update was rejected. +// +TEST_F(SimpleRemoveTransactionTest, removingFwdRRsHandler_OtherRcode) { + SimpleRemoveStubPtr name_remove; + // Create the transaction. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_FWD_RRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, + FWD_AND_REV_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectFwdServer()); + + // Run removingFwdRRsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Simulate receiving server rejection response. Per RFC, anything other + // than no error is failure (we are also treating FQDN not in use is + // success). Arbitrarily choosing refused. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED()); + + // Run removingFwdRRsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // We should have failed the transaction. Verify that we transitioned + // correctly. + CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(SimpleRemoveTransactionTest, removingFwdRRsHandler_Timeout) { + SimpleRemoveStubPtr name_remove; + + // Create the transaction. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_FWD_RRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, + FWD_AND_REV_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectFwdServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run removingFwdRRsHandler to send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Simulate a server IO timeout. + name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT); + name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run removingFwdRRsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_FWD_RRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + } else { + // Server retries should be exhausted, time for a new server. + CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SERVER_IO_ERROR_EVT); + } + } +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes a forward and reverse change. +// Initial posted event is UPDATE_OK_EVT. +// The update request is sent but a corrupt response is received, this occurs +// MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(SimpleRemoveTransactionTest, removingFwdRRsHandler_InvalidResponse) { + SimpleRemoveStubPtr name_remove; + + // Create the transaction. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_FWD_RRS_ST, + NameChangeTransaction::UPDATE_OK_EVT, FWD_AND_REV_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectFwdServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run removingFwdRRsHandler to send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Simulate a corrupt server response. + name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE); + name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run removingFwdRRsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_FWD_RRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + } else { + // Server retries should be exhausted, time for a new server. + CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::SELECTING_FWD_SERVER_ST, + NameChangeTransaction::SERVER_IO_ERROR_EVT); + } + } +} + + +// Tests the selectingRevServerHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is SELECT_SERVER_EVT +// 2. Posted event is SERVER_IO_ERROR_EVT +// 3. Posted event is invalid +// +TEST_F(SimpleRemoveTransactionTest, selectingRevServerHandler) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::SELECTING_REV_SERVER_ST, + NameChangeTransaction::SELECT_SERVER_EVT) + ); + + // Call selectingRevServerHandler enough times to select all of the + // servers in it's current domain. The first time, it will be with + // next event of SELECT_SERVER_EVT. Thereafter it will be with a next + // event of SERVER_IO_ERROR_EVT. + int num_servers = name_remove->getReverseDomain()->getServers()->size(); + for (int i = 0; i < num_servers; ++i) { + SCOPED_TRACE(testing::Message() << " num_servers: " << num_servers + << " selections:" << i); + // Run selectingRevServerHandler. + ASSERT_NO_THROW(name_remove->selectingRevServerHandler()); + + // Verify that a server was selected. + ASSERT_TRUE(name_remove->getCurrentServer()); + + // Verify that we transitioned correctly. + CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + + // Post a server IO error event. This simulates an IO error occurring + // and a need to select the new server. + ASSERT_NO_THROW(name_remove->postNextEvent(NameChangeTransaction:: + SERVER_IO_ERROR_EVT)); + } + + // We should have exhausted the list of servers. Processing another + // SERVER_IO_ERROR_EVT should transition us to failure. + EXPECT_NO_THROW(name_remove->selectingRevServerHandler()); + CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::NO_MORE_SERVERS_EVT); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::SELECTING_REV_SERVER_ST, + StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_remove->selectingRevServerHandler(), + SimpleRemoveTransactionError); +} + +//************************** removingRevPtrsHandler tests ***************** + +// Tests that removingRevPtrsHandler rejects invalid events. +TEST_F(SimpleRemoveTransactionTest, removingRevPtrsHandler_InvalidEvent) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler but with + // an invalid event. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_remove->removingRevPtrsHandler(), + SimpleRemoveTransactionError); +} + +// Tests removingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates successful update. +// +TEST_F(SimpleRemoveTransactionTest, removingRevPtrsHandler_RevOnlyOK) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, REVERSE_CHG) + ); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Run removingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Verify that an update message was constructed properly. + checkSimpleRemoveRevPtrsRequest(*name_remove); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + StateModel::NOP_EVT); + + // Simulate receiving a successful update response. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NOERROR()); + + // Run removingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Forward completion should be false, reverse should be true. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_TRUE(name_remove->getReverseChangeCompleted()); + + // Since it is a reverse change, we should be done. + // Verify that we transitioned correctly. + CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_OK_ST, + NameChangeTransaction::UPDATE_OK_EVT); +} + +// Tests removingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates FQDN is NOT in use. +// +TEST_F(SimpleRemoveTransactionTest, removingRevPtrsHandler_FqdnNotInUse) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, REVERSE_CHG) + ); + + // Should not be an update message yet. + D2UpdateMessagePtr update_msg = name_remove->getDnsUpdateRequest(); + ASSERT_FALSE(update_msg); + + // At this point completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Run removingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Verify that an update message was constructed properly. + checkSimpleRemoveRevPtrsRequest(*name_remove); + + // Verify that we are still in this state and next event is NOP_EVT. + // This indicates we "sent" the message and are waiting for IO completion. + CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + StateModel::NOP_EVT); + + // Simulate receiving a RRSET does not exist. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::NXRRSET()); + + // Run removingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Forward completion should be false, reverse should be true. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_TRUE(name_remove->getReverseChangeCompleted()); + + // Since it is a reverse change, we should be done. + // Verify that we transitioned correctly. + CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_OK_ST, + NameChangeTransaction::UPDATE_OK_EVT); +} + +// Tests removingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent without error. +// A server response is received which indicates the update was rejected. +// +TEST_F(SimpleRemoveTransactionTest, removingRevPtrsHandler_OtherRcode) { + SimpleRemoveStubPtr name_remove; + // Create the transaction. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, REVERSE_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectRevServer()); + + // Run removingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Simulate receiving server rejection response. Per RFC, anything other + // than no error is failure. Arbitrarily choosing refused. + name_remove->fakeResponse(DNSClient::SUCCESS, dns::Rcode::REFUSED()); + + // Run removingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Completion flags should still be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // We should have failed the transaction. Verify that we transitioned + // correctly. + CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +// Tests removingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request send times out MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(SimpleRemoveTransactionTest, removingRevPtrsHandler_Timeout) { + SimpleRemoveStubPtr name_remove; + // Create the transaction. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, + REVERSE_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectRevServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run removingRevPtrsHandler to send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Simulate a server IO timeout. + name_remove->setDnsUpdateStatus(DNSClient::TIMEOUT); + name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run removingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + } else { + // Server retries should be exhausted, time for a new server. + CHECK_CONTEXT(name_remove, NameChangeTransaction::SELECTING_REV_SERVER_ST, + NameChangeTransaction::SERVER_IO_ERROR_EVT); + } + } +} + + +// Tests removingRevPtrsHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The update request is sent but a corrupt response is received, this occurs +// MAX_UPDATE_TRIES_PER_SERVER times. +// +TEST_F(SimpleRemoveTransactionTest, removingRevPtrsHandler_CorruptResponse) { + SimpleRemoveStubPtr name_remove; + // Create the transaction. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, REVERSE_CHG) + ); + + // Select a server to satisfy log statements. + ASSERT_TRUE(name_remove->selectRevServer()); + + // Verify that we can make maximum number of update attempts permitted + // and then transition to selecting a new server. + int max_tries = NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER; + for (int i = 1; i <= max_tries; ++i) { + // Run removingRevPtrsHandler to send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Simulate a server corrupt response. + name_remove->setDnsUpdateStatus(DNSClient::INVALID_RESPONSE); + name_remove->postNextEvent(NameChangeTransaction::IO_COMPLETED_EVT); + + // Run removingRevPtrsHandler again to process the response. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + if (i < max_tries) { + // We should be ready to try again. + CHECK_CONTEXT(name_remove, SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT); + } else { + // Server retries should be exhausted, time for a new server. + CHECK_CONTEXT(name_remove, NameChangeTransaction::SELECTING_REV_SERVER_ST, + NameChangeTransaction::SERVER_IO_ERROR_EVT); + } + } +} + +// Tests the processRemoveOkHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is UPDATE_OK_EVT +// 2. Posted event is invalid +// +TEST_F(SimpleRemoveTransactionTest, processRemoveOkHandler) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST, + NameChangeTransaction::UPDATE_OK_EVT) + ); + + // Run processRemoveOkHandler. + EXPECT_NO_THROW(name_remove->processRemoveOkHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_remove->getNcrStatus()); + + // Verify that the model has ended. + CHECK_CONTEXT(name_remove, StateModel::END_ST, StateModel::END_EVT); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_OK_ST, + StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_remove->processRemoveOkHandler(), + SimpleRemoveTransactionError); +} + +// Tests the processRemoveFailedHandler functionality. +// It verifies behavior for the following scenarios: +// +// 1. Posted event is UPDATE_FAILED_EVT +// 2. Posted event is invalid +// +TEST_F(SimpleRemoveTransactionTest, processRemoveFailedHandler) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT) + ); + + // Run processRemoveFailedHandler. + EXPECT_NO_THROW(name_remove->processRemoveFailedHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus()); + + // Verify that the model has ended. (Remember, the transaction failed NOT + // the model. The model should have ended normally.) + CHECK_CONTEXT(name_remove, StateModel::END_ST, StateModel::END_EVT); + + // Create and prep transaction, poised to run the handler but with an + // invalid event. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + StateModel::NOP_EVT) + ); + + // Running the handler should throw. + EXPECT_THROW(name_remove->processRemoveFailedHandler(), + SimpleRemoveTransactionError); +} + +// Tests the processRemoveFailedHandler functionality. +// It verifies behavior for posted event of NO_MORE_SERVERS_EVT. +TEST_F(SimpleRemoveTransactionTest, processRemoveFailedHandler_NoMoreServers) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::NO_MORE_SERVERS_EVT) + ); + + // Run processRemoveFailedHandler. + EXPECT_NO_THROW(name_remove->processRemoveFailedHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus()); + + // Verify that the model has ended. (Remember, the transaction failed NOT + // the model. The model should have ended normally.) + CHECK_CONTEXT(name_remove, StateModel::END_ST, StateModel::END_EVT); +} + +// Tests the processRemoveFailedHandler functionality. +// It verifies behavior for posted event of SERVER_IO_ERROR_EVT. +TEST_F(SimpleRemoveTransactionTest, processRemoveFailedHandler_ServerIOError) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::SERVER_IO_ERROR_EVT) + ); + + // Run processRemoveFailedHandler. + EXPECT_NO_THROW(name_remove->processRemoveFailedHandler()); + + // Verify that a server was selected. + EXPECT_EQ(dhcp_ddns::ST_FAILED, name_remove->getNcrStatus()); + + // Verify that the model has ended. (Remember, the transaction failed NOT + // the model. The model should have ended normally.) + CHECK_CONTEXT(name_remove, StateModel::END_ST, StateModel::END_EVT); +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The send update request fails due to an unexpected exception. +// +TEST_F(SimpleRemoveTransactionTest, removingFwdRRsHandler_SendUpdateException) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_FWD_RRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, FORWARD_CHG) + ); + + name_remove->simulate_send_exception_ = true; + + // Run removingFwdRRsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +// Tests removingRevPtrHandler with the following scenario: +// +// The request includes only a reverse change. +// Initial posted event is SERVER_SELECTED_EVT. +// The send update request fails due to an unexpected exception. +// +TEST_F(SimpleRemoveTransactionTest, removingRevPtrsHandler_SendUpdateException) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, REVERSE_CHG) + ); + + name_remove->simulate_send_exception_ = true; + + // Run removingRevPtrsHandler to construct and send the request. + EXPECT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +// Tests removingFwdRRsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The request build fails due to an unexpected exception. +// +TEST_F(SimpleRemoveTransactionTest, removingFwdRRsHandler_BuildRequestException) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_FWD_RRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, FORWARD_CHG) + ); + + // Set the one-shot exception simulation flag. + name_remove->simulate_build_request_exception_ = true; + + // Run removingFwdRRsHandler to construct and send the request. + // This should fail with a build request throw which should be caught + // in the state handler. + ASSERT_NO_THROW(name_remove->removingFwdRRsHandler()); + + // Verify we did not attempt to send anything. + EXPECT_EQ(0, name_remove->getUpdateAttempts()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +// Tests removingRevPTRsHandler with the following scenario: +// +// The request includes only a forward change. +// Initial posted event is SERVER_SELECTED_EVT. +// The request build fails due to an unexpected exception. +// +TEST_F(SimpleRemoveTransactionTest, removingRevPTRsHandler_BuildRequestException) { + SimpleRemoveStubPtr name_remove; + // Create and prep a transaction, poised to run the handler. + ASSERT_NO_THROW( + name_remove = prepHandlerTest(SimpleRemoveTransaction::REMOVING_REV_PTRS_ST, + NameChangeTransaction::SERVER_SELECTED_EVT, FORWARD_CHG) + ); + + // Set the one-shot exception simulation flag. + name_remove->simulate_build_request_exception_ = true; + + // Run removingRevPtrsHandler to construct and send the request. + // This should fail with a build request throw which should be caught + // in the state handler. + ASSERT_NO_THROW(name_remove->removingRevPtrsHandler()); + + // Verify we did not attempt to send anything. + EXPECT_EQ(0, name_remove->getUpdateAttempts()); + + // Completion flags should be false. + EXPECT_FALSE(name_remove->getForwardChangeCompleted()); + EXPECT_FALSE(name_remove->getReverseChangeCompleted()); + + // Since IO exceptions should be gracefully handled, any that occur + // are unanticipated, and deemed unrecoverable, so the transaction should + // be transitioned to failure. + CHECK_CONTEXT(name_remove, NameChangeTransaction::PROCESS_TRANS_FAILED_ST, + NameChangeTransaction::UPDATE_FAILED_EVT); +} + +} diff --git a/src/bin/d2/tests/test_callout_libraries.h.in b/src/bin/d2/tests/test_callout_libraries.h.in new file mode 100644 index 0000000..a88d131 --- /dev/null +++ b/src/bin/d2/tests/test_callout_libraries.h.in @@ -0,0 +1,24 @@ +// 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_TEST_CALLOUT_LIBRARIES_H +#define D2_TEST_CALLOUT_LIBRARIES_H + +#include <config.h> + +namespace { + +// Names of the libraries used in these tests. These libraries are built using +// libtool, so we need to look in the hidden ".libs" directory to locate the +// .so file. Note that we access the .so file - libtool creates this as a +// like to the real shared library. + +// Basic callout library with context_create and three "standard" callouts. +static const char* CALLOUT_LIBRARY = "@abs_builddir@/.libs/libcallout.so"; + +} // anonymous namespace + +#endif // D2_TEST_CALLOUT_LIBRARIES_H diff --git a/src/bin/d2/tests/test_configured_libraries.h.in b/src/bin/d2/tests/test_configured_libraries.h.in new file mode 100644 index 0000000..2b235ae --- /dev/null +++ b/src/bin/d2/tests/test_configured_libraries.h.in @@ -0,0 +1,26 @@ +// 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_TEST_CONFIGURED_LIBRARIES_H +#define D2_TEST_CONFIGURED_LIBRARIES_H + +#include <config.h> + +namespace { + +// Names of the libraries used in these tests. These libraries are built using +// libtool, so we need to look in the hidden ".libs" directory to locate the +// .so file. Note that we access the .so file - libtool creates this as a +// like to the real shared library. + +// Configured library with d2_srv_configured testing: if there is a toplevel +// user context with an error entry the returned status is DROP with the +// content of the error entry. +static const char* CONFIGURED_LIBRARY = "@abs_builddir@/.libs/libconfigured.so"; + +} // anonymous namespace + +#endif // D2_TEST_CONFIGURED_LIBRARIES_H diff --git a/src/bin/d2/tests/test_data_files_config.h.in b/src/bin/d2/tests/test_data_files_config.h.in new file mode 100644 index 0000000..40122c6 --- /dev/null +++ b/src/bin/d2/tests/test_data_files_config.h.in @@ -0,0 +1,10 @@ +// Copyright (C) 2013-2015 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/. + +/// @brief Path to D2 source dir so tests against the dhcp-ddns.spec file +/// can find it reliably. +#define D2_SRC_DIR "@abs_top_srcdir@/src/bin/d2" +#define D2_TEST_DATA_DIR "@abs_top_srcdir@/src/bin/d2/tests/testdata" diff --git a/src/bin/d2/tests/testdata/d2_cfg_tests.json b/src/bin/d2/tests/testdata/d2_cfg_tests.json new file mode 100644 index 0000000..6cb087f --- /dev/null +++ b/src/bin/d2/tests/testdata/d2_cfg_tests.json @@ -0,0 +1,1577 @@ +# 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/. + + +# File of DHCP-DDNS configuration permutation tests +# Each test entry consists of: +# +# description - text describing the test (optional) +# syntax-error - syntax error the JSON parsing should emit for this test +# defaults to "" = no error +# logic-error - indicates whether a post-JSON parsing logic error should occur +# defaults to false +# data {} - Configuration text to submit for parsing. +# +# The vast majority of the tests in this file are invalid and are expected +# to fail either as a syntax error caught by the JSON parser or a logic error +# caught during element processing. There are some that should succeed and are +# used more or less as sanity checks. + +{ "test-list" : [ +#----- +{ +# This test is a bit of sanity check for the "file of configs" test mechanism, +# as well as validating this as the smallest config which makes writing +# permutations easier. +"description" : "D2 smallest, valid config", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +# Map should be supplied through setDefaults +,{ +"description" : "D2 missing forward-ddns map", +"data" : + { + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +# Map should be supplied through setDefaults +,{ +"description" : "D2 missing reverse-ddns map", +"data" : + { + "forward-ddns" : {}, + "tsig-keys" : [] + } +} + + +#----- +# Map should be supplied through setDefaults +,{ +"description" : "D2 missing tsig-keys list", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {} + } +} + +#----- +,{ +"description" : "D2 unknown scalar", +"syntax-error" : "<string>:1.3-16: got unexpected keyword \"bogus-scalar\" in DhcpDdns map.", +"data" : + { + "bogus-scalar" : true, + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2 unknown map", +"syntax-error" : "<string>:1.3-13: got unexpected keyword \"bogus-map\" in DhcpDdns map.", +"data" : + { + "bogus-map" : {}, + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2 unknown list", +"syntax-error" : "<string>:1.3-14: got unexpected keyword \"bogus-list\" in DhcpDdns map.", +"data" : + { + "bogus-list" : [], + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- D2Params Test + +#----- D2Params.ip-address +,{ +"description" : "D2Params.ip-address: valid v4", +"data" : + { + "ip-address" : "192.168.0.1", + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2Params.ip-address: valid v6", +"data" : + { + "ip-address" : "2001:db8::1", + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2Params.ip-address invalid value", +"logic-error" : "Failed to convert 'bogus' to address: Failed to convert string to address 'bogus': Invalid argument(<string>:1:39)", +"data" : + { + "ip-address" : "bogus", + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +#----- +} + +#----- D2Params.port +,{ +"description" : "D2Params.port, valid value", +"data" : + { + "port" : 100, + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2Params.port can't be 0", +"syntax-error" : "<string>:1.33: port must be greater than zero but less than 65536", +"data" : + { + "port" : 0, + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2Params.port, non numeric", +"syntax-error" : "<string>:1.33-39: syntax error, unexpected constant string, expecting integer", +"data" : + { + "port" : "bogus", + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- D2Params.dns-server-timeout +,{ +"description" : "D2Params.dns-server-timeout, valid value", +"data" : + { + "dns-server-timeout" : 1000, + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2Params.dns-server-timeout can't be 0", +"syntax-error" : "<string>:1.25: dns-server-timeout must be greater than zero", +"data" : + { + "dns-server-timeout" : 0, + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2Params.dns-server-timeout, non numeric", +"syntax-error" : "<string>:1.25-31: syntax error, unexpected constant string, expecting integer", +"data" : + { + "dns-server-timeout" : "bogus", + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} +#----- + +#----- D2Params.ncr-protocol +,{ +"description" : "D2Params.ncr-protocol, valid UDP", +"data" : + { + "ncr-protocol" : "UDP", + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2Params.ncr-protocol, unsupported TCP", +"logic-error" : "ncr-protocol : TCP is not yet supported (<string>:1:41)", +"data" : + { + "ncr-protocol" : "TCP", + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + + +#----- +,{ +"description" : "D2Params.ncr-protocol, invalid value", +"syntax-error" : "<string>:1.41-47: syntax error, unexpected constant string, expecting UDP or TCP", +"data" : + { + "ncr-protocol" : "bogus", + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + + +#----- D2Params.ncr-format tests + +,{ +"description" : "D2Params.ncr-format, valid JSON", +"data" : + { + "ncr-format" : "JSON", + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2Params.ncr-format, invalid value", +"syntax-error" : "<string>:1.39-45: syntax error, unexpected constant string, expecting JSON", +"data" : + { + "ncr-format" : "bogus", + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- TSIGKey Tests + +#----- +,{ +# This test is a sanity check that valid TSIG entries work. +"description" : "D2.tsig-keys, valid key", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.md5.key", + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, missing key name", +"logic-error" : "missing parameter 'name' (<string>:1:62)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, blank key name", +"syntax-error" : "<string>:1.95: TSIG key name cannot be blank", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "", + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, duplicate key name", +"logic-error" : "Duplicate TSIG key name specified : first.key (<string>:1:185)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "first.key", + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + }, + { + "name" : "first.key", + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- D2.tsig-keys, algorithm tests + +,{ +"description" : "D2.tsig-keys, all valid algorithms", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.md5.key", + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + }, + { + "name" : "d2.sha1.key", + "algorithm" : "HMAC-SHA1", + "secret" : "hRrp29wzUv3uzSNRLlY68w==" + }, + { + "name" : "d2.sha224.key", + "algorithm" : "HMAC-SHA224", + "secret" : "bZEG7Ow8OgAUPfLWV3aAUQ==" + }, + { + "name" : "d2.sha256.key", + "algorithm" : "hmac-sha256", + "secret" : "bjF4hYhTfQ5MX0siagelsw==" + }, + { + "name" : "d2.sha384.key", + "algorithm" : "hmac-sha384", + "secret" : "Gwk53fvy3CmbupoI9TgigA==" + }, + { + "name" : "d2.sha512.key", + "algorithm" : "hmac-sha512", + "secret" : "wP+5FqMnKXCxBWljU/BZAA==" + } + ] + } +} + +#----- D2.tsig-keys, algorithm tests +,{ +"description" : "D2.tsig-keys, missing algorithm", +"logic-error" : "missing parameter 'algorithm' (<string>:1:62)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "first.key", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, blank algorithm", +"syntax-error" : "<string>:1.75: TSIG key algorithm cannot be blank", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "first.key", + "algorithm" : "", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, invalid algorithm", +"logic-error" : "tsig-key : Unknown TSIG Key algorithm: bogus (<string>:1:77)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "first.key", + "algorithm" : "bogus", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- D2.tsig-keys, digest-bits tests +,{ +"description" : "D2.tsig-keys, all valid algorithms", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.md5.key", + "algorithm" : "HMAC-MD5", + "digest-bits" : 80, + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + }, + { + "name" : "d2.sha1.key", + "algorithm" : "HMAC-SHA1", + "digest-bits" : 80, + "secret" : "hRrp29wzUv3uzSNRLlY68w==" + }, + { + "name" : "d2.sha224.key", + "algorithm" : "HMAC-SHA224", + "digest-bits" : 112, + "secret" : "bZEG7Ow8OgAUPfLWV3aAUQ==" + }, + { + "name" : "d2.sha256.key", + "algorithm" : "hmac-sha256", + "digest-bits" : 128, + "secret" : "bjF4hYhTfQ5MX0siagelsw==" + }, + { + "name" : "d2.sha384.key", + "algorithm" : "hmac-sha384", + "digest-bits" : 192, + "secret" : "Gwk53fvy3CmbupoI9TgigA==" + }, + { + "name" : "d2.sha512.key", + "algorithm" : "hmac-sha512", + "digest-bits" : 256, + "secret" : "wP+5FqMnKXCxBWljU/BZAA==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, invalid digest-bits", +"syntax-error" : "<string>:1.104-105: TSIG key digest-bits must either be zero or a positive, multiple of eight", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.md5.key", + "algorithm" : "HMAC-MD5", + "digest-bits" : 84, + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, too small truncated HMAC-MD5", +"logic-error" : "tsig-key: digest-bits too small : (<string>:1:104)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.md5.key", + "algorithm" : "HMAC-MD5", + "digest-bits" : 72, + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, too small truncated HMAC-SHA1", +"logic-error" : "tsig-key: digest-bits too small : (<string>:1:105)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.sha1.key", + "algorithm" : "HMAC-SHA1", + "digest-bits" : 72, + "secret" : "hRrp29wzUv3uzSNRLlY68w==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, too small truncated HMAC-SHA224", +"logic-error" : "tsig-key: digest-bits too small : (<string>:1:107)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.sha224.key", + "algorithm" : "HMAC-SHA224", + "digest-bits" : 104, + "secret" : "bZEG7Ow8OgAUPfLWV3aAUQ==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, too small truncated HMAC-SHA256", +"logic-error" : "tsig-key: digest-bits too small : (<string>:1:107)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.sha256.key", + "algorithm" : "hmac-sha256", + "digest-bits" : 120, + "secret" : "bjF4hYhTfQ5MX0siagelsw==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, too small truncated HMAC-SHA384", +"logic-error" : "tsig-key: digest-bits too small : (<string>:1:107)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.sha384.key", + "algorithm" : "hmac-sha384", + "digest-bits" : 184, + "secret" : "Gwk53fvy3CmbupoI9TgigA==" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, too small truncated HMAC-SHA512", +"logic-error" : "tsig-key: digest-bits too small : (<string>:1:107)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.sha512.key", + "algorithm" : "hmac-sha512", + "digest-bits" : 248, + "secret" : "wP+5FqMnKXCxBWljU/BZAA==" + } + ] + } +} + +#----- D2.tsig-keys, secret tests +,{ +"description" : "D2.tsig-keys, missing secret", +"logic-error" : "missing parameter 'secret' (<string>:1:62)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "first.key", + "algorithm" : "HMAC-MD5" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, blank secret", +"syntax-error" : "<string>:1.118: TSIG key secret cannot be blank", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "first.key", + "algorithm" : "HMAC-MD5", + "secret" : "" + } + ] + } +} + +#----- +,{ +"description" : "D2.tsig-keys, invalid secret", +"logic-error" : "Cannot make D2TsigKey: Incomplete input for base64: bogus (<string>:1:62)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "first.key", + "algorithm" : "HMAC-MD5", + "secret" : "bogus" + } + ] + } +} + +#----- D2.forward-ddns tests +,{ +"description" : "D2.forward-ddns, valid, empty ddns-domains", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : [] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#------ +,{ +"description" : "D2.forward-ddns, unknown parameter", +"syntax-error" : "<string>:1.21-27: got unexpected keyword \"bogus\" in forward-ddns map.", +"data" : + { + "forward-ddns" : + { + "bogus" : true, + "ddns-domains" : [] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#------ +,{ +"description" : "D2.forward-ddns, one valid, domain", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "key-name" : "d2.md5.key", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.1" + } + ] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.md5.key", + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#------ +,{ +"description" : "D2.forward-ddns, duplicate domain", +"logic-error" : "Duplicate domain specified:four.example.com. (<string>:1:184)", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.1" + } + ] + }, + { + "name" : "four.example.com.", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.2" + } + ] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- D2.forward-ddns.dhcp-ddns tests +,{ +"description" : "D2.forward-ddns.dhcp-ddns, unknown parameter", +"syntax-error" : "<string>:1.41-47: got unexpected keyword \"bogus\" in ddns-domains map.", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "bogus" : true + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- D2.forward-ddns.dhcp-ddns.name tests +,{ +"description" : "D2.forward-ddns.dhcp-ddns, empty domain", +"syntax-error" : "<string>:1.42: syntax error, unexpected }", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.forward-ddns.dhcp-ddns, blank name", +"syntax-error" : "<string>:1.47: Ddns domain name cannot be blank", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "" + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#------ "D2.forward-ddns.dhcp-ddns, key-name tests +,{ +"description" : "D2.forward-ddns, no matching key name", +"logic-error" : "DdnsDomain : specifies an undefined key: no.such.key (<string>:1:104)", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "key-name" : "no.such.key", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.1" + } + ] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.md5.key", + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- D2.forward-ddns.dhcp-ddns.dns-servers tests +,{ +"description" : "D2.forward-ddns.dhcp-ddns.dns-servers, no servers", +"syntax-error" : "<string>:1.59: syntax error, unexpected ], expecting {", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "dns-servers" : [] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- D2.forward-ddns.dhcp-ddns.dns-servers tests +,{ +"description" : "D2.forward-ddns.dhcp-ddns.dns-servers, unknown parameter", +"syntax-error" : "<string>:1.60-66: got unexpected keyword \"bogus\" in dns-servers map.", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "dns-servers" : + [ + { + "bogus" : true + } + ] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.forward-ddns.dhcp-ddns.dns-servers.hostname unsupported", +"syntax-error" : "<string>:1.70: hostname is not yet supported", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "dns-servers" : + [ + { + "hostname" : "myhost.com" + } + ] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.forward-ddns.dhcp-ddns.dns-servers.ip-address v4 address ", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.1" + } + ] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.forward-ddns.dhcp-ddns.dns-servers.ip-address v6 address ", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "dns-servers" : + [ + { + "ip-address" : "2001:db8::1" + } + ] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.forward-ddns.dhcp-ddns.dns-servers.ip-address invalid address ", +"logic-error" : "Dns Server : invalid IP address : bogus (<string>:1:74)", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "dns-servers" : + [ + { + "ip-address" : "bogus" + } + ] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + + +#----- +,{ +"description" : "D2.forward-ddns.dhcp-ddns.dns-servers.port valid value ", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "dns-servers" : + [ + { + "ip-address" : "2001:db8::1", + "port" : 77 + } + ] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.forward-ddns.dhcp-ddns.dns-servers.port cannot be 0 ", +"syntax-error" : "<string>:1.97: port must be greater than zero but less than 65536", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "dns-servers" : + [ + { + "ip-address" : "2001:db8::1", + "port" : 0 + } + ] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.forward-ddns.dhcp-ddns.dns-servers.key-name, no matching key name", +"logic-error" : "Dns Server : specifies an undefined key: no.such.key (<string>:1:100)", +"data" : + { + "forward-ddns" : + { + "ddns-domains" : + [ + { + "name" : "four.example.com.", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.1", + "key-name" : "no.such.key" + } + ] + } + ] + }, + "reverse-ddns" : {}, + "tsig-keys" : + [ + { + "name" : "d2.md5.key", + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- D2.reverse-ddns tests +,{ +"description" : "D2.reverse-ddns, valid, empty ddns-domains", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : [] + }, + "tsig-keys" : [] + } +} + +#------ +,{ +"description" : "D2.reverse-ddns, unknown parameter", +"syntax-error" : "<string>:1.43-49: got unexpected keyword \"bogus\" in reverse-ddns map.", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "bogus" : true, + "ddns-domains" : [] + }, + "tsig-keys" : [] + } +} + +#------ +,{ +"description" : "D2.reverse-ddns, one valid, domain", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addra.arpa.", + "key-name" : "d2.md5.key", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.1" + } + ] + } + ] + }, + "tsig-keys" : + [ + { + "name" : "d2.md5.key", + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#------ +,{ +"description" : "D2.reverse-ddns, duplicate domain", +"logic-error" : "Duplicate domain specified:2.0.192.in-addra.arpa. (<string>:1:211)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addra.arpa.", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.1" + } + ] + }, + { + "name" : "2.0.192.in-addra.arpa.", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.2" + } + ] + } + ] + }, + "tsig-keys" : [] + } +} + +#----- D2.reverse-ddns.dhcp-ddns tests +,{ +"description" : "D2.reverse-ddns.dhcp-ddns, unknown parameter", +"syntax-error" : "<string>:1.63-69: got unexpected keyword \"bogus\" in ddns-domains map.", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "bogus" : true + } + ] + }, + "tsig-keys" : [] + } +} + +#----- D2.reverse-ddns.dhcp-ddns.name tests +,{ +"description" : "D2.reverse-ddns.dhcp-ddns, no name", +"syntax-error" : "<string>:1.64: syntax error, unexpected }", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + } + ] + }, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.reverse-ddns.dhcp-ddns, blank name", +"syntax-error" : "<string>:1.69: Ddns domain name cannot be blank", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "" + } + ] + }, + "tsig-keys" : [] + } +} + +#------ "D2.reverse-ddns.dhcp-ddns, key-name tests +,{ +"description" : "D2.reverse-ddns, no matching key name", +"logic-error" : "DdnsDomain : specifies an undefined key: no.such.key (<string>:1:126)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addr.arpa.", + "key-name" : "no.such.key", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.1" + } + ] + } + ] + }, + "tsig-keys" : + [ + { + "name" : "d2.md5.key", + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +#----- D2.reverse-ddns.dhcp-ddns.dns-servers tests +,{ +"description" : "D2.reverse-ddns.dhcp-ddns.dns-servers, no servers", +"syntax-error" : "<string>:1.81: syntax error, unexpected ], expecting {", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addr.arpa.", + "dns-servers" : [] + } + ] + }, + "tsig-keys" : [] + } +} + +#----- D2.reverse-ddns.dhcp-ddns.dns-servers tests +,{ +"description" : "D2.reverse-ddns.dhcp-ddns.dns-servers, unknown parameter", +"syntax-error" : "<string>:1.82-88: got unexpected keyword \"bogus\" in dns-servers map.", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addr.arpa.", + "dns-servers" : + [ + { + "bogus" : true + } + ] + } + ] + }, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.reverse-ddns.dhcp-ddns.dns-servers.hostname unsupported", +"syntax-error" : "<string>:1.92: hostname is not yet supported", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addr.arpa.", + "dns-servers" : + [ + { + "hostname" : "myhost.com" + } + ] + } + ] + }, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.reverse-ddns.dhcp-ddns.dns-servers.ip-address v4 address ", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addr.arpa.", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.1" + } + ] + } + ] + }, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.reverse-ddns.dhcp-ddns.dns-servers.ip-address v6 address ", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addr.arpa.", + "dns-servers" : + [ + { + "ip-address" : "2001:db8::1" + } + ] + } + ] + }, + "tsig-keys" : [] + } +} +#----- +,{ +"description" : "D2.reverse-ddns.dhcp-ddns.dns-servers.ip-address invalid value", +"logic-error" : "Dns Server : invalid IP address : bogus (<string>:1:96)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addr.arpa.", + "dns-servers" : + [ + { + "ip-address" : "bogus" + } + ] + } + ] + }, + "tsig-keys" : [] + } +} + + +#----- +,{ +"description" : "D2.reverse-ddns.dhcp-ddns.dns-servers.port valid value ", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addr.arpa.", + "dns-servers" : + [ + { + "ip-address" : "2001:db8::1", + "port" : 77 + } + ] + } + ] + }, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.reverse-ddns.dhcp-ddns.dns-servers.port cannot be 0 ", +"syntax-error" : "<string>:1.119: port must be greater than zero but less than 65536", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addr.arpa.", + "dns-servers" : + [ + { + "ip-address" : "2001:db8::1", + "port" : 0 + } + ] + } + ] + }, + "tsig-keys" : [] + } +} + +#----- +,{ +"description" : "D2.reverse-ddns.dhcp-ddns.dns-servers.key-name, no matching key name", +"logic-error" : "Dns Server : specifies an undefined key: no.such.key (<string>:1:122)", +"data" : + { + "forward-ddns" : {}, + "reverse-ddns" : + { + "ddns-domains" : + [ + { + "name" : "2.0.192.in-addr.arpa.", + "dns-servers" : + [ + { + "ip-address" : "172.16.1.1", + "key-name" : "no.such.key" + } + ] + } + ] + }, + "tsig-keys" : + [ + { + "name" : "d2.md5.key", + "algorithm" : "HMAC-MD5", + "secret" : "LSWXnfkKZjdPJI5QxlpnfQ==" + } + ] + } +} + +# ----- End of Tests +]} diff --git a/src/bin/d2/tests/testdata/get_config.json b/src/bin/d2/tests/testdata/get_config.json new file mode 100644 index 0000000..d9613a3 --- /dev/null +++ b/src/bin/d2/tests/testdata/get_config.json @@ -0,0 +1,106 @@ +{ + "DhcpDdns": { + "control-socket": { + "socket-name": "/tmp/kea-ddns-ctrl-socket", + "socket-type": "unix" + }, + "dns-server-timeout": 1000, + "forward-ddns": { + "ddns-domains": [ + { + "dns-servers": [ + { + "hostname": "", + "ip-address": "172.16.1.1", + "port": 53 + } + ], + "key-name": "d2.md5.key", + "name": "four.example.com.", + "user-context": { + "comment": "DdnsDomain example" + } + }, + { + "dns-servers": [ + { + "hostname": "", + "ip-address": "2001:db8:1::10", + "port": 7802 + } + ], + "name": "six.example.com." + } + ] + }, + "hooks-libraries": [ + { + "library": "/opt/local/ddns-server-commands.so", + "parameters": { + "param1": "foo" + } + } + ], + "ip-address": "127.0.0.1", + "loggers": [ + { + "debuglevel": 0, + "name": "kea-dhcp-ddns", + "output_options": [ + { + "flush": true, + "output": "stdout", + "pattern": "%d [%c/%i] %m\n" + } + ], + "severity": "INFO" + } + ], + "ncr-format": "JSON", + "ncr-protocol": "UDP", + "port": 53001, + "reverse-ddns": { + "ddns-domains": [ + { + "dns-servers": [ + { + "hostname": "", + "ip-address": "172.16.1.1", + "port": 53001 + }, + { + "hostname": "", + "ip-address": "192.168.2.10", + "port": 53 + } + ], + "key-name": "d2.sha1.key", + "name": "2.0.192.in-addr.arpa." + } + ] + }, + "tsig-keys": [ + { + "algorithm": "HMAC-MD5", + "digest-bits": 0, + "name": "d2.md5.key", + "secret": "LSWXnfkKZjdPJI5QxlpnfQ==" + }, + { + "algorithm": "HMAC-SHA1", + "digest-bits": 0, + "name": "d2.sha1.key", + "secret": "hRrp29wzUv3uzSNRLlY68w==" + }, + { + "algorithm": "HMAC-SHA512", + "digest-bits": 256, + "name": "d2.sha512.key", + "secret": "/4wklkm04jeH4anx2MKGJLcya+ZLHldL5d6mK+4q6UXQP7KJ9mS2QG29hh0SJR4LA0ikxNJTUMvir42gLx6fGQ==" + } + ], + "user-context": { + "version": 1 + } + } +} |