Browse Source

[1959] Merge branch 'master' into trac1959

Merge needed to get the StatsMgr.h and recent changes to DHCP Lib.

Conflicts:
	tests/tools/perfdhcp/Makefile.am
	tests/tools/perfdhcp/tests/Makefile.am
        src/lib/dhcp/iface_mgr.cc
Marcin Siodelski 12 years ago
parent
commit
e34ad6d48b
100 changed files with 3962 additions and 3157 deletions
  1. 1 0
      .gitignore
  2. 42 1
      ChangeLog
  3. 7 1
      Makefile.am
  4. 160 97
      configure.ac
  5. 1 1
      dns++.pc.in
  6. 2 1
      doc/Doxyfile
  7. 5 3
      doc/devel/02-dhcp.dox
  8. 388 330
      doc/guide/bind10-guide.html
  9. 558 522
      doc/guide/bind10-guide.txt
  10. 155 140
      doc/guide/bind10-guide.xml
  11. 61 11
      doc/guide/bind10-messages.html
  12. 98 18
      doc/guide/bind10-messages.xml
  13. 1 1
      src/bin/Makefile.am
  14. 14 13
      src/bin/auth/Makefile.am
  15. 0 4
      src/bin/auth/auth.spec.pre.in
  16. 9 90
      src/bin/auth/auth_config.cc
  17. 52 146
      src/bin/auth/auth_srv.cc
  18. 29 103
      src/bin/auth/auth_srv.h
  19. 1 6
      src/bin/auth/b10-auth.8
  20. 0 13
      src/bin/auth/b10-auth.xml
  21. 14 14
      src/bin/auth/benchmarks/Makefile.am
  22. 7 31
      src/bin/auth/benchmarks/query_bench.cc
  23. 30 137
      src/bin/auth/command.cc
  24. 223 0
      src/bin/auth/datasrc_configurator.h
  25. 12 8
      src/bin/auth/main.cc
  26. 27 27
      src/bin/auth/query.cc
  27. 9 11
      src/bin/auth/query.h
  28. 22 14
      src/bin/auth/tests/Makefile.am
  29. 284 216
      src/bin/auth/tests/auth_srv_unittest.cc
  30. 78 185
      src/bin/auth/tests/command_unittest.cc
  31. 5 383
      src/bin/auth/tests/config_unittest.cc
  32. 298 0
      src/bin/auth/tests/datasrc_configurator_unittest.cc
  33. 122 86
      src/bin/auth/tests/query_unittest.cc
  34. 8 0
      src/bin/auth/tests/testdata/.gitignore
  35. 1 0
      src/bin/auth/tests/testdata/Makefile.am
  36. 5 0
      src/bin/auth/tests/testdata/spec.spec
  37. 1 8
      src/bin/bind10/bind10.8
  38. 0 11
      src/bin/bind10/bind10.xml
  39. 2 7
      src/bin/bind10/bind10_src.py.in
  40. 0 2
      src/bin/bind10/tests/bind10_test.py.in
  41. 13 4
      src/bin/bindctl/bindctl.1
  42. 2 1
      src/bin/cfgmgr/Makefile.am
  43. 2 0
      src/bin/cfgmgr/plugins/.gitignore
  44. 6 3
      src/bin/cfgmgr/plugins/Makefile.am
  45. 74 0
      src/bin/cfgmgr/plugins/datasrc.spec.pre.in
  46. 80 0
      src/bin/cfgmgr/plugins/datasrc_config_plugin.py
  47. 1 1
      src/bin/cfgmgr/plugins/tests/Makefile.am
  48. 159 0
      src/bin/cfgmgr/plugins/tests/datasrc_test.py
  49. 9 0
      src/bin/cmdctl/b10-cmdctl.8
  50. 17 21
      src/bin/ddns/tests/ddns_test.py
  51. 6 6
      src/bin/dhcp4/Makefile.am
  52. 4 4
      src/bin/dhcp4/ctrl_dhcp4_srv.cc
  53. 12 7
      src/bin/dhcp4/dhcp4_srv.cc
  54. 36 15
      src/bin/dhcp4/main.cc
  55. 7 7
      src/bin/dhcp4/tests/Makefile.am
  56. 3 3
      src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
  57. 40 5
      src/bin/dhcp4/tests/dhcp4_test.py
  58. 7 4
      src/bin/dhcp6/Makefile.am
  59. 159 0
      src/bin/dhcp6/ctrl_dhcp6_srv.cc
  60. 123 0
      src/bin/dhcp6/ctrl_dhcp6_srv.h
  61. 14 2
      src/bin/dhcp6/dhcp6.spec
  62. 24 16
      src/bin/dhcp6/dhcp6_srv.cc
  63. 3 1
      src/bin/dhcp6/dhcp6_srv.h
  64. 56 49
      src/bin/dhcp6/main.cc
  65. 8 5
      src/bin/dhcp6/tests/Makefile.am
  66. 85 0
      src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
  67. 39 5
      src/bin/dhcp6/tests/dhcp6_test.py
  68. 3 3
      src/bin/host/Makefile.am
  69. 0 4
      src/bin/loadzone/Makefile.am
  70. 2 4
      src/bin/msgq/b10-msgq.8
  71. 14 14
      src/bin/resolver/Makefile.am
  72. 9 0
      src/bin/resolver/b10-resolver.8
  73. 16 16
      src/bin/resolver/tests/Makefile.am
  74. 0 2
      src/bin/showtech/.gitignore
  75. 2 2
      src/bin/sockcreator/Makefile.am
  76. 1 1
      src/bin/sockcreator/tests/Makefile.am
  77. 2 0
      src/bin/sysinfo/.gitignore
  78. 5 5
      src/bin/showtech/Makefile.am
  79. 9 9
      src/bin/showtech/b10-showtech.1
  80. 6 6
      src/bin/showtech/b10-showtech.xml
  81. 39 30
      src/bin/showtech/showtech.py.in
  82. 7 143
      src/bin/xfrin/tests/xfrin_test.py
  83. 1 1
      src/bin/xfrin/xfrin.py.in
  84. 3 4
      src/bin/xfrin/xfrin_messages.mes
  85. 10 1
      src/bin/zonemgr/b10-zonemgr.8
  86. 13 13
      src/lib/acl/Makefile.am
  87. 7 7
      src/lib/acl/tests/Makefile.am
  88. 17 17
      src/lib/asiodns/Makefile.am
  89. 6 6
      src/lib/asiodns/tests/Makefile.am
  90. 23 23
      src/lib/asiolink/Makefile.am
  91. 3 3
      src/lib/asiolink/tests/Makefile.am
  92. 2 2
      src/lib/bench/Makefile.am
  93. 1 1
      src/lib/bench/example/Makefile.am
  94. 4 4
      src/lib/bench/tests/Makefile.am
  95. 12 12
      src/lib/cache/Makefile.am
  96. 7 7
      src/lib/cache/tests/Makefile.am
  97. 5 5
      src/lib/cc/Makefile.am
  98. 3 3
      src/lib/cc/tests/Makefile.am
  99. 9 9
      src/lib/config/Makefile.am
  100. 0 0
      src/lib/config/module_spec.cc

+ 1 - 0
.gitignore

@@ -35,3 +35,4 @@ TAGS
 /coverage-cpp-html
 /dns++.pc
 /report.info
+/logger_lockfile

+ 42 - 1
ChangeLog

@@ -1,3 +1,44 @@
+459.	[func]		tomek
+	b10-dhcp6: DHCPv6 server component is now integrated into
+	BIND10 framework. It can be started from BIND10 (using bindctl)
+	and can receive commands. The only supported command for now
+	is 'Dhcp6 shutdown'.
+	b10-dhcp4: Command line-switch '-s' to disable msgq was added.
+	b10-dhcp6: Command line-switch '-s' to disable msgq was added.
+	(Trac #1708, git e0d7c52a71414f4de1361b09d3c70431c96daa3f)
+
+458.	[build]*	jinmei
+	BIND 10 now relies on Boost offset_ptr, which caused some new
+	portability issues.  Such issues are detected at ./configure time.
+	If ./configure stops due to this, try the following workaround:
+	- If it's about the use of mutable for a reference with clang++,
+	  upgrade Boost version to 1.44 or higher, or try a different
+	  compiler (e.g. g++ generally seems to be free from this issue)
+	- If it's about the use of "variadic templates", specify
+	  --without-werror so the warning won't be promoted to an error.
+	  Specifying BOOST_NO_USER_CONFIG in CXXFLAGS may also work
+	  (which would be the case if Boost is installed via pkgsrc)
+	(Trac #2147, git 30061d1139aad8716e97d6b620c259752fd0a3cd)
+
+457.	[build]*	muks
+	BIND 10 library names now have a "b10-" prefix. This is to avoid
+	clashes with other similarly named libraries on the system.
+	(Trac #2071, git ac20a00c28069804edc0a36050995df52f601efb)
+
+456.	[build]*	muks
+	BIND 10 now compiles against log4cplus-1.1.0 (RC releases)
+	also.  Note: some older versions of log4cplus don't work any more;
+	known oldest workable version is 1.0.4.  Thanks to John Lumby for
+	sending a patch.
+	(Trac #2169, git 7d7e5269d57451191c0aef1b127d292d3615fe2c)
+
+455.	[func]*		vorner
+	The server now uses newer API for data sources. This would be an
+	internal change, however, the data sources are now configured
+	differently. Please, migrate your configuration to the top-level
+	"data_sources" module.
+	(Trac #1976, git 0d4685b3e7603585afde1b587cbfefdfaf6a1bb3)
+
 454.	[bug]		jelte
 	b10-cfgmgr now loads its configuration check plugins directly from
 	the plugin search path, as opposed to importing them from the
@@ -13,7 +54,7 @@
 	(Trac #1986, git bd6b0a5ed3481f78fb4e5cb0b18c7b6e5920f9f8)
 
 452.	[func]*		muks
-	b10-showtech: An initial implementation of the b10-showtech tool
+	isc-sysinfo: An initial implementation of the isc-sysinfo tool
 	is now available. It gathers and outputs system information which
 	can be used by future tech support staff.
 	(Trac #2062, git 144e80212746f8d55e6a59edcf689fec9f32ae95)

+ 7 - 1
Makefile.am

@@ -2,7 +2,7 @@ ACLOCAL_AMFLAGS = -I m4macros ${ACLOCAL_FLAGS}
 # ^^^^^^^^ This has to be the first line and cannot come later in this
 # Makefile.am due to some bork in some versions of autotools.
 
-SUBDIRS = compatcheck doc src tests
+SUBDIRS = compatcheck doc . src tests
 USE_LCOV=@USE_LCOV@
 LCOV=@LCOV@
 GENHTML=@GENHTML@
@@ -427,3 +427,9 @@ pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = dns++.pc
 
 CLEANFILES = $(abs_top_builddir)/logger_lockfile
+
+if HAVE_GTEST_SOURCE
+noinst_LIBRARIES = libgtest.a
+libgtest_a_CXXFLAGS = $(GTEST_INCLUDES) $(AM_CXXFLAGS)
+nodist_libgtest_a_SOURCES = $(GTEST_SOURCE)/src/gtest-all.cc
+endif

+ 160 - 97
configure.ac

@@ -454,20 +454,30 @@ AC_SUBST(PYCOVERAGE)
 AC_SUBST(PYCOVERAGE_RUN)
 AC_SUBST(USE_PYCOVERAGE)
 
+enable_gtest="no"
+GTEST_INCLUDES=
+
+AC_ARG_WITH([gtest-source],
+            [AS_HELP_STRING([--with-gtest-source=PATH],
+                            [location of the Googletest source, defaults to /usr/src/gtest])],
+            [enable_gtest="yes" ; GTEST_SOURCE="$withval"],
+            [GTEST_SOURCE="/usr/src/gtest"])
+
+AC_ARG_WITH([gtest],
+	    [AS_HELP_STRING([--with-gtest=PATH],
+			    [specify a path to gtest header files (PATH/include) and library (PATH/lib)])],
+	[gtest_path="$withval"; enable_gtest="yes"], [gtest_path="no"])
+
 AC_ARG_WITH(lcov,
 [  --with-lcov[=PROGRAM]         enable gtest and coverage target using the specified lcov], lcov="$withval", lcov="no")
 
-AC_ARG_WITH(gtest,
-[  --with-gtest=PATH       specify a path to gtest header files (PATH/include) and library (PATH/lib)],
-    gtest_path="$withval", gtest_path="no")
-
 USE_LCOV="no"
 if test "$lcov" != "no"; then
 	# force gtest if not set
-	if test "$gtest_path" = "no"; then
+	if test "$enable_gtest" = "no"; then
 #		AC_MSG_ERROR("lcov needs gtest for test coverage report")
 		AC_MSG_NOTICE([gtest support is now enabled, because used by coverage tests])
-		gtest_path="yes"
+		enable_gtest="yes"
 	fi
 	if test "$lcov" != "yes"; then
 		LCOV=$lcov
@@ -799,6 +809,17 @@ AC_TRY_COMPILE([
  CPPFLAGS_BOOST_THREADCONF="-DBOOST_DISABLE_THREADS=1"],
 [AC_MSG_RESULT(yes)])
 
+# Boost offset_ptr is required in one library (not optional right now), and
+# it's known it doesn't compile on some platforms, depending on boost version,
+# its local configuration, and compiler.
+AC_MSG_CHECKING([Boost offset_ptr compiles])
+AC_TRY_COMPILE([
+#include <boost/interprocess/offset_ptr.hpp>
+],,
+[AC_MSG_RESULT(yes)],
+[AC_MSG_RESULT(no)
+ AC_MSG_ERROR([Failed to compile a required header file.  Try upgrading Boost to 1.44 or higher (when using clang++) or specifying --without-werror.  See the ChangeLog entry for Trac no. 2147 for more details.])])
+
 CPPFLAGS="$CPPFLAGS_SAVES $CPPFLAGS_BOOST_THREADCONF"
 AC_SUBST(BOOST_INCLUDES)
 
@@ -813,98 +834,138 @@ AC_SUBST(MULTITHREADING_FLAG)
 #
 # Check availability of gtest, which will be used for unit tests.
 #
-if test "$gtest_path" != "no"
-then
-	DISTCHECK_GTEST_CONFIGURE_FLAG="--with-gtest=\"$gtest_path\""
-	if test "$gtest_path" != "yes"; then
-		GTEST_PATHS=$gtest_path
-		if test -x "${gtest_path}/bin/gtest-config" ; then
-			GTEST_CONFIG="${gtest_path}/bin/gtest-config"
-		fi
-	else
-		AC_PATH_PROG([GTEST_CONFIG], [gtest-config])
-	fi
-	if test -x "${GTEST_CONFIG}" ; then :
-		# using cppflags instead of cxxflags
-		GTEST_INCLUDES=`${GTEST_CONFIG} --cppflags`
-		GTEST_LDFLAGS=`${GTEST_CONFIG} --ldflags`
-		GTEST_LDADD=`${GTEST_CONFIG} --libs`
-		GTEST_FOUND="true"
-	else
-		AC_MSG_WARN([Unable to locate Google Test gtest-config.])
-		if test -z "${GTEST_PATHS}" ; then
-			GTEST_PATHS="/usr /usr/local"
-		fi
-		GTEST_FOUND="false"
-	fi
-	if test "${GTEST_FOUND}" != "true"; then
-		GTEST_FOUND="false"
-		for dir in $GTEST_PATHS; do
-			if test -f "$dir/include/gtest/gtest.h"; then
-				GTEST_INCLUDES="-I$dir/include"
-				GTEST_LDFLAGS="-L$dir/lib"
-				GTEST_LDADD="-lgtest"
-				GTEST_FOUND="true"
-				# There is no gtest-config script on this
-				# system, which is supposed to inform us
-				# whether we need pthreads as well (a
-				# gtest compile-time option). So we still
-				# need to test that manually.
-				CPPFLAGS_SAVED="$CPPFLAGS"
-				CPPFLAGS="$CPPFLAGS $GTEST_INCLUDES"
-				LDFLAGS_SAVED="$LDFLAGS"
-				LDFLAGS="$LDFLAGS $GTEST_LDFLAGS"
-				LIBS_SAVED=$LIBS
-				LIBS="$LIBS $GTEST_LDADD"
-				AC_MSG_CHECKING([Checking whether gtest tests need pthreads])
-				# First try to compile without pthreads
-				AC_TRY_LINK([
-					#include <gtest/gtest.h>
-					],[
-						int i = 0;
-						char* c = NULL;
-						::testing::InitGoogleTest(&i, &c);
-						return (0);
-					],
-					[ AC_MSG_RESULT(no) ],
-					[
-						LIBS="$SAVED_LIBS $GTEST_LDADD $PTHREAD_LDFLAGS"
-						# Now try to compile with pthreads
-						AC_TRY_LINK([
-							#include <gtest/gtest.h>
-							],[
-								int i = 0;
-								char* c = NULL;
-								::testing::InitGoogleTest(&i, &c);
-								return (0);
-							],
-							[ AC_MSG_RESULT(yes)
-							  GTEST_LDADD="$GTEST_LDADD $PTHREAD_LDFLAGS"
-							],
-							# Apparently we can't compile it at all
-							[ AC_MSG_ERROR(unable to compile with gtest) ])
-				])
-				CPPFLAGS=$CPPFLAGS_SAVED
-				LDFLAGS=$LDFLAGS_SAVED
-				LIBS=$LIBS_SAVED
-				break
-			fi
-		done
-	fi
-	if test "${GTEST_FOUND}" != "true"; then
-		AC_MSG_ERROR([Cannot find gtest in: $GTEST_PATHS])
-	fi
-else
-	GTEST_INCLUDES=
-	GTEST_LDFLAGS=
-	GTEST_LDADD=
-	DISTCHECK_GTEST_CONFIGURE_FLAG=
+GTEST_LDFLAGS=
+GTEST_LDADD=
+# TODO: set DISTCHECK_GTEST_CONFIGURE_FLAG for --with-gtest too
+DISTCHECK_GTEST_CONFIGURE_FLAG=
+
+if test "x$enable_gtest" = "xyes" ; then
+
+    if test -n "$with_gtest_source" ; then
+
+          if test "x$GTEST_SOURCE" = "xyes" ; then
+
+            AC_MSG_CHECKING([for gtest source])
+            # If not specified, try some common paths.
+            GTEST_SOURCE=
+            for d in /usr/src/gtest /usr/local /usr/pkg /opt /opt/local ; do
+                if test -f $d/src/gtest-all.cc -a $d/src/gtest_main.cc; then
+                    GTEST_SOURCE=$d
+                    AC_MSG_RESULT([$GTEST_SOURCE])
+                    break
+                fi
+            done
+            if test -z $GTEST_SOURCE ; then
+                AC_MSG_ERROR([no gtest source but it was selected])
+            fi
+         else
+            AC_CHECK_FILES([$GTEST_SOURCE/src/gtest-all.cc]
+               [$GTEST_SOURCE/src/gtest_main.cc],
+               [have_gtest_source=yes],
+               [AC_MSG_ERROR([no gtest source at $GTEST_SOURCE])])
+          fi
+          have_gtest_source=yes
+          GTEST_LDFLAGS="\$(top_builddir)/libgtest.a"
+          DISTCHECK_GTEST_CONFIGURE_FLAG="--with-gtest-source=$GTEST_SOURCE"
+          GTEST_INCLUDES="-I$GTEST_SOURCE -I$GTEST_SOURCE/include"
+          # See $GTEST_SOURCE/include/gtest/internal/gtest-port.h
+          # about GTEST_HAS_PTHREAD.
+          case "$host" in
+            *-solaris*|*-linux*|*-hpux*)
+                GTEST_LDADD="$GTEST_LDADD $PTHREAD_LDFLAGS"
+                ;;
+          esac
+        fi
+
+if test "$gtest_path" != "no" ; then
+    if test "$gtest_path" != "yes"; then
+        GTEST_PATHS=$gtest_path
+        if test -x "${gtest_path}/bin/gtest-config" ; then
+            GTEST_CONFIG="${gtest_path}/bin/gtest-config"
+        fi
+    else
+        AC_PATH_PROG([GTEST_CONFIG], [gtest-config])
+    fi
+    if test -x "${GTEST_CONFIG}" ; then :
+        # using cppflags instead of cxxflags
+        GTEST_INCLUDES=`${GTEST_CONFIG} --cppflags`
+        GTEST_LDFLAGS=`${GTEST_CONFIG} --ldflags`
+        GTEST_LDADD=`${GTEST_CONFIG} --libs`
+        GTEST_FOUND="true"
+    else
+        AC_MSG_WARN([Unable to locate Google Test gtest-config.])
+        if test -z "${GTEST_PATHS}" ; then
+            GTEST_PATHS="/usr /usr/local"
+        fi
+        GTEST_FOUND="false"
+    fi
+    if test "${GTEST_FOUND}" != "true"; then
+        GTEST_FOUND="false"
+        for dir in $GTEST_PATHS; do
+            if test -f "$dir/include/gtest/gtest.h"; then
+                GTEST_INCLUDES="-I$dir/include"
+                GTEST_LDFLAGS="-L$dir/lib"
+                GTEST_LDADD="-lgtest"
+                GTEST_FOUND="true"
+                # There is no gtest-config script on this
+                # system, which is supposed to inform us
+                # whether we need pthreads as well (a
+                # gtest compile-time option). So we still
+                # need to test that manually.
+                CPPFLAGS_SAVED="$CPPFLAGS"
+                CPPFLAGS="$CPPFLAGS $GTEST_INCLUDES"
+                LDFLAGS_SAVED="$LDFLAGS"
+                LDFLAGS="$LDFLAGS $GTEST_LDFLAGS"
+                LIBS_SAVED=$LIBS
+                LIBS="$LIBS $GTEST_LDADD"
+                AC_MSG_CHECKING([Checking whether gtest tests need pthreads])
+                # First try to compile without pthreads
+                AC_TRY_LINK([
+                    #include <gtest/gtest.h>
+                    ],[
+                        int i = 0;
+                        char* c = NULL;
+                        ::testing::InitGoogleTest(&i, &c);
+                        return (0);
+                    ],
+                    [ AC_MSG_RESULT(no) ],
+                    [
+                        LIBS="$SAVED_LIBS $GTEST_LDADD $PTHREAD_LDFLAGS"
+                        # Now try to compile with pthreads
+                        AC_TRY_LINK([
+                            #include <gtest/gtest.h>
+                            ],[
+                                int i = 0;
+                                char* c = NULL;
+                                ::testing::InitGoogleTest(&i, &c);
+                                return (0);
+                            ],
+                            [ AC_MSG_RESULT(yes)
+                              GTEST_LDADD="$GTEST_LDADD $PTHREAD_LDFLAGS"
+                            ],
+                            # Apparently we can't compile it at all
+                            [ AC_MSG_ERROR(unable to compile with gtest) ])
+                ])
+                CPPFLAGS=$CPPFLAGS_SAVED
+                LDFLAGS=$LDFLAGS_SAVED
+                LIBS=$LIBS_SAVED
+                break
+            fi
+        done
+    fi
+    if test "${GTEST_FOUND}" != "true"; then
+        AC_MSG_ERROR([Cannot find gtest in: $GTEST_PATHS])
+    fi
+
+  fi
 fi
-AM_CONDITIONAL(HAVE_GTEST, test $gtest_path != "no")
+AM_CONDITIONAL(HAVE_GTEST, test $enable_gtest != "no")
+AM_CONDITIONAL(HAVE_GTEST_SOURCE, test "X$have_gtest_source" = "Xyes")
 AC_SUBST(DISTCHECK_GTEST_CONFIGURE_FLAG)
 AC_SUBST(GTEST_INCLUDES)
 AC_SUBST(GTEST_LDFLAGS)
 AC_SUBST(GTEST_LDADD)
+AC_SUBST(GTEST_SOURCE)
 
 dnl check for pkg-config itself so we don't try the m4 macro without pkg-config
 AC_CHECK_PROG(HAVE_PKG_CONFIG, pkg-config, yes, no)
@@ -1018,6 +1079,7 @@ AC_CONFIG_FILES([Makefile
                  src/bin/msgq/tests/Makefile
                  src/bin/auth/Makefile
                  src/bin/auth/tests/Makefile
+                 src/bin/auth/tests/testdata/Makefile
                  src/bin/auth/benchmarks/Makefile
                  src/bin/ddns/Makefile
                  src/bin/ddns/tests/Makefile
@@ -1027,7 +1089,7 @@ AC_CONFIG_FILES([Makefile
                  src/bin/dhcp4/tests/Makefile
                  src/bin/resolver/Makefile
                  src/bin/resolver/tests/Makefile
-                 src/bin/showtech/Makefile
+                 src/bin/sysinfo/Makefile
                  src/bin/sockcreator/Makefile
                  src/bin/sockcreator/tests/Makefile
                  src/bin/xfrin/Makefile
@@ -1142,6 +1204,7 @@ AC_CONFIG_FILES([Makefile
 AC_OUTPUT([doc/version.ent
            src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/cfgmgr/tests/b10-cfgmgr_test.py
+           src/bin/cfgmgr/plugins/datasrc.spec.pre
            src/bin/cmdctl/cmdctl.py
            src/bin/cmdctl/run_b10-cmdctl.sh
            src/bin/cmdctl/tests/cmdctl_test
@@ -1164,7 +1227,7 @@ AC_OUTPUT([doc/version.ent
            src/bin/zonemgr/zonemgr.spec.pre
            src/bin/zonemgr/tests/zonemgr_test
            src/bin/zonemgr/run_b10-zonemgr.sh
-           src/bin/showtech/showtech.py
+           src/bin/sysinfo/sysinfo.py
            src/bin/stats/stats.py
            src/bin/stats/stats_httpd.py
            src/bin/bind10/bind10_src.py
@@ -1306,12 +1369,12 @@ Features:
   $enable_features
 
 Developer:
-  Google Tests:  $gtest_path
+  Google Tests: $enable_gtest
   Valgrind: $found_valgrind
   C++ Code Coverage: $USE_LCOV
   Python Code Coverage: $USE_PYCOVERAGE
   Logger checks: $enable_logger_checks
-  Generate Manuals:  $enable_man
+  Generate Manuals: $enable_man
 
 END
 

+ 1 - 1
dns++.pc.in

@@ -8,4 +8,4 @@ Description: BIND 10 DNS library
 Version: @PACKAGE_VERSION@
 Requires: botan-1.8
 Cflags: -I${includedir}/@PACKAGE_NAME@
-Libs: -L${libdir} -ldns++ -lcryptolink -lutil -lexceptions -lm
+Libs: -L${libdir} -lb10-dns++ -lb10-cryptolink -lb10-util -lb10-exceptions -lm

+ 2 - 1
doc/Doxyfile

@@ -573,7 +573,8 @@ WARN_LOGFILE           =
 # with spaces.
 
 INPUT                  = ../src/lib/exceptions ../src/lib/cc \
-    ../src/lib/config ../src/lib/cryptolink ../src/lib/dns ../src/lib/datasrc \
+    ../src/lib/config ../src/lib/cryptolink ../src/lib/dns \
+    ../src/lib/datasrc ../src/lib/datasrc/memory \
     ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log \
     ../src/lib/log/compiler ../src/lib/asiolink/ ../src/lib/nsas \
     ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \

+ 5 - 3
doc/devel/02-dhcp.dox

@@ -72,11 +72,13 @@
  * DHCPv6 server component does not support relayed traffic yet, as
  * support for relay decapsulation is not implemented yet.
  *
- * DHCPv6 server component does not listen to BIND10 message queue.
- *
  * DHCPv6 server component does not use BIND10 logging yet.
  *
- * DHCPv6 server component is not integrated with boss yet.
+ * @section dhcpv6Session BIND10 message queue integration
+ *
+ * DHCPv4 server component is now integrated with BIND10 message queue.
+ * It follows the same principle as DHCPv4. See \ref dhcpv4Session for
+ * details.
  *
  * @page libdhcp libdhcp++
  *

File diff suppressed because it is too large
+ 388 - 330
doc/guide/bind10-guide.html


File diff suppressed because it is too large
+ 558 - 522
doc/guide/bind10-guide.txt


+ 155 - 140
doc/guide/bind10-guide.xml

@@ -1485,134 +1485,165 @@ can use various data source backends.
 
     </section>
 
-    <section>
+    <section id='datasrc'>
       <title>Data Source Backends</title>
 
+      <para>
+        Bind 10 has the concept of data sources. A data source is a place
+        where authoritative zone data reside and where they can be served
+        from. This can be a master file, a database or something completely
+        different.
+      </para>
+
+      <para>
+        Once a query arrives, <command>b10-auth</command> goes through a
+        configured list of data sources and finds the one containing a best
+        matching zone. From the equally good ones, the first one is taken.
+        This data source is then used to answer the query.
+      </para>
+
       <note><para>
-        For the development prototype release, <command>b10-auth</command>
-        supports a SQLite3 data source backend and in-memory data source
-        backend.
+        In the development prototype release, <command>b10-auth</command>
+        can serve data from a SQLite3 data source backend and from master
+        files.
         Upcoming versions will be able to use multiple different
         data sources, such as MySQL and Berkeley DB.
       </para></note>
 
-
       <para>
-        By default, the SQLite3 backend uses the data file located at
+        The configuration is located in data_sources/classes. Each item there
+        represents one RR class and a list used to answer queries for that
+        class. The default contains two classes. The CH class contains a static
+        data source &mdash; one that serves things like
+        <quote>AUTHORS.BIND.</quote>. The IN class contains single SQLite3
+        data source with database file located at
         <filename>/usr/local/var/bind10-devel/zone.sqlite3</filename>.
-        (The full path is what was defined at build configure time for
-        <option>--localstatedir</option>.
-        The default is <filename>/usr/local/var/</filename>.)
-	This data file location may be changed by defining the
-	<quote>database_file</quote> configuration.
       </para>
 
-      <section id="in-memory-datasource">
-	<title>In-memory Data Source</title>
-
-	<para>
-<!--	  How to configure it. -->
-	  The following commands to <command>bindctl</command>
-	  provide an example of configuring an in-memory data
-	  source containing the <quote>example.com</quote> zone
-	  with the zone file named <quote>example.com.zone</quote>:
-
-<!--
-	  <screen>&gt; <userinput> config set Auth/datasources/ [{"type": "memory", "zones": [{"origin": "example.com", "file": "example.com.zone"}]}]</userinput></screen>
--->
-
-          <screen>&gt; <userinput>config add Auth/datasources</userinput>
-&gt; <userinput>config set Auth/datasources[0]/type "<option>memory</option>"</userinput>
-&gt; <userinput>config add Auth/datasources[0]/zones</userinput>
-&gt; <userinput>config set Auth/datasources[0]/zones[0]/origin "<option>example.com</option>"</userinput>
-&gt; <userinput>config set Auth/datasources[0]/zones[0]/file "<option>example.com.zone</option>"</userinput>
-&gt; <userinput>config commit</userinput></screen>
-
-	  The authoritative server will begin serving it immediately
-	  after the zone data is loaded from the master text file.
-	</para>
-
-      </section>
-
-      <section id="in-memory-datasource-with-sqlite3-backend">
-	<title>In-memory Data Source with SQLite3 Backend</title>
-
-	<para>
-<!--	  How to configure it. -->
-	  The following commands to <command>bindctl</command>
-	  provide an example of configuring an in-memory data
-	  source containing the <quote>example.org</quote> zone
-	  with a SQLite3 backend file named <quote>example.org.sqlite3</quote>:
-
-<!--
-	  <screen>&gt; <userinput> config set Auth/datasources/ [{"type": "memory", "zones": [{"origin": "example.org", "file": "example.org.sqlite3", "filetype": "sqlite3"}]}]</userinput></screen>
--->
-
-          <screen>&gt; <userinput>config add Auth/datasources</userinput>
-&gt; <userinput>config set Auth/datasources[1]/type "<option>memory</option>"</userinput>
-&gt; <userinput>config add Auth/datasources[1]/zones</userinput>
-&gt; <userinput>config set Auth/datasources[1]/zones[0]/origin "<option>example.org</option>"</userinput>
-&gt; <userinput>config set Auth/datasources[1]/zones[0]/file "<option>example.org.sqlite3</option>"</userinput>
-&gt; <userinput>config set Auth/datasources[1]/zones[0]/filetype "<option>sqlite3</option>"</userinput>
-&gt; <userinput>config commit</userinput></screen>
-
-	  The authoritative server will begin serving it immediately
-	  after the zone data is loaded from the database file.
-	</para>
-
-      </section>
-
-      <section id="in-memory-datasource-loading">
-	<title>Reloading an In-memory Data Source</title>
-
-	<para>
-	  Use the <command>Auth loadzone</command> command in
-	  <command>bindctl</command> to reload a changed master
-	  file into memory; for example:
+      <para>
+        Each data source has several options. The first one is
+        <varname>type</varname>, which specifies the type of data source to
+        use. Valid types include the ones listed below, but bind10 uses
+        dynamically loaded modules for them, so there may be more in your
+        case. This option is mandatory.
+      </para>
 
-	  <screen>&gt; <userinput>Auth loadzone origin="example.com"</userinput>
-</screen>
+      <para>
+        Another option is <varname>params</varname>. This option is type
+        specific; it holds different data depending on the type
+        above. Also, depending on the type, it could be possible to omit it.
+      </para>
 
-	</para>
+      <para>
+        There are two options related to the so-called cache. If you enable
+        cache, zone data from the data source are loaded into memory.
+        Then, when answering a query, <command>b10-auth</command> looks
+        into the memory only instead of the data source, which speeds
+        answering up. The first option is <varname>cache-enable</varname>,
+        a boolean value turning the cache on and off (off is the default).
+        The second one, <varname>cache-zones</varname>, is a list of zone
+        origins to load into in-memory. Remember that zones in the data source
+        not listed here will not be loaded and will not be available at all.
+      </para>
 
-<!--
+      <section id='datasource-types'>
+        <title>Data source types</title>
         <para>
-          The <varname>file</varname> may be an absolute path to the
-          master zone file or it is relative to the directory BIND 10 is
-          started from.
-	</para>
--->
+          As mentioned, the type used by default is <quote>sqlite3</quote>.
+          It has single configuration option inside <varname>params</varname>
+          &mdash; <varname>database_file</varname>, which contains the path
+          to the sqlite3 file containing the data.
+        </para>
 
+        <para>
+          Another type is called <quote>MasterFiles</quote>. This one is
+          slightly special. The data are stored in RFC1034 master files.
+          Because answering directly from them would be impractical,
+          this type mandates the cache to be enabled. Also, the list of
+          zones (<varname>cache-zones</varname>) should be omitted. The
+          <varname>params</varname> is a dictionary mapping from zone
+          origins to the files they reside in.
+        </para>
       </section>
-      <section id="in-memory-datasource-disabling">
-	<title>Disabling In-memory Data Sources</title>
 
+      <section id='datasrc-examples'>
+        <title>Examples</title>
         <para>
-	By default, the memory data source is disabled; it must be
-	configured explicitly.  To disable all the in-memory zones,
-	specify a null list for <varname>Auth/datasources</varname>:
+          As this is one of the more complex configurations of Bind10,
+          we show some examples. They all assume they start with default
+          configuration.
+        </para>
 
-<!-- TODO: this assumes that Auth/datasources is for memory only -->
+        <para>
+          First, let's disable the static data source
+          (<quote>VERSION.BIND</quote> and friends). As it is the only
+          data source in the CH class, we can remove the whole class.
 
-	  <screen>&gt; <userinput>config set Auth/datasources/ []</userinput>
+          <screen>&gt; <userinput>config remove data_sources/classes CH</userinput>
 &gt; <userinput>config commit</userinput></screen>
-	</para>
+        </para>
 
-	<para>
-          The following example stops serving a specific zone:
+        <para>
+          Another one, let's say our default data source contains zones
+          <quote>example.org.</quote> and <quote>example.net.</quote>.
+          We want them to be served from memory to make the answering
+          faster.
 
-	  <screen>&gt; <userinput>config remove Auth/datasources[<option>0</option>]/zones[<option>0</option>]</userinput>
+          <screen>&gt; <userinput>config set data_sources/classes/IN[0]/cache-enable true</userinput>
+&gt; <userinput>config add data_sources/classes/IN[0]/cache-zones example.org.</userinput>
+&gt; <userinput>config add data_sources/classes/IN[0]/cache-zones example.net.</userinput>
 &gt; <userinput>config commit</userinput></screen>
 
-	  (Replace the list number(s) in
-	  <varname>datasources[<replaceable>0</replaceable>]</varname>
-	  and/or <varname>zones[<replaceable>0</replaceable>]</varname>
-	  for the relevant zone as needed.)
+          Now every time the zone in the data source is changed by the
+          operator, Bind10 needs to be told to reload it, by
+          <screen>&gt; <userinput>Auth loadzone example.org</userinput></screen>
+          You don't need to do this when the zone is modified by
+          XfrIn, it does so automatically.
+        </para>
+
+        <para>
+          Now, the last example is when there are master files we want to
+          serve in addition to whatever is inside the sqlite3 database.
 
-	</para>
+          <screen>&gt; <userinput>config add data_sources/classes/IN</userinput>
+&gt; <userinput>config set data_sources/classes/IN[1]/type MasterFiles</userinput>
+&gt; <userinput>config set data_sources/classes/IN[1]/cache-enable true</userinput>
+&gt; <userinput>config set data_sources/classes/IN[1]/params { "example.org": "/path/to/example.org", "example.com": "/path/to/example.com" }</userinput>
+&gt; <userinput>config commit</userinput></screen>
 
+          Initially, a map value has to be set, but this value may be an
+          empty map. After that, key/value pairs can be added with 'config
+          add' and keys can be removed with 'config remove'. The initial
+          value may be an empty map, but it has to be set before zones are
+          added or removed.
+
+          <screen>
+&gt; <userinput>config set data_sources/classes/IN[1]/params {}</userinput>
+&gt; <userinput>config add data_sources/classes/IN[1]/params another.example.org /path/to/another.example.org</userinput>
+&gt; <userinput>config add data_sources/classes/IN[1]/params another.example.com /path/to/another.example.com</userinput>
+&gt; <userinput>config remove data_sources/classes/IN[1]/params another.example.org</userinput>
+          </screen>
+
+          <command>bindctl</command>. To reload a zone, you the same command
+          as above.
+        </para>
       </section>
 
+      <note>
+      <para>
+        There's also <varname>Auth/database_file</varname> configuration
+        variable, pointing to a sqlite3 database file. This is no longer
+        used by <command>b10-auth</command>, but it is left in place for
+        now, since other modules use it. Once <command>b10-xfrin</command>,
+        <command>b10-xfrout</command> and <command>b10-ddns</command>
+        are ported to the new configuration, this will disappear. But for
+        now, make sure that if you use any of these modules, the new
+        and old configuration correspond. The defaults are consistent, so
+        unless you tweaked either the new or the old configuration, you're
+        good.
+      </para>
+      </note>
+
     </section>
 
     <section>
@@ -1854,7 +1885,7 @@ http://bind10.isc.org/wiki/ScalableZoneLoadDesign#a7.2UpdatingaZone
 	The administrator doesn't have to do anything for
 	<command>b10-auth</command> to serve the new version of the
 	zone, except for the configuration such as the one described in
-	<xref linkend="in-memory-datasource-with-sqlite3-backend" />.
+	<xref linkend="datasrc" />.
       </para>
     </section>
 
@@ -1965,11 +1996,11 @@ what is XfroutClient xfr_client??
       notify <command>b10-xfrout</command> so that other secondary
       servers will be notified via the DNS NOTIFY protocol.
       In addition, if <command>b10-auth</command> serves the updated
-      zone from its in-memory cache (as described in
-      <xref linkend="in-memory-datasource-with-sqlite3-backend" />),
+      zone (as described in
+      <xref linkend="datasrc" />),
       <command>b10-ddns</command> will also
       notify <command>b10-auth</command> so that <command>b10-auth</command>
-      will re-cache the updated zone content.
+      will re-cache the updated zone content if necessary.
     </para>
 
     <para>
@@ -2479,7 +2510,7 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
 &gt; <userinput>config commit</userinput></screen></para>
 
       <para>
-        At start, the server will detect available network interfaces
+        During start-up the server will detect available network interfaces
         and will attempt to open UDP sockets on all interfaces that
         are up, running, are not loopback, and have IPv4 address
         assigned.
@@ -2489,17 +2520,8 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
         will respond to them with OFFER and ACK, respectively.
 
         Since the DHCPv4 server opens privileged ports, it requires root
-        access. Make sure you run this daemon as root.</para>
-
-        <note>
-          <para>
-            Integration with <command>bind10</command> is
-            planned. Ultimately, <command>b10-dhcp4</command> will not
-            be started directly, but rather via
-            <command>bind10</command>. Please be aware of this planned
-            change.
-          </para>
-        </note>
+        access. Make sure you run this daemon as root.
+      </para>
 
     </section>
 
@@ -2548,7 +2570,7 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
     </section>
 
     <section id="dhcp4-limit">
-      <title>DHCPv4 Server Limitations</title> 
+      <title>DHCPv4 Server Limitations</title>
       <para>These are the current limitations of the DHCPv4 server
       software. Most of them are reflections of the early stage of
       development and should be treated as <quote>not implemented
@@ -2664,22 +2686,25 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
       </para>
 
       <para>
-        The DHCPv6 server is implemented as <command>b10-dhcp6</command>
-        daemon. As it is not configurable yet, it is fully autonomous,
-        that is it does not interact with <command>b10-cfgmgr</command>.
-        To start DHCPv6 server, simply input:
-
-        <screen>
-#<userinput>cd src/bin/dhcp6</userinput>
-#<userinput>./b10-dhcp6</userinput>
-</screen>
+        <command>b10-dhcp6</command> is a BIND10 component and is being
+        run under BIND10 framework. To add a DHCPv6 process to the set of running
+        BIND10 services, you can use following commands in <command>bindctl</command>:
+        <screen>&gt; <userinput>config add Boss/components b10-dhcp6</userinput>
+&gt; <userinput>config set Boss/components/b10-dhcp6/kind dispensable</userinput>
+&gt; <userinput>config commit</userinput></screen>
+      </para>
 
-        Depending on your installation, <command>b10-dhcp6</command>
-        binary may reside in src/bin/dhcp6 in your source code
-        directory, in /usr/local/bin/b10-dhcp6 or other directory
-        you specified during compilation.
+       <para>
+         To shutdown running <command>b10-dhcp6</command>, please use the
+         following command:
+         <screen>&gt; <userinput>Dhcp6 shutdown</userinput></screen>
+         or
+         <screen>&gt; <userinput>config remove Boss/components b10-dhcp6</userinput>
+&gt; <userinput>config commit</userinput></screen>
+       </para>
 
-        At start, server will detect available network interfaces
+      <para>
+        During start-up the server will detect available network interfaces
         and will attempt to open UDP sockets on all interfaces that
         are up, running, are not loopback, are multicast-capable, and
         have IPv6 address assigned.
@@ -2692,16 +2717,6 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
         access. Make sure you run this daemon as root.
       </para>
 
-        <note>
-          <para>
-            Integration with <command>bind10</command> is
-            planned. Ultimately, <command>b10-dhcp6</command> will not
-            be started directly, but rather via
-            <command>bind10</command>. Please be aware of this planned
-            change.
-          </para>
-        </note>
-
     </section>
 
     <section id="dhcp6-config">
@@ -2715,7 +2730,7 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
       <para>
         At this stage of development, the only way to alter server
         configuration is to tweak its source code. To do so, please
-        edit src/bin/dhcp6/dhcp6_srv.cc file and modify following
+        edit src/bin/dhcp6/dhcp6_srv.cc file, modify the following
         parameters and recompile:
         <screen>
 const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";

File diff suppressed because it is too large
+ 61 - 11
doc/guide/bind10-messages.html


+ 98 - 18
doc/guide/bind10-messages.xml

@@ -413,6 +413,13 @@ a command on the command channel.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="AUTH_RECEIVED_NOTIFY">
+<term>AUTH_RECEIVED_NOTIFY received incoming NOTIFY for zone name %1, zone class %2</term>
+<listitem><para>
+This is a debug message reporting that an incoming NOTIFY was received.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="AUTH_RECEIVED_SENDSTATS">
 <term>AUTH_RECEIVED_SENDSTATS command 'sendstats' received</term>
 <listitem><para>
@@ -523,6 +530,17 @@ so no further validation is needed.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="AUTH_START_DDNS_FORWARDER">
+<term>AUTH_START_DDNS_FORWARDER DDNS UPDATE handling started</term>
+<listitem><para>
+This is a debug message indicating that b10-auth has received a message
+that it should internally forward UPDATE message to b10-ddns. When b10-ddns
+is not running, b10-auth will respond to UPDATE requests with rcode NOTIMP.
+When b10-ddns is running, b10-ddns will handle and respond to the UPDATE
+message.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="AUTH_STATS_CHANNEL_CREATED">
 <term>AUTH_STATS_CHANNEL_CREATED STATS session channel created</term>
 <listitem><para>
@@ -578,6 +596,19 @@ at the specified interval.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="AUTH_STOP_DDNS_FORWARDER">
+<term>AUTH_STOP_DDNS_FORWARDER DDNS UPDATE handling stopped</term>
+<listitem><para>
+This is a debug message indicating that b10-auth has received a message
+that it should stop internally forwarding UPDATE message to b10-ddns.
+b10-auth will no longer forward UPDATE messages to b10-ddns, but will
+respond itself with error code NOTIMP.
+This message is also logged when the forwarding is restarted (for instance
+if b10-ddns is restarted and the internal connection needs to be created
+again), in which case it should be followed by AUTH_START_DDNS_FORWARDER.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="AUTH_UNSUPPORTED_OPCODE">
 <term>AUTH_UNSUPPORTED_OPCODE unsupported opcode: %1</term>
 <listitem><para>
@@ -2476,6 +2507,17 @@ processed.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DATASRC_LIST_NOT_CACHED">
+<term>DATASRC_LIST_NOT_CACHED zone %1/%2 not cached, cache disabled globally. Will not be available.</term>
+<listitem><para>
+The process disabled caching of RR data completely. However, the given zone
+is provided as a master file and it can be served from memory cache only.
+Therefore, the zone will not be available for this process. If this is
+a problem, you should move the zone to some database backend (sqlite3, for
+example) and use it from there.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DATASRC_MEM_ADD_RRSET">
 <term>DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'</term>
 <listitem><para>
@@ -3885,6 +3927,33 @@ and updates.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DDNS_START_FORWARDER_ERROR">
+<term>DDNS_START_FORWARDER_ERROR Error from b10-auth when requesting DDNS UPDATE forwarding: %1</term>
+<listitem><para>
+There was an error response from b10-auth to the command to start
+forwarding DDNS UPDATE messages to b10-ddns.
+It is almost certain that DDNS UPDATE messages are not handled now, and that
+they will be responded to with a NOTIMP error code, even though b10-ddns is
+running.
+The error message is printed, and additional information may be found in
+the b10-auth log output. Since this is an error that is sent by the
+b10-auth module, it should have more information as to what the actual
+problem was.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DDNS_START_FORWARDER_FAIL">
+<term>DDNS_START_FORWARDER_FAIL Error sending request for DDNS UPDATE forwarding to b10-auth: %1</term>
+<listitem><para>
+There was an error when attempting to send b10-auth the request to forward
+DDNS UPDATE messages to the b10-ddns module. This points to an internal
+problem using the inter-module messaging system.
+This needs to be inspected, as it is almost certain that DDNS UPDATE messages
+are not handled now.
+The specific error is printed in the log message.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DDNS_STOPPED">
 <term>DDNS_STOPPED ddns server has stopped</term>
 <listitem><para>
@@ -3901,6 +3970,32 @@ process will now shut down.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DDNS_STOP_FORWARDER_ERROR">
+<term>DDNS_STOP_FORWARDER_ERROR Error from b10-auth when requesting to stop DDNS UPDATE forwarding: %1</term>
+<listitem><para>
+There was an error response from b10-auth to the command to stop
+forwarding DDNS UPDATE messages to b10-ddns.
+This specific error may not be fatal; instead of returning NOTIMP for future
+DDNS UPDATE messages, it will return SERVFAIL. However, this does point to
+an underlying problem in the messaging system, and should be inspected.
+The error message is printed, and additional information may be found in
+the b10-auth log output.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DDNS_STOP_FORWARDER_FAIL">
+<term>DDNS_STOP_FORWARDER_FAIL Error sending request to stop DDNS UPDATE forwarding to b10-auth: %1</term>
+<listitem><para>
+There was an error when attempting to send b10-auth the request to stop
+forwarding DDNS UPDATE messages to the b10-ddns module. This points to an
+internal problem using the inter-module messaging system.
+This specific error may not be fatal; instead of returning NOTIMP for future
+DDNS UPDATE messages, it will return SERVFAIL. However, this does point to
+an underlying problem in the messaging system, and should be inspected.
+The specific error is printed in the log message.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DDNS_UNCAUGHT_EXCEPTION">
 <term>DDNS_UNCAUGHT_EXCEPTION uncaught exception of type %1: %2</term>
 <listitem><para>
@@ -4781,20 +4876,6 @@ bug report.
 </para></listitem>
 </varlistentry>
 
-<varlistentry id="PYSERVER_COMMON_AUTH_CONFIG_NAME_PARSER_ERROR">
-<term>PYSERVER_COMMON_AUTH_CONFIG_NAME_PARSER_ERROR Invalid name when parsing Auth configuration: %1</term>
-<listitem><para>
-There was an invalid name when parsing Auth configuration.
-</para></listitem>
-</varlistentry>
-
-<varlistentry id="PYSERVER_COMMON_AUTH_CONFIG_RRCLASS_ERROR">
-<term>PYSERVER_COMMON_AUTH_CONFIG_RRCLASS_ERROR Invalid RRClass when parsing Auth configuration: %1</term>
-<listitem><para>
-There was an invalid RR class when parsing Auth configuration.
-</para></listitem>
-</varlistentry>
-
 <varlistentry id="PYSERVER_COMMON_DNS_TCP_SEND_DONE">
 <term>PYSERVER_COMMON_DNS_TCP_SEND_DONE completed sending TCP message to %1 (%2 bytes in total)</term>
 <listitem><para>
@@ -6078,11 +6159,10 @@ Please check your installation.
 </varlistentry>
 
 <varlistentry id="XFRIN_AUTH_LOADZONE">
-<term>XFRIN_AUTH_LOADZONE sending Auth loadzone for origin=%1, class=%2, datasrc=%3</term>
+<term>XFRIN_AUTH_LOADZONE sending Auth loadzone for origin=%1, class=%2</term>
 <listitem><para>
-There was a successful zone transfer, and the zone is served by b10-auth
-in the in-memory data source using sqlite3 as a backend. We send the
-"loadzone" command for the zone to b10-auth.
+There was a successful zone transfer.  We send the "loadzone" command for the
+zone to b10-auth.
 </para></listitem>
 </varlistentry>
 

+ 1 - 1
src/bin/Makefile.am

@@ -1,5 +1,5 @@
 SUBDIRS = bind10 bindctl cfgmgr ddns loadzone msgq host cmdctl auth xfrin \
 	xfrout usermgr zonemgr stats tests resolver sockcreator dhcp4 dhcp6 \
-	dbutil showtech
+	dbutil sysinfo
 
 check-recursive: all-recursive

+ 14 - 13
src/bin/auth/Makefile.am

@@ -48,6 +48,7 @@ b10_auth_SOURCES += auth_config.cc auth_config.h
 b10_auth_SOURCES += command.cc command.h
 b10_auth_SOURCES += common.h common.cc
 b10_auth_SOURCES += statistics.cc statistics.h
+b10_auth_SOURCES += datasrc_configurator.h
 b10_auth_SOURCES += main.cc
 # This is a temporary workaround for #1206, where the InMemoryClient has been
 # moved to an ldopened library. We could add that library to LDADD, but that
@@ -59,19 +60,19 @@ b10_auth_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
 nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc
 EXTRA_DIST += auth_messages.mes
 
-b10_auth_LDADD =  $(top_builddir)/src/lib/datasrc/libdatasrc.la
-b10_auth_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-b10_auth_LDADD += $(top_builddir)/src/lib/util/libutil.la
-b10_auth_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
-b10_auth_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
-b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
-b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-b10_auth_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
-b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-b10_auth_LDADD += $(top_builddir)/src/lib/log/liblog.la
-b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
-b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
-b10_auth_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
+b10_auth_LDADD =  $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
+b10_auth_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+b10_auth_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+b10_auth_LDADD += $(top_builddir)/src/lib/util/io/libb10-util-io.la
+b10_auth_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+b10_auth_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+b10_auth_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
+b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+b10_auth_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libb10-xfr.la
+b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common.la
+b10_auth_LDADD += $(top_builddir)/src/lib/statistics/libb10-statistics.la
 b10_auth_LDADD += $(SQLITE_LIBS)
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir

+ 0 - 4
src/bin/auth/auth.spec.pre.in

@@ -125,10 +125,6 @@
 	  {
             "item_name": "origin", "item_type": "string",
             "item_optional": false, "item_default": ""
-          },
-	  {
-            "item_name": "datasrc", "item_type": "string",
-            "item_optional": true, "item_default": "memory"
           }
         ]
       },

+ 9 - 90
src/bin/auth/auth_config.cc

@@ -43,20 +43,6 @@ using namespace isc::datasrc;
 using namespace isc::server_common::portconfig;
 
 namespace {
-/// A derived \c AuthConfigParser class for the "datasources" configuration
-/// identifier.
-class DatasourcesConfig : public AuthConfigParser {
-public:
-    DatasourcesConfig(AuthSrv& server) : server_(server)
-    {}
-    virtual void build(ConstElementPtr config_value);
-    virtual void commit();
-private:
-    AuthSrv& server_;
-    vector<boost::shared_ptr<AuthConfigParser> > datasources_;
-    set<string> configured_sources_;
-    vector<pair<RRClass, DataSourceClientContainerPtr> > clients_;
-};
 
 /// A derived \c AuthConfigParser for the version value
 /// (which is not used at this moment)
@@ -67,79 +53,6 @@ public:
     virtual void commit() {};
 };
 
-void
-DatasourcesConfig::build(ConstElementPtr config_value) {
-    BOOST_FOREACH(ConstElementPtr datasrc_elem, config_value->listValue()) {
-        // The caller is supposed to perform syntax-level checks, but we'll
-        // do minimum level of validation ourselves so that we won't crash due
-        // to a buggy application.
-        ConstElementPtr datasrc_type = datasrc_elem->get("type");
-        if (!datasrc_type) {
-            isc_throw(AuthConfigError, "Missing data source type");
-        }
-
-        if (configured_sources_.find(datasrc_type->stringValue()) !=
-            configured_sources_.end()) {
-            isc_throw(AuthConfigError, "Data source type '" <<
-                      datasrc_type->stringValue() << "' already configured");
-        }
-
-        // Apart from that it's not really easy to get at the default
-        // class value for the class here, it should probably really
-        // be a property of the instantiated data source. For now
-        // use hardcoded default IN.
-        const RRClass rrclass =
-            datasrc_elem->contains("class") ?
-            RRClass(datasrc_elem->get("class")->stringValue()) : RRClass::IN();
-
-        // Right now, we only support the in-memory data source for the
-        // RR class of IN.  We reject other cases explicitly by hardcoded
-        // checks.  This will soon be generalized, at which point these
-        // checks will also have to be cleaned up.
-        if (rrclass != RRClass::IN()) {
-            isc_throw(isc::InvalidParameter, "Unsupported data source class: "
-                      << rrclass);
-        }
-        if (datasrc_type->stringValue() != "memory") {
-            isc_throw(AuthConfigError, "Unsupported data source type: "
-                      << datasrc_type->stringValue());
-        }
-
-        // Create a new client for the specified data source and store it
-        // in the local vector.  For now, we always build a new client
-        // from the scratch, and replace any existing ones with the new ones.
-        // We might eventually want to optimize building zones (in case of
-        // reloading) by selectively loading fresh zones for data source
-        // where zone loading is expensive (such as in-memory).
-        clients_.push_back(
-            pair<RRClass, DataSourceClientContainerPtr>(
-                rrclass,
-                DataSourceClientContainerPtr(new DataSourceClientContainer(
-                                                 datasrc_type->stringValue(),
-                                                 datasrc_elem))));
-
-        configured_sources_.insert(datasrc_type->stringValue());
-    }
-}
-
-void
-DatasourcesConfig::commit() {
-    // As noted in build(), the current implementation only supports the
-    // in-memory data source for class IN, and build() should have ensured
-    // it.  So, depending on the vector is empty or not, we either clear
-    // or install an in-memory data source for the server.
-    //
-    // When we generalize it, we'll somehow install all data source clients
-    // built in the vector, clearing deleted ones from the server.
-    if (clients_.empty()) {
-        server_.setInMemoryClient(RRClass::IN(),
-                                  DataSourceClientContainerPtr());
-    } else {
-        server_.setInMemoryClient(clients_.front().first,
-                                  clients_.front().second);
-    }
-}
-
 /// A derived \c AuthConfigParser class for the "statistics-internal"
 /// configuration identifier.
 class StatisticsIntervalConfig : public AuthConfigParser {
@@ -242,9 +155,7 @@ createAuthConfigParser(AuthSrv& server, const std::string& config_id) {
     // simplicity.  In future we'll probably generalize it using map-like
     // data structure, and may even provide external register interface so
     // that it can be dynamically customized.
-    if (config_id == "datasources") {
-        return (new DatasourcesConfig(server));
-    } else if (config_id == "statistics-interval") {
+    if (config_id == "statistics-interval") {
         return (new StatisticsIntervalConfig(server));
     } else if (config_id == "listen_on") {
         return (new ListenAddressConfig(server));
@@ -261,6 +172,14 @@ createAuthConfigParser(AuthSrv& server, const std::string& config_id) {
         // later be used to mark backwards incompatible changes in the
         // config data
         return (new VersionConfig());
+    } else if (config_id == "datasources") {
+        // TODO: Ignored for now, since the value is probably used by
+        // other modules. Once they have been removed from there, remove
+        // it from here and the spec file.
+
+        // We need to return something. The VersionConfig is empty now,
+        // so we may abuse that one, as it is a short-term solution only.
+        return (new VersionConfig());
     } else {
         isc_throw(AuthConfigError, "Unknown configuration identifier: " <<
                   config_id);

+ 52 - 146
src/bin/auth/auth_srv.cc

@@ -43,9 +43,9 @@
 
 #include <datasrc/query.h>
 #include <datasrc/data_source.h>
-#include <datasrc/memory_datasrc.h>
 #include <datasrc/static_datasrc.h>
 #include <datasrc/sqlite3_datasrc.h>
+#include <datasrc/client_list.h>
 
 #include <xfr/xfrout_client.h>
 
@@ -222,10 +222,9 @@ private:
     AuthSrvImpl(const AuthSrvImpl& source);
     AuthSrvImpl& operator=(const AuthSrvImpl& source);
 public:
-    AuthSrvImpl(const bool use_cache, AbstractXfroutClient& xfrout_client,
+    AuthSrvImpl(AbstractXfroutClient& xfrout_client,
                 BaseSocketSessionForwarder& ddns_forwarder);
     ~AuthSrvImpl();
-    isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
 
     bool processNormalQuery(const IOMessage& io_message, Message& message,
                             OutputBuffer& buffer,
@@ -248,13 +247,6 @@ public:
     ModuleCCSession* config_session_;
     AbstractSession* xfrin_session_;
 
-    /// In-memory data source.  Currently class IN only for simplicity.
-    const RRClass memory_client_class_;
-    isc::datasrc::DataSourceClientContainerPtr memory_client_container_;
-
-    /// Hot spot cache
-    isc::datasrc::HotCache cache_;
-
     /// Interval timer for periodic submission of statistics counters.
     IntervalTimer statistics_timer_;
 
@@ -267,6 +259,22 @@ public:
     /// The TSIG keyring
     const boost::shared_ptr<TSIGKeyRing>* keyring_;
 
+    /// The client list
+    std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >
+        client_lists_;
+
+    boost::shared_ptr<ConfigurableClientList> getClientList(const RRClass&
+                                                            rrclass)
+    {
+        const std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
+            const_iterator it(client_lists_.find(rrclass));
+        if (it == client_lists_.end()) {
+            return (boost::shared_ptr<ConfigurableClientList>());
+        } else {
+            return (it->second);
+        }
+    }
+
     /// Bind the ModuleSpec object in config_session_ with
     /// isc:config::ModuleSpec::validateStatistics.
     void registerStatisticsValidator();
@@ -296,14 +304,6 @@ public:
                       bool done);
 
 private:
-    std::string db_file_;
-
-    MetaDataSrc data_sources_;
-    /// We keep a pointer to the currently running sqlite datasource
-    /// so that we can specifically remove that one should the database
-    /// file change
-    ConstDataSrcPtr cur_datasrc_;
-
     bool xfrout_connected_;
     AbstractXfroutClient& xfrout_client_;
 
@@ -316,12 +316,10 @@ private:
     auth::Query query_;
 };
 
-AuthSrvImpl::AuthSrvImpl(const bool use_cache,
-                         AbstractXfroutClient& xfrout_client,
+AuthSrvImpl::AuthSrvImpl(AbstractXfroutClient& xfrout_client,
                          BaseSocketSessionForwarder& ddns_forwarder) :
     config_session_(NULL),
     xfrin_session_(NULL),
-    memory_client_class_(RRClass::IN()),
     statistics_timer_(io_service_),
     counters_(),
     keyring_(NULL),
@@ -329,17 +327,7 @@ AuthSrvImpl::AuthSrvImpl(const bool use_cache,
     ddns_forwarder_(NULL),
     xfrout_connected_(false),
     xfrout_client_(xfrout_client)
-{
-    // cur_datasrc_ is automatically initialized by the default constructor,
-    // effectively being an empty (sqlite) data source.  once ccsession is up
-    // the datasource will be set by the configuration setting
-
-    // add static data source
-    data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
-
-    // enable or disable the cache
-    cache_.setEnabled(use_cache);
-}
+{}
 
 AuthSrvImpl::~AuthSrvImpl() {
     if (xfrout_connected_) {
@@ -398,11 +386,10 @@ private:
     AuthSrv* server_;
 };
 
-AuthSrv::AuthSrv(const bool use_cache,
-                 isc::xfr::AbstractXfroutClient& xfrout_client,
+AuthSrv::AuthSrv(isc::xfr::AbstractXfroutClient& xfrout_client,
                  isc::util::io::BaseSocketSessionForwarder& ddns_forwarder)
 {
-    impl_ = new AuthSrvImpl(use_cache, xfrout_client, ddns_forwarder);
+    impl_ = new AuthSrvImpl(xfrout_client, ddns_forwarder);
     checkin_ = new ConfigChecker(this);
     dns_lookup_ = new MessageLookup(this);
     dns_answer_ = new MessageAnswer(this);
@@ -482,16 +469,6 @@ AuthSrv::getIOService() {
 }
 
 void
-AuthSrv::setCacheSlots(const size_t slots) {
-    impl_->cache_.setSlots(slots);
-}
-
-size_t
-AuthSrv::getCacheSlots() const {
-    return (impl_->cache_.getSlots());
-}
-
-void
 AuthSrv::setXfrinSession(AbstractSession* xfrin_session) {
     impl_->xfrin_session_ = xfrin_session;
 }
@@ -512,48 +489,6 @@ AuthSrv::getConfigSession() const {
     return (impl_->config_session_);
 }
 
-isc::datasrc::DataSourceClientContainerPtr
-AuthSrv::getInMemoryClientContainer(const RRClass& rrclass) {
-    if (rrclass != impl_->memory_client_class_) {
-        isc_throw(InvalidParameter,
-                  "Memory data source is not supported for RR class "
-                  << rrclass);
-    }
-    return (impl_->memory_client_container_);
-}
-
-isc::datasrc::DataSourceClient*
-AuthSrv::getInMemoryClient(const RRClass& rrclass) {
-    if (hasInMemoryClient()) {
-        return (&getInMemoryClientContainer(rrclass)->getInstance());
-    } else {
-        return (NULL);
-    }
-}
-
-bool
-AuthSrv::hasInMemoryClient() const {
-    return (impl_->memory_client_container_);
-}
-
-void
-AuthSrv::setInMemoryClient(const isc::dns::RRClass& rrclass,
-                           DataSourceClientContainerPtr memory_client)
-{
-    if (rrclass != impl_->memory_client_class_) {
-        isc_throw(InvalidParameter,
-                  "Memory data source is not supported for RR class "
-                  << rrclass);
-    } else if (!impl_->memory_client_container_ && memory_client) {
-        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_ENABLED)
-                  .arg(rrclass);
-    } else if (impl_->memory_client_container_ && !memory_client) {
-        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_DISABLED)
-                  .arg(rrclass);
-    }
-    impl_->memory_client_container_ = memory_client;
-}
-
 uint32_t
 AuthSrv::getStatisticsTimerInterval() const {
     return (impl_->statistics_timer_.getInterval() / 1000);
@@ -725,18 +660,16 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
     }
 
     try {
-        // If a memory data source is configured call the separate
-        // Query::process()
         const ConstQuestionPtr question = *message.beginQuestion();
-        if (memory_client_container_ &&
-            memory_client_class_ == question->getClass()) {
+        const boost::shared_ptr<datasrc::ClientList>
+            list(getClientList(question->getClass()));
+        if (list) {
             const RRType& qtype = question->getType();
             const Name& qname = question->getName();
-            query_.process(memory_client_container_->getInstance(),
-                           qname, qtype, message, dnssec_ok);
+            query_.process(*list, qname, qtype, message, dnssec_ok);
         } else {
-            datasrc::Query query(message, cache_, dnssec_ok);
-            data_sources_.doQuery(query);
+            makeErrorMessage(renderer_, message, buffer, Rcode::REFUSED());
+            return (true);
         }
     } catch (const Exception& ex) {
         LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());
@@ -926,56 +859,6 @@ AuthSrvImpl::validateStatistics(isc::data::ConstElementPtr data) const {
             data, true));
 }
 
-ConstElementPtr
-AuthSrvImpl::setDbFile(ConstElementPtr config) {
-    ConstElementPtr answer = isc::config::createAnswer();
-
-    if (config && config->contains("database_file")) {
-        db_file_ = config->get("database_file")->stringValue();
-    } else if (config_session_ != NULL) {
-        bool is_default;
-        string item("database_file");
-        ConstElementPtr value = config_session_->getValue(is_default, item);
-        ElementPtr final = Element::createMap();
-
-        // If the value is the default, and we are running from
-        // a specific directory ('from build'), we need to use
-        // a different value than the default (which may not exist)
-        // (btw, this should not be done here in the end, i think
-        //  the from-source script should have a check for this,
-        //  but for that we need offline access to config, so for
-        //  now this is a decent solution)
-        if (is_default && getenv("B10_FROM_BUILD")) {
-            value = Element::create(string(getenv("B10_FROM_BUILD")) +
-                                    "/bind10_zones.sqlite3");
-        }
-        final->set(item, value);
-        config = final;
-
-        db_file_ = value->stringValue();
-    } else {
-        return (answer);
-    }
-    LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_DATA_SOURCE).arg(db_file_);
-
-    // create SQL data source
-    // Note: the following step is tricky to be exception-safe and to ensure
-    // exception guarantee: We first need to perform all operations that can
-    // fail, while acquiring resources in the RAII manner.  We then perform
-    // delete and swap operations which should not fail.
-    DataSrcPtr datasrc_ptr(DataSrcPtr(new Sqlite3DataSrc));
-    datasrc_ptr->init(config);
-    data_sources_.addDataSrc(datasrc_ptr);
-
-    // The following code should be exception free.
-    if (cur_datasrc_ != NULL) {
-        data_sources_.removeDataSrc(cur_datasrc_);
-    }
-    cur_datasrc_ = datasrc_ptr;
-
-    return (answer);
-}
-
 void
 AuthSrvImpl::resumeServer(DNSServer* server, Message& message, bool done) {
     if (done) {
@@ -992,7 +875,7 @@ AuthSrv::updateConfig(ConstElementPtr new_config) {
         if (new_config) {
             configureAuthServer(*this, new_config);
         }
-        return (impl_->setDbFile(new_config));
+        return (isc::config::createAnswer());
     } catch (const isc::Exception& error) {
         LOG_ERROR(auth_logger, AUTH_CONFIG_UPDATE_FAIL).arg(error.what());
         return (isc::config::createAnswer(1, error.what()));
@@ -1056,4 +939,27 @@ AuthSrv::destroyDDNSForwarder() {
     }
 }
 
+void
+AuthSrv::setClientList(const RRClass& rrclass,
+                       const boost::shared_ptr<ConfigurableClientList>& list) {
+    if (list) {
+        impl_->client_lists_[rrclass] = list;
+    } else {
+        impl_->client_lists_.erase(rrclass);
+    }
+}
+boost::shared_ptr<ConfigurableClientList>
+AuthSrv::getClientList(const RRClass& rrclass) {
+    return (impl_->getClientList(rrclass));
+}
 
+vector<RRClass>
+AuthSrv::getClientListClasses() const {
+    vector<RRClass> result;
+    for (std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
+         const_iterator it(impl_->client_lists_.begin());
+         it != impl_->client_lists_.end(); ++it) {
+        result.push_back(it->first);
+    }
+    return (result);
+}

+ 29 - 103
src/bin/auth/auth_srv.h

@@ -43,7 +43,7 @@ class BaseSocketSessionForwarder;
 }
 }
 namespace datasrc {
-class InMemoryClient;
+class ConfigurableClientList;
 }
 namespace xfr {
 class AbstractXfroutClient;
@@ -92,13 +92,11 @@ private:
 public:
     /// The constructor.
     ///
-    /// \param use_cache Whether to enable hot spot cache for lookup results.
     /// \param xfrout_client Communication interface with a separate xfrout
     /// process.  It's normally a reference to an xfr::XfroutClient object,
     /// but can refer to a local mock object for testing (or other
     /// experimental) purposes.
-    AuthSrv(const bool use_cache,
-            isc::xfr::AbstractXfroutClient& xfrout_client,
+    AuthSrv(isc::xfr::AbstractXfroutClient& xfrout_client,
             isc::util::io::BaseSocketSessionForwarder& ddns_forwarder);
     ~AuthSrv();
     //@}
@@ -129,20 +127,7 @@ public:
                         isc::util::OutputBuffer& buffer,
                         isc::asiodns::DNSServer* server);
 
-    /// \brief Updates the data source for the \c AuthSrv object.
-    ///
-    /// This method installs or replaces the data source that the \c AuthSrv
-    /// object refers to for query processing.
-    /// Although the method name is generic, the only thing it does is to
-    /// update the data source information.
-    /// If there is a data source installed, it will be replaced with the
-    /// new one.
-    ///
-    /// In the current implementation, the SQLite data source and InMemoryClient
-    /// are assumed.
-    /// We can enable memory data source and get the path of SQLite database by
-    /// the \c config parameter.  If we disabled memory data source, the SQLite
-    /// data source will be used.
+    /// \brief Updates the configuration for the \c AuthSrv object.
     ///
     /// On success this method returns a data \c Element (in the form of a
     /// pointer like object) indicating the successful result,
@@ -200,26 +185,6 @@ public:
     /// \brief Return pointer to the Checkin callback function
     isc::asiolink::SimpleCallback* getCheckinProvider() const { return (checkin_); }
 
-    /// \brief Set or update the size (number of slots) of hot spot cache.
-    ///
-    /// If the specified size is 0, it means the size will be unlimited.
-    /// The specified size is recorded even if the cache is disabled; the
-    /// new size will be effective when the cache is enabled.
-    ///
-    /// This method never throws an exception.
-    ///
-    /// \param slots The number of cache slots.
-    void setCacheSlots(const size_t slots);
-
-    /// \brief Get the current size (number of slots) of hot spot cache.
-    ///
-    /// It always returns the recorded size regardless of the cache is enabled.
-    ///
-    /// This method never throws an exception.
-    ///
-    /// \return The current number of cache slots.
-    size_t getCacheSlots() const;
-
     /// \brief Set the communication session with a separate process for
     /// outgoing zone transfers.
     ///
@@ -238,71 +203,6 @@ public:
     ///
     void setXfrinSession(isc::cc::AbstractSession* xfrin_session);
 
-    /// Returns the in-memory data source configured for the \c AuthSrv,
-    /// if any, as a pointer.
-    ///
-    /// This is mostly a convenience function around
-    /// \c getInMemoryClientContainer, which saves the caller the step
-    /// of having to call getInstance().
-    /// The pointer is of course only valid as long as the container
-    /// exists.
-    ///
-    /// The in-memory data source is configured per RR class.  However,
-    /// the data source may not be available for all RR classes.
-    /// If it is not available for the specified RR class, an exception of
-    /// class \c InvalidParameter will be thrown.
-    /// This method never throws an exception otherwise.
-    ///
-    /// Even for supported RR classes, the in-memory data source is not
-    /// configured by default.  In that case a NULL (shared) pointer will
-    /// be returned.
-    ///
-    /// \param rrclass The RR class of the requested in-memory data source.
-    /// \return A pointer to the in-memory data source, if configured;
-    /// otherwise NULL.
-    isc::datasrc::DataSourceClient* getInMemoryClient(
-        const isc::dns::RRClass& rrclass);
-
-    /// Returns the DataSourceClientContainer of the in-memory datasource
-    ///
-    /// \exception InvalidParameter if the given class does not match
-    ///            the one in the memory data source, or if the memory
-    ///            datasource has not been set (callers can check with
-    ///            \c hasMemoryDataSource())
-    ///
-    /// \param rrclass The RR class of the requested in-memory data source.
-    /// \return A shared pointer to the in-memory data source, if configured;
-    /// otherwise an empty shared pointer.
-    isc::datasrc::DataSourceClientContainerPtr getInMemoryClientContainer(
-        const isc::dns::RRClass& rrclass);
-
-    /// Checks if the in-memory data source has been set.
-    ///
-    /// Right now, only one datasource at a time is effectively supported.
-    /// This is a helper method to check whether it is the in-memory one.
-    /// This is mostly useful for current testing, and is expected to be
-    /// removed (or changed in behaviour) soon, when the general
-    /// multi-data-source framework is completed.
-    ///
-    /// \return True if the in-memory datasource has been set.
-    bool hasInMemoryClient() const;
-
-    /// Sets or replaces the in-memory data source of the specified RR class.
-    ///
-    /// Some RR classes may not be supported, in which case an exception
-    /// of class \c InvalidParameter will be thrown.
-    /// This method never throws an exception otherwise.
-    ///
-    /// If there is already an in memory data source configured, it will be
-    /// replaced with the newly specified one.
-    /// \c memory_client can be an empty shared pointer, in which case it
-    /// will (re)disable the in-memory data source.
-    ///
-    /// \param rrclass The RR class of the in-memory data source to be set.
-    /// \param memory_client A (shared) pointer to \c InMemoryClient to be set.
-    void setInMemoryClient(const isc::dns::RRClass& rrclass,
-        isc::datasrc::DataSourceClientContainerPtr memory_client);
-
     /// \brief Set the communication session with Statistics.
     ///
     /// This function never throws an exception as far as
@@ -437,6 +337,32 @@ public:
     /// If there was no forwarder yet, this method does nothing.
     void destroyDDNSForwarder();
 
+    /// \brief Sets the currently used list for data sources of given
+    ///     class.
+    ///
+    /// Replaces the internally used client list with a new one. Other
+    /// classes are not changed.
+    ///
+    /// \param rrclass The class to modify.
+    /// \param list Shared pointer to the client list. If it is NULL,
+    ///     the list is removed instead.
+    void setClientList(const isc::dns::RRClass& rrclass, const
+                       boost::shared_ptr<isc::datasrc::ConfigurableClientList>&
+                       list);
+
+    /// \brief Returns the currently used client list for the class.
+    ///
+    /// \param rrclass The class for which to get the list.
+    /// \return The list, or NULL if no list is set for the class.
+    boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+        getClientList(const isc::dns::RRClass& rrclass);
+
+    /// \brief Returns a list of classes that have a client list.
+    ///
+    /// \return List of classes for which a non-NULL client list
+    ///     has been set by setClientList.
+    std::vector<isc::dns::RRClass> getClientListClasses() const;
+
 private:
     AuthSrvImpl* impl_;
     isc::asiolink::SimpleCallback* checkin_;

+ 1 - 6
src/bin/auth/b10-auth.8

@@ -22,7 +22,7 @@
 b10-auth \- Authoritative DNS server
 .SH "SYNOPSIS"
 .HP \w'\fBb10\-auth\fR\ 'u
-\fBb10\-auth\fR [\fB\-n\fR] [\fB\-v\fR]
+\fBb10\-auth\fR [\fB\-v\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -42,11 +42,6 @@ It receives its configurations from
 .PP
 The arguments are as follows:
 .PP
-\fB\-n\fR
-.RS 4
-Do not cache answers in memory\&. The default is to use the cache for faster responses\&. The cache keeps the most recent 30,000 answers (positive and negative) in memory for 30 seconds (instead of querying the data source, such as SQLite3 database, each time)\&.
-.RE
-.PP
 \fB\-v\fR
 .RS 4
 Enable verbose logging mode\&. This enables logging of diagnostic messages at the maximum debug level\&.

+ 0 - 13
src/bin/auth/b10-auth.xml

@@ -44,7 +44,6 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>b10-auth</command>
-      <arg><option>-n</option></arg>
       <arg><option>-v</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -80,18 +79,6 @@
 
     <variablelist>
       <varlistentry>
-        <term><option>-n</option></term>
-        <listitem><para>
-          Do not cache answers in memory.
-          The default is to use the cache for faster responses.
-	  The cache keeps the most recent 30,000 answers (positive
-	  and negative) in memory for 30 seconds (instead of querying
-	  the data source, such as SQLite3 database, each time).
-        </para></listitem>
-<!-- TODO: this is SQLite3 only -->
-      </varlistentry>
-
-      <varlistentry>
         <term><option>-v</option></term>
         <listitem><para>
 	  Enable verbose logging mode. This enables logging of

+ 14 - 14
src/bin/auth/benchmarks/Makefile.am

@@ -26,19 +26,19 @@ query_bench_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
 
 nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc
 
-query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
-query_bench_LDADD += $(top_builddir)/src/lib/util/libutil.la
-query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-query_bench_LDADD += $(top_builddir)/src/lib/bench/libbench.la
-query_bench_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
-query_bench_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
-query_bench_LDADD += $(top_builddir)/src/lib/cc/libcc.la
-query_bench_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
-query_bench_LDADD += $(top_builddir)/src/lib/log/liblog.la
-query_bench_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
-query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-query_bench_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
-query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
-query_bench_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
+query_bench_LDADD = $(top_builddir)/src/lib/dns/libb10-dns++.la
+query_bench_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+query_bench_LDADD += $(top_builddir)/src/lib/bench/libb10-bench.la
+query_bench_LDADD += $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
+query_bench_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+query_bench_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+query_bench_LDADD += $(top_builddir)/src/lib/xfr/libb10-xfr.la
+query_bench_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+query_bench_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la
+query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+query_bench_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common.la
+query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
+query_bench_LDADD += $(top_builddir)/src/lib/statistics/libb10-statistics.la
 query_bench_LDADD += $(SQLITE_LIBS)
 

+ 7 - 31
src/bin/auth/benchmarks/query_bench.cc

@@ -77,10 +77,9 @@ protected:
 private:
     typedef boost::shared_ptr<const IOEndpoint> IOEndpointPtr;
 protected:
-    QueryBenchMark(const bool enable_cache,
-                   const BenchQueries& queries, Message& query_message,
+    QueryBenchMark(const BenchQueries& queries, Message& query_message,
                    OutputBuffer& buffer) :
-        server_(new AuthSrv(enable_cache, xfrout_client, ddns_forwarder)),
+        server_(new AuthSrv(xfrout_client, ddns_forwarder)),
         queries_(queries),
         query_message_(query_message),
         buffer_(buffer),
@@ -119,17 +118,12 @@ private:
 
 class Sqlite3QueryBenchMark  : public QueryBenchMark {
 public:
-    Sqlite3QueryBenchMark(const int cache_slots,
-                          const char* const datasrc_file,
+    Sqlite3QueryBenchMark(const char* const datasrc_file,
                           const BenchQueries& queries,
                           Message& query_message,
                           OutputBuffer& buffer) :
-        QueryBenchMark(cache_slots >= 0 ? true : false, queries,
-                       query_message, buffer)
+        QueryBenchMark(queries, query_message, buffer)
     {
-        if (cache_slots >= 0) {
-            server_->setCacheSlots(cache_slots);
-        }
         server_->updateConfig(Element::fromJSON("{\"database_file\": \"" +
                                                 string(datasrc_file) + "\"}"));
     }
@@ -142,7 +136,7 @@ public:
                           const BenchQueries& queries,
                           Message& query_message,
                           OutputBuffer& buffer) :
-        QueryBenchMark(false, queries, query_message, buffer)
+        QueryBenchMark(queries, query_message, buffer)
     {
         configureAuthServer(*server_,
                             Element::fromJSON(
@@ -274,27 +268,9 @@ main(int argc, char* argv[]) {
 
     switch (datasrc_type) {
     case SQLITE3:
-        cout << "Benchmark enabling Hot Spot Cache with unlimited slots "
-             << endl;
-        BenchMark<Sqlite3QueryBenchMark>(
-            iteration, Sqlite3QueryBenchMark(0, datasrc_file, queries,
-                                             message, buffer));
-
-        cout << "Benchmark enabling Hot Spot Cache with 10*#queries slots "
-             << endl;
-        BenchMark<Sqlite3QueryBenchMark>(
-            iteration, Sqlite3QueryBenchMark(10 * queries.size(), datasrc_file,
-                                             queries, message, buffer));
-
-        cout << "Benchmark enabling Hot Spot Cache with #queries/2 slots "
-             << endl;
-        BenchMark<Sqlite3QueryBenchMark>(
-            iteration, Sqlite3QueryBenchMark(queries.size() / 2, datasrc_file,
-                                             queries, message, buffer));
-
-        cout << "Benchmark disabling Hot Spot Cache" << endl;
+        cout << "Benchmark with SQLite3" << endl;
         BenchMark<Sqlite3QueryBenchMark>(
-            iteration, Sqlite3QueryBenchMark(-1, datasrc_file, queries,
+            iteration, Sqlite3QueryBenchMark(datasrc_file, queries,
                                              message, buffer));
         break;
     case MEMORY:

+ 30 - 137
src/bin/auth/command.cc

@@ -17,8 +17,7 @@
 #include <auth/auth_srv.h>
 
 #include <cc/data.h>
-#include <datasrc/memory_datasrc.h>
-#include <datasrc/factory.h>
+#include <datasrc/client_list.h>
 #include <config/ccsession.h>
 #include <exceptions/exceptions.h>
 #include <dns/rrclass.h>
@@ -159,156 +158,50 @@ public:
 class LoadZoneCommand : public AuthCommand {
 public:
     virtual void exec(AuthSrv& server, isc::data::ConstElementPtr args) {
-        // parse and validate the args.
-        if (!validate(server, args)) {
-            return;
-        }
-
-        const ConstElementPtr zone_config = getZoneConfig(server);
-
-        // Load a new zone and replace the current zone with the new one.
-        // TODO: eventually this should be incremental or done in some way
-        // that doesn't block other server operations.
-        // TODO: we may (should?) want to check the "last load time" and
-        // the timestamp of the file and skip loading if the file isn't newer.
-        const ConstElementPtr type(zone_config->get("filetype"));
-        boost::shared_ptr<InMemoryZoneFinder> zone_finder(
-            new InMemoryZoneFinder(old_zone_finder_->getClass(),
-                                   old_zone_finder_->getOrigin()));
-        if (type && type->stringValue() == "sqlite3") {
-            scoped_ptr<DataSourceClientContainer>
-                container(new DataSourceClientContainer("sqlite3",
-                                                        Element::fromJSON(
-                    "{\"database_file\": \"" +
-                    zone_config->get("file")->stringValue() + "\"}")));
-            zone_finder->load(*container->getInstance().getIterator(
-                old_zone_finder_->getOrigin()));
-        } else {
-            zone_finder->load(old_zone_finder_->getFileName());
-        }
-        old_zone_finder_->swap(*zone_finder);
-        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE)
-                  .arg(zone_finder->getOrigin()).arg(zone_finder->getClass());
-    }
-
-private:
-    // zone finder to be updated with the new file.
-    boost::shared_ptr<InMemoryZoneFinder> old_zone_finder_;
-
-    // A helper private method to parse and validate command parameters.
-    // On success, it sets 'old_zone_finder_' to the zone to be updated.
-    // It returns true if everything is okay; and false if the command is
-    // valid but there's no need for further process.
-    bool validate(AuthSrv& server, isc::data::ConstElementPtr args) {
         if (args == NULL) {
             isc_throw(AuthCommandError, "Null argument");
         }
 
-        // In this initial implementation, we assume memory data source
-        // for class IN by default.
-        ConstElementPtr datasrc_elem = args->get("datasrc");
-        if (datasrc_elem) {
-            if (datasrc_elem->stringValue() == "sqlite3") {
-                LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_SQLITE3);
-                return (false);
-            } else if (datasrc_elem->stringValue() != "memory") {
-                // (note: at this point it's guaranteed that datasrc_elem
-                // is of string type)
-                isc_throw(AuthCommandError,
-                          "Data source type " << datasrc_elem->stringValue()
-                          << " is not supported");
-            }
-        }
-
         ConstElementPtr class_elem = args->get("class");
-        const RRClass zone_class =
-            class_elem ? RRClass(class_elem->stringValue()) : RRClass::IN();
-
-        isc::datasrc::DataSourceClient* datasrc(
-            server.getInMemoryClient(zone_class));
-        if (datasrc == NULL) {
-            isc_throw(AuthCommandError, "Memory data source is disabled");
-        }
+        RRClass zone_class(class_elem ? RRClass(class_elem->stringValue()) :
+            RRClass::IN());
 
         ConstElementPtr origin_elem = args->get("origin");
         if (!origin_elem) {
             isc_throw(AuthCommandError, "Zone origin is missing");
         }
-        const Name origin = Name(origin_elem->stringValue());
+        Name origin(origin_elem->stringValue());
 
-        // Get the current zone
-        const DataSourceClient::FindResult result = datasrc->findZone(origin);
-        if (result.code != result::SUCCESS) {
-            isc_throw(AuthCommandError, "Zone " << origin <<
-                      " is not found in data source");
-        }
-
-        // It would appear that dynamic_cast does not work on all systems;
-        // it seems to confuse the RTTI system, resulting in NULL return
-        // values. So we use the more dangerous static_pointer_cast here.
-        old_zone_finder_ = boost::static_pointer_cast<InMemoryZoneFinder>(
-            result.zone_finder);
-
-        return (true);
-    }
+        const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+            list(server.getClientList(zone_class));
 
-    ConstElementPtr getZoneConfig(const AuthSrv &server) {
-        if (!server.getConfigSession()) {
-            // FIXME: This is a hack to make older tests pass. We should
-            // update these tests as well sometime and remove this hack.
-            // (note that under normal situation, the
-            // server.getConfigSession() does not return NULL)
-
-            // We provide an empty map, which means no configuration --
-            // defaults.
-            return (ConstElementPtr(new MapElement()));
+        if (!list) {
+            isc_throw(AuthCommandError, "There's no client list for "
+                      "class " << zone_class);
         }
 
-        // Find the config corresponding to the zone.
-        // We expect the configuration to be valid, as we have it and we
-        // accepted it before, therefore it must be validated.
-        const ConstElementPtr config(server.getConfigSession()->
-                                     getValue("datasources"));
-        ConstElementPtr zone_list;
-        // Unfortunately, we need to walk the list to find the correct data
-        // source.
-        // TODO: Make it named sets. These lists are uncomfortable.
-        for (size_t i(0); i < config->size(); ++i) {
-            // We use the getValue to get defaults as well
-            const ConstElementPtr dsrc_config(config->get(i));
-            const ConstElementPtr class_config(dsrc_config->get("class"));
-            const string class_type(class_config ?
-                                    class_config->stringValue() : "IN");
-            // It is in-memory and our class matches.
-            // FIXME: Is it allowed to have two datasources for the same
-            // type and class at once? It probably would not work now
-            // anyway and we may want to change the configuration of
-            // datasources somehow.
-            if (dsrc_config->get("type")->stringValue() == "memory" &&
-                RRClass(class_type) == old_zone_finder_->getClass()) {
-                zone_list = dsrc_config->get("zones");
-                break;
-            }
-        }
-
-        if (!zone_list) {
-            isc_throw(AuthCommandError,
-                      "Corresponding data source configuration was not found");
-        }
-
-        // Now we need to walk the zones and find the correct one.
-        for (size_t i(0); i < zone_list->size(); ++i) {
-            const ConstElementPtr zone_config(zone_list->get(i));
-            if (Name(zone_config->get("origin")->stringValue()) ==
-                old_zone_finder_->getOrigin()) {
-                // The origins are the same, so we consider this config to be
-                // for the zone.
-                return (zone_config);
-            }
+        switch (list->reload(origin)) {
+            case ConfigurableClientList::ZONE_RELOADED:
+                // Everything worked fine.
+                LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE)
+                    .arg(zone_class).arg(origin);
+                return;
+            case ConfigurableClientList::ZONE_NOT_FOUND:
+                isc_throw(AuthCommandError, "Zone " << origin << "/" <<
+                          zone_class << " was not found in any configured "
+                          "data source. Configure it first.");
+            case ConfigurableClientList::ZONE_NOT_CACHED:
+                isc_throw(AuthCommandError, "Zone " << origin << "/" <<
+                          zone_class << " is not served from memory, but "
+                          "direcly from the data source. It is not possible "
+                          "to reload it into memory. Configure it to be cached "
+                          "first.");
+            case ConfigurableClientList::CACHE_DISABLED:
+                // This is an internal error. Auth server must have the cache
+                // enabled.
+                isc_throw(isc::Unexpected, "Cache disabled in client list of "
+                          "class " << zone_class);
         }
-
-        isc_throw(AuthCommandError,
-                  "Corresponding zone configuration was not found");
     }
 };
 

+ 223 - 0
src/bin/auth/datasrc_configurator.h

@@ -0,0 +1,223 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_CONFIGURATOR_H
+#define DATASRC_CONFIGURATOR_H
+
+#include "auth_srv.h"
+
+#include <datasrc/client_list.h>
+#include <config/ccsession.h>
+#include <cc/data.h>
+
+#include <set>
+
+/// \brief A class to configure the authoritative server's data source lists
+///
+/// This will hook into the data_sources module configuration and it will
+/// keep the local copy of data source clients in the list in the authoritative
+/// server.
+///
+/// The class is slightly unusual. Due to some technical limitations, the hook
+/// needs to be static method. Therefore it is not possible to create instances
+/// of the class.
+///
+/// Also, the class is a template. This is simply because of easier testing.
+/// You don't need to pay attention to it, use the DataSourceConfigurator
+/// type alias instead.
+template<class Server, class List>
+class DataSourceConfiguratorGeneric {
+private:
+    /// \brief Disallow creation of instances
+    DataSourceConfiguratorGeneric();
+    /// \brief Internal method to hook into the ModuleCCSession
+    ///
+    /// It simply calls reconfigure.
+    static void reconfigureInternal(const std::string&,
+                                    isc::data::ConstElementPtr config,
+                                    const isc::config::ConfigData&)
+    {
+        if (config->contains("classes")) {
+            reconfigure(config->get("classes"));
+        }
+    }
+    static Server* server_;
+    static isc::config::ModuleCCSession* session_;
+    typedef boost::shared_ptr<List> ListPtr;
+public:
+    /// \brief Initializes the class.
+    ///
+    /// This configures which session and server should be used.
+    /// It hooks to the session now and downloads the configuration.
+    /// It is synchronous (it may block for some time).
+    ///
+    /// Note that you need to call cleanup before the server or
+    /// session dies, otherwise it might access them after they
+    /// are destroyed.
+    ///
+    /// \param session The session to hook into and to access the configuration
+    ///     through.
+    /// \param server It is the server to configure.
+    /// \throw isc::InvalidOperation if this is called when already initialized.
+    /// \throw isc::InvalidParameter if any of the parameters is NULL
+    /// \throw isc::config::ModuleCCError if the remote configuration is not
+    ///     available for some reason.
+    static void init(isc::config::ModuleCCSession* session,
+                     Server* server)
+    {
+        if (session == NULL) {
+            isc_throw(isc::InvalidParameter, "The session must not be NULL");
+        }
+        if (server == NULL) {
+            isc_throw(isc::InvalidParameter, "The server must not be NULL");
+        }
+        if (server_ != NULL) {
+            isc_throw(isc::InvalidOperation,
+                      "The configurator is already initialized");
+        }
+        server_ = server;
+        session_ = session;
+        session->addRemoteConfig("data_sources", reconfigureInternal, false);
+    }
+    /// \brief Deinitializes the class.
+    ///
+    /// This detaches from the session and removes the server from internal
+    /// storage. The current configuration in the server is preserved.
+    ///
+    /// This can be called even if it is not initialized currently. You
+    /// can initialize it again after this.
+    static void cleanup() {
+        if (session_ != NULL) {
+            session_->removeRemoteConfig("data_sources");
+        }
+        session_ = NULL;
+        server_ = NULL;
+    }
+    /// \brief Reads new configuration and replaces the old one.
+    ///
+    /// It instructs the server to replace the lists with new ones as needed.
+    /// You don't need to call it directly (but you could, though the benefit
+    /// is unknown and it would be questionable at least). It is called
+    /// automatically on normal updates.
+    ///
+    /// \param config The configuration value to parse. It is in the form
+    ///     as an update from the config manager.
+    /// \throw InvalidOperation if it is called when not initialized.
+    static void reconfigure(const isc::data::ConstElementPtr& config) {
+        if (server_ == NULL) {
+            isc_throw(isc::InvalidOperation,
+                      "Can't reconfigure while not initialized by init()");
+        }
+        typedef std::map<std::string, isc::data::ConstElementPtr> Map;
+        typedef std::pair<isc::dns::RRClass, ListPtr> RollbackPair;
+        typedef std::pair<isc::dns::RRClass, isc::data::ConstElementPtr>
+            RollbackConfiguration;
+        // Some structures to be able to perform a rollback
+        std::vector<RollbackPair> rollback_sets;
+        std::vector<RollbackConfiguration> rollback_configurations;
+        try {
+            // Get the configuration and current state.
+            const Map& map(config->mapValue());
+            const std::vector<isc::dns::RRClass>
+                activeVector(server_->getClientListClasses());
+            std::set<isc::dns::RRClass> active(activeVector.begin(),
+                                               activeVector.end());
+            // Go through the configuration and change everything.
+            for (Map::const_iterator it(map.begin()); it != map.end(); ++it) {
+                isc::dns::RRClass rrclass(it->first);
+                active.erase(rrclass);
+                ListPtr list(server_->getClientList(rrclass));
+                bool need_set(false);
+                if (list) {
+                    rollback_configurations.
+                        push_back(RollbackConfiguration(rrclass,
+                            list->getConfiguration()));
+                } else {
+                    list.reset(new List(rrclass));
+                    need_set = true;
+                    rollback_sets.push_back(RollbackPair(rrclass, ListPtr()));
+                }
+                list->configure(it->second, true);
+                if (need_set) {
+                    server_->setClientList(rrclass, list);
+                }
+            }
+            // Remove the ones that are not in the configuration.
+            for (std::set<isc::dns::RRClass>::iterator it(active.begin());
+                 it != active.end(); ++it) {
+                // There seems to be no way the setClientList could throw.
+                // But this is just to make sure in case it did to restore
+                // the original.
+                rollback_sets.push_back(
+                    RollbackPair(*it, server_->getClientList(*it)));
+                server_->setClientList(*it, ListPtr());
+            }
+        } catch (...) {
+            // Perform a rollback of the changes. The old configuration should
+            // work.
+            for (typename std::vector<RollbackPair>::const_iterator
+                 it(rollback_sets.begin()); it != rollback_sets.end(); ++it) {
+                server_->setClientList(it->first, it->second);
+            }
+            for (typename std::vector<RollbackConfiguration>::const_iterator
+                 it(rollback_configurations.begin());
+                 it != rollback_configurations.end(); ++it) {
+                server_->getClientList(it->first)->configure(it->second, true);
+            }
+            throw;
+        }
+    }
+    /// \brief Version of reconfigure for easier testing.
+    ///
+    /// This method can be used to reconfigure a server without first
+    /// initializing the configurator. This does not need a session.
+    /// Otherwise, it acts the same as reconfigure.
+    ///
+    /// This is not meant for production code. Do not use there.
+    ///
+    /// \param server The server to configure.
+    /// \param config The config to use.
+    /// \throw isc::InvalidOperation if the configurator is initialized.
+    /// \throw anything that reconfigure does.
+    static void testReconfigure(Server* server,
+                                const isc::data::ConstElementPtr& config)
+    {
+        if (server_ != NULL) {
+            isc_throw(isc::InvalidOperation, "Currently initialized.");
+        }
+        try {
+            server_ = server;
+            reconfigure(config);
+            server_ = NULL;
+        } catch (...) {
+            server_ = NULL;
+            throw;
+        }
+    }
+};
+
+template<class Server, class List>
+isc::config::ModuleCCSession*
+DataSourceConfiguratorGeneric<Server, List>::session_(NULL);
+
+template<class Server, class List>
+Server* DataSourceConfiguratorGeneric<Server, List>::server_(NULL);
+
+/// \brief Concrete version of DataSourceConfiguratorGeneric for the
+///     use in authoritative server.
+typedef DataSourceConfiguratorGeneric<AuthSrv,
+        isc::datasrc::ConfigurableClientList>
+    DataSourceConfigurator;
+
+#endif

+ 12 - 8
src/bin/auth/main.cc

@@ -45,6 +45,7 @@
 #include <auth/command.h>
 #include <auth/auth_srv.h>
 #include <auth/auth_log.h>
+#include <auth/datasrc_configurator.h>
 #include <asiodns/asiodns.h>
 #include <asiolink/asiolink.h>
 #include <log/logger_support.h>
@@ -84,9 +85,8 @@ my_command_handler(const string& command, ConstElementPtr args) {
 
 void
 usage() {
-    cerr << "Usage:  b10-auth [-u user] [-nv]"
+    cerr << "Usage:  b10-auth [-v]"
          << endl;
-    cerr << "\t-n: do not cache answers in memory" << endl;
     cerr << "\t-v: verbose logging (debug-level)" << endl;
     exit(1);
 }
@@ -96,14 +96,10 @@ usage() {
 int
 main(int argc, char* argv[]) {
     int ch;
-    bool cache = true;
     bool verbose = false;
 
     while ((ch = getopt(argc, argv, ":nu:v")) != -1) {
         switch (ch) {
-        case 'n':
-            cache = false;
-            break;
         case 'v':
             verbose = true;
             break;
@@ -142,7 +138,7 @@ main(int argc, char* argv[]) {
             specfile = string(AUTH_SPECFILE_LOCATION);
         }
 
-        auth_server = new AuthSrv(cache, xfrout_client, ddns_forwarder);
+        auth_server = new AuthSrv(xfrout_client, ddns_forwarder);
         LOG_INFO(auth_logger, AUTH_SERVER_CREATED);
 
         SimpleCallback* checkin = auth_server->getCheckinProvider();
@@ -204,6 +200,14 @@ main(int argc, char* argv[]) {
         isc::server_common::initKeyring(*config_session);
         auth_server->setTSIGKeyRing(&isc::server_common::keyring);
 
+        // Start the data source configuration
+        DataSourceConfigurator::init(config_session, auth_server);
+        // HACK: The default is not passed to the handler. This one will
+        // get the default (or, current value). Further updates will work
+        // the usual way.
+        DataSourceConfigurator::reconfigure(
+            config_session->getRemoteConfigValue("data_sources", "classes"));
+
         // Now start asynchronous read.
         config_session->start();
         LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_CONFIG_CHANNEL_STARTED);
@@ -217,7 +221,6 @@ main(int argc, char* argv[]) {
         cc_session->group_sendmsg(
             isc::config::createCommand(AUTH_STARTED_NOTIFICATION), "DDNS");
         io_service.run();
-
     } catch (const std::exception& ex) {
         LOG_FATAL(auth_logger, AUTH_SERVER_FAILED).arg(ex.what());
         ret = 1;
@@ -231,6 +234,7 @@ main(int argc, char* argv[]) {
         xfrin_session->disconnect();
     }
 
+    DataSourceConfigurator::cleanup();
     delete statistics_session;
     delete xfrin_session;
     delete config_session;

+ 27 - 27
src/bin/auth/query.cc

@@ -19,6 +19,7 @@
 #include <dns/rdataclass.h>
 
 #include <datasrc/client.h>
+#include <datasrc/client_list.h>
 
 #include <auth/query.h>
 
@@ -341,17 +342,17 @@ namespace {
 // the qname consists of a single label, which also means it's the root name),
 // we should search the deepest zone we have (which should be the root zone;
 // otherwise it's a query error).
-DataSourceClient::FindResult
-findZone(const DataSourceClient& client, const Name& qname, RRType qtype) {
+ClientList::FindResult
+findZone(const ClientList& list, const Name& qname, RRType qtype) {
     if (qtype != RRType::DS() || qname.getLabelCount() == 1) {
-        return (client.findZone(qname));
+        return (list.find(qname));
     }
-    return (client.findZone(qname.split(1)));
+    return (list.find(qname.split(1)));
 }
 }
 
 void
-Query::process(datasrc::DataSourceClient& datasrc_client,
+Query::process(datasrc::ClientList& client_list,
                const isc::dns::Name& qname, const isc::dns::RRType& qtype,
                isc::dns::Message& response, bool dnssec)
 {
@@ -360,19 +361,18 @@ Query::process(datasrc::DataSourceClient& datasrc_client,
     QueryCleaner cleaner(*this);
 
     // Set up query parameters for the rest of the (internal) methods
-    initialize(datasrc_client, qname, qtype, response, dnssec);
+    initialize(client_list, qname, qtype, response, dnssec);
 
     // Found a zone which is the nearest ancestor to QNAME
-    const DataSourceClient::FindResult result = findZone(*datasrc_client_,
-                                                         *qname_, *qtype_);
+    const ClientList::FindResult result = findZone(*client_list_, *qname_,
+                                                   *qtype_);
 
     // If we have no matching authoritative zone for the query name, return
     // REFUSED.  In short, this is to be compatible with BIND 9, but the
     // background discussion is not that simple.  See the relevant topic
     // at the BIND 10 developers's ML:
     // https://lists.isc.org/mailman/htdig/bind10-dev/2010-December/001633.html
-    if (result.code != result::SUCCESS &&
-        result.code != result::PARTIALMATCH) {
+    if (result.dsrc_client_ == NULL) {
         // If we tried to find a "parent zone" for a DS query and failed,
         // we may still have authority at the child side.  If we do, the query
         // has to be handled there.
@@ -384,7 +384,7 @@ Query::process(datasrc::DataSourceClient& datasrc_client,
         response_->setRcode(Rcode::REFUSED());
         return;
     }
-    ZoneFinder& zfinder = *result.zone_finder;
+    ZoneFinder& zfinder = *result.finder_;
 
     // We have authority for a zone that contain the query name (possibly
     // indirectly via delegation).  Look into the zone.
@@ -457,7 +457,7 @@ Query::process(datasrc::DataSourceClient& datasrc_client,
             // If the answer is a result of wildcard substitution,
             // add a proof that there's no closer name.
             if (dnssec_ && db_context->isWildcard()) {
-                addWildcardProof(*result.zone_finder, *db_context);
+                addWildcardProof(*result.finder_, *db_context);
             }
             break;
         case ZoneFinder::SUCCESS:
@@ -475,17 +475,17 @@ Query::process(datasrc::DataSourceClient& datasrc_client,
             // section.
             // Checking the findZone() is a lightweight check to see if
             // qname is the zone origin.
-            if (result.code != result::SUCCESS ||
+            if (!result.exact_match_ ||
                 db_context->code != ZoneFinder::SUCCESS ||
                 (*qtype_ != RRType::NS() && !qtype_is_any))
             {
-                addAuthAdditional(*result.zone_finder, additionals_);
+                addAuthAdditional(*result.finder_, additionals_);
             }
 
             // If the answer is a result of wildcard substitution,
             // add a proof that there's no closer name.
             if (dnssec_ && db_context->isWildcard()) {
-                addWildcardProof(*result.zone_finder, *db_context);
+                addWildcardProof(*result.finder_, *db_context);
             }
             break;
         case ZoneFinder::DELEGATION:
@@ -505,12 +505,12 @@ Query::process(datasrc::DataSourceClient& datasrc_client,
             // If DNSSEC is requested, see whether there is a DS
             // record for this delegation.
             if (dnssec_) {
-                addDS(*result.zone_finder, db_context->rrset->getName());
+                addDS(*result.finder_, db_context->rrset->getName());
             }
             break;
         case ZoneFinder::NXDOMAIN:
             response_->setRcode(Rcode::NXDOMAIN());
-            addSOA(*result.zone_finder);
+            addSOA(*result.finder_);
             if (dnssec_) {
                 if (db_context->isNSECSigned() && db_context->rrset) {
                     addNXDOMAINProofByNSEC(zfinder, db_context->rrset);
@@ -520,7 +520,7 @@ Query::process(datasrc::DataSourceClient& datasrc_client,
             }
             break;
         case ZoneFinder::NXRRSET:
-            addSOA(*result.zone_finder);
+            addSOA(*result.finder_);
             if (dnssec_) {
                 addNXRRsetProof(zfinder, *db_context);
             }
@@ -538,11 +538,11 @@ Query::process(datasrc::DataSourceClient& datasrc_client,
 }
 
 void
-Query::initialize(datasrc::DataSourceClient& datasrc_client,
+Query::initialize(datasrc::ClientList& client_list,
                   const isc::dns::Name& qname, const isc::dns::RRType& qtype,
                   isc::dns::Message& response, bool dnssec)
 {
-    datasrc_client_ = &datasrc_client;
+    client_list_ = &client_list;
     qname_ = &qname;
     qtype_ = &qtype;
     response_ = &response;
@@ -553,7 +553,7 @@ Query::initialize(datasrc::DataSourceClient& datasrc_client,
 
 void
 Query::reset() {
-    datasrc_client_ = NULL;
+    client_list_ = NULL;
     qname_ = NULL;
     qtype_ = NULL;
     response_ = NULL;
@@ -565,10 +565,10 @@ Query::reset() {
 
 bool
 Query::processDSAtChild() {
-    const DataSourceClient::FindResult zresult =
-        datasrc_client_->findZone(*qname_);
+    const ClientList::FindResult zresult =
+        client_list_->find(*qname_, true);
 
-    if (zresult.code != result::SUCCESS) {
+    if (zresult.dsrc_client_ == NULL) {
         return (false);
     }
 
@@ -583,12 +583,12 @@ Query::processDSAtChild() {
     // by seeing the SOA.
     response_->setHeaderFlag(Message::HEADERFLAG_AA);
     response_->setRcode(Rcode::NOERROR());
-    addSOA(*zresult.zone_finder);
+    addSOA(*zresult.finder_);
     ConstZoneFinderContextPtr ds_context =
-        zresult.zone_finder->find(*qname_, RRType::DS(), dnssec_opt_);
+        zresult.finder_->find(*qname_, RRType::DS(), dnssec_opt_);
     if (ds_context->code == ZoneFinder::NXRRSET) {
         if (dnssec_) {
-            addNXRRsetProof(*zresult.zone_finder, *ds_context);
+            addNXRRsetProof(*zresult.finder_, *ds_context);
         }
     }
 

+ 9 - 11
src/bin/auth/query.h

@@ -32,7 +32,7 @@ class RRset;
 }
 
 namespace datasrc {
-class DataSourceClient;
+class ClientList;
 }
 
 namespace auth {
@@ -55,8 +55,6 @@ namespace auth {
 ///   separate attribute setter.
 /// - likewise, we'll eventually need to do per zone access control, for which
 ///   we need querier's information such as its IP address.
-/// - datasrc_client and response may better be parameters to process() instead
-///   of the constructor.
 ///
 /// <b>Note:</b> The class name is intentionally the same as the one used in
 /// the datasrc library.  This is because the plan is to eventually merge
@@ -240,14 +238,14 @@ private:
     /// This is the first step of the process() method, and initializes
     /// the member data
     ///
-    /// \param datasrc_client The datasource wherein the answer to the query is
-    /// to be found.
+    /// \param client_list The datasource list wherein the answer to the query
+    /// is to be found.
     /// \param qname The query name
     /// \param qtype The RR type of the query
     /// \param response The response message to store the answer to the query.
     /// \param dnssec If the answer should include signatures and NSEC/NSEC3 if
     ///     possible.
-    void initialize(datasrc::DataSourceClient& datasrc_client,
+    void initialize(datasrc::ClientList& client_list,
                     const isc::dns::Name& qname, const isc::dns::RRType& qtype,
                     isc::dns::Message& response, bool dnssec = false);
 
@@ -281,7 +279,7 @@ public:
     /// Query parameters will be set by the call to process()
     ///
     Query() :
-        datasrc_client_(NULL), qname_(NULL), qtype_(NULL),
+        client_list_(NULL), qname_(NULL), qtype_(NULL),
         dnssec_(false), dnssec_opt_(isc::datasrc::ZoneFinder::FIND_DEFAULT),
         response_(NULL)
     {
@@ -318,14 +316,14 @@ public:
     /// shouldn't happen in real-life (as BadZone means wrong data, it should
     /// have been rejected upon loading).
     ///
-    /// \param datasrc_client The datasource wherein the answer to the query is
-    /// to be found.
+    /// \param client_list The datasource list wherein the answer to the query
+    /// is to be found.
     /// \param qname The query name
     /// \param qtype The RR type of the query
     /// \param response The response message to store the answer to the query.
     /// \param dnssec If the answer should include signatures and NSEC/NSEC3 if
     ///     possible.
-    void process(datasrc::DataSourceClient& datasrc_client,
+    void process(datasrc::ClientList& client_list,
                  const isc::dns::Name& qname, const isc::dns::RRType& qtype,
                  isc::dns::Message& response, bool dnssec = false);
 
@@ -483,7 +481,7 @@ public:
     };
 
 private:
-    const isc::datasrc::DataSourceClient* datasrc_client_;
+    const isc::datasrc::ClientList* client_list_;
     const isc::dns::Name* qname_;
     const isc::dns::RRType* qtype_;
     bool dnssec_;

+ 22 - 14
src/bin/auth/tests/Makefile.am

@@ -1,3 +1,5 @@
+SUBDIRS = testdata .
+
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
@@ -5,7 +7,10 @@ AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += -DAUTH_OBJ_DIR=\"$(abs_top_builddir)/src/bin/auth\"
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
+AM_CPPFLAGS += -DTEST_OWN_DATA_DIR=\"$(abs_top_srcdir)/src/bin/auth/tests/testdata\"
 AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
+AM_CPPFLAGS += -DDSRC_DIR=\"$(abs_top_builddir)/src/lib/datasrc\"
+AM_CPPFLAGS += -DPLUGIN_DATA_PATH=\"$(abs_top_builddir)/src/bin/cfgmgr/plugins\"
 AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -18,6 +23,7 @@ AM_CPPFLAGS += -DUSE_STATIC_LINK=1
 endif
 
 CLEANFILES = *.gcno *.gcda
+CLEANFILES += $(abs_top_builddir)/src/lib/testutils/testdata/does-not-exist.sqlite3
 
 TESTS_ENVIRONMENT = \
         $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
@@ -44,6 +50,7 @@ run_unittests_SOURCES += command_unittest.cc
 run_unittests_SOURCES += common_unittest.cc
 run_unittests_SOURCES += query_unittest.cc
 run_unittests_SOURCES += statistics_unittest.cc
+run_unittests_SOURCES += datasrc_configurator_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 # This is a temporary workaround for #1206, where the InMemoryClient has been
 # moved to an ldopened library. We could add that library to LDADD, but that
@@ -57,21 +64,22 @@ nodist_run_unittests_SOURCES = ../auth_messages.h ../auth_messages.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(top_builddir)/src/lib/testutils/libtestutils.la
-run_unittests_LDADD +=  $(top_builddir)/src/lib/datasrc/libdatasrc.la
-run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
-run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
-run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
-run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+run_unittests_LDADD = $(top_builddir)/src/lib/testutils/libb10-testutils.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libb10-dns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libb10-xfr.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common.la
+run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
+run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libb10-statistics.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/tests/libfake_session.la
 run_unittests_LDADD += $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
 

+ 284 - 216
src/bin/auth/tests/auth_srv_unittest.cc

@@ -30,10 +30,12 @@
 #include <server_common/keyring.h>
 
 #include <datasrc/memory_datasrc.h>
+#include <datasrc/client_list.h>
 #include <auth/auth_srv.h>
 #include <auth/command.h>
 #include <auth/common.h>
 #include <auth/statistics.h>
+#include <auth/datasrc_configurator.h>
 
 #include <util/unittests/mock_socketsession.h>
 #include <dns/tests/unittest_util.h>
@@ -48,6 +50,7 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
+#include <boost/foreach.hpp>
 
 #include <vector>
 
@@ -79,19 +82,17 @@ const char* const CONFIG_TESTDB =
 const char* const BADCONFIG_TESTDB =
     "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}";
 
+const char* const STATIC_DSRC_FILE = DSRC_DIR "/static.zone";
+
 // This is a configuration that uses the in-memory data source containing
 // a signed example zone.
-const char* const CONFIG_INMEMORY_EXAMPLE =
-    "{\"datasources\": [{\"type\": \"memory\","
-    "\"zones\": [{\"origin\": \"example\","
-    "\"file\": \"" TEST_DATA_DIR "/rfc5155-example.zone.signed\"}]}]}";
+const char* const CONFIG_INMEMORY_EXAMPLE = TEST_DATA_DIR "/rfc5155-example.zone.signed";
 
 class AuthSrvTest : public SrvTestBase {
 protected:
     AuthSrvTest() :
         dnss_(),
-        server(true, xfrout, ddns_forwarder),
-        rrclass(RRClass::IN()),
+        server(xfrout, ddns_forwarder),
         // The empty string is expected value of the parameter of
         // requestSocket, not the app_name (there's no fallback, it checks
         // the empty string is passed).
@@ -183,7 +184,6 @@ protected:
     MockXfroutClient xfrout;
     MockSocketSessionForwarder ddns_forwarder;
     AuthSrv server;
-    const RRClass rrclass;
     vector<uint8_t> response_data;
     AddressList address_store_;
     TestSocketRequestor sock_requestor_;
@@ -194,7 +194,8 @@ protected:
 // by default.  The resulting wire-format data will be stored in 'data'.
 void
 createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
-    const Name version_name("version.bind");
+    const Name version_name("VERSION.BIND.");
+    const Name apex_name("BIND.");
     Message message(Message::RENDER);
 
     UnitTestUtil::createRequestMessage(message, Opcode::QUERY(),
@@ -207,9 +208,9 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
     rrset_version->addRdata(generic::TXT(PACKAGE_STRING));
     message.addRRset(Message::SECTION_ANSWER, rrset_version);
 
-    RRsetPtr rrset_version_ns = RRsetPtr(new RRset(version_name, RRClass::CH(),
+    RRsetPtr rrset_version_ns = RRsetPtr(new RRset(apex_name, RRClass::CH(),
                                                    RRType::NS(), RRTTL(0)));
-    rrset_version_ns->addRdata(generic::NS(version_name));
+    rrset_version_ns->addRdata(generic::NS(apex_name));
     message.addRRset(Message::SECTION_AUTHORITY, rrset_version_ns);
 
     MessageRenderer renderer;
@@ -221,69 +222,18 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
                 renderer.getLength());
 }
 
-// In the following tests we confirm the response data is rendered in
-// wire format in the expected way.
-
-// The most primitive check: checking the result of the processMessage()
-// method
-TEST_F(AuthSrvTest, builtInQuery) {
+// We did not configure any client lists. Therefore it should be REFUSED
+TEST_F(AuthSrvTest, noClientList) {
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("version.bind"),
                                        RRClass::CH(), RRType::TXT());
     createRequestPacket(request_message, IPPROTO_UDP);
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
-    createBuiltinVersionResponse(default_qid, response_data);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        response_obuffer->getData(),
-                        response_obuffer->getLength(),
-                        &response_data[0], response_data.size());
-    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
-}
-
-// Same test emulating the UDPServer class behavior (defined in libasiolink).
-// This is not a good test in that it assumes internal implementation details
-// of UDPServer, but we've encountered a regression due to the introduction
-// of that class, so we add a test for that case to prevent such a regression
-// in future.
-// Besides, the generalization of UDPServer is probably too much for the
-// authoritative only server in terms of performance, and it's quite likely
-// we need to drop it for the authoritative server implementation.
-// At that point we can drop this test, too.
-TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
-    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
-                                       default_qid, Name("version.bind"),
-                                       RRClass::CH(), RRType::TXT());
-    createRequestPacket(request_message, IPPROTO_UDP);
 
-    (*server.getDNSLookupProvider())(*io_message, parse_message,
-                                     response_message,
-                                     response_obuffer, &dnsserv);
-    (*server.getDNSAnswerProvider())(*io_message, parse_message,
-                                     response_message, response_obuffer);
-
-    createBuiltinVersionResponse(default_qid, response_data);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        response_obuffer->getData(),
-                        response_obuffer->getLength(),
-                        &response_data[0], response_data.size());
-}
-
-// Same type of test as builtInQueryViaDNSServer but for an error response.
-TEST_F(AuthSrvTest, iqueryViaDNSServer) {
-    createDataFromFile("iquery_fromWire.wire");
-    (*server.getDNSLookupProvider())(*io_message, parse_message,
-                                     response_message,
-                                     response_obuffer, &dnsserv);
-    (*server.getDNSAnswerProvider())(*io_message, parse_message,
-                                     response_message, response_obuffer);
-
-    UnitTestUtil::readWireData("iquery_response_fromWire.wire",
-                               response_data);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        response_obuffer->getData(),
-                        response_obuffer->getLength(),
-                        &response_data[0], response_data.size());
+    EXPECT_TRUE(dnsserv.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
+                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
 }
 
 // Unsupported requests.  Should result in NOTIMP.
@@ -350,43 +300,6 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
     checkAllRcodeCountersZero();
 }
 
-// Try giving the server a TSIG signed request and see it can anwer signed as
-// well
-TEST_F(AuthSrvTest, TSIGSigned) {
-    // Prepare key, the client message, etc
-    const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
-    TSIGContext context(key);
-    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
-                                       Name("version.bind"), RRClass::CH(),
-                                       RRType::TXT());
-    createRequestPacket(request_message, IPPROTO_UDP, &context);
-
-    // Run the message through the server
-    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
-    keyring->add(key);
-    server.setTSIGKeyRing(&keyring);
-    server.processMessage(*io_message, *parse_message, *response_obuffer,
-                          &dnsserv);
-
-    // What did we get?
-    EXPECT_TRUE(dnsserv.hasAnswer());
-    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
-                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
-    // We need to parse the message ourself, or getTSIGRecord won't work
-    InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
-    Message m(Message::PARSE);
-    m.fromWire(ib);
-
-    const TSIGRecord* tsig = m.getTSIGRecord();
-    ASSERT_TRUE(tsig != NULL) << "Missing TSIG signature";
-    TSIGError error(context.verify(tsig, response_obuffer->getData(),
-                                   response_obuffer->getLength()));
-    EXPECT_EQ(TSIGError::NOERROR(), error) <<
-        "The server signed the response, but it doesn't seem to be valid";
-
-    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
-}
-
 // Give the server a signed request, but don't give it the key. It will
 // not be able to verify it, returning BADKEY
 TEST_F(AuthSrvTest, TSIGSignedBadKey) {
@@ -813,23 +726,171 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
 }
 
 void
-updateConfig(AuthSrv* server, const char* const config_data,
-             const bool expect_success)
-{
-    ConstElementPtr config_answer =
-        server->updateConfig(Element::fromJSON(config_data));
-    EXPECT_EQ(Element::map, config_answer->getType());
-    EXPECT_TRUE(config_answer->contains("result"));
+updateDatabase(AuthSrv* server, const char* params) {
+    const ConstElementPtr config(Element::fromJSON("{"
+        "\"IN\": [{"
+        "    \"type\": \"sqlite3\","
+        "    \"params\": " + string(params) +
+        "}]}"));
+    DataSourceConfigurator::testReconfigure(server, config);
+}
+
+void
+updateInMemory(AuthSrv* server, const char* origin, const char* filename) {
+    const ConstElementPtr config(Element::fromJSON("{"
+        "\"IN\": [{"
+        "   \"type\": \"MasterFiles\","
+        "   \"params\": {"
+        "       \"" + string(origin) + "\": \"" + string(filename) + "\""
+        "   },"
+        "   \"cache-enable\": true"
+        "}],"
+        "\"CH\": [{"
+        "   \"type\": \"static\","
+        "   \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
+        "}]}"));
+    DataSourceConfigurator::testReconfigure(server, config);
+}
+
+void
+updateBuiltin(AuthSrv* server) {
+    const ConstElementPtr config(Element::fromJSON("{"
+        "\"CH\": [{"
+        "   \"type\": \"static\","
+        "   \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
+        "}]}"));
+    DataSourceConfigurator::testReconfigure(server, config);
+}
+
+// Try giving the server a TSIG signed request and see it can anwer signed as
+// well
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_TSIGSigned) { // Needs builtin
+#else
+TEST_F(AuthSrvTest, TSIGSigned) {
+#endif
+    // Prepare key, the client message, etc
+    updateBuiltin(&server);
+    const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
+    TSIGContext context(key);
+    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+                                       Name("VERSION.BIND."), RRClass::CH(),
+                                       RRType::TXT());
+    createRequestPacket(request_message, IPPROTO_UDP, &context);
+
+    // Run the message through the server
+    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+    keyring->add(key);
+    server.setTSIGKeyRing(&keyring);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
+
+    // What did we get?
+    EXPECT_TRUE(dnsserv.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
+    // We need to parse the message ourself, or getTSIGRecord won't work
+    InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
+    Message m(Message::PARSE);
+    m.fromWire(ib);
+
+    const TSIGRecord* tsig = m.getTSIGRecord();
+    ASSERT_TRUE(tsig != NULL) << "Missing TSIG signature";
+    TSIGError error(context.verify(tsig, response_obuffer->getData(),
+                                   response_obuffer->getLength()));
+    EXPECT_EQ(TSIGError::NOERROR(), error) <<
+        "The server signed the response, but it doesn't seem to be valid";
+
+    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
+}
+
+// Same test emulating the UDPServer class behavior (defined in libasiolink).
+// This is not a good test in that it assumes internal implementation details
+// of UDPServer, but we've encountered a regression due to the introduction
+// of that class, so we add a test for that case to prevent such a regression
+// in future.
+// Besides, the generalization of UDPServer is probably too much for the
+// authoritative only server in terms of performance, and it's quite likely
+// we need to drop it for the authoritative server implementation.
+// At that point we can drop this test, too.
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_builtInQueryViaDNSServer) {
+#else
+TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
+#endif
+    updateBuiltin(&server);
+    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+                                       default_qid, Name("VERSION.BIND."),
+                                       RRClass::CH(), RRType::TXT());
+    createRequestPacket(request_message, IPPROTO_UDP);
+
+    (*server.getDNSLookupProvider())(*io_message, parse_message,
+                                     response_message,
+                                     response_obuffer, &dnsserv);
+    (*server.getDNSAnswerProvider())(*io_message, parse_message,
+                                     response_message, response_obuffer);
+
+    createBuiltinVersionResponse(default_qid, response_data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        response_obuffer->getData(),
+                        response_obuffer->getLength(),
+                        &response_data[0], response_data.size());
+}
+
+// In the following tests we confirm the response data is rendered in
+// wire format in the expected way.
+
+// The most primitive check: checking the result of the processMessage()
+// method
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_builtInQuery) {
+#else
+TEST_F(AuthSrvTest, builtInQuery) {
+#endif
+    updateBuiltin(&server);
+    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+                                       default_qid, Name("VERSION.BIND."),
+                                       RRClass::CH(), RRType::TXT());
+    createRequestPacket(request_message, IPPROTO_UDP);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
+    createBuiltinVersionResponse(default_qid, response_data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        response_obuffer->getData(),
+                        response_obuffer->getLength(),
+                        &response_data[0], response_data.size());
+    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
+}
+
+// Same type of test as builtInQueryViaDNSServer but for an error response.
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_iqueryViaDNSServer) { // Needs builtin
+#else
+TEST_F(AuthSrvTest, iqueryViaDNSServer) { // Needs builtin
+#endif
+    updateBuiltin(&server);
+    createDataFromFile("iquery_fromWire.wire");
+    (*server.getDNSLookupProvider())(*io_message, parse_message,
+                                     response_message,
+                                     response_obuffer, &dnsserv);
+    (*server.getDNSAnswerProvider())(*io_message, parse_message,
+                                     response_message, response_obuffer);
 
-    ConstElementPtr result = config_answer->get("result");
-    EXPECT_EQ(Element::list, result->getType());
-    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue()) <<
-        "Bad result from updateConfig: " << result->str();
+    UnitTestUtil::readWireData("iquery_response_fromWire.wire",
+                               response_data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        response_obuffer->getData(),
+                        response_obuffer->getLength(),
+                        &response_data[0], response_data.size());
 }
 
 // Install a Sqlite3 data source with testing data.
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_updateConfig) {
+#else
 TEST_F(AuthSrvTest, updateConfig) {
-    updateConfig(&server, CONFIG_TESTDB, true);
+#endif
+    updateDatabase(&server, CONFIG_TESTDB);
 
     // query for existent data in the installed data source.  The resulting
     // response should have the AA flag on, and have an RR in each answer
@@ -842,8 +903,12 @@ TEST_F(AuthSrvTest, updateConfig) {
                 QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
 
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_datasourceFail) {
+#else
 TEST_F(AuthSrvTest, datasourceFail) {
-    updateConfig(&server, CONFIG_TESTDB, true);
+#endif
+    updateDatabase(&server, CONFIG_TESTDB);
 
     // This query will hit a corrupted entry of the data source (the zoneload
     // tool and the data source itself naively accept it).  This will result
@@ -857,40 +922,40 @@ TEST_F(AuthSrvTest, datasourceFail) {
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
 }
 
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_updateConfigFail) {
+#else
 TEST_F(AuthSrvTest, updateConfigFail) {
+#endif
     // First, load a valid data source.
-    updateConfig(&server, CONFIG_TESTDB, true);
+    updateDatabase(&server, CONFIG_TESTDB);
 
     // Next, try to update it with a non-existent one.  This should fail.
-    updateConfig(&server, BADCONFIG_TESTDB, false);
+    EXPECT_THROW(updateDatabase(&server, BADCONFIG_TESTDB),
+                 isc::datasrc::DataSourceError);
 
     // The original data source should still exist.
     createDataFromFile("examplequery_fromWire.wire");
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
-    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
-                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
 
-TEST_F(AuthSrvTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_updateWithInMemoryClient
-#else
-       updateWithInMemoryClient
-#endif
-    )
-{
+TEST_F(AuthSrvTest, updateWithInMemoryClient) {
     // Test configuring memory data source.  Detailed test cases are covered
     // in the configuration tests.  We only check the AuthSrv interface here.
 
-    // By default memory data source isn't enabled
-    EXPECT_FALSE(server.hasInMemoryClient());
-    updateConfig(&server,
-                 "{\"datasources\": [{\"type\": \"memory\"}]}", true);
+    // Create an empty in-memory
+    const ConstElementPtr config(Element::fromJSON("{"
+        "\"IN\": [{"
+        "   \"type\": \"MasterFiles\","
+        "   \"params\": {},"
+        "   \"cache-enable\": true"
+        "}]}"));
+    DataSourceConfigurator::testReconfigure(&server, config);
     // after successful configuration, we should have one (with empty zoneset).
-    EXPECT_TRUE(server.hasInMemoryClient());
-    EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
 
     // The memory data source is empty, should return REFUSED rcode.
     createDataFromFile("examplequery_fromWire.wire");
@@ -901,21 +966,16 @@ TEST_F(AuthSrvTest,
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
 }
 
-TEST_F(AuthSrvTest,
 #ifdef USE_STATIC_LINK
-       DISABLED_queryWithInMemoryClientNoDNSSEC
+TEST_F(AuthSrvTest, DISABLED_queryWithInMemoryClientNoDNSSEC) {
 #else
-       queryWithInMemoryClientNoDNSSEC
+TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) {
 #endif
-    )
-{
     // In this example, we do simple check that query is handled from the
     // query handler class, and confirm it returns no error and a non empty
     // answer section.  Detailed examination on the response content
     // for various types of queries are tested in the query tests.
-    updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
-    EXPECT_TRUE(server.hasInMemoryClient());
-    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
+    updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
 
     createDataFromFile("nsec3query_nodnssec_fromWire.wire");
     server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -926,20 +986,15 @@ TEST_F(AuthSrvTest,
                 opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
 }
 
-TEST_F(AuthSrvTest,
 #ifdef USE_STATIC_LINK
-       DISABLED_queryWithInMemoryClientDNSSEC
+TEST_F(AuthSrvTest, DISABLED_queryWithInMemoryClientDNSSEC) {
 #else
-       queryWithInMemoryClientDNSSEC
+TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) {
 #endif
-    )
-{
     // Similar to the previous test, but the query has the DO bit on.
     // The response should contain RRSIGs, and should have more RRs than
     // the previous case.
-    updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
-    EXPECT_TRUE(server.hasInMemoryClient());
-    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
+    updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
 
     createDataFromFile("nsec3query_fromWire.wire");
     server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -958,13 +1013,12 @@ TEST_F(AuthSrvTest,
 #endif
     )
 {
-    // Configure memory data source for class IN
-    updateConfig(&server, "{\"datasources\": "
-                 "[{\"class\": \"IN\", \"type\": \"memory\"}]}", true);
+    // Set up the in-memory
+    updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
 
     // This shouldn't affect the result of class CH query
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
-                                       default_qid, Name("version.bind"),
+                                       default_qid, Name("VERSION.BIND."),
                                        RRClass::CH(), RRType::TXT());
     createRequestPacket(request_message, IPPROTO_UDP);
     server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -974,16 +1028,6 @@ TEST_F(AuthSrvTest,
                 opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
 
-TEST_F(AuthSrvTest, cacheSlots) {
-    // simple check for the get/set operations
-    server.setCacheSlots(10);    // 10 = arbitrary choice
-    EXPECT_EQ(10, server.getCacheSlots());
-
-    // 0 is a valid size
-    server.setCacheSlots(0);
-    EXPECT_EQ(00, server.getCacheSlots());
-}
-
 // Submit UDP normal query and check query counter
 TEST_F(AuthSrvTest, queryCounterUDPNormal) {
     // The counter should be initialized to 0.
@@ -1290,7 +1334,7 @@ public:
     ///                      throw std::exception
     /// \param fake_rrset If non NULL, it will be used as an answer to
     /// find() for that name and type.
-    FakeClient(isc::datasrc::DataSourceClientContainerPtr real_client,
+    FakeClient(const DataSourceClient* real_client,
                ThrowWhen throw_when, bool isc_exception,
                ConstRRsetPtr fake_rrset = ConstRRsetPtr()) :
         real_client_ptr_(real_client),
@@ -1309,7 +1353,7 @@ public:
     findZone(const isc::dns::Name& name) const {
         checkThrow(THROW_AT_FIND_ZONE, throw_when_, isc_exception_);
         const FindResult result =
-            real_client_ptr_->getInstance().findZone(name);
+            real_client_ptr_->findZone(name);
         return (FindResult(result.code, isc::datasrc::ZoneFinderPtr(
                                         new FakeZoneFinder(result.zone_finder,
                                                            throw_when_,
@@ -1329,38 +1373,39 @@ public:
                   "fake data source");
     }
 private:
-    const isc::datasrc::DataSourceClientContainerPtr real_client_ptr_;
+    const DataSourceClient* real_client_ptr_;
     ThrowWhen throw_when_;
     bool isc_exception_;
     ConstRRsetPtr fake_rrset_;
 };
 
-class FakeContainer : public isc::datasrc::DataSourceClientContainer {
+class FakeList : public isc::datasrc::ConfigurableClientList {
 public:
-    /// \brief Creates a fake container for the given in-memory client
+    /// \brief Creates a fake list for the given in-memory client
     ///
-    /// The initializer creates a fresh instance of a memory datasource,
-    /// which is ignored for the rest (but we do not allow 'null' containers
-    /// atm, and this is only needed in these tests, this may be changed
-    /// if we generalize the container class a bit more)
-    ///
-    /// It will also create a FakeClient, with the given arguments, which
-    /// is actually used when the instance is requested.
-    FakeContainer(isc::datasrc::DataSourceClientContainerPtr real_client,
-                  ThrowWhen throw_when, bool isc_exception,
-                  ConstRRsetPtr fake_rrset = ConstRRsetPtr()) :
-        DataSourceClientContainer("memory",
-                                  Element::fromJSON("{\"type\": \"memory\"}")),
-        client_(new FakeClient(real_client, throw_when, isc_exception,
-                               fake_rrset))
-    {}
-
-    isc::datasrc::DataSourceClient& getInstance() {
-        return (*client_);
+    /// It will create a FakeClient for each client in the original list,
+    /// with the given arguments, which is used when searching for the
+    /// corresponding data source.
+    FakeList(const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+             real_list, ThrowWhen throw_when, bool isc_exception,
+             ConstRRsetPtr fake_rrset = ConstRRsetPtr()) :
+        ConfigurableClientList(RRClass::IN()),
+        real_(real_list)
+    {
+        BOOST_FOREACH(const DataSourceInfo& info, real_->getDataSources()) {
+             const isc::datasrc::DataSourceClientPtr
+                 client(new FakeClient(info.data_src_client_ != NULL ?
+                                       info.data_src_client_ :
+                                       info.cache_.get(),
+                                       throw_when, isc_exception, fake_rrset));
+             clients_.push_back(client);
+             data_sources_.push_back(DataSourceInfo(client.get(),
+                 isc::datasrc::DataSourceClientContainerPtr(), false));
+        }
     }
-
 private:
-    const boost::scoped_ptr<isc::datasrc::DataSourceClient> client_;
+    const boost::shared_ptr<isc::datasrc::ConfigurableClientList> real_;
+    vector<isc::datasrc::DataSourceClientPtr> clients_;
 };
 
 } // end anonymous namespace for throwing proxy classes
@@ -1378,13 +1423,11 @@ TEST_F(AuthSrvTest,
     )
 {
     // Set real inmem client to proxy
-    updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
-    EXPECT_TRUE(server.hasInMemoryClient());
-
-    isc::datasrc::DataSourceClientContainerPtr fake_client_container(
-        new FakeContainer(server.getInMemoryClientContainer(rrclass),
-                          THROW_NEVER, false));
-    server.setInMemoryClient(rrclass, fake_client_container);
+    updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
+    boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+        list(new FakeList(server.getClientList(RRClass::IN()), THROW_NEVER,
+                          false));
+    server.setClientList(RRClass::IN(), list);
 
     createDataFromFile("nsec3query_nodnssec_fromWire.wire");
     server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -1402,21 +1445,15 @@ TEST_F(AuthSrvTest,
 // If non null rrset is given, it will be passed to the proxy so it can
 // return some faked response.
 void
-setupThrow(AuthSrv* server, const char *config, ThrowWhen throw_when,
-           bool isc_exception, ConstRRsetPtr rrset = ConstRRsetPtr())
+setupThrow(AuthSrv* server, ThrowWhen throw_when, bool isc_exception,
+           ConstRRsetPtr rrset = ConstRRsetPtr())
 {
-    // Set real inmem client to proxy
-    updateConfig(server, config, true);
+    updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
 
-    // Set it to throw on findZone(), this should result in
-    // SERVFAIL on any exception
-    isc::datasrc::DataSourceClientContainerPtr fake_client_container(
-        new FakeContainer(
-            server->getInMemoryClientContainer(isc::dns::RRClass::IN()),
-            throw_when, isc_exception, rrset));
-
-    ASSERT_TRUE(server->hasInMemoryClient());
-    server->setInMemoryClient(isc::dns::RRClass::IN(), fake_client_container);
+    boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+        list(new FakeList(server->getClientList(RRClass::IN()), throw_when,
+                          isc_exception, rrset));
+    server->setClientList(RRClass::IN(), list);
 }
 
 TEST_F(AuthSrvTest,
@@ -1439,11 +1476,11 @@ TEST_F(AuthSrvTest,
                                              RRClass::IN(), RRType::TXT());
     for (ThrowWhen* when(throws); *when != THROW_NEVER; ++when) {
         createRequestPacket(request_message, IPPROTO_UDP);
-        setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, true);
+        setupThrow(&server, *when, true);
         processAndCheckSERVFAIL();
         // To be sure, check same for non-isc-exceptions
         createRequestPacket(request_message, IPPROTO_UDP);
-        setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, false);
+        setupThrow(&server, *when, false);
         processAndCheckSERVFAIL();
     }
 }
@@ -1459,7 +1496,7 @@ TEST_F(AuthSrvTest,
     )
 {
     createDataFromFile("nsec3query_nodnssec_fromWire.wire");
-    setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, THROW_AT_GET_CLASS, true);
+    setupThrow(&server, THROW_AT_GET_CLASS, true);
 
     // getClass is not called so it should just answer
     server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -1483,8 +1520,7 @@ TEST_F(AuthSrvTest,
     ConstRRsetPtr empty_rrset(new RRset(Name("foo.example"),
                                         RRClass::IN(), RRType::TXT(),
                                         RRTTL(0)));
-    setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, THROW_NEVER, true,
-               empty_rrset);
+    setupThrow(&server, THROW_NEVER, true, empty_rrset);
 
     // Repeat the query processing two times.  Due to the faked RRset,
     // toWire() should throw, and it should result in SERVFAIL.
@@ -1627,7 +1663,7 @@ TEST_F(AuthSrvTest, DDNSForwardPushFail) {
 }
 
 TEST_F(AuthSrvTest, DDNSForwardClose) {
-    scoped_ptr<AuthSrv> tmp_server(new AuthSrv(true, xfrout, ddns_forwarder));
+    scoped_ptr<AuthSrv> tmp_server(new AuthSrv(xfrout, ddns_forwarder));
     tmp_server->createDDNSForwarder();
     UnitTestUtil::createRequestMessage(request_message, Opcode::UPDATE(),
                                        default_qid, Name("example.com"),
@@ -1660,7 +1696,7 @@ TEST_F(AuthSrvTest, DDNSForwardCreateDestroy) {
     // that the ddns_forwarder is connected when the 'start_ddns_forwarder'
     // command has been sent, and that it is no longer connected and auth
     // returns NOTIMP after the stop_ddns_forwarding command is sent.
-    scoped_ptr<AuthSrv> tmp_server(new AuthSrv(true, xfrout, ddns_forwarder));
+    scoped_ptr<AuthSrv> tmp_server(new AuthSrv(xfrout, ddns_forwarder));
 
     // Prepare update message to send
     UnitTestUtil::createRequestMessage(request_message, Opcode::UPDATE(),
@@ -1724,4 +1760,36 @@ TEST_F(AuthSrvTest, DDNSForwardCreateDestroy) {
                 Opcode::UPDATE().getCode(), QR_FLAG, 0, 0, 0, 0);
 }
 
+// Check the client list accessors
+TEST_F(AuthSrvTest, clientList) {
+    // The lists don't exist. Therefore, the list of RRClasses is empty.
+    // We also have no IN list.
+    EXPECT_TRUE(server.getClientListClasses().empty());
+    EXPECT_EQ(boost::shared_ptr<const isc::datasrc::ClientList>(),
+              server.getClientList(RRClass::IN()));
+    // Put something in.
+    const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+        list(new isc::datasrc::ConfigurableClientList(RRClass::IN()));
+    const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+        list2(new isc::datasrc::ConfigurableClientList(RRClass::CH()));
+    server.setClientList(RRClass::IN(), list);
+    server.setClientList(RRClass::CH(), list2);
+    // There are two things in the list and they are IN and CH
+    vector<RRClass> classes(server.getClientListClasses());
+    ASSERT_EQ(2, classes.size());
+    EXPECT_EQ(RRClass::IN(), classes[0]);
+    EXPECT_EQ(RRClass::CH(), classes[1]);
+    // And the lists can be retrieved.
+    EXPECT_EQ(list, server.getClientList(RRClass::IN()));
+    EXPECT_EQ(list2, server.getClientList(RRClass::CH()));
+    // Remove one of them
+    server.setClientList(RRClass::CH(),
+        boost::shared_ptr<isc::datasrc::ConfigurableClientList>());
+    // This really got deleted, including the class.
+    classes = server.getClientListClasses();
+    ASSERT_EQ(1, classes.size());
+    EXPECT_EQ(RRClass::IN(), classes[0]);
+    EXPECT_EQ(list, server.getClientList(RRClass::IN()));
+}
+
 }

+ 78 - 185
src/bin/auth/tests/command_unittest.cc

@@ -19,6 +19,7 @@
 #include <auth/auth_srv.h>
 #include <auth/auth_config.h>
 #include <auth/command.h>
+#include <auth/datasrc_configurator.h>
 
 #include <dns/name.h>
 #include <dns/rrclass.h>
@@ -62,14 +63,16 @@ namespace {
 class AuthCommandTest : public ::testing::Test {
 protected:
     AuthCommandTest() :
-        server_(false, xfrout_, ddns_forwarder_),
+        server_(xfrout_, ddns_forwarder_),
         rcode_(-1),
         expect_rcode_(0),
         itimer_(server_.getIOService())
     {
         server_.setStatisticsSession(&statistics_session_);
     }
-    void checkAnswer(const int expected_code) {
+    void checkAnswer(const int expected_code, const char* name = "") {
+        SCOPED_TRACE(name);
+
         parseAnswer(rcode_, result_);
         EXPECT_EQ(expected_code, rcode_) << result_->str();
     }
@@ -182,18 +185,17 @@ TEST_F(AuthCommandTest, shutdownIncorrectPID) {
 // zones, and checks the zones are correctly loaded.
 void
 zoneChecks(AuthSrv& server) {
-    EXPECT_TRUE(server.getInMemoryClient(RRClass::IN()));
-    EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
-              findZone(Name("ns.test1.example")).zone_finder->
+    EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
+              find(Name("ns.test1.example")).finder_->
               find(Name("ns.test1.example"), RRType::A())->code);
-    EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
-              findZone(Name("ns.test1.example")).zone_finder->
+    EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())->
+              find(Name("ns.test1.example")).finder_->
               find(Name("ns.test1.example"), RRType::AAAA())->code);
-    EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
-              findZone(Name("ns.test2.example")).zone_finder->
+    EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
+              find(Name("ns.test2.example")).finder_->
               find(Name("ns.test2.example"), RRType::A())->code);
-    EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
-              findZone(Name("ns.test2.example")).zone_finder->
+    EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())->
+              find(Name("ns.test2.example")).finder_->
               find(Name("ns.test2.example"), RRType::AAAA())->code);
 }
 
@@ -203,48 +205,44 @@ configureZones(AuthSrv& server) {
                         TEST_DATA_BUILDDIR "/test1.zone.copied"));
     ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test2.zone.in "
                         TEST_DATA_BUILDDIR "/test2.zone.copied"));
-    configureAuthServer(server, Element::fromJSON(
-                            "{\"datasources\": "
-                            " [{\"type\": \"memory\","
-                            "   \"zones\": "
-                            "[{\"origin\": \"test1.example\","
-                            "  \"file\": \""
-                               TEST_DATA_BUILDDIR "/test1.zone.copied\"},"
-                            " {\"origin\": \"test2.example\","
-                            "  \"file\": \""
-                               TEST_DATA_BUILDDIR "/test2.zone.copied\"}"
-                            "]}]}"));
+
+    const ConstElementPtr config(Element::fromJSON("{"
+        "\"IN\": [{"
+        "   \"type\": \"MasterFiles\","
+        "   \"params\": {"
+        "       \"test1.example\": \"" +
+                string(TEST_DATA_BUILDDIR "/test1.zone.copied") + "\","
+        "       \"test2.example\": \"" +
+                string(TEST_DATA_BUILDDIR "/test2.zone.copied") + "\""
+        "   },"
+        "   \"cache-enable\": true"
+        "}]}"));
+
+    DataSourceConfigurator::testReconfigure(&server, config);
+
     zoneChecks(server);
 }
 
 void
 newZoneChecks(AuthSrv& server) {
-    EXPECT_TRUE(server.getInMemoryClient(RRClass::IN()));
-    EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
-              findZone(Name("ns.test1.example")).zone_finder->
+    EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
+              find(Name("ns.test1.example")).finder_->
               find(Name("ns.test1.example"), RRType::A())->code);
     // now test1.example should have ns/AAAA
-    EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
-              findZone(Name("ns.test1.example")).zone_finder->
+    EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
+              find(Name("ns.test1.example")).finder_->
               find(Name("ns.test1.example"), RRType::AAAA())->code);
 
     // test2.example shouldn't change
-    EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
-              findZone(Name("ns.test2.example")).zone_finder->
+    EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
+              find(Name("ns.test2.example")).finder_->
               find(Name("ns.test2.example"), RRType::A())->code);
-    EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
-              findZone(Name("ns.test2.example")).zone_finder->
+    EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())->
+              find(Name("ns.test2.example")).finder_->
               find(Name("ns.test2.example"), RRType::AAAA())->code);
 }
 
-TEST_F(AuthCommandTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_loadZone
-#else
-       loadZone
-#endif
-    )
-{
+TEST_F(AuthCommandTest, loadZone) {
     configureZones(server_);
 
     ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
@@ -269,47 +267,24 @@ TEST_F(AuthCommandTest,
 #endif
     )
 {
-    const char* const SPEC_FILE = AUTH_OBJ_DIR "/auth.spec";
-
     // Prepare the database first
     const string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
     const string bad_db = TEST_DATA_BUILDDIR "/does-not-exist.sqlite3";
     stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
     createSQLite3DB(RRClass::IN(), Name("example.org"), test_db.c_str(), ss);
-
-    // Then store a config of the zone to the auth server
-    // This omits many config options of the auth server, but these are
-    // not read now.
-    isc::testutils::MockSession session;
-    // The session should not take care of anything or start anything, we
-    // need it only to hold the config we're going to put into it.
-    ModuleCCSession module_session(SPEC_FILE, session, NULL, NULL, false,
-                                  false);
     // This describes the data source in the configuration
-    const ElementPtr
-        map(Element::fromJSON("{\"datasources\": ["
-                              "  {"
-                              "    \"type\": \"memory\","
-                              "    \"zones\": ["
-                              "      {"
-                              "        \"origin\": \"example.org\","
-                              "        \"file\": \"" + test_db + "\","
-                              "        \"filetype\": \"sqlite3\""
-                              "      }"
-                              "    ]"
-                              "  }"
-                              "],"
-                              " \"database_file\": \"" + test_db + "\""
-                              "}"));
-    module_session.setLocalConfig(map);
-    server_.setConfigSession(&module_session);
-
-    server_.updateConfig(map);
+    const ConstElementPtr config(Element::fromJSON("{"
+        "\"IN\": [{"
+        "    \"type\": \"sqlite3\","
+        "    \"params\": {\"database_file\": \"" + test_db + "\"},"
+        "    \"cache-enable\": true,"
+        "    \"cache-zones\": [\"example.org\"]"
+        "}]}"));
+    DataSourceConfigurator::testReconfigure(&server_, config);
 
     // Check that the A record at www.example.org does not exist
-    ASSERT_TRUE(server_.hasInMemoryClient());
-    EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getInMemoryClient(RRClass::IN())->
-              findZone(Name("example.org")).zone_finder->
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(RRClass::IN())->
+              find(Name("example.org")).finder_->
               find(Name("www.example.org"), RRType::A())->code);
 
     // Add the record to the underlying sqlite database, by loading
@@ -328,90 +303,52 @@ TEST_F(AuthCommandTest,
     sql_updater->addRRset(*rrset);
     sql_updater->commit();
 
-    // This new record is in the database now, but should not be in the
-    // memory-datasource yet, so check again
-    EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getInMemoryClient(RRClass::IN())->
-              findZone(Name("example.org")).zone_finder->
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(RRClass::IN())->
+              find(Name("example.org")).finder_->
               find(Name("www.example.org"), RRType::A())->code);
 
     // Now send the command to reload it
     result_ = execAuthServerCommand(server_, "loadzone",
                                     Element::fromJSON(
                                         "{\"origin\": \"example.org\"}"));
-    checkAnswer(0);
+    checkAnswer(0, "Successful load");
 
     // And now it should be present too.
-    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
-              findZone(Name("example.org")).zone_finder->
+    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
+              find(Name("example.org")).finder_->
               find(Name("www.example.org"), RRType::A())->code);
 
     // Some error cases. First, the zone has no configuration. (note .com here)
     result_ = execAuthServerCommand(server_, "loadzone",
         Element::fromJSON("{\"origin\": \"example.com\"}"));
-    checkAnswer(1);
+    checkAnswer(1, "example.com");
+
     // The previous zone is not hurt in any way
-    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
-              findZone(Name("example.org")).zone_finder->
+    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
+              find(Name("example.org")).finder_->
               find(Name("example.org"), RRType::SOA())->code);
 
-    module_session.setLocalConfig(Element::fromJSON("{\"datasources\": []}"));
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"example.org\"}"));
-    checkAnswer(1);
+    const ConstElementPtr config2(Element::fromJSON("{"
+        "\"IN\": [{"
+        "    \"type\": \"sqlite3\","
+        "    \"params\": {\"database_file\": \"" + bad_db + "\"},"
+        "    \"cache-enable\": true,"
+        "    \"cache-zones\": [\"example.com\"]"
+        "}]}"));
+    EXPECT_THROW(DataSourceConfigurator::testReconfigure(&server_, config2),
+                 ConfigurableClientList::ConfigurationError);
 
-    // The previous zone is not hurt in any way
-    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
-              findZone(Name("example.org")).zone_finder->
-              find(Name("example.org"), RRType::SOA())->code);
-    // Configure an unreadable zone. Should fail, but leave the original zone
-    // data there
-    const ElementPtr
-        mapBad(Element::fromJSON("{\"datasources\": ["
-                                 "  {"
-                                 "    \"type\": \"memory\","
-                                 "    \"zones\": ["
-                                 "      {"
-                                 "        \"origin\": \"example.org\","
-                                 "        \"file\": \"" + bad_db + "\","
-                                 "        \"filetype\": \"sqlite3\""
-                                 "      }"
-                                 "    ]"
-                                 "  }"
-                                 "]}"));
-    module_session.setLocalConfig(mapBad);
     result_ = execAuthServerCommand(server_, "loadzone",
         Element::fromJSON("{\"origin\": \"example.com\"}"));
-    checkAnswer(1);
-    // The previous zone is not hurt in any way
-    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
-              findZone(Name("example.org")).zone_finder->
-              find(Name("example.org"), RRType::SOA())->code);
+    checkAnswer(1, "Unreadable");
 
-    // Broken configuration (not valid against the spec)
-    const ElementPtr
-        broken(Element::fromJSON("{\"datasources\": ["
-                                 "  {"
-                                 "    \"type\": \"memory\","
-                                 "    \"zones\": [[]]"
-                                 "  }"
-                                 "]}"));
-    module_session.setLocalConfig(broken);
-    checkAnswer(1);
     // The previous zone is not hurt in any way
-    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
-              findZone(Name("example.org")).zone_finder->
+    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
+              find(Name("example.org")).finder_->
               find(Name("example.org"), RRType::SOA())->code);
 }
 
-TEST_F(AuthCommandTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_loadBrokenZone
-#else
-       loadBrokenZone
-#endif
-    )
-{
+TEST_F(AuthCommandTest, loadBrokenZone) {
     configureZones(server_);
 
     ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
@@ -424,14 +361,7 @@ TEST_F(AuthCommandTest,
     zoneChecks(server_);     // zone shouldn't be replaced
 }
 
-TEST_F(AuthCommandTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_loadUnreadableZone
-#else
-       loadUnreadableZone
-#endif
-    )
-{
+TEST_F(AuthCommandTest, loadUnreadableZone) {
     configureZones(server_);
 
     // install the zone file as unreadable
@@ -454,82 +384,45 @@ TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) {
     checkAnswer(1);
 }
 
-TEST_F(AuthCommandTest, loadSqlite3DataSrc) {
-    // For sqlite3 data source we don't have to do anything (the data source
-    // (re)loads itself automatically)
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"test1.example\","
-                                        " \"datasrc\": \"sqlite3\"}"));
-    checkAnswer(0);
-}
-
-TEST_F(AuthCommandTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_loadZoneInvalidParams
-#else
-       loadZoneInvalidParams
-#endif
-    )
-{
+TEST_F(AuthCommandTest, loadZoneInvalidParams) {
     configureZones(server_);
 
     // null arg
     result_ = execAuthServerCommand(server_, "loadzone", ElementPtr());
-    checkAnswer(1);
+    checkAnswer(1, "Null arg");
 
     // zone class is bogus
     result_ = execAuthServerCommand(server_, "loadzone",
                                     Element::fromJSON(
                                         "{\"origin\": \"test1.example\","
                                         " \"class\": \"no_such_class\"}"));
-    checkAnswer(1);
+    checkAnswer(1, "No such class");
 
     result_ = execAuthServerCommand(server_, "loadzone",
                                     Element::fromJSON(
                                         "{\"origin\": \"test1.example\","
                                         " \"class\": 1}"));
-    checkAnswer(1);
+    checkAnswer(1, "Integral class");
 
-    // unsupported zone class
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"test1.example\","
-                                        " \"class\": \"CH\"}"));
-    checkAnswer(1);
-
-    // unsupported data source class
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"test1.example\","
-                                        " \"datasrc\": \"not supported\"}"));
-    checkAnswer(1);
-
-    // data source is bogus
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"test1.example\","
-                                        " \"datasrc\": 0}"));
-    checkAnswer(1);
 
     // origin is missing
     result_ = execAuthServerCommand(server_, "loadzone",
                                     Element::fromJSON("{}"));
-    checkAnswer(1);
+    checkAnswer(1, "Missing origin");
 
     // zone doesn't exist in the data source
     result_ = execAuthServerCommand(server_, "loadzone",
                                     Element::fromJSON("{\"origin\": \"xx\"}"));
-    checkAnswer(1);
+    checkAnswer(1, "No such zone");
 
     // origin is bogus
     result_ = execAuthServerCommand(server_, "loadzone",
                                     Element::fromJSON(
                                         "{\"origin\": \"...\"}"));
-    checkAnswer(1);
+    checkAnswer(1, "Wrong name");
 
     result_ = execAuthServerCommand(server_, "loadzone",
                                     Element::fromJSON("{\"origin\": 10}"));
-    checkAnswer(1);
+    checkAnswer(1, "Integral name");
 }
 }

+ 5 - 383
src/bin/auth/tests/config_unittest.cc

@@ -54,7 +54,7 @@ protected:
     AuthConfigTest() :
         dnss_(),
         rrclass(RRClass::IN()),
-        server(true, xfrout, ddns_forwarder),
+        server(xfrout, ddns_forwarder),
         // The empty string is expected value of the parameter of
         // requestSocket, not the app_name (there's no fallback, it checks
         // the empty string is passed).
@@ -72,32 +72,6 @@ private:
     isc::testutils::TestSocketRequestor sock_requestor_;
 };
 
-TEST_F(AuthConfigTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_datasourceConfig
-#else
-       datasourceConfig
-#endif
-    )
-{
-    // By default, we don't have any in-memory data source.
-    EXPECT_FALSE(server.hasInMemoryClient());
-    configureAuthServer(server, Element::fromJSON(
-                            "{\"datasources\": [{\"type\": \"memory\"}]}"));
-    // after successful configuration, we should have one (with empty zoneset).
-    EXPECT_TRUE(server.hasInMemoryClient());
-    EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
-}
-
-TEST_F(AuthConfigTest, databaseConfig) {
-    // right now, "database_file" is handled separately, so the parser
-    // doesn't recognize it, but it shouldn't throw an exception due to that.
-    EXPECT_NO_THROW(configureAuthServer(
-                        server,
-                        Element::fromJSON(
-                            "{\"database_file\": \"should_be_ignored\"}")));
-}
-
 TEST_F(AuthConfigTest, versionConfig) {
     // make sure it does not throw on 'version'
     EXPECT_NO_THROW(configureAuthServer(
@@ -106,32 +80,17 @@ TEST_F(AuthConfigTest, versionConfig) {
 }
 
 TEST_F(AuthConfigTest, exceptionGuarantee) {
-    EXPECT_FALSE(server.hasInMemoryClient());
+    server.setStatisticsTimerInterval(1234);
+    EXPECT_EQ(1234, server.getStatisticsTimerInterval());
     // This configuration contains an invalid item, which will trigger
     // an exception.
     EXPECT_THROW(configureAuthServer(
                      server,
                      Element::fromJSON(
-                         "{\"datasources\": [{\"type\": \"memory\"}], "
-                         " \"no_such_config_var\": 1}")),
+                         "{ \"no_such_config_var\": 1}")),
                  AuthConfigError);
     // The server state shouldn't change
-    EXPECT_FALSE(server.hasInMemoryClient());
-}
-
-TEST_F(AuthConfigTest, exceptionConversion) {
-    // This configuration contains a bogus RR class, which will trigger an
-    // exception from libdns++.  configureAuthServer() should convert this
-    // to AuthConfigError and rethrow the converted one.
-    EXPECT_THROW(configureAuthServer(
-                     server,
-                     Element::fromJSON(
-                         "{\"datasources\": "
-                         " [{\"type\": \"memory\","
-                         "   \"class\": \"BADCLASS\","
-                         "   \"zones\": [{\"origin\": \"example.com\","
-                         "                \"file\": \"example.zone\"}]}]}")),
-                 AuthConfigError);
+    EXPECT_EQ(1234, server.getStatisticsTimerInterval());
 }
 
 TEST_F(AuthConfigTest, badConfig) {
@@ -172,343 +131,6 @@ TEST_F(AuthConfigTest, listenAddressConfig) {
     EXPECT_EQ(DNSService::SERVER_SYNC_OK, dnss_.getUDPFdParams().at(1).options);
 }
 
-class MemoryDatasrcConfigTest : public AuthConfigTest {
-protected:
-    MemoryDatasrcConfigTest() :
-        parser(createAuthConfigParser(server, "datasources"))
-    {}
-    ~MemoryDatasrcConfigTest() {
-        delete parser;
-    }
-    AuthConfigParser* parser;
-};
-
-TEST_F(MemoryDatasrcConfigTest, addZeroDataSrc) {
-    parser->build(Element::fromJSON("[]"));
-    parser->commit();
-    EXPECT_FALSE(server.hasInMemoryClient());
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_addEmpty
-#else
-       addEmpty
-#endif
-    )
-{
-    // By default, we don't have any in-memory data source.
-    EXPECT_FALSE(server.hasInMemoryClient());
-    parser->build(Element::fromJSON("[{\"type\": \"memory\"}]"));
-    parser->commit();
-    EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_addZeroZone
-#else
-       addZeroZone
-#endif
-    )
-{
-    parser->build(Element::fromJSON("[{\"type\": \"memory\","
-                                    "  \"zones\": []}]"));
-    parser->commit();
-    EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_addOneZone
-#else
-       addOneZone
-#endif
-    )
-{
-    EXPECT_NO_THROW(parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.com\","
-                      "               \"file\": \"" TEST_DATA_DIR
-                      "/example.zone\"}]}]")));
-    EXPECT_NO_THROW(parser->commit());
-    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
-    // Check it actually loaded something
-    EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(rrclass)->findZone(
-        Name("ns.example.com.")).zone_finder->find(Name("ns.example.com."),
-        RRType::A())->code);
-}
-
-// This test uses dynamic load of a data source module, and won't work when
-// statically linked.
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_addOneWithFiletypeSQLite3
-#else
-       addOneWithFiletypeSQLite3
-#endif
-    )
-{
-    const string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
-    stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
-    createSQLite3DB(rrclass, Name("example.org"), test_db.c_str(), ss);
-
-    // In-memory with an SQLite3 data source as the backend.
-    parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.org\","
-                      "               \"file\": \""
-                      + test_db +  "\","
-                      "               \"filetype\": \"sqlite3\"}]}]"));
-    parser->commit();
-    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
-
-    // Failure case: the specified zone doesn't exist in the DB file.
-    delete parser;
-    parser = createAuthConfigParser(server, "datasources");
-    EXPECT_THROW(parser->build(
-                     Element::fromJSON(
-                         "[{\"type\": \"memory\","
-                         "  \"zones\": [{\"origin\": \"example.com\","
-                         "               \"file\": \""
-                         + test_db +  "\","
-                         "               \"filetype\": \"sqlite3\"}]}]")),
-                 DataSourceError);
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_addOneWithFiletypeText
-#else
-       addOneWithFiletypeText
-#endif
-    )
-{
-    // Explicitly specifying "text" is okay.
-    parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.com\","
-                      "               \"file\": \""
-                      TEST_DATA_DIR "/example.zone\","
-                      "               \"filetype\": \"text\"}]}]"));
-    parser->commit();
-    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_addMultiZones
-#else
-       addMultiZones
-#endif
-    )
-{
-    EXPECT_NO_THROW(parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.com\","
-                      "               \"file\": \"" TEST_DATA_DIR
-                      "/example.zone\"},"
-                      "              {\"origin\": \"example.org\","
-                      "               \"file\": \"" TEST_DATA_DIR
-                      "/example.org.zone\"},"
-                      "              {\"origin\": \"example.net\","
-                      "               \"file\": \"" TEST_DATA_DIR
-                      "/example.net.zone\"}]}]")));
-    EXPECT_NO_THROW(parser->commit());
-    EXPECT_EQ(3, server.getInMemoryClient(rrclass)->getZoneCount());
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_replace
-#else
-       replace
-#endif
-    )
-{
-    EXPECT_NO_THROW(parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.com\","
-                      "               \"file\": \"" TEST_DATA_DIR
-                      "/example.zone\"}]}]")));
-    EXPECT_NO_THROW(parser->commit());
-    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
-    EXPECT_EQ(isc::datasrc::result::SUCCESS,
-              server.getInMemoryClient(rrclass)->findZone(
-                  Name("example.com")).code);
-
-    // create a new parser, and install a new set of configuration.  It
-    // should replace the old one.
-    delete parser;
-    parser = createAuthConfigParser(server, "datasources"); 
-    EXPECT_NO_THROW(parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.org\","
-                      "               \"file\": \"" TEST_DATA_DIR
-                      "/example.org.zone\"},"
-                      "              {\"origin\": \"example.net\","
-                      "               \"file\": \"" TEST_DATA_DIR
-                      "/example.net.zone\"}]}]")));
-    EXPECT_NO_THROW(parser->commit());
-    EXPECT_EQ(2, server.getInMemoryClient(rrclass)->getZoneCount());
-    EXPECT_EQ(isc::datasrc::result::NOTFOUND,
-              server.getInMemoryClient(rrclass)->findZone(
-                  Name("example.com")).code);
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_exception
-#else
-       exception
-#endif
-    )
-{
-    // Load a zone
-    EXPECT_NO_THROW(parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.com\","
-                      "               \"file\": \"" TEST_DATA_DIR
-                      "/example.zone\"}]}]")));
-    EXPECT_NO_THROW(parser->commit());
-    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
-    EXPECT_EQ(isc::datasrc::result::SUCCESS,
-              server.getInMemoryClient(rrclass)->findZone(
-                  Name("example.com")).code);
-
-    // create a new parser, and try to load something. It will throw,
-    // the given master file should not exist
-    delete parser;
-    parser = createAuthConfigParser(server, "datasources");
-    EXPECT_THROW(parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.org\","
-                      "               \"file\": \"" TEST_DATA_DIR
-                      "/example.org.zone\"},"
-                      "              {\"origin\": \"example.net\","
-                      "               \"file\": \"" TEST_DATA_DIR
-                      "/nonexistent.zone\"}]}]")),
-                 isc::datasrc::DataSourceError);
-    // As that one throwed exception, it is not expected from us to
-    // commit it
-
-    // The original should be untouched
-    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
-    EXPECT_EQ(isc::datasrc::result::SUCCESS,
-              server.getInMemoryClient(rrclass)->findZone(
-                  Name("example.com")).code);
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_remove
-#else
-       remove
-#endif
-    )
-{
-    EXPECT_NO_THROW(parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.com\","
-                      "               \"file\": \"" TEST_DATA_DIR
-                      "/example.zone\"}]}]")));
-    EXPECT_NO_THROW(parser->commit());
-    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
-
-    delete parser;
-    parser = createAuthConfigParser(server, "datasources"); 
-    EXPECT_NO_THROW(parser->build(Element::fromJSON("[]")));
-    EXPECT_NO_THROW(parser->commit());
-    EXPECT_FALSE(server.hasInMemoryClient());
-}
-
-TEST_F(MemoryDatasrcConfigTest, addDuplicateZones) {
-    EXPECT_THROW(parser->build(
-                     Element::fromJSON(
-                         "[{\"type\": \"memory\","
-                         "  \"zones\": [{\"origin\": \"example.com\","
-                         "               \"file\": \"" TEST_DATA_DIR
-                         "/example.zone\"},"
-                         "              {\"origin\": \"example.com\","
-                         "               \"file\": \"" TEST_DATA_DIR
-                         "/example.com.zone\"}]}]")),
-                 DataSourceError);
-}
-
-TEST_F(MemoryDatasrcConfigTest, addBadZone) {
-    // origin and file are missing
-    EXPECT_THROW(parser->build(
-                     Element::fromJSON(
-                         "[{\"type\": \"memory\","
-                         "  \"zones\": [{}]}]")),
-                 DataSourceError);
-
-    // origin is missing
-    EXPECT_THROW(parser->build(
-                     Element::fromJSON(
-                         "[{\"type\": \"memory\","
-                         "  \"zones\": [{\"file\": \"example.zone\"}]}]")),
-                 DataSourceError);
-
-    // file is missing
-    EXPECT_THROW(parser->build(
-                     Element::fromJSON(
-                         "[{\"type\": \"memory\","
-                         "  \"zones\": [{\"origin\": \"example.com\"}]}]")),
-                 DataSourceError);
-
-    // missing zone file
-    EXPECT_THROW(parser->build(
-                     Element::fromJSON(
-                         "[{\"type\": \"memory\","
-                         "  \"zones\": [{\"origin\": \"example.com\"}]}]")),
-                 DataSourceError);
-
-    // bogus origin name
-    EXPECT_THROW(parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example..com\","
-                      "               \"file\": \"example.zone\"}]}]")),
-                 DataSourceError);
-
-    // bogus RR class name
-    EXPECT_THROW(parser->build(
-                     Element::fromJSON(
-                         "[{\"type\": \"memory\","
-                         "  \"class\": \"BADCLASS\","
-                         "  \"zones\": [{\"origin\": \"example.com\","
-                         "               \"file\": \"example.zone\"}]}]")),
-                 InvalidRRClass);
-
-    // valid RR class, but not currently supported
-    EXPECT_THROW(parser->build(
-                     Element::fromJSON(
-                         "[{\"type\": \"memory\","
-                         "  \"class\": \"CH\","
-                         "  \"zones\": [{\"origin\": \"example.com\","
-                         "               \"file\": \"example.zone\"}]}]")),
-                 isc::InvalidParameter);
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_badDatasrcType
-#else
-       badDatasrcType
-#endif
-    )
-{
-    EXPECT_THROW(parser->build(Element::fromJSON("[{\"type\": \"badsrc\"}]")),
-                 AuthConfigError);
-    EXPECT_THROW(parser->build(Element::fromJSON("[{\"notype\": \"memory\"}]")),
-                 AuthConfigError);
-    EXPECT_THROW(parser->build(Element::fromJSON("[{\"type\": 1}]")),
-                                      isc::data::TypeError);
-    EXPECT_THROW(parser->build(Element::fromJSON("[{\"type\": \"memory\"},"
-                                                 " {\"type\": \"memory\"}]")),
-                 AuthConfigError);
-}
-
 class StatisticsIntervalConfigTest : public AuthConfigTest {
 protected:
     StatisticsIntervalConfigTest() :

+ 298 - 0
src/bin/auth/tests/datasrc_configurator_unittest.cc

@@ -0,0 +1,298 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <auth/datasrc_configurator.h>
+
+#include <config/tests/fake_session.h>
+#include <config/ccsession.h>
+
+#include <gtest/gtest.h>
+#include <memory>
+#include <boost/shared_ptr.hpp>
+
+using namespace isc;
+using namespace isc::cc;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dns;
+using namespace std;
+using namespace boost;
+
+namespace {
+
+class DatasrcConfiguratorTest;
+
+class FakeList {
+public:
+    FakeList(const RRClass&) :
+        configuration_(new ListElement)
+    {}
+    void configure(const ConstElementPtr& configuration, bool allow_cache) {
+        EXPECT_TRUE(allow_cache);
+        conf_ = configuration->get(0)->get("type")->stringValue();
+        configuration_ = configuration;
+    }
+    const string& getConf() const {
+        return (conf_);
+    }
+    ConstElementPtr getConfiguration() const {
+        return (configuration_);
+    }
+private:
+    string conf_;
+    ConstElementPtr configuration_;
+};
+
+typedef shared_ptr<FakeList> ListPtr;
+
+// We use the test fixture as both parameters, this makes it possible
+// to easily fake all needed methods and look that they were called.
+typedef DataSourceConfiguratorGeneric<DatasrcConfiguratorTest,
+        FakeList> Configurator;
+
+class DatasrcConfiguratorTest : public ::testing::Test {
+public:
+    // These pretend to be the server
+    ListPtr getClientList(const RRClass& rrclass) {
+        log_ += "get " + rrclass.toText() + "\n";
+        return (lists_[rrclass]);
+    }
+    void setClientList(const RRClass& rrclass, const ListPtr& list) {
+        log_ += "set " + rrclass.toText() + " " +
+            (list ? list->getConf() : "") + "\n";
+        lists_[rrclass] = list;
+    }
+    vector<RRClass> getClientListClasses() const {
+        vector<RRClass> result;
+        for (std::map<RRClass, ListPtr>::const_iterator it(lists_.begin());
+             it != lists_.end(); ++it) {
+            result.push_back(it->first);
+        }
+        return (result);
+    }
+protected:
+    DatasrcConfiguratorTest() :
+        session(ElementPtr(new ListElement), ElementPtr(new ListElement),
+                ElementPtr(new ListElement)),
+        specfile(string(TEST_OWN_DATA_DIR) + "/spec.spec")
+    {
+        initSession();
+    }
+    void initSession() {
+        session.getMessages()->add(createAnswer());
+        mccs.reset(new ModuleCCSession(specfile, session, NULL, NULL, false,
+                                       false));
+    }
+    void TearDown() {
+        // Make sure no matter what we did, it is cleaned up.
+        Configurator::cleanup();
+    }
+    void init(const ElementPtr& config = ElementPtr()) {
+        session.getMessages()->
+            add(createAnswer(0,
+                             moduleSpecFromFile(string(PLUGIN_DATA_PATH) +
+                                                "/datasrc.spec").
+                             getFullSpec()));
+        if (config) {
+            session.getMessages()->add(createAnswer(0, config));
+        } else {
+            session.getMessages()->
+                add(createAnswer(0, ElementPtr(new MapElement)));
+        }
+        Configurator::init(mccs.get(), this);
+    }
+    void SetUp() {
+        init();
+    }
+    ElementPtr buildConfig(const string& config) const {
+        const ElementPtr internal(Element::fromJSON(config));
+        const ElementPtr external(Element::fromJSON("{\"version\": 1}"));
+        external->set("classes", internal);
+        return (external);
+    }
+    void initializeINList() {
+        const ElementPtr
+            config(buildConfig("{\"IN\": [{\"type\": \"xxx\"}]}"));
+        session.addMessage(createCommand("config_update", config), "data_sources",
+                           "*");
+        mccs->checkCommand();
+        // Check it called the correct things (check that there's no IN yet and
+        // set a new one.
+        EXPECT_EQ("get IN\nset IN xxx\n", log_);
+        EXPECT_EQ(1, lists_.size());
+    }
+    FakeSession session;
+    auto_ptr<ModuleCCSession> mccs;
+    const string specfile;
+    std::map<RRClass, ListPtr> lists_;
+    string log_;
+};
+
+// Check the initialization (and cleanup)
+TEST_F(DatasrcConfiguratorTest, initialization) {
+    // It can't be initialized again
+    EXPECT_THROW(init(), InvalidOperation);
+    EXPECT_TRUE(session.haveSubscription("data_sources", "*"));
+    // Deinitialize to make the tests reasonable
+    Configurator::cleanup();
+    EXPECT_FALSE(session.haveSubscription("data_sources", "*"));
+    // We can't reconfigure now (not even manually)
+    EXPECT_THROW(Configurator::reconfigure(ElementPtr(new MapElement())),
+                 InvalidOperation);
+    // If one of them is NULL, it does not work
+    EXPECT_THROW(Configurator::init(NULL, this), InvalidParameter);
+    EXPECT_FALSE(session.haveSubscription("data_sources", "*"));
+    EXPECT_THROW(Configurator::init(mccs.get(), NULL), InvalidParameter);
+    EXPECT_FALSE(session.haveSubscription("data_sources", "*"));
+    // But we can initialize it again now
+    EXPECT_NO_THROW(init());
+    EXPECT_TRUE(session.haveSubscription("data_sources", "*"));
+}
+
+// Push there a configuration with a single list.
+TEST_F(DatasrcConfiguratorTest, createList) {
+    initializeINList();
+}
+
+TEST_F(DatasrcConfiguratorTest, modifyList) {
+    // First, initialize the list
+    initializeINList();
+    // And now change the configuration of the list
+    const ElementPtr
+        config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}]}"));
+    session.addMessage(createCommand("config_update", config), "data_sources",
+                       "*");
+    log_ = "";
+    mccs->checkCommand();
+    // This one does not set
+    EXPECT_EQ("get IN\n", log_);
+    // But this should contain the yyy configuration
+    EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
+    EXPECT_EQ(1, lists_.size());
+}
+
+// Check we can have multiple lists at once
+TEST_F(DatasrcConfiguratorTest, multiple) {
+    const ElementPtr
+        config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}], "
+                                 "\"CH\": [{\"type\": \"xxx\"}]}"));
+    session.addMessage(createCommand("config_update", config), "data_sources",
+                       "*");
+    mccs->checkCommand();
+    // We have set commands for both classes.
+    EXPECT_EQ("get CH\nset CH xxx\nget IN\nset IN yyy\n", log_);
+    // We should have both there
+    EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
+    EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
+    EXPECT_EQ(2, lists_.size());
+}
+
+// Check we can add another one later and the old one does not get
+// overwritten.
+//
+// It's almost like above, but we initialize first with single-list
+// config.
+TEST_F(DatasrcConfiguratorTest, updateAdd) {
+    initializeINList();
+    const ElementPtr
+        config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}], "
+                           "\"CH\": [{\"type\": \"xxx\"}]}"));
+    session.addMessage(createCommand("config_update", config), "data_sources",
+                       "*");
+    log_ = "";
+    mccs->checkCommand();
+    // The CH is set, IN not
+    EXPECT_EQ("get CH\nset CH xxx\nget IN\n", log_);
+    // But this should contain the yyy configuration
+    EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
+    EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
+    EXPECT_EQ(2, lists_.size());
+}
+
+// We delete a class list in this test.
+TEST_F(DatasrcConfiguratorTest, updateDelete) {
+    initializeINList();
+    const ElementPtr
+        config(buildConfig("{}"));
+    session.addMessage(createCommand("config_update", config), "data_sources",
+                       "*");
+    log_ = "";
+    mccs->checkCommand();
+    EXPECT_EQ("get IN\nset IN \n", log_);
+    EXPECT_FALSE(lists_[RRClass::IN()]);
+    // In real auth server, the NULL one would be removed. However, we just
+    // store it, so the IN bucket is still in there. This checks there's nothing
+    // else.
+    EXPECT_EQ(1, lists_.size());
+}
+
+// Check that we can rollback an addition if something else fails
+TEST_F(DatasrcConfiguratorTest, rollbackAddition) {
+    initializeINList();
+    // The configuration is wrong. However, the CH one will get done first.
+    const ElementPtr
+        config(buildConfig("{\"IN\": [{\"type\": 13}], "
+                           "\"CH\": [{\"type\": \"xxx\"}]}"));
+    session.addMessage(createCommand("config_update", config), "data_sources",
+                       "*");
+    log_ = "";
+    // It does not throw, as it is handled in the ModuleCCSession.
+    // Throwing from the reconfigure is checked in other tests.
+    EXPECT_NO_THROW(mccs->checkCommand());
+    // Anyway, the result should not contain CH now and the original IN should
+    // be there.
+    EXPECT_EQ("xxx", lists_[RRClass::IN()]->getConf());
+    EXPECT_FALSE(lists_[RRClass::CH()]);
+}
+
+// Check that we can rollback a deletion if something else fails
+TEST_F(DatasrcConfiguratorTest, rollbackDeletion) {
+    initializeINList();
+    // Put the CH there
+    const ElementPtr
+        config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], "
+                                  "\"CH\": [{\"type\": \"xxx\"}]}"));
+    Configurator::reconfigure(config1);
+    const ElementPtr
+        config2(Element::fromJSON("{\"IN\": [{\"type\": 13}]}"));
+    // This would delete CH. However, the IN one fails.
+    // As the deletions happen after the additions/settings
+    // and there's no known way to cause an exception during the
+    // deletions, it is not a true rollback, but the result should
+    // be the same.
+    EXPECT_THROW(Configurator::reconfigure(config2), TypeError);
+    EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
+    EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
+}
+
+// Check that we can roll back configuration change if something
+// fails later on.
+TEST_F(DatasrcConfiguratorTest, rollbackConfiguration) {
+    initializeINList();
+    // Put the CH there
+    const ElementPtr
+        config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], "
+                                  "\"CH\": [{\"type\": \"xxx\"}]}"));
+    Configurator::reconfigure(config1);
+    // Now, the CH happens first. But nevertheless, it should be
+    // restored to the previoeus version.
+    const ElementPtr
+        config2(Element::fromJSON("{\"IN\": [{\"type\": 13}], "
+                                  "\"CH\": [{\"type\": \"yyy\"}]}"));
+    EXPECT_THROW(Configurator::reconfigure(config2), TypeError);
+    EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
+    EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
+}
+
+}

+ 122 - 86
src/bin/auth/tests/query_unittest.cc

@@ -32,6 +32,7 @@
 #include <dns/rdataclass.h>
 
 #include <datasrc/memory_datasrc.h>
+#include <datasrc/client_list.h>
 
 #include <auth/query.h>
 
@@ -48,6 +49,37 @@ using namespace isc::testutils;
 
 namespace {
 
+// Simple wrapper for a sincle data source client.
+// The list simply delegates all the answers to the single
+// client.
+class SingletonList : public ClientList {
+public:
+    SingletonList(DataSourceClient& client) :
+        client_(client)
+    {}
+    virtual FindResult find(const Name& zone, bool exact, bool) const {
+        DataSourceClient::FindResult result(client_.findZone(zone));
+        // We don't complicate the tests with real life keepers, but we
+        // need to put something to the parameter anyway.
+        const boost::shared_ptr<ClientList::FindResult::LifeKeeper> keeper;
+        switch (result.code) {
+            case result::SUCCESS:
+                return (FindResult(&client_, result.zone_finder, true,
+                                   keeper));
+            case result::PARTIALMATCH:
+                if (!exact) {
+                    return (FindResult(&client_, result.zone_finder, false,
+                                       keeper));
+                }
+            default:
+                return (FindResult());
+        }
+    }
+private:
+    DataSourceClient& client_;
+};
+
+
 // This is the content of the mock zone (see below).
 // It's a sequence of textual RRs that is supposed to be parsed by
 // dns::masterLoad().  Some of the RRs are also used as the expected
@@ -875,6 +907,7 @@ MockZoneFinder::find(const Name& name, const RRType& type,
 class QueryTest : public ::testing::Test {
 protected:
     QueryTest() :
+        list(memory_client),
         qname(Name("www.example.com")), qclass(RRClass::IN()),
         qtype(RRType::A()), response(Message::RENDER),
         qid(response.getQid()), query_code(Opcode::QUERY().getCode()),
@@ -898,6 +931,8 @@ protected:
     // (originally named MemoryDataSrc) and was tested with it, so we keep
     // it like this for now.
     InMemoryClient memory_client;
+    // A wrapper client list to wrap the single data source.
+    SingletonList list;
     const Name qname;
     const RRClass qclass;
     const RRType qtype;
@@ -949,20 +984,21 @@ TEST_F(QueryTest, noZone) {
     // There's no zone in the memory datasource.  So the response should have
     // REFUSED.
     InMemoryClient empty_memory_client;
-    EXPECT_NO_THROW(query.process(empty_memory_client, qname, qtype,
+    SingletonList empty_list(empty_memory_client);
+    EXPECT_NO_THROW(query.process(empty_list, qname, qtype,
                                   response));
     EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
 }
 
 TEST_F(QueryTest, exactMatch) {
-    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
+    EXPECT_NO_THROW(query.process(list, qname, qtype, response));
     // find match rrset
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
                   www_a_txt, zone_ns_txt, ns_addrs_txt);
 }
 
 TEST_F(QueryTest, exactMatchMultipleQueries) {
-    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
+    EXPECT_NO_THROW(query.process(list, qname, qtype, response));
     // find match rrset
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
                   www_a_txt, zone_ns_txt, ns_addrs_txt);
@@ -971,7 +1007,7 @@ TEST_F(QueryTest, exactMatchMultipleQueries) {
     response.clear(isc::dns::Message::RENDER);
     response.setRcode(Rcode::NOERROR());
     response.setOpcode(Opcode::QUERY());
-    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
+    EXPECT_NO_THROW(query.process(list, qname, qtype, response));
     // find match rrset
     SCOPED_TRACE("Second query");
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
@@ -982,7 +1018,7 @@ TEST_F(QueryTest, exactMatchIgnoreSIG) {
     // Check that we do not include the RRSIG when not requested even when
     // we receive it from the data source.
     mock_finder->setIncludeRRSIGAnyway(true);
-    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
+    EXPECT_NO_THROW(query.process(list, qname, qtype, response));
     // find match rrset
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
                   www_a_txt, zone_ns_txt, ns_addrs_txt);
@@ -990,7 +1026,7 @@ TEST_F(QueryTest, exactMatchIgnoreSIG) {
 
 TEST_F(QueryTest, dnssecPositive) {
     // Just like exactMatch, but the signatures should be included as well
-    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response,
+    EXPECT_NO_THROW(query.process(list, qname, qtype, response,
                                   true));
     // find match rrset
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6,
@@ -1009,7 +1045,7 @@ TEST_F(QueryTest, dnssecPositive) {
 TEST_F(QueryTest, exactAddrMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(query.process(memory_client,
+    EXPECT_NO_THROW(query.process(list,
                                   Name("noglue.example.com"),
                                   qtype, response));
 
@@ -1022,7 +1058,7 @@ TEST_F(QueryTest, exactAddrMatch) {
 TEST_F(QueryTest, apexNSMatch) {
     // find match rrset, omit authority data which has already been provided
     // in the answer section from the authority section.
-    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+    EXPECT_NO_THROW(query.process(list, Name("example.com"),
                                   RRType::NS(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 0, 3,
@@ -1033,7 +1069,7 @@ TEST_F(QueryTest, apexNSMatch) {
 TEST_F(QueryTest, exactAnyMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(query.process(memory_client, Name("noglue.example.com"),
+    EXPECT_NO_THROW(query.process(list, Name("noglue.example.com"),
                                   RRType::ANY(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 3, 2,
@@ -1047,7 +1083,7 @@ TEST_F(QueryTest, exactAnyMatch) {
 TEST_F(QueryTest, apexAnyMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+    EXPECT_NO_THROW(query.process(list, Name("example.com"),
                                   RRType::ANY(), response));
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 5, 0, 3,
                   (string(soa_txt) + string(zone_ns_txt) +
@@ -1056,7 +1092,7 @@ TEST_F(QueryTest, apexAnyMatch) {
 }
 
 TEST_F(QueryTest, mxANYMatch) {
-    EXPECT_NO_THROW(query.process(memory_client, Name("mx.example.com"),
+    EXPECT_NO_THROW(query.process(list, Name("mx.example.com"),
                                   RRType::ANY(), response));
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 4, 3, 4,
                   (string(mx_txt) + string(nsec_mx_txt)).c_str(), zone_ns_txt,
@@ -1064,14 +1100,14 @@ TEST_F(QueryTest, mxANYMatch) {
 }
 
 TEST_F(QueryTest, glueANYMatch) {
-    EXPECT_NO_THROW(query.process(memory_client, Name("delegation.example.com"),
+    EXPECT_NO_THROW(query.process(list, Name("delegation.example.com"),
                                   RRType::ANY(), response));
     responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
                   NULL, delegation_txt, ns_addrs_txt);
 }
 
 TEST_F(QueryTest, nodomainANY) {
-    EXPECT_NO_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+    EXPECT_NO_THROW(query.process(list, Name("nxdomain.example.com"),
                                   RRType::ANY(), response));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
                   NULL, soa_txt, NULL, mock_finder->getOrigin());
@@ -1084,13 +1120,13 @@ TEST_F(QueryTest, noApexNS) {
     // Disable apex NS record
     mock_finder->setApexNSFlag(false);
 
-    EXPECT_THROW(query.process(memory_client, Name("noglue.example.com"), qtype,
+    EXPECT_THROW(query.process(list, Name("noglue.example.com"), qtype,
                                response), Query::NoApexNS);
     // We don't look into the response, as it threw
 }
 
 TEST_F(QueryTest, delegation) {
-    EXPECT_NO_THROW(query.process(memory_client,
+    EXPECT_NO_THROW(query.process(list,
                                   Name("delegation.example.com"),
                                   qtype, response));
 
@@ -1102,7 +1138,7 @@ TEST_F(QueryTest, delegationWithDNSSEC) {
     // Similar to the previous one, but with requesting DNSSEC.
     // In this case the parent zone would behave as unsigned, so the result
     // should be just like non DNSSEC delegation.
-    query.process(memory_client, Name("www.nosec-delegation.example.com"),
+    query.process(list, Name("www.nosec-delegation.example.com"),
                   qtype, response, true);
 
     responseCheck(response, Rcode::NOERROR(), 0, 0, 1, 0,
@@ -1110,7 +1146,7 @@ TEST_F(QueryTest, delegationWithDNSSEC) {
 }
 
 TEST_F(QueryTest, secureDelegation) {
-    EXPECT_NO_THROW(query.process(memory_client,
+    EXPECT_NO_THROW(query.process(list,
                                   Name("foo.signed-delegation.example.com"),
                                   qtype, response, true));
 
@@ -1125,7 +1161,7 @@ TEST_F(QueryTest, secureDelegation) {
 }
 
 TEST_F(QueryTest, secureUnsignedDelegation) {
-    EXPECT_NO_THROW(query.process(memory_client,
+    EXPECT_NO_THROW(query.process(list,
                                   Name("foo.unsigned-delegation.example.com"),
                                   qtype, response, true));
 
@@ -1146,7 +1182,7 @@ TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3) {
     mock_finder->setNSEC3Flag(true);
     mock_finder->addRecord(unsigned_delegation_nsec3_txt);
 
-    query.process(memory_client,
+    query.process(list,
                   Name("foo.unsigned-delegation.example.com"),
                   qtype, response, true);
 
@@ -1165,7 +1201,7 @@ TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3OptOut) {
     // Similar to the previous case, but the delegation is an optout.
     mock_finder->setNSEC3Flag(true);
 
-    query.process(memory_client,
+    query.process(list,
                   Name("foo.unsigned-delegation.example.com"),
                   qtype, response, true);
 
@@ -1190,20 +1226,20 @@ TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3OptOut) {
 TEST_F(QueryTest, badSecureDelegation) {
     // Test whether exception is raised if DS query at delegation results in
     // something different than SUCCESS or NXRRSET
-    EXPECT_THROW(query.process(memory_client,
+    EXPECT_THROW(query.process(list,
                                Name("bad-delegation.example.com"),
                                qtype, response, true), Query::BadDS);
 
     // But only if DNSSEC is requested (it shouldn't even try to look for
     // the DS otherwise)
-    EXPECT_NO_THROW(query.process(memory_client,
+    EXPECT_NO_THROW(query.process(list,
                                   Name("bad-delegation.example.com"),
                                   qtype, response));
 }
 
 
 TEST_F(QueryTest, nxdomain) {
-    EXPECT_NO_THROW(query.process(memory_client,
+    EXPECT_NO_THROW(query.process(list,
                                   Name("nxdomain.example.com"), qtype,
                                   response));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
@@ -1214,7 +1250,7 @@ TEST_F(QueryTest, nxdomainWithNSEC) {
     // NXDOMAIN with DNSSEC proof.  We should have SOA, NSEC that proves
     // NXDOMAIN and NSEC that proves nonexistence of matching wildcard,
     // as well as their RRSIGs.
-    EXPECT_NO_THROW(query.process(memory_client,
+    EXPECT_NO_THROW(query.process(list,
                                   Name("nxdomain.example.com"), qtype,
                                   response, true));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
@@ -1235,7 +1271,7 @@ TEST_F(QueryTest, nxdomainWithNSEC2) {
     // is derived from the next domain of the NSEC that proves NXDOMAIN, and
     // the NSEC to provide the non existence of wildcard is different from
     // the first NSEC.
-    query.process(memory_client, Name("(.no.example.com"), qtype, response,
+    query.process(list, Name("(.no.example.com"), qtype, response,
                   true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
                   NULL, (string(soa_txt) +
@@ -1253,7 +1289,7 @@ TEST_F(QueryTest, nxdomainWithNSEC2) {
 TEST_F(QueryTest, nxdomainWithNSECDuplicate) {
     // See comments about nz_txt.  In this case we only need one NSEC,
     // which proves both NXDOMAIN and the non existence of wildcard.
-    query.process(memory_client, Name("nx.no.example.com"), qtype, response,
+    query.process(list, Name("nx.no.example.com"), qtype, response,
                   true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 4, 0,
                   NULL, (string(soa_txt) +
@@ -1270,7 +1306,7 @@ TEST_F(QueryTest, nxdomainBadNSEC1) {
     mock_finder->setNSECResult(Name("badnsec.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(query.process(memory_client, Name("badnsec.example.com"),
+    EXPECT_THROW(query.process(list, Name("badnsec.example.com"),
                                qtype, response, true),
                  std::bad_cast);
 }
@@ -1280,7 +1316,7 @@ TEST_F(QueryTest, nxdomainBadNSEC2) {
     mock_finder->setNSECResult(Name("emptynsec.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(query.process(memory_client, Name("emptynsec.example.com"),
+    EXPECT_THROW(query.process(list, Name("emptynsec.example.com"),
                                qtype, response, true),
                  Query::BadNSEC);
 }
@@ -1290,7 +1326,7 @@ TEST_F(QueryTest, nxdomainBadNSEC3) {
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::SUCCESS,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"),
                                qtype, response, true),
                  Query::BadNSEC);
 }
@@ -1299,7 +1335,7 @@ TEST_F(QueryTest, nxdomainBadNSEC4) {
     // "no-wildcard proof" doesn't return RRset.
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::NXDOMAIN, ConstRRsetPtr());
-    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"),
                                qtype, response, true),
                  Query::BadNSEC);
 }
@@ -1310,7 +1346,7 @@ TEST_F(QueryTest, nxdomainBadNSEC5) {
                                ZoneFinder::NXDOMAIN,
                                mock_finder->dname_rrset_);
     // This is a bit odd, but we'll simply include the returned RRset.
-    query.process(memory_client, Name("nxdomain.example.com"), qtype,
+    query.process(list, Name("nxdomain.example.com"), qtype,
                   response, true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
                   NULL, (string(soa_txt) +
@@ -1330,13 +1366,13 @@ TEST_F(QueryTest, nxdomainBadNSEC6) {
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"),
                                qtype, response, true),
                  Query::BadNSEC);
 }
 
 TEST_F(QueryTest, nxrrset) {
-    EXPECT_NO_THROW(query.process(memory_client, Name("www.example.com"),
+    EXPECT_NO_THROW(query.process(list, Name("www.example.com"),
                                   RRType::TXT(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
@@ -1346,7 +1382,7 @@ TEST_F(QueryTest, nxrrset) {
 TEST_F(QueryTest, nxrrsetWithNSEC) {
     // NXRRSET with DNSSEC proof.  We should have SOA, NSEC that proves the
     // NXRRSET and their RRSIGs.
-    query.process(memory_client, Name("www.example.com"), RRType::TXT(),
+    query.process(list, Name("www.example.com"), RRType::TXT(),
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
@@ -1367,7 +1403,7 @@ TEST_F(QueryTest, emptyNameWithNSEC) {
     // exact match), so we only need one NSEC.
     // From the point of the Query::process(), this is actually no different
     // from the other NXRRSET case, but we check that explicitly just in case.
-    query.process(memory_client, Name("no.example.com"), RRType::A(),
+    query.process(list, Name("no.example.com"), RRType::A(),
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
@@ -1383,7 +1419,7 @@ TEST_F(QueryTest, nxrrsetWithoutNSEC) {
     // NXRRSET with DNSSEC proof requested, but there's no NSEC at that node.
     // This is an unexpected event (if the zone is supposed to be properly
     // signed with NSECs), but we accept and ignore the oddity.
-    query.process(memory_client, Name("nonsec.example.com"), RRType::TXT(),
+    query.process(list, Name("nonsec.example.com"), RRType::TXT(),
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
@@ -1395,7 +1431,7 @@ TEST_F(QueryTest, nxrrsetWithoutNSEC) {
 TEST_F(QueryTest, wildcardNSEC) {
     // The qname matches *.wild.example.com.  The response should contain
     // an NSEC that proves the non existence of a closer name.
-    query.process(memory_client, Name("www.wild.example.com"), RRType::A(),
+    query.process(list, Name("www.wild.example.com"), RRType::A(),
                   response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 6, 6,
                   (string(wild_txt).replace(0, 1, "www") +
@@ -1415,7 +1451,7 @@ TEST_F(QueryTest, wildcardNSEC) {
 TEST_F(QueryTest, CNAMEwildNSEC) {
     // Similar to the previous case, but the matching wildcard record is
     // CNAME.
-    query.process(memory_client, Name("www.cnamewild.example.com"),
+    query.process(list, Name("www.cnamewild.example.com"),
                   RRType::A(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
                   (string(cnamewild_txt).replace(0, 1, "www") +
@@ -1438,7 +1474,7 @@ TEST_F(QueryTest, wildcardNSEC3) {
     // of identifying the next closer name.
     mock_finder->addRecord(nsec3_atwild_txt);
 
-    query.process(memory_client, Name("x.y.wild.example.com"), RRType::A(),
+    query.process(list, Name("x.y.wild.example.com"), RRType::A(),
                   response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 6, 6,
                   (string(wild_txt).replace(0, 1, "x.y") +
@@ -1463,7 +1499,7 @@ TEST_F(QueryTest, CNAMEwildNSEC3) {
     mock_finder->setNSEC3Flag(true);
     mock_finder->addRecord(nsec3_atcnamewild_txt);
 
-    query.process(memory_client, Name("www.cnamewild.example.com"),
+    query.process(list, Name("www.cnamewild.example.com"),
                   RRType::A(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
                   (string(cnamewild_txt).replace(0, 1, "www") +
@@ -1486,7 +1522,7 @@ TEST_F(QueryTest, badWildcardNSEC3) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+    EXPECT_THROW(query.process(list, Name("www.wild.example.com"),
                                RRType::A(), response, true),
                  Query::BadNSEC3);
 }
@@ -1497,7 +1533,7 @@ TEST_F(QueryTest, badWildcardProof1) {
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::SUCCESS,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+    EXPECT_THROW(query.process(list, Name("www.wild.example.com"),
                                RRType::A(), response, true),
                  Query::BadNSEC);
 }
@@ -1506,7 +1542,7 @@ TEST_F(QueryTest, badWildcardProof2) {
     // "wildcard proof" doesn't return RRset.
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::NXDOMAIN, ConstRRsetPtr());
-    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+    EXPECT_THROW(query.process(list, Name("www.wild.example.com"),
                                RRType::A(), response, true),
                  Query::BadNSEC);
 }
@@ -1516,7 +1552,7 @@ TEST_F(QueryTest, badWildcardProof3) {
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+    EXPECT_THROW(query.process(list, Name("www.wild.example.com"),
                                RRType::A(), response, true),
                  Query::BadNSEC);
 }
@@ -1525,7 +1561,7 @@ TEST_F(QueryTest, wildcardNxrrsetWithDuplicateNSEC) {
     // NXRRSET on WILDCARD with DNSSEC proof.  We should have SOA, NSEC that
     // proves the NXRRSET and their RRSIGs. In this case we only need one NSEC,
     // which proves both NXDOMAIN and the non existence RRSETs of wildcard.
-    query.process(memory_client, Name("www.wild.example.com"), RRType::TXT(),
+    query.process(list, Name("www.wild.example.com"), RRType::TXT(),
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
@@ -1542,7 +1578,7 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC) {
     // proves the NXRRSET and their RRSIGs. In this case we need two NSEC RRs,
     // one proves NXDOMAIN and the other proves non existence RRSETs of
     // wildcard.
-    query.process(memory_client, Name("www1.uwild.example.com"),
+    query.process(list, Name("www1.uwild.example.com"),
                   RRType::TXT(), response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
@@ -1565,7 +1601,7 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3) {
     mock_finder->addRecord(nsec3_uwild_txt);
     mock_finder->setNSEC3Flag(true);
 
-    query.process(memory_client, Name("www1.uwild.example.com"),
+    query.process(list, Name("www1.uwild.example.com"),
                   RRType::TXT(), response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 8, 0, NULL,
@@ -1599,7 +1635,7 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Collision) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(query.process(memory_client, Name("www1.uwild.example.com"),
+    EXPECT_THROW(query.process(list, Name("www1.uwild.example.com"),
                                RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
@@ -1616,7 +1652,7 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Broken) {
     mock_finder->addRecord(nsec3_wild_txt);
     mock_finder->addRecord(nsec3_uwild_txt);
 
-    EXPECT_THROW(query.process(memory_client, Name("www1.uwild.example.com"),
+    EXPECT_THROW(query.process(list, Name("www1.uwild.example.com"),
                                RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
@@ -1625,7 +1661,7 @@ TEST_F(QueryTest, wildcardEmptyWithNSEC) {
     // Empty WILDCARD with DNSSEC proof.  We should have SOA, NSEC that proves
     // the NXDOMAIN and their RRSIGs. In this case we need two NSEC RRs,
     // one proves NXDOMAIN and the other proves non existence wildcard.
-    query.process(memory_client, Name("a.t.example.com"), RRType::A(),
+    query.process(list, Name("a.t.example.com"), RRType::A(),
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
@@ -1649,19 +1685,19 @@ TEST_F(QueryTest, noSOA) {
     mock_finder->setSOAFlag(false);
 
     // The NX Domain
-    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"),
                                qtype, response), Query::NoSOA);
     // Of course, we don't look into the response, as it throwed
 
     // NXRRSET
-    EXPECT_THROW(query.process(memory_client, Name("nxrrset.example.com"),
+    EXPECT_THROW(query.process(list, Name("nxrrset.example.com"),
                                qtype, response), Query::NoSOA);
 }
 
 TEST_F(QueryTest, noMatchZone) {
     // there's a zone in the memory datasource but it doesn't match the qname.
     // should result in REFUSED.
-    query.process(memory_client, Name("example.org"), qtype, response);
+    query.process(list, Name("example.org"), qtype, response);
     EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
 }
 
@@ -1672,7 +1708,7 @@ TEST_F(QueryTest, noMatchZone) {
  * A record, other to unknown out of zone one.
  */
 TEST_F(QueryTest, MX) {
-    query.process(memory_client, Name("mx.example.com"), RRType::MX(),
+    query.process(list, Name("mx.example.com"), RRType::MX(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 3, 4,
@@ -1686,7 +1722,7 @@ TEST_F(QueryTest, MX) {
  * This should not trigger the additional processing for the exchange.
  */
 TEST_F(QueryTest, MXAlias) {
-    query.process(memory_client, Name("cnamemx.example.com"), RRType::MX(),
+    query.process(list, Name("cnamemx.example.com"), RRType::MX(),
                   response);
 
     // there shouldn't be no additional RRs for the exchanges (we have 3
@@ -1706,7 +1742,7 @@ TEST_F(QueryTest, MXAlias) {
  * returned.
  */
 TEST_F(QueryTest, CNAME) {
-    query.process(memory_client, Name("cname.example.com"), RRType::A(),
+    query.process(list, Name("cname.example.com"), RRType::A(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
@@ -1716,7 +1752,7 @@ TEST_F(QueryTest, CNAME) {
 TEST_F(QueryTest, explicitCNAME) {
     // same owner name as the CNAME test but explicitly query for CNAME RR.
     // expect the same response as we don't provide a full chain yet.
-    query.process(memory_client, Name("cname.example.com"), RRType::CNAME(),
+    query.process(list, Name("cname.example.com"), RRType::CNAME(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
@@ -1728,7 +1764,7 @@ TEST_F(QueryTest, CNAME_NX_RRSET) {
     // note: with chaining, what should be expected is not trivial:
     // BIND 9 returns the CNAME in answer and SOA in authority, no additional.
     // NSD returns the CNAME, NS in authority, A/AAAA for NS in additional.
-    query.process(memory_client, Name("cname.example.com"), RRType::TXT(),
+    query.process(list, Name("cname.example.com"), RRType::TXT(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
@@ -1737,7 +1773,7 @@ TEST_F(QueryTest, CNAME_NX_RRSET) {
 
 TEST_F(QueryTest, explicitCNAME_NX_RRSET) {
     // same owner name as the NXRRSET test but explicitly query for CNAME RR.
-    query.process(memory_client, Name("cname.example.com"), RRType::CNAME(),
+    query.process(list, Name("cname.example.com"), RRType::CNAME(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
@@ -1751,7 +1787,7 @@ TEST_F(QueryTest, CNAME_NX_DOMAIN) {
     // RCODE being NXDOMAIN.
     // NSD returns the CNAME, NS in authority, A/AAAA for NS in additional,
     // RCODE being NOERROR.
-    query.process(memory_client, Name("cnamenxdom.example.com"), RRType::A(),
+    query.process(list, Name("cnamenxdom.example.com"), RRType::A(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
@@ -1760,7 +1796,7 @@ TEST_F(QueryTest, CNAME_NX_DOMAIN) {
 
 TEST_F(QueryTest, explicitCNAME_NX_DOMAIN) {
     // same owner name as the NXDOMAIN test but explicitly query for CNAME RR.
-    query.process(memory_client, Name("cnamenxdom.example.com"),
+    query.process(list, Name("cnamenxdom.example.com"),
                   RRType::CNAME(), response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
@@ -1776,7 +1812,7 @@ TEST_F(QueryTest, CNAME_OUT) {
      * Then the same test should be done with .org included there and
      * see what it does (depends on what we want to do)
      */
-    query.process(memory_client, Name("cnameout.example.com"), RRType::A(),
+    query.process(list, Name("cnameout.example.com"), RRType::A(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
@@ -1785,7 +1821,7 @@ TEST_F(QueryTest, CNAME_OUT) {
 
 TEST_F(QueryTest, explicitCNAME_OUT) {
     // same owner name as the OUT test but explicitly query for CNAME RR.
-    query.process(memory_client, Name("cnameout.example.com"), RRType::CNAME(),
+    query.process(list, Name("cnameout.example.com"), RRType::CNAME(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
@@ -1801,7 +1837,7 @@ TEST_F(QueryTest, explicitCNAME_OUT) {
  * pointing to NXRRSET and NXDOMAIN cases (similarly as with CNAME).
  */
 TEST_F(QueryTest, DNAME) {
-    query.process(memory_client, Name("www.dname.example.com"), RRType::A(),
+    query.process(list, Name("www.dname.example.com"), RRType::A(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
@@ -1817,7 +1853,7 @@ TEST_F(QueryTest, DNAME) {
  * DNAME.
  */
 TEST_F(QueryTest, DNAME_ANY) {
-    query.process(memory_client, Name("www.dname.example.com"), RRType::ANY(),
+    query.process(list, Name("www.dname.example.com"), RRType::ANY(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
@@ -1826,7 +1862,7 @@ TEST_F(QueryTest, DNAME_ANY) {
 
 // Test when we ask for DNAME explicitly, it does no synthetizing.
 TEST_F(QueryTest, explicitDNAME) {
-    query.process(memory_client, Name("dname.example.com"), RRType::DNAME(),
+    query.process(list, Name("dname.example.com"), RRType::DNAME(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
@@ -1838,7 +1874,7 @@ TEST_F(QueryTest, explicitDNAME) {
  * the CNAME, it should return the RRset.
  */
 TEST_F(QueryTest, DNAME_A) {
-    query.process(memory_client, Name("dname.example.com"), RRType::A(),
+    query.process(list, Name("dname.example.com"), RRType::A(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
@@ -1850,7 +1886,7 @@ TEST_F(QueryTest, DNAME_A) {
  * It should not synthetize the CNAME.
  */
 TEST_F(QueryTest, DNAME_NX_RRSET) {
-    EXPECT_NO_THROW(query.process(memory_client, Name("dname.example.com"),
+    EXPECT_NO_THROW(query.process(list, Name("dname.example.com"),
                     RRType::TXT(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
@@ -1870,7 +1906,7 @@ TEST_F(QueryTest, LongDNAME) {
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "dname.example.com.");
-    EXPECT_NO_THROW(query.process(memory_client, longname, RRType::A(),
+    EXPECT_NO_THROW(query.process(list, longname, RRType::A(),
                     response));
 
     responseCheck(response, Rcode::YXDOMAIN(), AA_FLAG, 1, 0, 0,
@@ -1889,7 +1925,7 @@ TEST_F(QueryTest, MaxLenDNAME) {
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "dname.example.com.");
-    EXPECT_NO_THROW(query.process(memory_client, longname, RRType::A(),
+    EXPECT_NO_THROW(query.process(list, longname, RRType::A(),
                     response));
 
     // Check the answer is OK
@@ -2075,7 +2111,7 @@ TEST_F(QueryTest, dsAboveDelegation) {
 
     // The following will succeed only if the search goes to the parent
     // zone, not the child one we added above.
-    EXPECT_NO_THROW(query.process(memory_client,
+    EXPECT_NO_THROW(query.process(list,
                                   Name("delegation.example.com"),
                                   RRType::DS(), response, true));
 
@@ -2099,7 +2135,7 @@ TEST_F(QueryTest, dsAboveDelegationNoData) {
 
     // The following will succeed only if the search goes to the parent
     // zone, not the child one we added above.
-    EXPECT_NO_THROW(query.process(memory_client,
+    EXPECT_NO_THROW(query.process(list,
                                   Name("unsigned-delegation.example.com"),
                                   RRType::DS(), response, true));
 
@@ -2117,7 +2153,7 @@ TEST_F(QueryTest, dsAboveDelegationNoData) {
 // when it happens to be sent to the child zone, as described in RFC 4035,
 // section 3.1.4.1. The example is inspired by the B.8. example from the RFC.
 TEST_F(QueryTest, dsBelowDelegation) {
-    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+    EXPECT_NO_THROW(query.process(list, Name("example.com"),
                                   RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
@@ -2134,7 +2170,7 @@ TEST_F(QueryTest, dsBelowDelegation) {
 // In our implementation NSEC/NSEC3 isn't attached in this case.
 TEST_F(QueryTest, dsBelowDelegationWithDS) {
     mock_finder->addRecord(zone_ds_txt); // add the DS to the child's apex
-    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+    EXPECT_NO_THROW(query.process(list, Name("example.com"),
                                   RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
@@ -2147,7 +2183,7 @@ TEST_F(QueryTest, dsBelowDelegationWithDS) {
 // server.  It should just like the "noZone" test case, but DS query involves
 // special processing, so we test it explicitly.
 TEST_F(QueryTest, dsNoZone) {
-    query.process(memory_client, Name("example"), RRType::DS(), response,
+    query.process(list, Name("example"), RRType::DS(), response,
                   true);
     responseCheck(response, Rcode::REFUSED(), 0, 0, 0, 0, NULL, NULL, NULL);
 }
@@ -2155,7 +2191,7 @@ TEST_F(QueryTest, dsNoZone) {
 // DS query for a "grandchild" zone.  This should result in normal
 // delegation (unless this server also has authority of the grandchild zone).
 TEST_F(QueryTest, dsAtGrandParent) {
-    query.process(memory_client, Name("grand.delegation.example.com"),
+    query.process(list, Name("grand.delegation.example.com"),
                   RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), 0, 0, 6, 6, NULL,
                   (string(delegation_txt) + string(delegation_ds_txt) +
@@ -2174,7 +2210,7 @@ TEST_F(QueryTest, dsAtGrandParentAndChild) {
     const Name childname("grand.delegation.example.com");
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(childname)));
-    query.process(memory_client, childname, RRType::DS(), response, true);
+    query.process(list, childname, RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (childname.toText() + " 3600 IN SOA . . 0 0 0 0 0\n" +
                    childname.toText() + " 3600 IN RRSIG " +
@@ -2192,7 +2228,7 @@ TEST_F(QueryTest, dsAtRoot) {
     // Pretend to be a root server.
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(Name::ROOT_NAME())));
-    query.process(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
+    query.process(list, Name::ROOT_NAME(), RRType::DS(), response,
                   true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(". 3600 IN SOA . . 0 0 0 0 0\n") +
@@ -2209,7 +2245,7 @@ TEST_F(QueryTest, dsAtRootWithDS) {
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(Name::ROOT_NAME(),
                                                       true)));
-    query.process(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
+    query.process(list, Name::ROOT_NAME(), RRType::DS(), response,
                   true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
                   (string(". 3600 IN DS 57855 5 1 49FD46E6C4B45C55D4AC69CBD"
@@ -2226,7 +2262,7 @@ TEST_F(QueryTest, nxrrsetWithNSEC3) {
 
     // NXRRSET with DNSSEC proof.  We should have SOA, NSEC3 that proves the
     // NXRRSET and their RRSIGs.
-    query.process(memory_client, Name("www.example.com"), RRType::TXT(),
+    query.process(list, Name("www.example.com"), RRType::TXT(),
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
@@ -2249,7 +2285,7 @@ TEST_F(QueryTest, nxrrsetMissingNSEC3) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(query.process(memory_client, Name("www.example.com"),
+    EXPECT_THROW(query.process(list, Name("www.example.com"),
                                RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
@@ -2260,7 +2296,7 @@ TEST_F(QueryTest, nxrrsetWithNSEC3_ds_exact) {
 
     // This delegation has no DS, but does have a matching NSEC3 record
     // (See RFC5155 section 7.2.4)
-    query.process(memory_client, Name("unsigned-delegation.example.com."),
+    query.process(list, Name("unsigned-delegation.example.com."),
                   RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -2282,7 +2318,7 @@ TEST_F(QueryTest, nxrrsetWithNSEC3_ds_no_exact) {
     // 'next closer' should have opt-out set, though that is not
     // actually checked)
     // (See RFC5155 section 7.2.4)
-    query.process(memory_client, Name("unsigned-delegation-optout.example.com."),
+    query.process(list, Name("unsigned-delegation-optout.example.com."),
                   RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -2309,7 +2345,7 @@ TEST_F(QueryTest, nxdomainWithNSEC3Proof) {
     // This will be the covering NSEC3 for the possible wildcard
     mock_finder->addRecord(unsigned_delegation_nsec3_txt);
 
-    query.process(memory_client, Name("nxdomain.example.com"), qtype,
+    query.process(list, Name("nxdomain.example.com"), qtype,
                   response, true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 8, 0, NULL,
                   // SOA + its RRSIG
@@ -2344,7 +2380,7 @@ TEST_F(QueryTest, nxdomainWithBadNextNSEC3Proof) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"),
                                RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
@@ -2363,7 +2399,7 @@ TEST_F(QueryTest, nxdomainWithBadWildcardNSEC3Proof) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3, &wname);
 
-    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"), qtype,
+    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"), qtype,
                                response, true),
                  Query::BadNSEC3);
 }

+ 8 - 0
src/bin/auth/tests/testdata/.gitignore

@@ -0,0 +1,8 @@
+/badExampleQuery_fromWire.wire
+/examplequery_fromWire.wire
+/iqueryresponse_fromWire.wire
+/multiquestion_fromWire.wire
+/queryBadEDNS_fromWire.wire
+/shortanswer_fromWire.wire
+/simplequery_fromWire.wire
+/simpleresponse_fromWire.wire

+ 1 - 0
src/bin/auth/tests/testdata/Makefile.am

@@ -18,6 +18,7 @@ EXTRA_DIST += shortquestion_fromWire
 EXTRA_DIST += shortresponse_fromWire
 EXTRA_DIST += simplequery_fromWire.spec
 EXTRA_DIST += simpleresponse_fromWire.spec
+EXTRA_DIST += spec.spec
 
 EXTRA_DIST += example.com
 EXTRA_DIST += example.sqlite3

+ 5 - 0
src/bin/auth/tests/testdata/spec.spec

@@ -0,0 +1,5 @@
+{
+    "module_spec": {
+        "module_name": "test"
+    }
+}

File diff suppressed because it is too large
+ 1 - 8
src/bin/bind10/bind10.8


+ 0 - 11
src/bin/bind10/bind10.xml

@@ -47,7 +47,6 @@
       <arg><option>-c <replaceable>config-filename</replaceable></option></arg>
       <arg><option>-i</option></arg>
       <arg><option>-m <replaceable>file</replaceable></option></arg>
-      <arg><option>-n</option></arg>
       <arg><option>-p <replaceable>data_path</replaceable></option></arg>
       <arg><option>-u <replaceable>user</replaceable></option></arg>
       <arg><option>-v</option></arg>
@@ -57,7 +56,6 @@
       <arg><option>--config-file</option> <replaceable>config-filename</replaceable></arg>
       <arg><option>--data-path</option> <replaceable>directory</replaceable></arg>
       <arg><option>--msgq-socket-file <replaceable>file</replaceable></option></arg>
-      <arg><option>--no-cache</option></arg>
       <arg><option>--no-kill</option></arg>
       <arg><option>--pid-file</option> <replaceable>filename</replaceable></arg>
       <arg><option>--pretty-name <replaceable>name</replaceable></option></arg>
@@ -169,15 +167,6 @@
       </varlistentry>
 
       <varlistentry>
-        <term><option>-n</option>, <option>--no-cache</option></term>
-        <listitem>
-	  <para>Disables the hot-spot caching used by the
-	    <citerefentry><refentrytitle>b10-auth</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-	  daemon.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
         <term><option>-i</option>, <option>--no-kill</option></term>
         <listitem>
 	  <para>When this option is passed, <command>bind10</command>

+ 2 - 7
src/bin/bind10/bind10_src.py.in

@@ -168,7 +168,7 @@ class BoB:
     """Boss of BIND class."""
     
     def __init__(self, msgq_socket_file=None, data_path=None,
-                 config_filename=None, clear_config=False, nocache=False,
+                 config_filename=None, clear_config=False,
                  verbose=False, nokill=False, setuid=None, setgid=None,
                  username=None, cmdctl_port=None, wait_time=10):
         """
@@ -192,7 +192,6 @@ class BoB:
         self.ccs = None
         self.curproc = None
         self.msgq_socket_file = msgq_socket_file
-        self.nocache = nocache
         self.component_config = {}
         # Some time in future, it may happen that a single component has
         # multple processes (like a pipeline-like component). If so happens,
@@ -568,8 +567,6 @@ class BoB:
         if self.uid is not None and self.__started:
             logger.warn(BIND10_START_AS_NON_ROOT_AUTH)
         authargs = ['b10-auth']
-        if self.nocache:
-            authargs += ['-n']
         if self.verbose:
             authargs += ['-v']
 
@@ -1052,8 +1049,6 @@ def parse_args(args=sys.argv[1:], Parser=OptionParser):
     parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
                       type="string", default=None,
                       help="UNIX domain socket file the b10-msgq daemon will use")
-    parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
-                      default=False, help="disable hot-spot cache in authoritative DNS server")
     parser.add_option("-i", "--no-kill", action="store_true", dest="nokill",
                       default=False, help="do not send SIGTERM and SIGKILL signals to modules during shutdown")
     parser.add_option("-u", "--user", dest="user", type="string", default=None,
@@ -1208,7 +1203,7 @@ def main():
         # Go bob!
         boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
                            options.config_file, options.clear_config,
-                           options.nocache, options.verbose, options.nokill,
+                           options.verbose, options.nokill,
                            setuid, setgid, username, options.cmdctl_port,
                            options.wait_time)
         startup_result = boss_of_bind.startup()

+ 0 - 2
src/bin/bind10/tests/bind10_test.py.in

@@ -349,7 +349,6 @@ class TestBoB(unittest.TestCase):
         self.assertEqual(bob.runnable, False)
         self.assertEqual(bob.uid, None)
         self.assertEqual(bob.username, None)
-        self.assertEqual(bob.nocache, False)
         self.assertIsNone(bob._socket_cache)
 
     def test_set_creator(self):
@@ -377,7 +376,6 @@ class TestBoB(unittest.TestCase):
         self.assertEqual(bob.runnable, False)
         self.assertEqual(bob.uid, None)
         self.assertEqual(bob.username, None)
-        self.assertEqual(bob.nocache, False)
 
     def test_command_handler(self):
         class DummySession():

+ 13 - 4
src/bin/bindctl/bindctl.1

@@ -9,6 +9,15 @@
 .\"
 .TH "BINDCTL" "1" "June 20, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" disable hyphenation
@@ -54,7 +63,7 @@ The PEM formatted server certificate validation chain file\&.
 .PP
 \fB\-\-csv\-file\-dir\fR\fIfile\fR
 .RS 4
-The directory name in which the user/password CSV file is stored (see AUTHENTICATION)\&. By default this option doesn\'t have any value, in which case the "\&.bind10" directory under the user\'s home directory will be used\&.
+The directory name in which the user/password CSV file is stored (see AUTHENTICATION)\&. By default this option doesn\*(Aqt have any value, in which case the "\&.bind10" directory under the user\*(Aqs home directory will be used\&.
 .RE
 .PP
 \fB\-h\fR, \fB\-\-help\fR
@@ -105,17 +114,17 @@ prompt shows
 \fIparam1 = value1 , \fR\fI\fIparam2 = value2\fR\fR
 .PP
 
-\fBbindctl\fR\'s interactive interface provides command\-line completion and hints\&. Press the Tab key to get a hint for the module, command, and/or parameters\&.
+\fBbindctl\fR\*(Aqs interactive interface provides command\-line completion and hints\&. Press the Tab key to get a hint for the module, command, and/or parameters\&.
 The arrow keys and Emacs\-style editing keys may be used to edit and recall previous lines\&.
 .PP
 You can use the
 \fBhelp\fR
-keyword to receive usage assistance for a module or a module\'s command\&.
+keyword to receive usage assistance for a module or a module\*(Aqs command\&.
 .PP
 The
 \fBquit\fR
 command is used to exit
-\fBbindctl\fR\&. (It doesn\'t stop the BIND 10 services\&.)
+\fBbindctl\fR\&. (It doesn\*(Aqt stop the BIND 10 services\&.)
 .PP
 The following module is available by default:
 \fBconfig\fR

+ 2 - 1
src/bin/cfgmgr/Makefile.am

@@ -26,8 +26,9 @@ b10-cfgmgr: b10-cfgmgr.py
 
 install-data-local:
 	$(mkinstalldirs) $(DESTDIR)/@localstatedir@/@PACKAGE@
-# TODO: permissions handled later
 
+install-data-hook:
+	-chmod 2770 $(DESTDIR)/@localstatedir@/@PACKAGE@
 
 CLEANDIRS = __pycache__
 

+ 2 - 0
src/bin/cfgmgr/plugins/.gitignore

@@ -0,0 +1,2 @@
+/datasrc.spec
+/datasrc.spec.pre

+ 6 - 3
src/bin/cfgmgr/plugins/Makefile.am

@@ -2,13 +2,16 @@ SUBDIRS = tests
 
 EXTRA_DIST = README logging.spec tsig_keys.spec
 
+datasrc.spec: datasrc.spec.pre
+	$(SED) -e "s|@@PKGDATADIR@@|$(pkgdatadir)|;s|@@LOCALSTATEDIR@@|$(localstatedir)|" datasrc.spec.pre >$@
+
 config_plugindir = @prefix@/share/@PACKAGE@/config_plugins
-config_plugin_DATA = logging.spec tsig_keys.spec
+config_plugin_DATA = logging.spec tsig_keys.spec datasrc.spec
 
-python_PYTHON = b10logging.py tsig_keys.py
+python_PYTHON = b10logging.py tsig_keys.py datasrc_config_plugin.py
 pythondir = $(config_plugindir)
 
-CLEANFILES = b10logging.pyc tsig_keys.pyc
+CLEANFILES = b10logging.pyc tsig_keys.pyc datasrc.spec
 CLEANDIRS = __pycache__
 
 clean-local:

+ 74 - 0
src/bin/cfgmgr/plugins/datasrc.spec.pre.in

@@ -0,0 +1,74 @@
+{
+    "module_spec": {
+        "module_name": "data_sources",
+        "module_description": "The sources of authoritative DNS data",
+        "config_data": [
+            {
+                "item_name": "classes",
+                "item_type": "named_set",
+                "item_optional": false,
+                "item_default": {
+                    "IN": [
+                        {
+                            "type": "sqlite3",
+                            "params": {
+                                "database_file": "@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3"
+                            }
+                        }
+                    ],
+                    "CH": [
+                        {
+                            "type": "static",
+                            "cache-enable": false,
+                            "params": "@@PKGDATADIR@@/static.zone"
+                        }
+                    ]
+                },
+                "named_set_item_spec": {
+                    "item_name": "class",
+                    "item_type": "list",
+                    "item_optional": false,
+                    "item_default": [],
+                    "list_item_spec": {
+                        "item_name": "source",
+                        "item_type": "map",
+                        "item_optional": false,
+                        "item_default": {},
+                        "map_item_spec": [
+                            {
+                                "item_name": "type",
+                                "item_type": "string",
+                                "item_optional": false,
+                                "item_default": ""
+                            },
+                            {
+                                "item_name": "params",
+                                "item_type": "any",
+                                "item_optional": false,
+                                "item_default": null
+                            },
+                            {
+                                "item_name": "cache-enable",
+                                "item_type": "boolean",
+                                "item_optional": false,
+                                "item_default": false
+                            },
+                            {
+                                "item_name": "cache-zones",
+                                "item_type": "list",
+                                "item_optional": true,
+                                "list_item_spec": {
+                                    "item_name": "zone",
+                                    "item_type": "string",
+                                    "item_optional": false,
+                                    "item_default": ""
+                                }
+                            }
+                        ]
+                    }
+                }
+            }
+        ],
+        "commands": []
+    }
+}

+ 80 - 0
src/bin/cfgmgr/plugins/datasrc_config_plugin.py

@@ -0,0 +1,80 @@
+# Copyright (C) 2012  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from isc.config.module_spec import module_spec_from_file
+from isc.util.file import path_search
+from bind10_config import PLUGIN_PATHS
+import isc.dns
+import isc.datasrc
+import json
+import os.path
+import copy
+
+spec = module_spec_from_file(path_search('datasrc.spec', PLUGIN_PATHS))
+
+def check(config):
+    """
+    Check the configuration.
+    """
+    # Check the data layout first
+    errors=[]
+    if not spec.validate_config(False, config, errors):
+        return ' '.join(errors)
+
+    classes = config.get('classes')
+    # Nothing passed here
+    if classes is None:
+        return None
+
+    for rr_class_str in classes:
+        try:
+            rr_class = isc.dns.RRClass(rr_class_str)
+        except isc.dns.InvalidRRClass as irc:
+            return "The class '" + rr_class_str + "' is invalid"
+
+        dlist = isc.datasrc.ConfigurableClientList(rr_class)
+        # We get a copy here, as we are going to mangle the configuration.
+        # But we don't want our changes to propagate outside, to the real
+        # configuration.
+        client_config = copy.deepcopy(classes.get(rr_class_str))
+
+        for client in client_config:
+            if client['type'] == 'MasterFiles':
+                if not client.get('cache-enable', False):
+                    return 'The cache must be enabled in MasterFiles type'
+                params = client.get('params')
+                if type(params) != dict:
+                    return 'Params of MasterFiles must be a named set'
+                for name in params:
+                    try:
+                        isc.dns.Name(name)
+                    except Exception as e: # There are many related exceptions
+                        return str(e)
+                    if not os.path.exists(params[name]):
+                        return "Master file " + params[name] + " does not exist"
+                # We remove the list of zones locally. We already checked them,
+                # and the client list would have skipped them anyway, as we
+                # forbid cache. But it would produce a warning and we don't
+                # want that here.
+                client['params'] = {}
+
+        try:
+            dlist.configure(json.dumps(client_config), False)
+        except isc.datasrc.Error as dse:
+            return str(dse)
+    return None
+
+def load():
+    return (spec, check)

+ 1 - 1
src/bin/cfgmgr/plugins/tests/Makefile.am

@@ -1,5 +1,5 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = tsig_keys_test.py logging_test.py
+PYTESTS = tsig_keys_test.py logging_test.py datasrc_test.py
 
 EXTRA_DIST = $(PYTESTS)
 

+ 159 - 0
src/bin/cfgmgr/plugins/tests/datasrc_test.py

@@ -0,0 +1,159 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# Make sure we can load the module, put it into path
+import sys
+import os
+import unittest
+import json
+sys.path.extend(os.environ["B10_TEST_PLUGIN_DIR"].split(':'))
+import isc.log
+
+import datasrc_config_plugin
+
+class DatasrcTest(unittest.TestCase):
+    def reject(self, config):
+        """
+        Just a shortcut to check the config is rejected.
+        """
+        old = json.dumps(config)
+        self.assertIsNotNone(datasrc_config_plugin.check({"classes":
+                                                         config}))
+        # There's some data mangling inside the plugin. Check it does
+        # not propagate out, as it could change the real configuration.
+        self.assertEqual(old, json.dumps(config))
+
+    def accept(self, config):
+        """
+        Just a shortcut to check the config is accepted.
+        """
+        old = json.dumps(config)
+        self.assertIsNone(datasrc_config_plugin.check({"classes":
+                                                      config}))
+        # There's some data mangling inside the plugin. Check it does
+        # not propagate out, as it could change the real configuration.
+        self.assertEqual(old, json.dumps(config))
+
+    def test_load(self):
+        """
+        Checks the entry point returns the correct values.
+        """
+        (spec, check) = datasrc_config_plugin.load()
+        # It returns the checking function
+        self.assertEqual(check, datasrc_config_plugin.check)
+        # The plugin stores it's spec
+        self.assertEqual(spec, datasrc_config_plugin.spec)
+
+    def test_empty(self):
+        """
+        Check an empty input is OK.
+        """
+        self.accept({})
+
+    def test_invalid_spec(self):
+        """
+        Check it rejects stuff that is not well-formed according
+        to the spec.
+        """
+        self.reject("test")
+        self.reject(13)
+        self.reject([])
+        self.reject({"IN": {}})
+        self.reject({"IN": [{"bad-name": True}]})
+
+    def test_class(self):
+        """
+        The class is rejected, if it is wrong.
+        """
+        self.reject({"BAD": []})
+        self.reject({"": []})
+        # But with a good one, it works
+        for c in ["IN", "CH", "HS"]:
+            self.accept({c: []})
+
+    def test_mem_ok(self):
+        """
+        Test we accept an in-memory data source. It doesn't really matter
+        which one it is. We just want to make sure we accept something
+        and this one does not need any kind of path mangling to find
+        plugins.
+        """
+        self.accept({"IN": [{
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {}
+        }]})
+
+    def test_dstype_bad(self):
+        """
+        The configuration is correct by the spec, but it would be rejected
+        by the client list. Check we reject it.
+        """
+        self.reject({"IN": [{
+            "type": "No such type"
+        }]})
+
+    def test_invalid_mem_params(self):
+        """
+        The client list skips in-memory sources. So we check it locally that
+        invalid things are rejected.
+        """
+        # The 'params' key is mandatory for MasterFiles
+        self.reject({"IN": [{
+            "type": "MasterFiles",
+            "cache-enable": True
+        }]})
+        # The cache must be enabled
+        self.reject({"IN": [{
+            "type": "MasterFiles",
+            "cache-enable": False,
+            "params": {}
+        }]})
+        self.reject({"IN": [{
+            "type": "MasterFiles",
+            "params": {}
+        }]})
+        # Bad params type
+        self.reject({"IN": [{
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": []
+        }]})
+        # Bad name
+        self.reject({"IN": [{
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {
+                "example....org.": '/file/does/not/exist'
+            }
+        }]})
+
+    def test_no_such_file_mem(self):
+        """
+        We also check the existance of master files. Not the actual content,
+        though.
+        """
+        self.reject({"IN": [{
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {
+                "example.org.": '/file/does/not/exist'
+            }
+        }]})
+
+if __name__ == '__main__':
+    isc.log.init("bind10")
+    isc.log.resetUnitTestRootLogger()
+    unittest.main()

+ 9 - 0
src/bin/cmdctl/b10-cmdctl.8

@@ -9,6 +9,15 @@
 .\"
 .TH "B10\-CMDCTL" "8" "February 28, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" disable hyphenation

+ 17 - 21
src/bin/ddns/tests/ddns_test.py

@@ -1136,7 +1136,7 @@ class TestDDNSSession(unittest.TestCase):
         num_rrsets = len(self.__req_message.get_section(SECTION_PREREQUISITE))
         self.assertEqual(2, num_rrsets)
 
-    def check_session_msg(self, result, expect_recv=1, notify_auth=False):
+    def check_session_msg(self, result, expect_recv=2):
         '''Check post update communication with other modules.'''
         # iff the update succeeds, b10-ddns should tell interested other
         # modules the information about the update zone.  Possible modules
@@ -1145,32 +1145,28 @@ class TestDDNSSession(unittest.TestCase):
         #                         'zone_class', <updated_zone_class>}]}
         # for auth, it should be:
         # {'command': ['loadzone', {'origin': <updated_zone_name>,
-        #                           'class', <updated_zone_class>,
-        #                           'datasrc', <datasrc type, should be
-        #                                       "memory" in practice>}]}
+        #                           'class', <updated_zone_class>}]}
         # and expect an answer by calling group_recvmsg().
         #
         # expect_recv indicates the expected number of calls to
-        # group_recvmsg(), which is normally 1, but can be 0 if send fails;
+        # group_recvmsg(), which is normally 2, but can be 0 if send fails;
         # if the message is to be sent
         if result == UPDATE_SUCCESS:
-            expected_sentmsg = 2 if notify_auth else 1
+            expected_sentmsg = 2
             self.assertEqual(expected_sentmsg,
                              len(self.__cc_session._sent_msg))
             self.assertEqual(expect_recv, self.__cc_session._recvmsg_called)
             msg_cnt = 0
-            if notify_auth:
-                sent_msg, sent_group = self.__cc_session._sent_msg[msg_cnt]
-                sent_cmd = sent_msg['command']
-                self.assertEqual('Auth', sent_group)
-                self.assertEqual('loadzone', sent_cmd[0])
-                self.assertEqual(3, len(sent_cmd[1]))
-                self.assertEqual(TEST_ZONE_NAME.to_text(),
-                                 sent_cmd[1]['origin'])
-                self.assertEqual(TEST_RRCLASS.to_text(),
-                                 sent_cmd[1]['class'])
-                self.assertEqual('memory', sent_cmd[1]['datasrc'])
-                msg_cnt += 1
+            sent_msg, sent_group = self.__cc_session._sent_msg[msg_cnt]
+            sent_cmd = sent_msg['command']
+            self.assertEqual('Auth', sent_group)
+            self.assertEqual('loadzone', sent_cmd[0])
+            self.assertEqual(2, len(sent_cmd[1]))
+            self.assertEqual(TEST_ZONE_NAME.to_text(),
+                             sent_cmd[1]['origin'])
+            self.assertEqual(TEST_RRCLASS.to_text(),
+                             sent_cmd[1]['class'])
+            msg_cnt += 1
             sent_msg, sent_group = self.__cc_session._sent_msg[msg_cnt]
             sent_cmd = sent_msg['command']
             self.assertEqual('Xfrout', sent_group)
@@ -1267,21 +1263,21 @@ class TestDDNSSession(unittest.TestCase):
             [{'type': 'memory', 'class': 'IN', 'zones': [
                     {'origin': TEST_ZONE_NAME_STR, 'filetype': 'sqlite3'}]}]
         self.check_session()
-        self.check_session_msg(UPDATE_SUCCESS, expect_recv=2, notify_auth=True)
+        self.check_session_msg(UPDATE_SUCCESS)
 
         # Let sendmsg() raise an exception.  The first exception shouldn't
         # stop sending the second message.  There's just no recv calls.
         self.__cc_session.clear_msg()
         self.__cc_session._sendmsg_exception = SessionError('send error')
         self.check_session()
-        self.check_session_msg(UPDATE_SUCCESS, expect_recv=0, notify_auth=True)
+        self.check_session_msg(UPDATE_SUCCESS, expect_recv=0)
 
         # Likewise, in the case recvmsg() raises (and there should be recv
         # calls in this case)
         self.__cc_session.clear_msg()
         self.__cc_session._recvmsg_exception = SessionError('recv error')
         self.check_session()
-        self.check_session_msg(UPDATE_SUCCESS, expect_recv=2, notify_auth=True)
+        self.check_session_msg(UPDATE_SUCCESS)
 
     def test_session_with_config(self):
         '''Check a session with more realistic config setups.

+ 6 - 6
src/bin/dhcp4/Makefile.am

@@ -39,12 +39,12 @@ if USE_CLANGPP
 b10_dhcp4_CXXFLAGS = -Wno-unused-parameter
 endif
 
-b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la
-b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/liblog.la
-b10_dhcp4_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
-b10_dhcp4_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 
 
 b10_dhcp4dir = $(pkgdatadir)

+ 4 - 4
src/bin/dhcp4/ctrl_dhcp4_srv.cc

@@ -82,19 +82,19 @@ void ControlledDhcpv4Srv::sessionReader(void) {
 }
 
 void ControlledDhcpv4Srv::establishSession() {
-    
+
     string specfile;
     if (getenv("B10_FROM_BUILD")) {
         specfile = string(getenv("B10_FROM_BUILD")) +
-            "/src/bin/auth/dhcp4.spec";
+            "/src/bin/dhcp4/dhcp4.spec";
     } else {
         specfile = string(DHCP4_SPECFILE_LOCATION);
     }
 
     /// @todo: Check if session is not established already. Throw, if it is.
-    
+
     cout << "b10-dhcp4: my specfile is " << specfile << endl;
-    
+
     cc_session_ = new Session(io_service_.get_io_service());
 
     config_session_ = new ModuleCCSession(specfile, *cc_session_,

+ 12 - 7
src/bin/dhcp4/dhcp4_srv.cc

@@ -37,16 +37,21 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";
 Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
     cout << "Initialization: opening sockets on port " << port << endl;
 
-    // first call to instance() will create IfaceMgr (it's a singleton)
-    // it may throw something if things go wrong
-    IfaceMgr::instance();
+    try {
+        // first call to instance() will create IfaceMgr (it's a singleton)
+        // it may throw something if things go wrong
+        IfaceMgr::instance();
 
-    /// @todo: instantiate LeaseMgr here once it is imlpemented.
-    IfaceMgr::instance().printIfaces();
+        /// @todo: instantiate LeaseMgr here once it is imlpemented.
 
-    IfaceMgr::instance().openSockets4(port);
+        IfaceMgr::instance().openSockets4(port);
 
-    setServerID();
+        setServerID();
+    } catch (const std::exception &e) {
+        cerr << "Error during DHCPv4 server startup: " << e.what() << endl;
+        shutdown_ = true;
+        return;
+    }
 
     shutdown_ = false;
 }

+ 36 - 15
src/bin/dhcp4/main.cc

@@ -14,18 +14,14 @@
 
 #include <config.h>
 #include <iostream>
-#include <exceptions/exceptions.h>
 #include <log/dummylog.h>
 #include <log/logger_support.h>
 #include <dhcp4/ctrl_dhcp4_srv.h>
-#include <dhcp4/dhcp4_srv.h>
-#include <dhcp/dhcp4.h>
+#include <boost/lexical_cast.hpp>
 
 using namespace std;
 using namespace isc::dhcp;
 
-
-
 /// This file contains entry point (main() function) for standard DHCPv4 server
 /// component for BIND10 framework. It parses command-line arguments and
 /// instantiates ControlledDhcpv4Srv class that is responsible for establishing
@@ -44,7 +40,9 @@ usage() {
     cerr << "Usage:  b10-dhcp4 [-v]"
          << endl;
     cerr << "\t-v: verbose output" << endl;
-    cerr << "\t-p number: specify non-standard port number 1-65535 (useful for testing only)" << endl;
+    cerr << "\t-s: stand-alone mode (don't connect to BIND10)" << endl;
+    cerr << "\t-p number: specify non-standard port number 1-65535 "
+         << "(useful for testing only)" << endl;
     exit(EXIT_FAILURE);
 }
 } // end of anonymous namespace
@@ -55,16 +53,26 @@ main(int argc, char* argv[]) {
     bool verbose_mode = false; // should server be verbose?
     int port_number = DHCP4_SERVER_PORT; // The default. any other values are
                                          // useful for testing only.
+    bool stand_alone = false; // should be connect to BIND10 msgq?
 
-    while ((ch = getopt(argc, argv, "vp:")) != -1) {
+    while ((ch = getopt(argc, argv, "vsp:")) != -1) {
         switch (ch) {
         case 'v':
             verbose_mode = true;
             isc::log::denabled = true;
             break;
+        case 's':
+            stand_alone = true;
+            break;
         case 'p':
-            port_number = strtol(optarg, NULL, 10);
-            if (port_number == 0) {
+            try {
+                port_number = boost::lexical_cast<int>(optarg);
+            } catch (const boost::bad_lexical_cast &) {
+                cerr << "Failed to parse port number: [" << optarg
+                     << "], 1-65535 allowed." << endl;
+                usage();
+            }
+            if (port_number <= 0 || port_number > 65535) {
                 cerr << "Failed to parse port number: [" << optarg
                      << "], 1-65535 allowed." << endl;
                 usage();
@@ -81,8 +89,9 @@ main(int argc, char* argv[]) {
                          (verbose_mode ? isc::log::DEBUG : isc::log::INFO),
                          isc::log::MAX_DEBUG_LEVEL, NULL);
 
-    cout << "b10-dhcp4: My pid=" << getpid() << ", binding to port " 
-         << port_number << ", verbose " << (verbose_mode?"yes":"no") << endl;
+    cout << "b10-dhcp4: My pid=" << getpid() << ", binding to port "
+         << port_number << ", verbose " << (verbose_mode?"yes":"no")
+         << ", stand-alone=" << (stand_alone?"yes":"no") << endl;
 
     if (argc - optind > 0) {
         usage();
@@ -94,11 +103,23 @@ main(int argc, char* argv[]) {
 
         cout << "[b10-dhcp4] Initiating DHCPv4 server operation." << endl;
 
-        ControlledDhcpv4Srv* server = new ControlledDhcpv4Srv(port_number);
-        server->run();
-        delete server;
-        server = NULL;
+        /// @todo: pass verbose to the actul server once logging is implemented
+        ControlledDhcpv4Srv server(port_number);
+
+        if (!stand_alone) {
+            try {
+                server.establishSession();
+            } catch (const std::exception& ex) {
+                cerr << "Failed to establish BIND10 session. "
+                    "Running in stand-alone mode:" << ex.what() << endl;
+                // Let's continue. It is useful to have the ability to run
+                // DHCP server in stand-alone mode, e.g. for testing
+            }
+        } else {
+            cout << "Skipping connection to the BIND10 msgq." << endl;
+        }
 
+        server.run();
     } catch (const std::exception& ex) {
         cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl;
         ret = EXIT_FAILURE;

+ 7 - 7
src/bin/dhcp4/tests/Makefile.am

@@ -60,13 +60,13 @@ endif
 dhcp4_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 dhcp4_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 dhcp4_unittests_LDADD = $(GTEST_LDADD)
-dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
-dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-dhcp4_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
-dhcp4_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 endif
 
 noinst_PROGRAMS = $(TESTS)

+ 3 - 3
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -64,7 +64,7 @@ TEST_F(CtrlDhcpv4SrvTest, commands) {
     ConstElementPtr comment = parseAnswer(rcode, result);
     EXPECT_EQ(1, rcode); // expect failure (no such command as blah)
 
-    // case 1: send shutdown command without any parameters
+    // case 2: send shutdown command without any parameters
     result = ControlledDhcpv4Srv::execDhcpv4ServerCommand("shutdown", params);
     comment = parseAnswer(rcode, result);
     EXPECT_EQ(0, rcode); // expect success
@@ -73,7 +73,7 @@ TEST_F(CtrlDhcpv4SrvTest, commands) {
     ConstElementPtr x(new isc::data::IntElement(pid));
     params->set("pid", x);
 
-    // case 2: send shutdown command with 1 parameter: pid
+    // case 3: send shutdown command with 1 parameter: pid
     result = ControlledDhcpv4Srv::execDhcpv4ServerCommand("shutdown", params);
     comment = parseAnswer(rcode, result);
     EXPECT_EQ(0, rcode); // expect success

+ 40 - 5
src/bin/dhcp4/tests/dhcp4_test.py

@@ -34,7 +34,7 @@ class TestDhcpv4Daemon(unittest.TestCase):
     def tearDown(self):
         pass
 
-    def runDhcp4(self, params, wait=1):
+    def runCommand(self, params, wait=1):
         """
         This method runs dhcp4 and returns a touple: (returncode, stdout, stderr)
         """
@@ -127,14 +127,14 @@ class TestDhcpv4Daemon(unittest.TestCase):
         print("Note: Purpose of some of the tests is to check if DHCPv4 server can be started,")
         print("      not that is can bind sockets correctly. Please ignore binding errors.")
 
-        (returncode, output, error) = self.runDhcp4(["../b10-dhcp4", "-v"])
+        (returncode, output, error) = self.runCommand(["../b10-dhcp4", "-v"])
 
         self.assertEqual( str(output).count("[b10-dhcp4] Initiating DHCPv4 server operation."), 1)
 
     def test_portnumber_0(self):
         print("Check that specifying port number 0 is not allowed.")
 
-        (returncode, output, error) = self.runDhcp4(['../b10-dhcp4', '-p', '0'])
+        (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-p', '0'])
 
         # When invalid port number is specified, return code must not be success
         self.assertTrue(returncode != 0)
@@ -145,7 +145,7 @@ class TestDhcpv4Daemon(unittest.TestCase):
     def test_portnumber_missing(self):
         print("Check that -p option requires a parameter.")
 
-        (returncode, output, error) = self.runDhcp4(['../b10-dhcp4', '-p'])
+        (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-p'])
 
         # When invalid port number is specified, return code must not be success
         self.assertTrue(returncode != 0)
@@ -153,10 +153,32 @@ class TestDhcpv4Daemon(unittest.TestCase):
         # Check that there is an error message about invalid port number printed on stderr
         self.assertEqual( str(error).count("option requires an argument"), 1)
 
+    def test_portnumber_invalid1(self):
+        print("Check that -p option is check against bogus port number (999999).")
+
+        (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-p','999999'])
+
+        # When invalid port number is specified, return code must not be success
+        self.assertTrue(returncode != 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(error).count("Failed to parse port number"), 1)
+
+    def test_portnumber_invalid2(self):
+        print("Check that -p option is check against bogus port number (123garbage).")
+
+        (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-p','123garbage'])
+
+        # When invalid port number is specified, return code must not be success
+        self.assertTrue(returncode != 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(error).count("Failed to parse port number"), 1)
+
     def test_portnumber_nonroot(self):
         print("Check that specifying unprivileged port number will work.")
 
-        (returncode, output, error) = self.runDhcp4(['../b10-dhcp4', '-p', '10057'])
+        (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-s', '-p', '10057'])
 
         # When invalid port number is specified, return code must not be success
         # TODO: Temporarily commented out as socket binding on systems that do not have
@@ -166,5 +188,18 @@ class TestDhcpv4Daemon(unittest.TestCase):
         # Check that there is an error message about invalid port number printed on stderr
         self.assertEqual( str(output).count("opening sockets on port 10057"), 1)
 
+    def test_skip_msgq(self):
+        print("Check that connection to BIND10 msgq can be disabled.")
+
+        (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-s', '-p', '10057'])
+
+        # When invalid port number is specified, return code must not be success
+        # TODO: Temporarily commented out as socket binding on systems that do not have
+        #       interface detection implemented currently fails.
+        # self.assertTrue(returncode == 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(output).count("Skipping connection to the BIND10 msgq."), 1)
+
 if __name__ == '__main__':
     unittest.main()

+ 7 - 4
src/bin/dhcp6/Makefile.am

@@ -33,6 +33,7 @@ BUILT_SOURCES = spec_config.h
 pkglibexec_PROGRAMS = b10-dhcp6
 
 b10_dhcp6_SOURCES = main.cc dhcp6_srv.cc dhcp6_srv.h
+b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
 
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
@@ -40,10 +41,12 @@ if USE_CLANGPP
 b10_dhcp6_CXXFLAGS = -Wno-unused-parameter
 endif
 
-b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
-b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/liblog.la
-b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
+b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 
 b10_dhcp6dir = $(pkgdatadir)
 b10_dhcp6_DATA = dhcp6.spec

+ 159 - 0
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -0,0 +1,159 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <cassert>
+#include <iostream>
+
+#include <cc/session.h>
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <cc/session.h>
+#include <config/ccsession.h>
+#include <util/buffer.h>
+#include <dhcp6/spec_config.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcp/iface_mgr.h>
+#include <asiolink/asiolink.h>
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::dhcp;
+using namespace isc::util;
+using namespace isc::data;
+using namespace isc::cc;
+using namespace isc::config;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;
+
+ConstElementPtr
+ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) {
+    cout << "b10-dhcp6: Received new config:" << new_config->str() << endl;
+    ConstElementPtr answer = isc::config::createAnswer(0,
+                             "Thank you for sending config.");
+    return (answer);
+}
+
+ConstElementPtr
+ControlledDhcpv6Srv::dhcp6CommandHandler(const string& command, ConstElementPtr args) {
+    cout << "b10-dhcp6: Received new command: [" << command << "], args="
+         << args->str() << endl;
+    if (command == "shutdown") {
+        if (ControlledDhcpv6Srv::server_) {
+            ControlledDhcpv6Srv::server_->shutdown();
+        } else {
+            cout << "Server not initialized yet or already shut down." << endl;
+            ConstElementPtr answer = isc::config::createAnswer(1,
+                                     "Shutdown failure.");
+            return (answer);
+        }
+        ConstElementPtr answer = isc::config::createAnswer(0,
+                                 "Shutting down.");
+        return (answer);
+    }
+
+    ConstElementPtr answer = isc::config::createAnswer(1,
+                             "Unrecognized command.");
+
+    return (answer);
+}
+
+void ControlledDhcpv6Srv::sessionReader(void) {
+    // Process one asio event. If there are more events, iface_mgr will call
+    // this callback more than once.
+    if (server_) {
+        server_->io_service_.run_one();
+    }
+}
+
+void ControlledDhcpv6Srv::establishSession() {
+    
+    string specfile;
+    if (getenv("B10_FROM_BUILD")) {
+        specfile = string(getenv("B10_FROM_BUILD")) +
+            "/src/bin/dhcp6/dhcp6.spec";
+    } else {
+        specfile = string(DHCP6_SPECFILE_LOCATION);
+    }
+
+    /// @todo: Check if session is not established already. Throw, if it is.
+    
+    cout << "b10-dhcp6: my specfile is " << specfile << endl;
+    
+    cc_session_ = new Session(io_service_.get_io_service());
+
+    config_session_ = new ModuleCCSession(specfile, *cc_session_,
+                                          dhcp6ConfigHandler,
+                                          dhcp6CommandHandler, false);
+    config_session_->start();
+
+    /// Integrate the asynchronous I/O model of BIND 10 configuration
+    /// control with the "select" model of the DHCP server.  This is
+    /// fully explained in \ref dhcpv6Session.
+    int ctrl_socket = cc_session_->getSocketDesc();
+    cout << "b10-dhcp6: Control session started, socket="
+         << ctrl_socket << endl;
+    IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader);
+}
+
+void ControlledDhcpv6Srv::disconnectSession() {
+    if (config_session_) {
+        delete config_session_;
+        config_session_ = NULL;
+    }
+    if (cc_session_) {
+        cc_session_->disconnect();
+        delete cc_session_;
+        cc_session_ = NULL;
+    }
+
+    // deregister session socket
+    IfaceMgr::instance().set_session_socket(IfaceMgr::INVALID_SOCKET, NULL);
+}
+
+ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port /*= DHCP6_SERVER_PORT*/)
+    :Dhcpv6Srv(port), cc_session_(NULL), config_session_(NULL) {
+    server_ = this; // remember this instance for use in callback
+}
+
+void ControlledDhcpv6Srv::shutdown() {
+    io_service_.stop(); // Stop ASIO transmissions
+    Dhcpv6Srv::shutdown(); // Initiate DHCPv6 shutdown procedure.
+}
+
+ControlledDhcpv6Srv::~ControlledDhcpv6Srv() {
+    disconnectSession();
+
+    server_ = NULL; // forget this instance. There should be no callback anymore
+                    // at this stage anyway.
+}
+
+isc::data::ConstElementPtr
+ControlledDhcpv6Srv::execDhcpv6ServerCommand(const std::string& command_id,
+                                             isc::data::ConstElementPtr args) {
+    try {
+        return (dhcp6CommandHandler(command_id, args));
+    } catch (const Exception& ex) {
+        ConstElementPtr answer = isc::config::createAnswer(1, ex.what());
+        return (answer);
+    }
+}
+
+
+};
+};

+ 123 - 0
src/bin/dhcp6/ctrl_dhcp6_srv.h

@@ -0,0 +1,123 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef CTRL_DHCPV6_SRV_H
+#define CTRL_DHCPV6_SRV_H
+
+#include <dhcp6/dhcp6_srv.h>
+#include <asiolink/asiolink.h>
+#include <cc/session.h>
+#include <config/ccsession.h>
+#include <cc/data.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Controlled version of the DHCPv6 server
+///
+/// This is a class that is responsible for establishing connection
+/// with msqg (receving commands and configuration). This is an extended
+/// version of Dhcpv6Srv class that is purely a DHCPv6 server, without
+/// external control. ControlledDhcpv6Srv should be used in typical BIND10
+/// (i.e. featuring msgq) environment, while Dhcpv6Srv should be used in
+/// embedded environments.
+///
+/// For detailed explanation or relations between main(), ControlledDhcpv6Srv,
+/// Dhcpv6Srv and other classes, see \ref dhcpv6Session.
+class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param port UDP port to be opened for DHCP traffic
+    ControlledDhcpv6Srv(uint16_t port = DHCP6_SERVER_PORT);
+
+    /// @brief Destructor.
+    ~ControlledDhcpv6Srv();
+
+    /// @brief Establishes msgq session.
+    ///
+    /// Creates session that will be used to receive commands and updated
+    /// configuration from boss (or indirectly from user via bindctl).
+    void establishSession();
+
+    /// @brief Terminates existing msgq session.
+    ///
+    /// This method terminates existing session with msgq. After calling
+    /// it, no further messages over msgq (commands or configuration updates)
+    /// may be received.
+    ///
+    /// It is ok to call this method when session is disconnected already.
+    void disconnectSession();
+
+    /// @brief Initiates shutdown procedure for the whole DHCPv6 server.
+    void shutdown();
+
+    /// @brief Session callback, processes received commands.
+    ///
+    /// @param command_id text represenation of the command (e.g. "shutdown")
+    /// @param args optional parameters
+    ///
+    /// @return status of the command
+    static isc::data::ConstElementPtr
+    execDhcpv6ServerCommand(const std::string& command,
+                            isc::data::ConstElementPtr args);
+
+protected:
+    /// @brief Static pointer to the sole instance of the DHCP server.
+    ///
+    /// This is required for config and command handlers to gain access to
+    /// the server
+    static ControlledDhcpv6Srv* server_;
+
+    /// @brief A callback for handling incoming configuration updates.
+    ///
+    /// As pointer to this method is used a callback in ASIO used in
+    /// ModuleCCSession, it has to be static.
+    ///
+    /// @param new_config textual representation of the new configuration
+    ///
+    /// @return status of the config update
+    static isc::data::ConstElementPtr
+    dhcp6ConfigHandler(isc::data::ConstElementPtr new_config);
+
+    /// @brief A callback for handling incoming commands.
+    ///
+    /// @param command textual representation of the command
+    /// @param args parameters of the command
+    ///
+    /// @return status of the processed command
+    static isc::data::ConstElementPtr
+    dhcp6CommandHandler(const std::string& command, isc::data::ConstElementPtr args);
+
+    /// @brief Callback that will be called from iface_mgr when command/config arrives.
+    ///
+    /// This static callback method is called from IfaceMgr::receive6() method,
+    /// when there is a new command or configuration sent over msgq.
+    static void sessionReader(void);
+
+    /// @brief IOService object, used for all ASIO operations.
+    isc::asiolink::IOService io_service_;
+
+    /// @brief Helper session object that represents raw connection to msgq.
+    isc::cc::Session* cc_session_;
+
+    /// @brief Session that receives configuation and commands
+    isc::config::ModuleCCSession* config_session_;
+};
+
+}; // namespace isc::dhcp
+}; // namespace isc
+
+#endif

+ 14 - 2
src/bin/dhcp6/dhcp6.spec

@@ -1,6 +1,6 @@
 {
   "module_spec": {
-    "module_name": "dhcp6",
+    "module_name": "Dhcp6",
     "module_description": "DHCPv6 server daemon",
     "config_data": [
       { "item_name": "interface",
@@ -9,6 +9,18 @@
         "item_default": "eth0"
       }
     ],
-    "commands": []
+    "commands": [
+        {
+            "command_name": "shutdown",
+            "command_description": "Shuts down DHCPv6 server.",
+            "command_args": [
+                {
+                    "item_name": "pid",
+                    "item_type": "integer",
+                    "item_optional": true
+                }
+            ]
+        }
+    ]
   }
 }

+ 24 - 16
src/bin/dhcp6/dhcp6_srv.cc

@@ -45,25 +45,26 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
     // first call to instance() will create IfaceMgr (it's a singleton)
     // it may throw something if things go wrong
     try {
-        IfaceMgr::instance();
-    } catch (const std::exception &e) {
-        cout << "Failed to instantiate InterfaceManager:" << e.what() << ". Aborting." << endl;
-        shutdown = true;
-    }
 
-    if (IfaceMgr::instance().countIfaces() == 0) {
-        cout << "Failed to detect any network interfaces. Aborting." << endl;
-        shutdown = true;
-    }
+        if (IfaceMgr::instance().countIfaces() == 0) {
+            cout << "Failed to detect any network interfaces. Aborting." << endl;
+            shutdown_ = true;
+            return;
+        }
+
+        IfaceMgr::instance().openSockets6(port);
 
-    // Now try to open IPv6 sockets on detected interfaces.
-    IfaceMgr::instance().openSockets6(port);
+        setServerID();
 
-    /// @todo: instantiate LeaseMgr here once it is imlpemented.
+        /// @todo: instantiate LeaseMgr here once it is imlpemented.
 
-    setServerID();
+    } catch (const std::exception &e) {
+        cerr << "Error during DHCPv4 server startup: " << e.what() << endl;
+        shutdown_ = true;
+        return;
+    }
 
-    shutdown = false;
+    shutdown_ = false;
 }
 
 Dhcpv6Srv::~Dhcpv6Srv() {
@@ -72,11 +73,18 @@ Dhcpv6Srv::~Dhcpv6Srv() {
     IfaceMgr::instance().closeSockets();
 }
 
+void Dhcpv6Srv::shutdown() {
+    cout << "b10-dhcp6: DHCPv6 server shutdown." << endl;
+    shutdown_ = true;
+}
+
 bool Dhcpv6Srv::run() {
-    while (!shutdown) {
+    while (!shutdown_) {
+        /// @todo: calculate actual timeout once we have lease database
+        int timeout = 1000;
 
         // client's message and server's response
-        Pkt6Ptr query = IfaceMgr::instance().receive6();
+        Pkt6Ptr query = IfaceMgr::instance().receive6(timeout);
         Pkt6Ptr rsp;
 
         if (query) {

+ 3 - 1
src/bin/dhcp6/dhcp6_srv.h

@@ -67,6 +67,8 @@ public:
     ///         critical error.
     bool run();
 
+    /// @brief Instructs the server to shut down.
+    void shutdown();
 protected:
     /// @brief Processes incoming SOLICIT and returns response.
     ///
@@ -184,7 +186,7 @@ protected:
 
     /// indicates if shutdown is in progress. Setting it to true will
     /// initiate server shutdown procedure.
-    volatile bool shutdown;
+    volatile bool shutdown_;
 };
 
 }; // namespace isc::dhcp

+ 56 - 49
src/bin/dhcp6/main.cc

@@ -13,47 +13,36 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <config.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include <cassert>
 #include <iostream>
-
-#include <exceptions/exceptions.h>
-#if 0
-// TODO cc is not used yet. It should be eventually
-#include <cc/session.h>
-#include <config/ccsession.h>
-#endif
-
-#include <util/buffer.h>
 #include <log/dummylog.h>
-
-#include <dhcp6/spec_config.h>
-#include "dhcp6/dhcp6_srv.h"
+#include <log/logger_support.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <boost/lexical_cast.hpp>
 
 using namespace std;
-using namespace isc::util;
-
-using namespace isc;
 using namespace isc::dhcp;
 
+/// This file contains entry point (main() function) for standard DHCPv6 server
+/// component for BIND10 framework. It parses command-line arguments and
+/// instantiates ControlledDhcpv6Srv class that is responsible for establishing
+/// connection with msgq (receiving commands and configuration) and also
+/// creating Dhcpv6 server object as well.
+///
+/// For detailed explanation or relations between main(), ControlledDhcpv6Srv,
+/// Dhcpv6Srv and other classes, see \ref dhcpv6Session.
+
 namespace {
 
-bool verbose_mode = false;
+const char* const DHCP6_NAME = "b10-dhcp6";
 
 void
 usage() {
-    cerr << "Usage:  b10-dhcp6 [-v]"
+    cerr << "Usage: b10-dhcp6 [-v]"
          << endl;
     cerr << "\t-v: verbose output" << endl;
-    cerr << "\t-p number: specify non-standard port number 1-65535 (useful for testing only)" << endl;
+    cerr << "\t-s: stand-alone mode (don't connect to BIND10)" << endl;
+    cerr << "\t-p number: specify non-standard port number 1-65535 "
+         << "(useful for testing only)" << endl;
     exit(EXIT_FAILURE);
 }
 } // end of anonymous namespace
@@ -63,16 +52,27 @@ main(int argc, char* argv[]) {
     int ch;
     int port_number = DHCP6_SERVER_PORT; // The default. Any other values are
                                          // useful for testing only.
+    bool verbose_mode = false; // Should server be verbose?
+    bool stand_alone = false; // should be connect to BIND10 msgq?
 
-    while ((ch = getopt(argc, argv, "vp:")) != -1) {
+    while ((ch = getopt(argc, argv, "vsp:")) != -1) {
         switch (ch) {
         case 'v':
             verbose_mode = true;
             isc::log::denabled = true;
             break;
+        case 's':
+            stand_alone = true;
+            break;
         case 'p':
-            port_number = strtol(optarg, NULL, 10);
-            if (port_number == 0) {
+            try {
+                port_number = boost::lexical_cast<int>(optarg);
+            } catch (const boost::bad_lexical_cast &) {
+                cerr << "Failed to parse port number: [" << optarg
+                     << "], 1-65535 allowed." << endl;
+                usage();
+            }
+            if (port_number <= 0 || port_number > 65535) {
                 cerr << "Failed to parse port number: [" << optarg
                      << "], 1-65535 allowed." << endl;
                 usage();
@@ -84,7 +84,14 @@ main(int argc, char* argv[]) {
         }
     }
 
-    cout << "My pid=" << getpid() << endl;
+    // Initialize logging.  If verbose, we'll use maximum verbosity.
+    isc::log::initLogger(DHCP6_NAME,
+                         (verbose_mode ? isc::log::DEBUG : isc::log::INFO),
+                         isc::log::MAX_DEBUG_LEVEL, NULL);
+
+    cout << "b10-dhcp6: My pid=" << getpid() << ", binding to port "
+         << port_number << ", verbose " << (verbose_mode?"yes":"no")
+         << ", stand-alone=" << (stand_alone?"yes":"no") << endl;
 
     if (argc - optind > 0) {
         usage();
@@ -92,27 +99,27 @@ main(int argc, char* argv[]) {
 
     int ret = EXIT_SUCCESS;
 
-    // TODO remainder of auth to dhcp6 code copy. We need to enable this in
-    //      dhcp6 eventually
-#if 0
-    Session* cc_session = NULL;
-    Session* statistics_session = NULL;
-    ModuleCCSession* config_session = NULL;
-#endif
     try {
-        string specfile;
-        if (getenv("B10_FROM_BUILD")) {
-            specfile = string(getenv("B10_FROM_BUILD")) +
-                "/src/bin/auth/dhcp6.spec";
-        } else {
-            specfile = string(DHCP6_SPECFILE_LOCATION);
-        }
 
-        cout << "[b10-dhcp6] Initiating DHCPv6 operation." << endl;
+        cout << "b10-dhcp6: Initiating DHCPv6 server operation." << endl;
 
-        Dhcpv6Srv* srv = new Dhcpv6Srv(port_number);
+        /// @todo: pass verbose to the actual server once logging is implemented
+        ControlledDhcpv6Srv server(port_number);
+
+        if (!stand_alone) {
+            try {
+                server.establishSession();
+            } catch (const std::exception& ex) {
+                cerr << "Failed to establish BIND10 session. "
+                    "Running in stand-alone mode:" << ex.what() << endl;
+                // Let's continue. It is useful to have the ability to run 
+                // DHCP server in stand-alone mode, e.g. for testing
+            }
+        } else {
+            cout << "Skipping connection to the BIND10 msgq." << endl;
+        }
 
-        srv->run();
+        server.run();
 
     } catch (const std::exception& ex) {
         cerr << "[b10-dhcp6] Server failed: " << ex.what() << endl;

+ 8 - 5
src/bin/dhcp6/tests/Makefile.am

@@ -42,9 +42,10 @@ if HAVE_GTEST
 
 TESTS += dhcp6_unittests
 
-dhcp6_unittests_SOURCES = ../dhcp6_srv.h ../dhcp6_srv.cc
+dhcp6_unittests_SOURCES = ../dhcp6_srv.h ../dhcp6_srv.cc ../ctrl_dhcp6_srv.cc
 dhcp6_unittests_SOURCES += dhcp6_unittests.cc
 dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
+dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
@@ -55,10 +56,12 @@ endif
 dhcp6_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 dhcp6_unittests_LDADD = $(GTEST_LDADD)
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 
 endif
 

+ 85 - 0
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc

@@ -0,0 +1,85 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <config/ccsession.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::config;
+
+namespace {
+
+class NakedControlledDhcpv6Srv: public ControlledDhcpv6Srv {
+    // "naked" DHCPv6 server, exposes internal fields
+public:
+    NakedControlledDhcpv6Srv():ControlledDhcpv6Srv(DHCP6_SERVER_PORT + 10000) { }
+};
+
+class CtrlDhcpv6SrvTest : public ::testing::Test {
+public:
+    CtrlDhcpv6SrvTest() {
+    }
+
+    ~CtrlDhcpv6SrvTest() {
+    };
+};
+
+TEST_F(CtrlDhcpv6SrvTest, commands) {
+
+    ControlledDhcpv6Srv* srv = NULL;
+    ASSERT_NO_THROW({
+        srv = new ControlledDhcpv6Srv(DHCP6_SERVER_PORT + 10000);
+    });
+
+    // use empty parameters list
+    ElementPtr params(new isc::data::MapElement());
+    int rcode = -1;
+
+    // case 1: send bogus command
+    ConstElementPtr result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("blah", params);
+    ConstElementPtr comment = parseAnswer(rcode, result);
+    EXPECT_EQ(1, rcode); // expect failure (no such command as blah)
+
+    // case 2: send shutdown command without any parameters
+    result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params);
+    comment = parseAnswer(rcode, result);
+    EXPECT_EQ(0, rcode); // expect success
+
+    const pid_t pid(getpid());
+    ConstElementPtr x(new isc::data::IntElement(pid));
+    params->set("pid", x);
+
+    // case 3: send shutdown command with 1 parameter: pid
+    result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params);
+    comment = parseAnswer(rcode, result);
+    EXPECT_EQ(0, rcode); // expect success
+
+
+    delete srv;
+}
+
+} // end of anonymous namespace

+ 39 - 5
src/bin/dhcp6/tests/dhcp6_test.py

@@ -1,4 +1,4 @@
-# Copyright (C) 2011,2012 Internet Systems Consortium.
+# copyright (C) 2011,2012 Internet Systems Consortium.
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -131,7 +131,7 @@ class TestDhcpv6Daemon(unittest.TestCase):
         print("      not that is can bind sockets correctly. Please ignore binding errors.")
         (returncode, output, error) = self.runCommand(["../b10-dhcp6", "-v"])
 
-        self.assertEqual( str(output).count("[b10-dhcp6] Initiating DHCPv6 operation."), 1)
+        self.assertEqual( str(output).count("b10-dhcp6: Initiating DHCPv6 server operation."), 1)
 
     def test_portnumber_0(self):
         print("Check that specifying port number 0 is not allowed.")
@@ -155,18 +155,52 @@ class TestDhcpv6Daemon(unittest.TestCase):
         # Check that there is an error message about invalid port number printed on stderr
         self.assertEqual( str(error).count("option requires an argument"), 1)
 
+    def test_portnumber_invalid1(self):
+        print("Check that -p option is check against bogus port number (999999).")
+
+        (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p','999999'])
+
+        # When invalid port number is specified, return code must not be success
+        self.assertTrue(returncode != 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(error).count("Failed to parse port number"), 1)
+
+    def test_portnumber_invalid2(self):
+        print("Check that -p option is check against bogus port number (123garbage).")
+
+        (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p','123garbage'])
+
+        # When invalid port number is specified, return code must not be success
+        self.assertTrue(returncode != 0)
+
+        # Check that there is an error message about invalid port number printed on stderr
+        self.assertEqual( str(error).count("Failed to parse port number"), 1)
+
     def test_portnumber_nonroot(self):
         print("Check that specifying unprivileged port number will work.")
 
-        (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-p', '10057'])
+        (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-s', '-p', '10547'])
+
+        # When invalid port number is specified, return code must not be success
+        # TODO: Temporarily commented out as socket binding on systems that do not have
+        #       interface detection implemented currently fails.
+        # self.assertTrue(returncode == 0)
+
+        self.assertEqual( str(output).count("opening sockets on port 10547"), 1)
+
+    def test_skip_msgq(self):
+        print("Check that connection to BIND10 msgq can be disabled.")
+
+        (returncode, output, error) = self.runCommand(['../b10-dhcp6', '-s', '-p', '10547'])
 
         # When invalid port number is specified, return code must not be success
         # TODO: Temporarily commented out as socket binding on systems that do not have
         #       interface detection implemented currently fails.
         # self.assertTrue(returncode == 0)
 
-        # Check that there is a message on stdout about opening proper port
-        self.assertEqual( str(output).count("opening sockets on port 10057"), 1)
+        self.assertEqual( str(output).count("Skipping connection to the BIND10 msgq."), 1)
+
 
 if __name__ == '__main__':
     unittest.main()

+ 3 - 3
src/bin/host/Makefile.am

@@ -12,9 +12,9 @@ CLEANFILES = *.gcno *.gcda
 
 bin_PROGRAMS = b10-host
 b10_host_SOURCES = host.cc
-b10_host_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
-b10_host_LDADD += $(top_builddir)/src/lib/util/libutil.la
-b10_host_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+b10_host_LDADD = $(top_builddir)/src/lib/dns/libb10-dns++.la
+b10_host_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+b10_host_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 
 man_MANS = b10-host.1
 EXTRA_DIST = $(man_MANS) b10-host.xml

+ 0 - 4
src/bin/loadzone/Makefile.am

@@ -20,10 +20,6 @@ b10-loadzone: b10-loadzone.py
 	       -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" b10-loadzone.py >$@
 	chmod a+x $@
 
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)/@localstatedir@/@PACKAGE@
-# TODO: permissions handled later
-
 EXTRA_DIST += tests/normal/README
 EXTRA_DIST += tests/normal/dsset-subzone.example.com
 EXTRA_DIST += tests/normal/example.com

+ 2 - 4
src/bin/msgq/b10-msgq.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-msgq
 .\"    Author: [see the "AUTHORS" section]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: August 4, 2010
+.\"      Date: June 25, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-MSGQ" "8" "August 4, 2010" "BIND10" "BIND10"
+.TH "B10\-MSGQ" "8" "June 25, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -87,8 +87,6 @@ is assigned a unique identifier \-\- this is the local name\&. The commands it h
 .sp
 .RE
 .PP
-It listens on 127\&.0\&.0\&.1\&.
-.PP
 The
 \fBb10\-msgq\fR
 daemon may be cleanly stopped by sending the SIGTERM signal to the process\&. This shutdown does not notify the subscribers\&.

+ 14 - 14
src/bin/resolver/Makefile.am

@@ -56,20 +56,20 @@ b10_resolver_SOURCES += common.cc common.h
 nodist_b10_resolver_SOURCES = resolver_messages.cc resolver_messages.h
 
 
-b10_resolver_LDADD =  $(top_builddir)/src/lib/dns/libdns++.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libcc.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/util/libutil.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/acl/libdnsacl.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/log/liblog.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libcache.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
-b10_resolver_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
+b10_resolver_LDADD =  $(top_builddir)/src/lib/dns/libb10-dns++.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/acl/libb10-dnsacl.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/xfr/libb10-xfr.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libb10-cache.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/resolve/libb10-resolve.la
 b10_resolver_LDFLAGS = -pthread
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir

+ 9 - 0
src/bin/resolver/b10-resolver.8

@@ -9,6 +9,15 @@
 .\"
 .TH "B10\-RESOLVER" "8" "February 28, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" disable hyphenation

+ 16 - 16
src/bin/resolver/tests/Makefile.am

@@ -37,22 +37,22 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 
 run_unittests_LDADD  = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
-run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
-run_unittests_LDADD += $(top_builddir)/src/lib/acl/libdnsacl.la
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
-run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
-run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
-run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
-run_unittests_LDADD += $(top_builddir)/src/lib/acl/libacl.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+run_unittests_LDADD += $(top_builddir)/src/lib/acl/libb10-dnsacl.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libb10-xfr.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common.la
+run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libb10-resolve.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cache/libb10-cache.la
+run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la
+run_unittests_LDADD += $(top_builddir)/src/lib/acl/libb10-acl.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 
 # Note the ordering matters: -Wno-... must follow -Wextra (defined in

+ 0 - 2
src/bin/showtech/.gitignore

@@ -1,2 +0,0 @@
-/b10-showtech
-/showtech.py

+ 2 - 2
src/bin/sockcreator/Makefile.am

@@ -15,5 +15,5 @@ CLEANFILES = *.gcno *.gcda
 pkglibexec_PROGRAMS = b10-sockcreator
 
 b10_sockcreator_SOURCES = sockcreator.cc sockcreator.h main.cc
-b10_sockcreator_LDADD  = $(top_builddir)/src/lib/util/io/libutil_io.la
-b10_sockcreator_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+b10_sockcreator_LDADD  = $(top_builddir)/src/lib/util/io/libb10-util-io.la
+b10_sockcreator_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la

+ 1 - 1
src/bin/sockcreator/tests/Makefile.am

@@ -22,7 +22,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libb10-util-io.la
 endif
 
 noinst_PROGRAMS = $(TESTS)

+ 2 - 0
src/bin/sysinfo/.gitignore

@@ -0,0 +1,2 @@
+/isc-sysinfo
+/sysinfo.py

+ 5 - 5
src/bin/showtech/Makefile.am

@@ -1,14 +1,14 @@
-bin_SCRIPTS = b10-showtech
+bin_SCRIPTS = isc-sysinfo
 
-CLEANFILES = b10-showtech showtech.pyc
+CLEANFILES = isc-sysinfo sysinfo.pyc
 
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-showtech: showtech.py
-	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" showtech.py >$@
+isc-sysinfo: sysinfo.py
+	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" sysinfo.py >$@
 	chmod a+x $@
 
 MAN1_FILES = \
-	b10-showtech.xml
+	isc-sysinfo.xml
 
 man_MANS = \
 	$(MAN1_FILES:.xml=.1)

+ 9 - 9
src/bin/showtech/b10-showtech.1

@@ -1,13 +1,13 @@
 '\" t
-.\"     Title: b10-showtech
+.\"     Title: isc-sysinfo
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: June 26, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-SHOWTECH" "1" "June 26, 2012" "BIND10" "BIND10"
+.TH "ISC\-SYSINFO" "1" "June 26, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -28,14 +28,14 @@
 .\" * MAIN CONTENT STARTS HERE *
 .\" -----------------------------------------------------------------
 .SH "NAME"
-b10-showtech \- BIND 10 system information display tool
+isc-sysinfo \- BIND 10 system information display tool
 .SH "SYNOPSIS"
-.HP \w'\fBb10\-showtech\fR\ 'u
-\fBb10\-showtech\fR
+.HP \w'\fBisc\-sysinfo\fR\ 'u
+\fBisc\-sysinfo\fR
 .SH "DESCRIPTION"
 .PP
 The
-\fBb10\-showtech\fR
+\fBisc\-sysinfo\fR
 program collects and outputs a variety of information about the system that BIND 10 is running on\&. This information can be useful to people involved in debugging and technical support\&.
 .SH "ARGUMENTS"
 .PP
@@ -47,7 +47,7 @@ Displays usage instructions\&.
 \-o \fIoutput\-file\fR
 .RS 4
 If an output file is specified, the output of
-\fBb10\-showtech\fR
+\fBisc\-sysinfo\fR
 is written to this file\&. By default, the output is written to standard output\&.
 .RE
 .SH "SEE ALSO"
@@ -58,7 +58,7 @@ BIND 10 Guide\&.
 .SH "HISTORY"
 .PP
 The
-\fBb10\-showtech\fR
+\fBisc\-sysinfo\fR
 daemon was initially implemented by ISC staff in June, 2012\&.
 .SH "COPYRIGHT"
 .br

+ 6 - 6
src/bin/showtech/b10-showtech.xml

@@ -24,13 +24,13 @@
   </refentryinfo>
 
   <refmeta>
-    <refentrytitle>b10-showtech</refentrytitle>
+    <refentrytitle>isc-sysinfo</refentrytitle>
     <manvolnum>1</manvolnum>
     <refmiscinfo>BIND10</refmiscinfo>
   </refmeta>
 
   <refnamediv>
-    <refname>b10-showtech</refname>
+    <refname>isc-sysinfo</refname>
     <refpurpose>BIND 10 system information display tool</refpurpose>
   </refnamediv>
 
@@ -43,14 +43,14 @@
 
   <refsynopsisdiv>
     <cmdsynopsis>
-      <command>b10-showtech</command>
+      <command>isc-sysinfo</command>
     </cmdsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>DESCRIPTION</title>
     <para>
-      The <command>b10-showtech</command> program collects and outputs a
+      The <command>isc-sysinfo</command> program collects and outputs a
       variety of information about the system that BIND 10 is running
       on. This information can be useful to people involved in debugging
       and technical support.
@@ -73,7 +73,7 @@
         <term>-o <replaceable class="parameter">output-file</replaceable></term>
         <listitem><para>
           If an output file is specified, the output
-          of <command>b10-showtech</command> is written to this file. By
+          of <command>isc-sysinfo</command> is written to this file. By
           default, the output is written to standard output.
         </para></listitem>
       </varlistentry>
@@ -95,7 +95,7 @@
   <refsect1>
     <title>HISTORY</title>
     <para>
-      The <command>b10-showtech</command> daemon was initially
+      The <command>isc-sysinfo</command> daemon was initially
       implemented by ISC staff in June, 2012.
     </para>
   </refsect1>

+ 39 - 30
src/bin/showtech/showtech.py.in

@@ -16,7 +16,7 @@
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 """
-BIND 10 showtech program.
+ISC sysinfo program.
 
 """
 
@@ -32,6 +32,15 @@ def usage():
               file=sys.stderr)
     exit(1)
 
+def write_value(out, fmt, call):
+    '''Helper function for standard value writing.
+       Writes the result from the call in the given format to out.
+       Does not write anything if the result of the call is None.
+    '''
+    value = call()
+    if value is not None:
+        out.write(fmt % value)
+
 def main():
     try:
         opts, args = getopt.getopt(sys.argv[1:], "o:h", \
@@ -57,39 +66,39 @@ def main():
 
     s = SysInfoFromFactory()
 
-    f.write('BIND 10 ShowTech tool\n')
-    f.write('=====================\n')
+    f.write('ISC Sysinfo tool\n')
+    f.write('================\n')
 
     f.write('\nCPU\n');
-    f.write(' + Number of processors: %d\n' % (s.get_num_processors()))
-    f.write(' + Endianness: %s\n' % (s.get_endianness()))
+    write_value(f, ' + Number of processors: %d\n', s.get_num_processors)
+    write_value(f, ' + Endianness: %s\n', s.get_endianness)
 
     f.write('\nPlatform\n');
-    f.write(' + Operating system: %s\n' % (s.get_platform_name()))
-    f.write(' + Distribution: %s\n' % (s.get_platform_distro()))
-    f.write(' + Kernel version: %s\n' % (s.get_platform_version()))
-
-    f.write(' + SMP kernel: ')
-    if s.get_platform_is_smp():
-        f.write('yes')
-    else:
-        f.write('no')
-    f.write('\n')
+    write_value(f, ' + Operating system: %s\n', s.get_platform_name)
+    write_value(f, ' + Distribution: %s\n', s.get_platform_distro)
+    write_value(f, ' + Kernel version: %s\n', s.get_platform_version)
+
+    if s.get_platform_is_smp() is not None:
+        f.write(' + SMP kernel: ')
+        if s.get_platform_is_smp():
+            f.write('yes')
+        else:
+            f.write('no')
+        f.write('\n')
 
-    f.write(' + Machine name: %s\n' % (s.get_platform_machine()))
-    f.write(' + Hostname: %s\n' % (s.get_platform_hostname()))
-    f.write(' + Uptime: %d seconds\n' % (s.get_uptime()))
+    write_value(f, ' + Machine name: %s\n', s.get_platform_machine)
+    write_value(f, ' + Hostname: %s\n', s.get_platform_hostname)
+    write_value(f, ' + Uptime: %d seconds\n', s.get_uptime)
 
-    l = s.get_loadavg()
-    f.write(' + Loadavg: %f %f %f\n' % (l[0], l[1], l[2]))
+    write_value(f, ' + Loadavg: %f %f %f\n', s.get_loadavg)
 
     f.write('\nMemory\n');
-    f.write(' + Total: %d bytes\n' % (s.get_mem_total()))
-    f.write(' + Free: %d bytes\n' % (s.get_mem_free()))
-    f.write(' + Cached: %d bytes\n' % (s.get_mem_cached()))
-    f.write(' + Buffers: %d bytes\n' % (s.get_mem_buffers()))
-    f.write(' + Swap total: %d bytes\n' % (s.get_mem_swap_total()))
-    f.write(' + Swap free: %d bytes\n' % (s.get_mem_swap_free()))
+    write_value(f, ' + Total: %d bytes\n', s.get_mem_total)
+    write_value(f, ' + Free: %d bytes\n', s.get_mem_free)
+    write_value(f, ' + Cached: %d bytes\n', s.get_mem_cached)
+    write_value(f, ' + Buffers: %d bytes\n', s.get_mem_buffers)
+    write_value(f, ' + Swap total: %d bytes\n', s.get_mem_swap_total)
+    write_value(f, ' + Swap free: %d bytes\n', s.get_mem_swap_free)
 
     f.write('\n\nNetwork\n');
     f.write('-------\n\n');
@@ -97,19 +106,19 @@ def main():
     f.write('Interfaces\n')
     f.write('~~~~~~~~~~\n\n')
 
-    f.write(s.get_net_interfaces())
+    write_value(f, '%s', s.get_net_interfaces)
 
     f.write('\nRouting table\n')
     f.write('~~~~~~~~~~~~~\n\n')
-    f.write(s.get_net_routing_table())
+    write_value(f, '%s', s.get_net_routing_table)
 
     f.write('\nStatistics\n')
     f.write('~~~~~~~~~~\n\n')
-    f.write(s.get_net_stats())
+    write_value(f, '%s', s.get_net_stats)
 
     f.write('\nConnections\n')
     f.write('~~~~~~~~~~~\n\n')
-    f.write(s.get_net_connections())
+    write_value(f, '%s', s.get_net_connections)
 
     try:
         if os.getuid() != 0:

+ 7 - 143
src/bin/xfrin/tests/xfrin_test.py

@@ -2739,18 +2739,8 @@ class TestMain(unittest.TestCase):
 
 class TestXfrinProcessMockCC:
     def __init__(self):
-        self.get_called = False
-        self.get_called_correctly = False
         self.config = []
 
-    def get_remote_config_value(self, module, identifier):
-        self.get_called = True
-        if module == 'Auth' and identifier == 'datasources':
-            self.get_called_correctly = True
-            return (self.config, False)
-        else:
-            return (None, True)
-
 class TestXfrinProcessMockCCSession:
     def __init__(self):
         self.send_called = False
@@ -2869,22 +2859,17 @@ class TestXfrinProcess(unittest.TestCase):
         # Create a connection for each attempt
         self.assertEqual(len(transfers), self.__created_connections)
         self.assertEqual([published], self.__published)
-        if published == XFRIN_OK:
-            self.assertTrue(self._module_cc.get_called)
-            self.assertTrue(self._module_cc.get_called_correctly)
-        else:
-            self.assertFalse(self._module_cc.get_called)
-            self.assertFalse(self._module_cc.get_called_correctly)
 
     def test_ixfr_ok(self):
         """
         Everything OK the first time, over IXFR.
         """
         self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
-        self.assertFalse(self._send_cc_session.send_called)
-        self.assertFalse(self._send_cc_session.send_called_correctly)
-        self.assertFalse(self._send_cc_session.recv_called)
-        self.assertFalse(self._send_cc_session.recv_called_correctly)
+        # Check there was loadzone command
+        self.assertTrue(self._send_cc_session.send_called)
+        self.assertTrue(self._send_cc_session.send_called_correctly)
+        self.assertTrue(self._send_cc_session.recv_called)
+        self.assertTrue(self._send_cc_session.recv_called_correctly)
 
     def test_axfr_ok(self):
         """
@@ -2916,137 +2901,16 @@ class TestXfrinProcess(unittest.TestCase):
         self.__do_test([XFRIN_FAIL, XFRIN_FAIL],
                        [RRType.IXFR(), RRType.AXFR()], RRType.IXFR())
 
-    def test_inmem_ok(self):
+    def test_send_loadzone(self):
         """
-        Inmem configuration where all the configuration is just right
-        for loadzone to be sent to b10-auth (origin is the name received
-        by xfrin, filetype is sqlite3, type is memory and class is the
-        one received by xfrin).
+        Check the loadzone command is sent after successful transfer.
         """
-        self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'sqlite3',
-                                              'file': 'data/inmem-xfrin.sqlite3'}],
-                                   'type': 'memory', 'class': 'IN'}]
         self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
         self.assertTrue(self._send_cc_session.send_called)
         self.assertTrue(self._send_cc_session.send_called_correctly)
         self.assertTrue(self._send_cc_session.recv_called)
         self.assertTrue(self._send_cc_session.recv_called_correctly)
 
-    def test_inmem_datasource_type_not_memory(self):
-        """
-        Inmem configuration where the datasource type is not memory. In
-        this case, loadzone should not be sent to b10-auth.
-        """
-        self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'sqlite3',
-                                              'file': 'data/inmem-xfrin.sqlite3'}],
-                                   'type': 'punched-card', 'class': 'IN'}]
-        self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
-        self.assertFalse(self._send_cc_session.send_called)
-        self.assertFalse(self._send_cc_session.send_called_correctly)
-        self.assertFalse(self._send_cc_session.recv_called)
-        self.assertFalse(self._send_cc_session.recv_called_correctly)
-
-    def test_inmem_datasource_type_is_missing(self):
-        """
-        Inmem configuration where the datasource type is missing. In
-        this case, loadzone should not be sent to b10-auth.
-        """
-        self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'sqlite3',
-                                              'file': 'data/inmem-xfrin.sqlite3'}],
-                                   'class': 'IN'}]
-        self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
-        self.assertFalse(self._send_cc_session.send_called)
-        self.assertFalse(self._send_cc_session.send_called_correctly)
-        self.assertFalse(self._send_cc_session.recv_called)
-        self.assertFalse(self._send_cc_session.recv_called_correctly)
-
-    def test_inmem_backend_type_not_sqlite3(self):
-        """
-        Inmem configuration where the datasource backing file is not of
-        type sqlite3. In this case, loadzone should not be sent to
-        b10-auth.
-        """
-        self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'postgresql',
-                                              'file': 'data/inmem-xfrin.db'}],
-                                   'type': 'memory', 'class': 'IN'}]
-        self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
-        self.assertFalse(self._send_cc_session.send_called)
-        self.assertFalse(self._send_cc_session.send_called_correctly)
-        self.assertFalse(self._send_cc_session.recv_called)
-        self.assertFalse(self._send_cc_session.recv_called_correctly)
-
-    def test_inmem_backend_type_is_missing(self):
-        """
-        Inmem configuration where the datasource backing file type is
-        not set. In this case, loadzone should not be sent to b10-auth.
-        """
-        self._module_cc.config = [{'zones': [{'origin': 'example.org',
-                                              'file': 'data/inmem-xfrin'}],
-                                   'type': 'memory', 'class': 'IN'}]
-        self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
-        self.assertFalse(self._send_cc_session.send_called)
-        self.assertFalse(self._send_cc_session.send_called_correctly)
-        self.assertFalse(self._send_cc_session.recv_called)
-        self.assertFalse(self._send_cc_session.recv_called_correctly)
-
-    def test_inmem_class_is_different(self):
-        """
-        Inmem configuration where the datasource class does not match
-        the received class. In this case, loadzone should not be sent to
-        b10-auth.
-        """
-        self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'sqlite3',
-                                              'file': 'data/inmem-xfrin.sqlite3'}],
-                                   'type': 'memory', 'class': 'XX'}]
-        self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
-        self.assertFalse(self._send_cc_session.send_called)
-        self.assertFalse(self._send_cc_session.send_called_correctly)
-        self.assertFalse(self._send_cc_session.recv_called)
-        self.assertFalse(self._send_cc_session.recv_called_correctly)
-
-    def test_inmem_class_is_missing(self):
-        """
-        Inmem configuration where the datasource class is missing. In
-        this case, we assume the IN class and loadzone may be sent to
-        b10-auth if everything else matches.
-        """
-        self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'sqlite3',
-                                              'file': 'data/inmem-xfrin.sqlite3'}],
-                                   'type': 'memory'}]
-        self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
-        self.assertTrue(self._send_cc_session.send_called)
-        self.assertTrue(self._send_cc_session.send_called_correctly)
-        self.assertTrue(self._send_cc_session.recv_called)
-        self.assertTrue(self._send_cc_session.recv_called_correctly)
-
-    def test_inmem_name_doesnt_match(self):
-        """
-        Inmem configuration where the origin does not match the received
-        name. In this case, loadzone should not be sent to b10-auth.
-        """
-        self._module_cc.config = [{'zones': [{'origin': 'isc.org', 'filetype': 'sqlite3',
-                                              'file': 'data/inmem-xfrin.sqlite3'}],
-                                   'type': 'memory', 'class': 'IN'}]
-        self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
-        self.assertFalse(self._send_cc_session.send_called)
-        self.assertFalse(self._send_cc_session.send_called_correctly)
-        self.assertFalse(self._send_cc_session.recv_called)
-        self.assertFalse(self._send_cc_session.recv_called_correctly)
-
-    def test_inmem_name_is_missing(self):
-        """
-        Inmem configuration where the origin is missing. In this case,
-        loadzone should not be sent to b10-auth.
-        """
-        self._module_cc.config = [{'zones': [{'filetype': 'sqlite3',
-                                              'file': 'data/inmem-xfrin.sqlite3'}],
-                                   'type': 'memory', 'class': 'IN'}]
-        self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
-        self.assertFalse(self._send_cc_session.send_called)
-        self.assertFalse(self._send_cc_session.send_called_correctly)
-        self.assertFalse(self._send_cc_session.recv_called)
-        self.assertFalse(self._send_cc_session.recv_called_correctly)
-
 class TestFormatting(unittest.TestCase):
     # If the formatting functions are moved to a more general library
     # (ticket #1379), these tests should be moved with them.

+ 1 - 1
src/bin/xfrin/xfrin.py.in

@@ -1256,7 +1256,7 @@ def _do_auth_loadzone(server, zone_name, zone_class):
     if msg is not None:
         param = msg['command'][1]
         logger.debug(DBG_XFRIN_TRACE, XFRIN_AUTH_LOADZONE, param["origin"],
-                     param["class"], param["datasrc"])
+                     param["class"])
         seq = server._send_cc_session.group_sendmsg(msg, AUTH_MODULE_NAME)
         answer, env = server._send_cc_session.group_recvmsg(False, seq)
 

+ 3 - 4
src/bin/xfrin/xfrin_messages.mes

@@ -15,10 +15,9 @@
 # No namespace declaration - these constants go in the global namespace
 # of the xfrin messages python module.
 
-% XFRIN_AUTH_LOADZONE sending Auth loadzone for origin=%1, class=%2, datasrc=%3
-There was a successful zone transfer, and the zone is served by b10-auth
-in the in-memory data source using sqlite3 as a backend. We send the
-"loadzone" command for the zone to b10-auth.
+% XFRIN_AUTH_LOADZONE sending Auth loadzone for origin=%1, class=%2
+There was a successful zone transfer.  We send the "loadzone" command for the
+zone to b10-auth.
 
 % XFRIN_AXFR_INCONSISTENT_SOA AXFR SOAs are inconsistent for %1: %2 expected, %3 received
 The serial fields of the first and last SOAs of AXFR (including AXFR-style

+ 10 - 1
src/bin/zonemgr/b10-zonemgr.8

@@ -9,6 +9,15 @@
 .\"
 .TH "B10\-ZONEMGR" "8" "February 28, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" disable hyphenation
@@ -92,7 +101,7 @@ The configuration commands are:
 (sent by
 \fBb10-auth\fR(8)) tells
 \fBb10\-zonemgr\fR
-the zone name and class, and the IP address for the master (source of the NOTIFY message)\&. This will set the zone\'s refresh time to now\&.
+the zone name and class, and the IP address for the master (source of the NOTIFY message)\&. This will set the zone\*(Aqs refresh time to now\&.
 This is an internal command and not exposed to the administrator\&.
 .PP
 

+ 13 - 13
src/lib/acl/Makefile.am

@@ -5,23 +5,23 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 # The core library
-lib_LTLIBRARIES = libacl.la
-libacl_la_SOURCES  = acl.h
-libacl_la_SOURCES += check.h
-libacl_la_SOURCES += ip_check.h ip_check.cc
-libacl_la_SOURCES += logic_check.h
-libacl_la_SOURCES += loader.h loader.cc
+lib_LTLIBRARIES = libb10-acl.la
+libb10_acl_la_SOURCES  = acl.h
+libb10_acl_la_SOURCES += check.h
+libb10_acl_la_SOURCES += ip_check.h ip_check.cc
+libb10_acl_la_SOURCES += logic_check.h
+libb10_acl_la_SOURCES += loader.h loader.cc
 
-libacl_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
-libacl_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
-libacl_la_LIBADD += $(top_builddir)/src/lib/util/libutil.la
+libb10_acl_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+libb10_acl_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+libb10_acl_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
 
 # DNS specialized one
-lib_LTLIBRARIES += libdnsacl.la
+lib_LTLIBRARIES += libb10-dnsacl.la
 
-libdnsacl_la_SOURCES = dns.h dns.cc dnsname_check.h
+libb10_dnsacl_la_SOURCES = dns.h dns.cc dnsname_check.h
 
-libdnsacl_la_LIBADD = libacl.la
-libdnsacl_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la
+libb10_dnsacl_la_LIBADD = libb10-acl.la
+libb10_dnsacl_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 
 CLEANFILES = *.gcno *.gcda

+ 7 - 7
src/lib/acl/tests/Makefile.am

@@ -31,13 +31,13 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/acl/libacl.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-run_unittests_LDADD += $(top_builddir)/src/lib/acl/libdnsacl.la
+run_unittests_LDADD += $(top_builddir)/src/lib/acl/libb10-acl.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/acl/libb10-dnsacl.la
 endif
 
 noinst_PROGRAMS = $(TESTS)

+ 17 - 17
src/lib/asiodns/Makefile.am

@@ -16,28 +16,28 @@ asiodns_messages.h asiodns_messages.cc: asiodns_messages.mes
 
 BUILT_SOURCES = asiodns_messages.h asiodns_messages.cc
 
-lib_LTLIBRARIES = libasiodns.la
-libasiodns_la_SOURCES = dns_answer.h
-libasiodns_la_SOURCES += asiodns.h
-libasiodns_la_SOURCES += dns_lookup.h
-libasiodns_la_SOURCES += dns_server.h
-libasiodns_la_SOURCES += dns_service.cc dns_service.h
-libasiodns_la_SOURCES += tcp_server.cc tcp_server.h
-libasiodns_la_SOURCES += udp_server.cc udp_server.h
-libasiodns_la_SOURCES += sync_udp_server.cc sync_udp_server.h
-libasiodns_la_SOURCES += io_fetch.cc io_fetch.h
-libasiodns_la_SOURCES += logger.h logger.cc
-
-nodist_libasiodns_la_SOURCES = asiodns_messages.cc asiodns_messages.h
+lib_LTLIBRARIES = libb10-asiodns.la
+libb10_asiodns_la_SOURCES = dns_answer.h
+libb10_asiodns_la_SOURCES += asiodns.h
+libb10_asiodns_la_SOURCES += dns_lookup.h
+libb10_asiodns_la_SOURCES += dns_server.h
+libb10_asiodns_la_SOURCES += dns_service.cc dns_service.h
+libb10_asiodns_la_SOURCES += tcp_server.cc tcp_server.h
+libb10_asiodns_la_SOURCES += udp_server.cc udp_server.h
+libb10_asiodns_la_SOURCES += sync_udp_server.cc sync_udp_server.h
+libb10_asiodns_la_SOURCES += io_fetch.cc io_fetch.h
+libb10_asiodns_la_SOURCES += logger.h logger.cc
+
+nodist_libb10_asiodns_la_SOURCES = asiodns_messages.cc asiodns_messages.h
 
 EXTRA_DIST = asiodns_messages.mes
 
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
-libasiodns_la_CXXFLAGS = $(AM_CXXFLAGS)
+libb10_asiodns_la_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_CLANGPP
 # Same for clang++, but we need to turn off -Werror completely.
-libasiodns_la_CXXFLAGS += -Wno-error
+libb10_asiodns_la_CXXFLAGS += -Wno-error
 endif
-libasiodns_la_CPPFLAGS = $(AM_CPPFLAGS)
-libasiodns_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
+libb10_asiodns_la_CPPFLAGS = $(AM_CPPFLAGS)
+libb10_asiodns_la_LIBADD = $(top_builddir)/src/lib/log/libb10-log.la

+ 6 - 6
src/lib/asiodns/tests/Makefile.am

@@ -28,13 +28,13 @@ run_unittests_SOURCES += io_fetch_unittest.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 
 run_unittests_LDADD  = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
-run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
 
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 

+ 23 - 23
src/lib/asiolink/Makefile.am

@@ -13,32 +13,32 @@ CLEANFILES = *.gcno *.gcda
 # gcc's unused-parameter warning, which would make the build fail
 # with -Werror (our default setting).
 
-lib_LTLIBRARIES = libasiolink.la
-
-libasiolink_la_LDFLAGS = -no-undefined -version-info 1:0:1
-
-libasiolink_la_SOURCES  = asiolink.h
-libasiolink_la_SOURCES += dummy_io_cb.h
-libasiolink_la_SOURCES += interval_timer.cc interval_timer.h
-libasiolink_la_SOURCES += io_address.cc io_address.h
-libasiolink_la_SOURCES += io_asio_socket.h
-libasiolink_la_SOURCES += io_endpoint.cc io_endpoint.h
-libasiolink_la_SOURCES += io_error.h
-libasiolink_la_SOURCES += io_message.h
-libasiolink_la_SOURCES += io_service.h io_service.cc
-libasiolink_la_SOURCES += io_socket.h io_socket.cc
-libasiolink_la_SOURCES += simple_callback.h
-libasiolink_la_SOURCES += tcp_endpoint.h
-libasiolink_la_SOURCES += tcp_socket.h
-libasiolink_la_SOURCES += udp_endpoint.h
-libasiolink_la_SOURCES += udp_socket.h
+lib_LTLIBRARIES = libb10-asiolink.la
+
+libb10_asiolink_la_LDFLAGS = -no-undefined -version-info 1:0:1
+
+libb10_asiolink_la_SOURCES  = asiolink.h
+libb10_asiolink_la_SOURCES += dummy_io_cb.h
+libb10_asiolink_la_SOURCES += interval_timer.cc interval_timer.h
+libb10_asiolink_la_SOURCES += io_address.cc io_address.h
+libb10_asiolink_la_SOURCES += io_asio_socket.h
+libb10_asiolink_la_SOURCES += io_endpoint.cc io_endpoint.h
+libb10_asiolink_la_SOURCES += io_error.h
+libb10_asiolink_la_SOURCES += io_message.h
+libb10_asiolink_la_SOURCES += io_service.h io_service.cc
+libb10_asiolink_la_SOURCES += io_socket.h io_socket.cc
+libb10_asiolink_la_SOURCES += simple_callback.h
+libb10_asiolink_la_SOURCES += tcp_endpoint.h
+libb10_asiolink_la_SOURCES += tcp_socket.h
+libb10_asiolink_la_SOURCES += udp_endpoint.h
+libb10_asiolink_la_SOURCES += udp_socket.h
 
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
-libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS)
+libb10_asiolink_la_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_CLANGPP
 # Same for clang++, but we need to turn off -Werror completely.
-libasiolink_la_CXXFLAGS += -Wno-error
+libb10_asiolink_la_CXXFLAGS += -Wno-error
 endif
-libasiolink_la_CPPFLAGS = $(AM_CPPFLAGS)
-libasiolink_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
+libb10_asiolink_la_CPPFLAGS = $(AM_CPPFLAGS)
+libb10_asiolink_la_LIBADD = $(top_builddir)/src/lib/log/libb10-log.la

+ 3 - 3
src/lib/asiolink/tests/Makefile.am

@@ -36,10 +36,10 @@ run_unittests_SOURCES += udp_socket_unittest.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 
-run_unittests_LDADD = $(top_builddir)/src/lib/asiolink/libasiolink.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 run_unittests_LDADD += $(GTEST_LDADD)
 
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)

+ 2 - 2
src/lib/bench/Makefile.am

@@ -6,6 +6,6 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 CLEANFILES = *.gcno *.gcda
 
-noinst_LTLIBRARIES = libbench.la
-libbench_la_SOURCES = benchmark_util.h benchmark_util.cc
+noinst_LTLIBRARIES = libb10-bench.la
+libb10_bench_la_SOURCES = benchmark_util.h benchmark_util.cc
 EXTRA_DIST = benchmark.h

+ 1 - 1
src/lib/bench/example/Makefile.am

@@ -5,5 +5,5 @@ CLEANFILES = *.gcno *.gcda
 noinst_PROGRAMS = search_bench
 search_bench_SOURCES = search_bench.cc
 
-search_bench_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+search_bench_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 

+ 4 - 4
src/lib/bench/tests/Makefile.am

@@ -17,11 +17,11 @@ run_unittests_SOURCES += loadquery_unittest.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD  = $(top_builddir)/src/lib/bench/libbench.la
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD  = $(top_builddir)/src/lib/bench/libb10-bench.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 run_unittests_LDADD += $(GTEST_LDADD)
 endif
 

+ 12 - 12
src/lib/cache/Makefile.am

@@ -21,18 +21,18 @@ if USE_CLANGPP
 AM_CXXFLAGS += -Wno-unused-parameter
 endif
 
-lib_LTLIBRARIES = libcache.la
-libcache_la_SOURCES  = resolver_cache.h resolver_cache.cc
-libcache_la_SOURCES  += message_cache.h message_cache.cc
-libcache_la_SOURCES  += message_entry.h message_entry.cc
-libcache_la_SOURCES  += rrset_cache.h rrset_cache.cc
-libcache_la_SOURCES  += rrset_entry.h rrset_entry.cc
-libcache_la_SOURCES  += cache_entry_key.h cache_entry_key.cc
-libcache_la_SOURCES  += rrset_copy.h rrset_copy.cc
-libcache_la_SOURCES  += local_zone_data.h local_zone_data.cc
-libcache_la_SOURCES  += message_utility.h message_utility.cc
-libcache_la_SOURCES  += logger.h logger.cc
-nodist_libcache_la_SOURCES = cache_messages.cc cache_messages.h
+lib_LTLIBRARIES = libb10-cache.la
+libb10_cache_la_SOURCES  = resolver_cache.h resolver_cache.cc
+libb10_cache_la_SOURCES  += message_cache.h message_cache.cc
+libb10_cache_la_SOURCES  += message_entry.h message_entry.cc
+libb10_cache_la_SOURCES  += rrset_cache.h rrset_cache.cc
+libb10_cache_la_SOURCES  += rrset_entry.h rrset_entry.cc
+libb10_cache_la_SOURCES  += cache_entry_key.h cache_entry_key.cc
+libb10_cache_la_SOURCES  += rrset_copy.h rrset_copy.cc
+libb10_cache_la_SOURCES  += local_zone_data.h local_zone_data.cc
+libb10_cache_la_SOURCES  += message_utility.h message_utility.cc
+libb10_cache_la_SOURCES  += logger.h logger.cc
+nodist_libb10_cache_la_SOURCES = cache_messages.cc cache_messages.h
 
 BUILT_SOURCES = cache_messages.cc cache_messages.h
 

+ 7 - 7
src/lib/cache/tests/Makefile.am

@@ -50,14 +50,14 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD    = $(GTEST_LDADD)
 
-run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
-run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cache/libb10-cache.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 endif
 
 noinst_PROGRAMS = $(TESTS)

+ 5 - 5
src/lib/cc/Makefile.am

@@ -20,11 +20,11 @@ if USE_CLANGPP
 AM_CXXFLAGS += -Wno-error
 endif
 
-lib_LTLIBRARIES = libcc.la
-libcc_la_SOURCES = data.cc data.h session.cc session.h
-libcc_la_SOURCES += logger.cc logger.h
-nodist_libcc_la_SOURCES = cc_messages.cc cc_messages.h
-libcc_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
+lib_LTLIBRARIES = libb10-cc.la
+libb10_cc_la_SOURCES = data.cc data.h session.cc session.h
+libb10_cc_la_SOURCES += logger.cc logger.h
+nodist_libb10_cc_la_SOURCES = cc_messages.cc cc_messages.h
+libb10_cc_la_LIBADD = $(top_builddir)/src/lib/log/libb10-log.la
 
 CLEANFILES = *.gcno *.gcda session_config.h cc_messages.cc cc_messages.h
 

+ 3 - 3
src/lib/cc/tests/Makefile.am

@@ -30,10 +30,10 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 # We need to put our libs first, in case gtest (or any dependency, really)
 # is installed in the same location as a different version of bind10
 # Otherwise the linker may not use the source tree libs
-run_unittests_LDADD =  $(top_builddir)/src/lib/cc/libcc.la
-run_unittests_LDADD +=  $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD =  $(top_builddir)/src/lib/cc/libb10-cc.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/log/libb10-log.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD +=  $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 run_unittests_LDADD += $(GTEST_LDADD)
 
 endif

+ 9 - 9
src/lib/config/Makefile.am

@@ -11,18 +11,18 @@ config_messages.h config_messages.cc: config_messages.mes
 
 BUILT_SOURCES = config_messages.h config_messages.cc
 
-lib_LTLIBRARIES = libcfgclient.la
-libcfgclient_la_SOURCES = config_data.h config_data.cc
-libcfgclient_la_SOURCES += module_spec.h module_spec.cc
-libcfgclient_la_SOURCES += ccsession.cc ccsession.h
-libcfgclient_la_SOURCES += config_log.h config_log.cc
+lib_LTLIBRARIES = libb10-cfgclient.la
+libb10_cfgclient_la_SOURCES = config_data.h config_data.cc
+libb10_cfgclient_la_SOURCES += module_spec.h module_spec.cc
+libb10_cfgclient_la_SOURCES += ccsession.cc ccsession.h
+libb10_cfgclient_la_SOURCES += config_log.h config_log.cc
 
-libcfgclient_la_LIBADD = $(top_builddir)/src/lib/cc/libcc.la
-libcfgclient_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+libb10_cfgclient_la_LIBADD = $(top_builddir)/src/lib/cc/libb10-cc.la
+libb10_cfgclient_la_LIBADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 
-libcfgclient_la_LDFLAGS = -no-undefined -version-info 1:0:1
+libb10_cfgclient_la_LDFLAGS = -no-undefined -version-info 1:0:1
 
-nodist_libcfgclient_la_SOURCES  = config_messages.h config_messages.cc
+nodist_libb10_cfgclient_la_SOURCES  = config_messages.h config_messages.cc
 
 # The message file should be in the distribution.
 EXTRA_DIST = config_messages.mes

+ 0 - 0
src/lib/config/module_spec.cc


Some files were not shown because too many files changed in this diff