Browse Source

Merge branch 'master' into trac950

Shane Kerr 14 years ago
parent
commit
356ffa38fc
100 changed files with 2875 additions and 707 deletions
  1. 80 1
      ChangeLog
  2. 3 0
      Makefile.am
  3. 62 3
      configure.ac
  4. 2 2
      doc/Doxyfile
  5. 1 1
      src/bin/auth/Makefile.am
  6. 14 0
      src/bin/auth/auth_config.cc
  7. 79 21
      src/bin/auth/auth_srv.cc
  8. 11 0
      src/bin/auth/auth_srv.h
  9. 1 0
      src/bin/auth/benchmarks/Makefile.am
  10. 15 1
      src/bin/auth/main.cc
  11. 1 0
      src/bin/auth/tests/Makefile.am
  12. 138 0
      src/bin/auth/tests/auth_srv_unittest.cc
  13. 7 0
      src/bin/auth/tests/config_unittest.cc
  14. 4 1
      src/bin/auth/tests/run_unittests.cc
  15. 1 0
      src/bin/cfgmgr/plugins/Makefile.am
  16. 96 0
      src/bin/cfgmgr/plugins/b10logging.py
  17. 81 0
      src/bin/cfgmgr/plugins/logging.spec
  18. 1 1
      src/bin/cfgmgr/plugins/tests/tsig_keys_test.py
  19. 3 2
      src/bin/cmdctl/Makefile.am
  20. 16 3
      src/bin/resolver/Makefile.am
  21. 26 20
      src/bin/resolver/main.cc
  22. 86 45
      src/bin/resolver/resolver.cc
  23. 19 0
      src/bin/resolver/resolver_log.cc
  24. 49 0
      src/bin/resolver/resolver_log.h
  25. 193 0
      src/bin/resolver/resolverdef.mes
  26. 10 4
      src/bin/resolver/tests/Makefile.am
  27. 4 1
      src/bin/resolver/tests/run_unittests.cc
  28. 2 3
      src/bin/sockcreator/tests/Makefile.am
  29. 2 1
      src/bin/sockcreator/tests/run_unittests.cc
  30. 51 1
      src/bin/xfrin/tests/xfrin_test.py
  31. 7 5
      src/bin/xfrin/xfrin.py.in
  32. 210 6
      src/bin/xfrout/tests/xfrout_test.py.in
  33. 90 20
      src/bin/xfrout/xfrout.py.in
  34. 12 0
      src/bin/xfrout/xfrout.spec.pre.in
  35. 1 6
      src/cppcheck-suppress.lst
  36. 1 1
      src/lib/Makefile.am
  37. 6 0
      src/lib/acl/Makefile.am
  38. 195 0
      src/lib/acl/check.h
  39. 15 0
      src/lib/acl/tests/Makefile.am
  40. 70 0
      src/lib/acl/tests/check_test.cc
  41. 4 3
      src/lib/util/io/tests/run_unittests.cc
  42. 3 10
      src/lib/asiodns/io_fetch.cc
  43. 2 2
      src/lib/asiodns/tests/Makefile.am
  44. 3 0
      src/lib/asiodns/tests/io_fetch_unittest.cc
  45. 4 3
      src/lib/asiodns/tests/run_unittests.cc
  46. 39 24
      src/lib/asiolink/interval_timer.cc
  47. 4 6
      src/lib/asiolink/interval_timer.h
  48. 2 3
      src/lib/asiolink/tests/Makefile.am
  49. 4 6
      src/lib/asiolink/tests/run_unittests.cc
  50. 3 3
      src/lib/bench/tests/Makefile.am
  51. 2 1
      src/lib/bench/tests/run_unittests.cc
  52. 3 2
      src/lib/cache/TODO
  53. 0 18
      src/lib/cache/message_cache.cc
  54. 2 14
      src/lib/cache/message_cache.h
  55. 0 10
      src/lib/cache/resolver_cache.cc
  56. 3 17
      src/lib/cache/resolver_cache.h
  57. 0 18
      src/lib/cache/rrset_cache.cc
  58. 3 22
      src/lib/cache/rrset_cache.h
  59. 14 14
      src/lib/cache/tests/Makefile.am
  60. 2 1
      src/lib/cache/tests/run_unittests.cc
  61. 1 0
      src/lib/cc/tests/Makefile.am
  62. 2 1
      src/lib/cc/tests/run_unittests.cc
  63. 182 31
      src/lib/config/ccsession.cc
  64. 35 5
      src/lib/config/ccsession.h
  65. 94 42
      src/lib/config/config_data.cc
  66. 10 0
      src/lib/config/config_data.h
  67. 7 0
      src/lib/config/configdef.mes
  68. 3 2
      src/lib/config/tests/Makefile.am
  69. 89 3
      src/lib/config/tests/ccsession_unittests.cc
  70. 17 3
      src/lib/config/tests/config_data_unittests.cc
  71. 1 0
      src/lib/config/tests/data_def_unittests_config.h.in
  72. 19 3
      src/lib/config/tests/fake_session.cc
  73. 9 0
      src/lib/config/tests/fake_session.h
  74. 3 7
      src/lib/config/tests/run_unittests.cc
  75. 2 0
      src/lib/config/tests/testdata/Makefile.am
  76. 45 0
      src/lib/config/tests/testdata/spec30.spec
  77. 63 0
      src/lib/config/tests/testdata/spec31.spec
  78. 15 5
      src/lib/cryptolink/crypto_hmac.cc
  79. 8 4
      src/lib/cryptolink/cryptolink.h
  80. 3 2
      src/lib/cryptolink/tests/Makefile.am
  81. 160 69
      src/lib/cryptolink/tests/crypto_unittests.cc
  82. 2 1
      src/lib/cryptolink/tests/run_unittests.cc
  83. 5 2
      src/lib/datasrc/tests/Makefile.am
  84. 5 1
      src/lib/datasrc/tests/run_unittests.cc
  85. 3 0
      src/lib/dns/python/tests/tsigkey_python_test.py
  86. 6 0
      src/lib/dns/python/tsigkey_python.cc
  87. 5 2
      src/lib/dns/tests/Makefile.am
  88. 2 1
      src/lib/dns/tests/run_unittests.cc
  89. 23 0
      src/lib/dns/tests/tsig_unittest.cc
  90. 12 0
      src/lib/dns/tests/tsigkey_unittest.cc
  91. 28 0
      src/lib/dns/tsigkey.cc
  92. 3 0
      src/lib/dns/tsigkey.h
  93. 3 0
      src/lib/exceptions/tests/run_unittests.cc
  94. 15 10
      src/lib/log/Makefile.am
  95. 26 73
      src/lib/log/README
  96. 3 5
      src/lib/log/compiler/Makefile.am
  97. 157 109
      src/lib/log/compiler/message.cc
  98. 29 0
      src/lib/log/impldef.cc
  99. 18 0
      src/lib/log/impldef.h
  100. 0 0
      src/lib/log/impldef.mes

+ 80 - 1
ChangeLog

@@ -1,3 +1,82 @@
+257.	[bug]           y-aharen
+	Fixed a bug an instance of IntervalTimerImpl may be destructed 
+	while deadline_timer is holding the handler. This fix addresses
+	occasional failure of IntervalTimerTest.destructIntervalTimer.
+	(Trac #957, git e59c215e14b5718f62699ec32514453b983ff603)
+
+256.	[bug]		jerry
+	src/bin/xfrin: update xfrin to check TSIG before other part of
+	incoming message.
+	(Trac955, git 261450e93af0b0406178e9ef121f81e721e0855c)
+
+255.	[func]		zhang likun
+	src/lib/cache:  remove empty code in lib/cache and the corresponding
+	suppression rule in	src/cppcheck-suppress.lst.
+	(Trac639, git 4f714bac4547d0a025afd314c309ca5cb603e212)
+
+254.	[bug]		jinmei
+	b10-xfrout: failed to send notifies over IPv6 correctly.
+	(Trac964, git 3255c92714737bb461fb67012376788530f16e40)
+
+253.    [func]		jelte
+	Add configuration options for logging through the virtual module
+	Logging.
+	(Trac 736, git 9fa2a95177265905408c51d13c96e752b14a0824)
+
+252.    [func]      	stephen
+	Add syslog as destination for logging.
+	(Trac976, git 31a30f5485859fd3df2839fc309d836e3206546e)
+
+251.	[bug]*		jinmei
+	Make sure bindctl private files are non readable to anyone except
+	the owner or users in the same group.  Note that if BIND 10 is run
+	with changing the user, this change means that the file owner or
+	group will have to be adjusted.  Also note that this change is
+	only effective for a fresh install; if these files already exist,
+	their permissions must be adjusted by hand (if necessary).
+	(Trac870, git 461fc3cb6ebabc9f3fa5213749956467a14ebfd4)
+
+250.    [bug]           ocean
+	src/lib/util/encode, in some conditions, the DecodeNormalizer's
+	iterator may reach the end() and when later being dereferenced
+	it will cause crash on some platform.
+	(Trac838, git 83e33ec80c0c6485d8b116b13045b3488071770f)
+
+249.    [func]      	jerry
+	xfrout: add support for TSIG verification.
+	(Trac816, git 3b2040e2af2f8139c1c319a2cbc429035d93f217)
+
+248.    [func]      	stephen
+	Add file and stderr as destinations for logging.
+	(Trac555, git 38b3546867425bd64dbc5920111a843a3330646b)
+
+247.    [func]      	jelte
+	Upstream queries from the resolver now set EDNS0 buffer size.
+	(Trac834, git 48e10c2530fe52c9bde6197db07674a851aa0f5d)
+
+246.    [func]      	stephen
+	Implement logging using log4cplus (http://log4cplus.sourceforge.net)
+	(Trac899, git 31d3f525dc01638aecae460cb4bc2040c9e4df10)
+
+245.    [func]      	vorner
+	Authoritative server can now sign the answers using TSIG
+	(configured in tsig_keys/keys, list of strings like
+	"name:<base64-secret>:sha1-hmac"). It doesn't use them for
+	ACL yet, only verifies them and signs if the request is signed.
+	(Trac875, git fe5e7003544e4e8f18efa7b466a65f336d8c8e4d)
+
+244.	[func] 		stephen
+	In unit tests, allow the choice of whether unhandled exceptions are
+	caught in the unit test program (and details printed) or allowed to
+	propagate to the default exception handler.  See the bind10-dev thread
+	https://lists.isc.org/pipermail/bind10-dev/2011-January/001867.html
+	for more details.
+	(Trac #542, git 1aa773d84cd6431aa1483eb34a7f4204949a610f)
+
+243.	[func]*		feng
+	Add optional hmac algorithm SHA224/384/812.
+	(Trac#782, git 77d792c9d7c1a3f95d3e6a8b721ac79002cd7db1)
+
 bind10-devel-20110519 released on May 19, 2011
 bind10-devel-20110519 released on May 19, 2011
 
 
 242.	[func]		jinmei
 242.	[func]		jinmei
@@ -80,7 +159,7 @@ bind10-devel-20110519 released on May 19, 2011
 	(Trac #900, git b395258c708b49a5da8d0cffcb48d83294354ba3)
 	(Trac #900, git b395258c708b49a5da8d0cffcb48d83294354ba3)
 
 
 231.	[func]*		vorner
 231.	[func]*		vorner
-	The logging interface changed slightly. We use
+    The logging interface changed slightly. We use
 	logger.foo(MESSAGE_ID).arg(bar); instead of logger.foo(MESSAGE_ID,
 	logger.foo(MESSAGE_ID).arg(bar); instead of logger.foo(MESSAGE_ID,
 	bar); internally. The message definitions use '%1,%2,...'
 	bar); internally. The message definitions use '%1,%2,...'
 	instead of '%s,%d', which allows us to cope better with
 	instead of '%s,%d', which allows us to cope better with

+ 3 - 0
Makefile.am

@@ -38,8 +38,11 @@ report-cpp-coverage:
 			c++/4.4\*/ext/\* \
 			c++/4.4\*/ext/\* \
 			c++/4.4\*/\*-\*/bits/\* \
 			c++/4.4\*/\*-\*/bits/\* \
 			boost/\* \
 			boost/\* \
+			botan/\* \
 			ext/asio/\* \
 			ext/asio/\* \
+			ext/coroutine/\* \
 			gtest/\* \
 			gtest/\* \
+			log4cplus/\* \
 			usr/include/\* \
 			usr/include/\* \
 			tests/\* \
 			tests/\* \
 			unittests/\* \
 			unittests/\* \

+ 62 - 3
configure.ac

@@ -447,6 +447,55 @@ AC_LINK_IFELSE(
 CPPFLAGS=$CPPFLAGS_SAVED
 CPPFLAGS=$CPPFLAGS_SAVED
 LDFLAGS=$LDFLAGS_SAVED
 LDFLAGS=$LDFLAGS_SAVED
 
 
+# Check for log4cplus
+log4cplus_path="yes"
+AC_ARG_WITH([log4cplus],
+  AC_HELP_STRING([--with-log4cplus=PATH],
+    [specify exact directory of log4cplus library and headers]),
+    [log4cplus_path="$withval"])
+if test "${log4cplus_path}" == "no" ; then
+    AC_MSG_ERROR([Need log4cplus])
+elif test "${log4cplus_path}" != "yes" ; then
+  LOG4CPLUS_INCLUDES="-I${log4cplus_path}/include"
+  LOG4CPLUS_LDFLAGS="-L${log4cplus_path}/lib"
+else
+# If not specified, try some common paths.
+	log4cplusdirs="/usr/local /usr/pkg /opt /opt/local"
+	for d in $log4cplusdirs
+	do
+		if test -f $d/include/log4cplus/logger.h; then
+			LOG4CPLUS_INCLUDES="-I$d/include"
+			LOG4CPLUS_LDFLAGS="-L$d/lib"
+			break
+		fi
+	done
+fi
+
+LOG4CPLUS_LDFLAGS="$LOG4CPLUS_LDFLAGS -llog4cplus $MULTITHREADING_FLAG"
+
+AC_SUBST(LOG4CPLUS_LDFLAGS)
+AC_SUBST(LOG4CPLUS_INCLUDES)
+
+CPPFLAGS_SAVED=$CPPFLAGS
+CPPFLAGS="$LOG4CPLUS_INCLUDES $CPPFLAGS"
+LDFLAGS_SAVED="$LDFLAGS"
+LDFLAGS="$LOG4CPLUS_LDFLAGS $LDFLAGS"
+
+AC_CHECK_HEADERS([log4cplus/logger.h],,AC_MSG_ERROR([Missing required header files.]))
+AC_LINK_IFELSE(
+        [AC_LANG_PROGRAM([#include <log4cplus/logger.h>
+                         ],
+                         [using namespace log4cplus;
+                          Logger logger = Logger::getInstance("main");
+                         ])],
+        [AC_MSG_RESULT([checking for log4cplus library... yes])],
+        [AC_MSG_RESULT([checking for log4cplus library... no])
+         AC_MSG_ERROR([Needs log4cplus library])]
+)
+
+CPPFLAGS=$CPPFLAGS_SAVED
+LDFLAGS=$LDFLAGS_SAVED
+
 #
 #
 # Configure Boost header path
 # Configure Boost header path
 #
 #
@@ -775,10 +824,11 @@ AC_CONFIG_FILES([Makefile
                  src/lib/server_common/tests/Makefile
                  src/lib/server_common/tests/Makefile
                  src/lib/util/Makefile
                  src/lib/util/Makefile
                  src/lib/util/io/Makefile
                  src/lib/util/io/Makefile
-                 src/lib/util/io/tests/Makefile
                  src/lib/util/unittests/Makefile
                  src/lib/util/unittests/Makefile
                  src/lib/util/pyunittests/Makefile
                  src/lib/util/pyunittests/Makefile
                  src/lib/util/tests/Makefile
                  src/lib/util/tests/Makefile
+                 src/lib/acl/Makefile
+                 src/lib/acl/tests/Makefile
                  tests/Makefile
                  tests/Makefile
                  tests/system/Makefile
                  tests/system/Makefile
                  tests/tools/Makefile
                  tests/tools/Makefile
@@ -842,7 +892,11 @@ AC_OUTPUT([doc/version.ent
            src/lib/dns/tests/testdata/gen-wiredata.py
            src/lib/dns/tests/testdata/gen-wiredata.py
            src/lib/cc/session_config.h.pre
            src/lib/cc/session_config.h.pre
            src/lib/cc/tests/session_unittests_config.h
            src/lib/cc/tests/session_unittests_config.h
-           src/lib/log/tests/run_time_init_test.sh
+           src/lib/log/tests/console_test.sh
+           src/lib/log/tests/destination_test.sh
+           src/lib/log/tests/local_file_test.sh
+           src/lib/log/tests/severity_test.sh
+           src/lib/log/tests/tempdir.h
            src/lib/util/python/mkpywrapper.py
            src/lib/util/python/mkpywrapper.py
            src/lib/server_common/tests/data_path.h
            src/lib/server_common/tests/data_path.h
            tests/system/conf.sh
            tests/system/conf.sh
@@ -869,7 +923,10 @@ AC_OUTPUT([doc/version.ent
            chmod +x src/bin/msgq/tests/msgq_test
            chmod +x src/bin/msgq/tests/msgq_test
            chmod +x src/lib/dns/gen-rdatacode.py
            chmod +x src/lib/dns/gen-rdatacode.py
            chmod +x src/lib/dns/tests/testdata/gen-wiredata.py
            chmod +x src/lib/dns/tests/testdata/gen-wiredata.py
-           chmod +x src/lib/log/tests/run_time_init_test.sh
+           chmod +x src/lib/log/tests/local_file_test.sh
+           chmod +x src/lib/log/tests/console_test.sh
+           chmod +x src/lib/log/tests/destination_test.sh
+           chmod +x src/lib/log/tests/severity_test.sh
            chmod +x src/lib/util/python/mkpywrapper.py
            chmod +x src/lib/util/python/mkpywrapper.py
            chmod +x tests/system/conf.sh
            chmod +x tests/system/conf.sh
           ])
           ])
@@ -902,6 +959,8 @@ dnl includes too
   Boost:         ${BOOST_INCLUDES}
   Boost:         ${BOOST_INCLUDES}
   Botan:         ${BOTAN_INCLUDES}
   Botan:         ${BOTAN_INCLUDES}
                  ${BOTAN_LDFLAGS}
                  ${BOTAN_LDFLAGS}
+  Log4cplus:     ${LOG4CPLUS_INCLUDES}
+                 ${LOG4CPLUS_LDFLAGS}
   SQLite:        $SQLITE_CFLAGS
   SQLite:        $SQLITE_CFLAGS
                  $SQLITE_LIBS
                  $SQLITE_LIBS
 
 

+ 2 - 2
doc/Doxyfile

@@ -573,8 +573,8 @@ INPUT                  = ../src/lib/cc ../src/lib/config \
     ../src/bin/auth ../src/bin/resolver ../src/lib/bench \
     ../src/bin/auth ../src/bin/resolver ../src/lib/bench \
     ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas \
     ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas \
     ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
     ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
-    ../src/bin/sockcreator/ ../src/lib/util/
+    ../src/bin/sockcreator/ ../src/lib/util/ \
-    ../src/lib/resolve
+    ../src/lib/resolve ../src/lib/acl
 
 
 # This tag can be used to specify the character encoding of the source files
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

+ 1 - 1
src/bin/auth/Makefile.am

@@ -51,7 +51,7 @@ 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/exceptions/libexceptions.la
 b10_auth_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.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/asiolink/libasiolink.la
-b10_auth_LDADD += $(top_builddir)/src/lib/nsas/libnsas.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/xfr/libxfr.la
 b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 b10_auth_LDADD += $(SQLITE_LIBS)
 b10_auth_LDADD += $(SQLITE_LIBS)

+ 14 - 0
src/bin/auth/auth_config.cc

@@ -60,6 +60,15 @@ private:
     set<string> configured_sources_;
     set<string> configured_sources_;
 };
 };
 
 
+/// A derived \c AuthConfigParser for the version value
+/// (which is not used at this moment)
+class VersionConfig : public AuthConfigParser {
+public:
+    VersionConfig() {}
+    virtual void build(ConstElementPtr) {};
+    virtual void commit() {};
+};
+
 void
 void
 DatasourcesConfig::build(ConstElementPtr config_value) {
 DatasourcesConfig::build(ConstElementPtr config_value) {
     BOOST_FOREACH(ConstElementPtr datasrc_elem, config_value->listValue()) {
     BOOST_FOREACH(ConstElementPtr datasrc_elem, config_value->listValue()) {
@@ -293,6 +302,11 @@ createAuthConfigParser(AuthSrv& server, const std::string& config_id,
         // we may introduce dynamic registration of configuration parsers,
         // we may introduce dynamic registration of configuration parsers,
         // and then this test can be done in a cleaner and safer way.
         // and then this test can be done in a cleaner and safer way.
         return (new ThrowerCommitConfig());
         return (new ThrowerCommitConfig());
+    } else if (config_id == "version") {
+        // Currently, the version identifier is ignored, but it should
+        // later be used to mark backwards incompatible changes in the
+        // config data
+        return (new VersionConfig());
     } else {
     } else {
         isc_throw(AuthConfigError, "Unknown configuration identifier: " <<
         isc_throw(AuthConfigError, "Unknown configuration identifier: " <<
                   config_id);
                   config_id);

+ 79 - 21
src/bin/auth/auth_srv.cc

@@ -20,6 +20,7 @@
 #include <cassert>
 #include <cassert>
 #include <iostream>
 #include <iostream>
 #include <vector>
 #include <vector>
+#include <memory>
 
 
 #include <boost/bind.hpp>
 #include <boost/bind.hpp>
 
 
@@ -43,6 +44,7 @@
 #include <dns/rrset.h>
 #include <dns/rrset.h>
 #include <dns/rrttl.h>
 #include <dns/rrttl.h>
 #include <dns/message.h>
 #include <dns/message.h>
+#include <dns/tsig.h>
 
 
 #include <datasrc/query.h>
 #include <datasrc/query.h>
 #include <datasrc/data_source.h>
 #include <datasrc/data_source.h>
@@ -73,6 +75,7 @@ using namespace isc::xfr;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
 using namespace isc::asiodns;
 using namespace isc::asiodns;
 using namespace isc::server_common::portconfig;
 using namespace isc::server_common::portconfig;
+using boost::shared_ptr;
 
 
 class AuthSrvImpl {
 class AuthSrvImpl {
 private:
 private:
@@ -85,11 +88,14 @@ public:
     isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
     isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
 
 
     bool processNormalQuery(const IOMessage& io_message, MessagePtr message,
     bool processNormalQuery(const IOMessage& io_message, MessagePtr message,
-                            OutputBufferPtr buffer);
+                            OutputBufferPtr buffer,
+                            auto_ptr<TSIGContext> tsig_context);
     bool processAxfrQuery(const IOMessage& io_message, MessagePtr message,
     bool processAxfrQuery(const IOMessage& io_message, MessagePtr message,
-                          OutputBufferPtr buffer);
+                          OutputBufferPtr buffer,
+                          auto_ptr<TSIGContext> tsig_context);
     bool processNotify(const IOMessage& io_message, MessagePtr message,
     bool processNotify(const IOMessage& io_message, MessagePtr message,
-                       OutputBufferPtr buffer);
+                       OutputBufferPtr buffer,
+                       auto_ptr<TSIGContext> tsig_context);
 
 
     IOService io_service_;
     IOService io_service_;
 
 
@@ -116,6 +122,9 @@ public:
 
 
     /// Addresses we listen on
     /// Addresses we listen on
     AddressList listen_addresses_;
     AddressList listen_addresses_;
+
+    /// The TSIG keyring
+    const shared_ptr<TSIGKeyRing>* keyring_;
 private:
 private:
     std::string db_file_;
     std::string db_file_;
 
 
@@ -139,6 +148,7 @@ AuthSrvImpl::AuthSrvImpl(const bool use_cache,
     memory_datasrc_class_(RRClass::IN()),
     memory_datasrc_class_(RRClass::IN()),
     statistics_timer_(io_service_),
     statistics_timer_(io_service_),
     counters_(verbose_mode_),
     counters_(verbose_mode_),
+    keyring_(NULL),
     xfrout_connected_(false),
     xfrout_connected_(false),
     xfrout_client_(xfrout_client)
     xfrout_client_(xfrout_client)
 {
 {
@@ -241,7 +251,9 @@ public:
 
 
 void
 void
 makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
 makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
-                 const Rcode& rcode, const bool verbose_mode)
+                 const Rcode& rcode, const bool verbose_mode,
+                 std::auto_ptr<TSIGContext> tsig_context =
+                 std::auto_ptr<TSIGContext>())
 {
 {
     // extract the parameters that should be kept.
     // extract the parameters that should be kept.
     // XXX: with the current implementation, it's not easy to set EDNS0
     // XXX: with the current implementation, it's not easy to set EDNS0
@@ -272,7 +284,11 @@ makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
     message->setRcode(rcode);
     message->setRcode(rcode);
 
 
     MessageRenderer renderer(*buffer);
     MessageRenderer renderer(*buffer);
-    message->toWire(renderer);
+    if (tsig_context.get() != NULL) {
+        message->toWire(renderer, *tsig_context);
+    } else {
+        message->toWire(renderer);
+    }
 
 
     if (verbose_mode) {
     if (verbose_mode) {
         cerr << "[b10-auth] sending an error response (" <<
         cerr << "[b10-auth] sending an error response (" <<
@@ -446,29 +462,51 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
     }
     }
 
 
     // Perform further protocol-level validation.
     // Perform further protocol-level validation.
+    // TSIG first
+    // If this is set to something, we know we need to answer with TSIG as well
+    std::auto_ptr<TSIGContext> tsig_context;
+    const TSIGRecord* tsig_record(message->getTSIGRecord());
+    TSIGError tsig_error(TSIGError::NOERROR());
+
+    // Do we do TSIG?
+    // The keyring can be null if we're in test
+    if (impl_->keyring_ != NULL && tsig_record != NULL) {
+        tsig_context.reset(new TSIGContext(tsig_record->getName(),
+                                           tsig_record->getRdata().
+                                                getAlgorithm(),
+                                           **impl_->keyring_));
+        tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
+                                          io_message.getDataSize());
+    }
 
 
     bool sendAnswer = true;
     bool sendAnswer = true;
-    if (message->getOpcode() == Opcode::NOTIFY()) {
+    if (tsig_error != TSIGError::NOERROR()) {
-        sendAnswer = impl_->processNotify(io_message, message, buffer);
+        makeErrorMessage(message, buffer, tsig_error.toRcode(),
+                         impl_->verbose_mode_, tsig_context);
+    } else if (message->getOpcode() == Opcode::NOTIFY()) {
+        sendAnswer = impl_->processNotify(io_message, message, buffer,
+                                          tsig_context);
     } else if (message->getOpcode() != Opcode::QUERY()) {
     } else if (message->getOpcode() != Opcode::QUERY()) {
         if (impl_->verbose_mode_) {
         if (impl_->verbose_mode_) {
             cerr << "[b10-auth] unsupported opcode" << endl;
             cerr << "[b10-auth] unsupported opcode" << endl;
         }
         }
         makeErrorMessage(message, buffer, Rcode::NOTIMP(),
         makeErrorMessage(message, buffer, Rcode::NOTIMP(),
-                         impl_->verbose_mode_);
+                         impl_->verbose_mode_, tsig_context);
     } else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
     } else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
         makeErrorMessage(message, buffer, Rcode::FORMERR(),
         makeErrorMessage(message, buffer, Rcode::FORMERR(),
-                         impl_->verbose_mode_);
+                         impl_->verbose_mode_, tsig_context);
     } else {
     } else {
         ConstQuestionPtr question = *message->beginQuestion();
         ConstQuestionPtr question = *message->beginQuestion();
         const RRType &qtype = question->getType();
         const RRType &qtype = question->getType();
         if (qtype == RRType::AXFR()) {
         if (qtype == RRType::AXFR()) {
-            sendAnswer = impl_->processAxfrQuery(io_message, message, buffer);
+            sendAnswer = impl_->processAxfrQuery(io_message, message, buffer,
+                                                 tsig_context);
         } else if (qtype == RRType::IXFR()) {
         } else if (qtype == RRType::IXFR()) {
             makeErrorMessage(message, buffer, Rcode::NOTIMP(),
             makeErrorMessage(message, buffer, Rcode::NOTIMP(),
-                             impl_->verbose_mode_);
+                             impl_->verbose_mode_, tsig_context);
         } else {
         } else {
-            sendAnswer = impl_->processNormalQuery(io_message, message, buffer);
+            sendAnswer = impl_->processNormalQuery(io_message, message, buffer,
+                                                   tsig_context);
         }
         }
     }
     }
 
 
@@ -477,7 +515,8 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
 
 
 bool
 bool
 AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
 AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
-                                OutputBufferPtr buffer)
+                                OutputBufferPtr buffer,
+                                auto_ptr<TSIGContext> tsig_context)
 {
 {
     ConstEDNSPtr remote_edns = message->getEDNS();
     ConstEDNSPtr remote_edns = message->getEDNS();
     const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
     const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
@@ -523,7 +562,11 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
     const bool udp_buffer =
     const bool udp_buffer =
         (io_message.getSocket().getProtocol() == IPPROTO_UDP);
         (io_message.getSocket().getProtocol() == IPPROTO_UDP);
     renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
     renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
-    message->toWire(renderer);
+    if (tsig_context.get() != NULL) {
+        message->toWire(renderer, *tsig_context);
+    } else {
+        message->toWire(renderer);
+    }
 
 
     if (verbose_mode_) {
     if (verbose_mode_) {
         cerr << "[b10-auth] sending a response ("
         cerr << "[b10-auth] sending a response ("
@@ -536,7 +579,8 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
 
 
 bool
 bool
 AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
 AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
-                              OutputBufferPtr buffer)
+                              OutputBufferPtr buffer,
+                              auto_ptr<TSIGContext> tsig_context)
 {
 {
     // Increment query counter.
     // Increment query counter.
     incCounter(io_message.getSocket().getProtocol());
     incCounter(io_message.getSocket().getProtocol());
@@ -545,7 +589,8 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
         if (verbose_mode_) {
         if (verbose_mode_) {
             cerr << "[b10-auth] AXFR query over UDP isn't allowed" << endl;
             cerr << "[b10-auth] AXFR query over UDP isn't allowed" << endl;
         }
         }
-        makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
+        makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_,
+                         tsig_context);
         return (true);
         return (true);
     }
     }
 
 
@@ -572,7 +617,8 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
             cerr << "[b10-auth] Error in handling XFR request: " << err.what()
             cerr << "[b10-auth] Error in handling XFR request: " << err.what()
                  << endl;
                  << endl;
         }
         }
-        makeErrorMessage(message, buffer, Rcode::SERVFAIL(), verbose_mode_);
+        makeErrorMessage(message, buffer, Rcode::SERVFAIL(), verbose_mode_,
+                         tsig_context);
         return (true);
         return (true);
     }
     }
 
 
@@ -581,7 +627,8 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
 
 
 bool
 bool
 AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message, 
 AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message, 
-                           OutputBufferPtr buffer)
+                           OutputBufferPtr buffer,
+                           std::auto_ptr<TSIGContext> tsig_context)
 {
 {
     // The incoming notify must contain exactly one question for SOA of the
     // The incoming notify must contain exactly one question for SOA of the
     // zone name.
     // zone name.
@@ -590,7 +637,8 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
                 cerr << "[b10-auth] invalid number of questions in notify: "
                 cerr << "[b10-auth] invalid number of questions in notify: "
                      << message->getRRCount(Message::SECTION_QUESTION) << endl;
                      << message->getRRCount(Message::SECTION_QUESTION) << endl;
         }
         }
-        makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
+        makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_,
+                         tsig_context);
         return (true);
         return (true);
     }
     }
     ConstQuestionPtr question = *message->beginQuestion();
     ConstQuestionPtr question = *message->beginQuestion();
@@ -599,7 +647,8 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
                 cerr << "[b10-auth] invalid question RR type in notify: "
                 cerr << "[b10-auth] invalid question RR type in notify: "
                      << question->getType() << endl;
                      << question->getType() << endl;
         }
         }
-        makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
+        makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_,
+                         tsig_context);
         return (true);
         return (true);
     }
     }
 
 
@@ -662,7 +711,11 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
     message->setRcode(Rcode::NOERROR());
     message->setRcode(Rcode::NOERROR());
 
 
     MessageRenderer renderer(*buffer);
     MessageRenderer renderer(*buffer);
-    message->toWire(renderer);
+    if (tsig_context.get() != NULL) {
+        message->toWire(renderer, *tsig_context);
+    } else {
+        message->toWire(renderer);
+    }
     return (true);
     return (true);
 }
 }
 
 
@@ -772,3 +825,8 @@ void
 AuthSrv::setDNSService(isc::asiodns::DNSService& dnss) {
 AuthSrv::setDNSService(isc::asiodns::DNSService& dnss) {
     dnss_ = &dnss;
     dnss_ = &dnss;
 }
 }
+
+void
+AuthSrv::setTSIGKeyRing(const shared_ptr<TSIGKeyRing>* keyring) {
+    impl_->keyring_ = keyring;
+}

+ 11 - 0
src/bin/auth/auth_srv.h

@@ -44,6 +44,9 @@ class MemoryDataSrc;
 namespace xfr {
 namespace xfr {
 class AbstractXfroutClient;
 class AbstractXfroutClient;
 }
 }
+namespace dns {
+class TSIGKeyRing;
+}
 }
 }
 
 
 
 
@@ -374,6 +377,14 @@ public:
     /// \brief Assign an ASIO DNS Service queue to this Auth object
     /// \brief Assign an ASIO DNS Service queue to this Auth object
     void setDNSService(isc::asiodns::DNSService& dnss);
     void setDNSService(isc::asiodns::DNSService& dnss);
 
 
+    /// \brief Sets the keyring used for verifying and signing
+    ///
+    /// The parameter is pointer to shared pointer, because the automatic
+    /// reloading routines of tsig keys replace the actual keyring object.
+    /// It is expected the pointer will point to some statically-allocated
+    /// object, it doesn't take ownership of it.
+    void setTSIGKeyRing(const boost::shared_ptr<isc::dns::TSIGKeyRing>*
+                        keyring);
 
 
 private:
 private:
     AuthSrvImpl* impl_;
     AuthSrvImpl* impl_;

+ 1 - 0
src/bin/auth/benchmarks/Makefile.am

@@ -26,3 +26,4 @@ query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
 query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.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/server_common/libserver_common.la
 query_bench_LDADD += $(SQLITE_LIBS)
 query_bench_LDADD += $(SQLITE_LIBS)
+

+ 15 - 1
src/bin/auth/main.cc

@@ -47,6 +47,7 @@
 #include <asiodns/asiodns.h>
 #include <asiodns/asiodns.h>
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
 #include <log/dummylog.h>
 #include <log/dummylog.h>
+#include <server_common/keyring.h>
 
 
 using namespace std;
 using namespace std;
 using namespace isc::data;
 using namespace isc::data;
@@ -152,9 +153,14 @@ main(int argc, char* argv[]) {
         cc_session = new Session(io_service.get_io_service());
         cc_session = new Session(io_service.get_io_service());
         cout << "[b10-auth] Configuration session channel created." << endl;
         cout << "[b10-auth] Configuration session channel created." << endl;
 
 
+        // We delay starting listening to new commands/config just before we
+        // go into the main loop to avoid confusion due to mixture of
+        // synchronous and asynchronous operations (this would happen in
+        // initializing TSIG keys below).  Until then all operations on the
+        // CC session will take place synchronously.
         config_session = new ModuleCCSession(specfile, *cc_session,
         config_session = new ModuleCCSession(specfile, *cc_session,
                                              my_config_handler,
                                              my_config_handler,
-                                             my_command_handler);
+                                             my_command_handler, false);
         cout << "[b10-auth] Configuration channel established." << endl;
         cout << "[b10-auth] Configuration channel established." << endl;
 
 
         xfrin_session = new Session(io_service.get_io_service());
         xfrin_session = new Session(io_service.get_io_service());
@@ -190,6 +196,14 @@ main(int argc, char* argv[]) {
             changeUser(uid);
             changeUser(uid);
         }
         }
 
 
+        cout << "[b10-auth] Loading TSIG keys" << endl;
+        isc::server_common::initKeyring(*config_session);
+        auth_server->setTSIGKeyRing(&isc::server_common::keyring);
+
+        // Now start asynchronous read.
+        config_session->start();
+        cout << "[b10-auth] Configuration channel started." << endl;
+
         cout << "[b10-auth] Server started." << endl;
         cout << "[b10-auth] Server started." << endl;
         io_service.run();
         io_service.run();
 
 

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

@@ -52,6 +52,7 @@ 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/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.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/nsas/libnsas.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 noinst_PROGRAMS = $(TESTS)

+ 138 - 0
src/bin/auth/tests/auth_srv_unittest.cc

@@ -16,6 +16,8 @@
 
 
 #include <vector>
 #include <vector>
 
 
+#include <boost/shared_ptr.hpp>
+
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 #include <dns/message.h>
 #include <dns/message.h>
@@ -25,8 +27,10 @@
 #include <dns/rrtype.h>
 #include <dns/rrtype.h>
 #include <dns/rrttl.h>
 #include <dns/rrttl.h>
 #include <dns/rdataclass.h>
 #include <dns/rdataclass.h>
+#include <dns/tsig.h>
 
 
 #include <server_common/portconfig.h>
 #include <server_common/portconfig.h>
+#include <server_common/keyring.h>
 
 
 #include <datasrc/memory_datasrc.h>
 #include <datasrc/memory_datasrc.h>
 #include <auth/auth_srv.h>
 #include <auth/auth_srv.h>
@@ -50,6 +54,7 @@ using namespace isc::asiolink;
 using namespace isc::testutils;
 using namespace isc::testutils;
 using namespace isc::server_common::portconfig;
 using namespace isc::server_common::portconfig;
 using isc::UnitTestUtil;
 using isc::UnitTestUtil;
+using boost::shared_ptr;
 
 
 namespace {
 namespace {
 const char* const CONFIG_TESTDB =
 const char* const CONFIG_TESTDB =
@@ -242,6 +247,139 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
     EXPECT_TRUE(xfrout.isConnected());
     EXPECT_TRUE(xfrout.isConnected());
 }
 }
 
 
+// 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
+    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";
+}
+
+// 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) {
+    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);
+
+    // Process the message, but use a different key there
+    shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+    server.setTSIGKeyRing(&keyring);
+    server.processMessage(*io_message, parse_message, response_obuffer,
+                          &dnsserv);
+
+    EXPECT_TRUE(dnsserv.hasAnswer());
+    headerCheck(*parse_message, default_qid, TSIGError::BAD_KEY().toRcode(),
+                opcode.getCode(), QR_FLAG, 1, 0, 0, 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 (we should have one even at error)";
+    EXPECT_EQ(TSIGError::BAD_KEY_CODE, tsig->getRdata().getError());
+    EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
+        "It should be unsigned with this error";
+}
+
+// Give the server a signed request, but signed by a different key
+// (with the same name). It should return BADSIG
+TEST_F(AuthSrvTest, TSIGBadSig) {
+    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);
+
+    // Process the message, but use a different key there
+    shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+    keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
+    server.setTSIGKeyRing(&keyring);
+    server.processMessage(*io_message, parse_message, response_obuffer,
+                          &dnsserv);
+
+    EXPECT_TRUE(dnsserv.hasAnswer());
+    headerCheck(*parse_message, default_qid, TSIGError::BAD_SIG().toRcode(),
+                opcode.getCode(), QR_FLAG, 1, 0, 0, 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 (we should have one even at error)";
+    EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
+    EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
+        "It should be unsigned with this error";
+}
+
+// Give the server a signed unsupported request with a bad signature.
+// This checks the server first verifies the signature before anything
+// else.
+TEST_F(AuthSrvTest, TSIGCheckFirst) {
+    TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
+    TSIGContext context(key);
+    // Pass a wrong opcode there. The server shouldn't know what to do
+    // about it.
+    UnitTestUtil::createRequestMessage(request_message, Opcode::RESERVED14(),
+                                       default_qid, Name("version.bind"),
+                                       RRClass::CH(), RRType::TXT());
+    createRequestPacket(request_message, IPPROTO_UDP, &context);
+
+    // Process the message, but use a different key there
+    shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+    keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
+    server.setTSIGKeyRing(&keyring);
+    server.processMessage(*io_message, parse_message, response_obuffer,
+                          &dnsserv);
+
+    EXPECT_TRUE(dnsserv.hasAnswer());
+    headerCheck(*parse_message, default_qid, TSIGError::BAD_SIG().toRcode(),
+                Opcode::RESERVED14().getCode(), QR_FLAG, 0, 0, 0, 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 (we should have one even at error)";
+    EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
+    EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
+        "It should be unsigned with this error";
+}
+
 TEST_F(AuthSrvTest, AXFRConnectFail) {
 TEST_F(AuthSrvTest, AXFRConnectFail) {
     EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
     EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
     xfrout.disableConnect();
     xfrout.disableConnect();

+ 7 - 0
src/bin/auth/tests/config_unittest.cc

@@ -74,6 +74,13 @@ TEST_F(AuthConfigTest, databaseConfig) {
                             "{\"database_file\": \"should_be_ignored\"}")));
                             "{\"database_file\": \"should_be_ignored\"}")));
 }
 }
 
 
+TEST_F(AuthConfigTest, versionConfig) {
+    // make sure it does not throw on 'version'
+    EXPECT_NO_THROW(configureAuthServer(
+                        server,
+                        Element::fromJSON("{\"version\": 0}")));
+}
+
 TEST_F(AuthConfigTest, exceptionGuarantee) {
 TEST_F(AuthConfigTest, exceptionGuarantee) {
     EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
     EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
     // This configuration contains an invalid item, which will trigger
     // This configuration contains an invalid item, which will trigger

+ 4 - 1
src/bin/auth/tests/run_unittests.cc

@@ -13,6 +13,8 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <log/logger_support.h>
+#include <util/unittests/run_all.h>
 
 
 #include <dns/tests/unittest_util.h>
 #include <dns/tests/unittest_util.h>
 
 
@@ -21,6 +23,7 @@ main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::InitGoogleTest(&argc, argv);
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
+    isc::log::initLogger();
 
 
-    return (RUN_ALL_TESTS());
+    return (isc::util::unittests::run_all());
 }
 }

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

@@ -1,5 +1,6 @@
 SUBDIRS = tests
 SUBDIRS = tests
 EXTRA_DIST = README tsig_keys.py tsig_keys.spec
 EXTRA_DIST = README tsig_keys.py tsig_keys.spec
+EXTRA_DIST += logging.spec b10logging.py
 
 
 config_plugindir = @prefix@/share/@PACKAGE@/config_plugins
 config_plugindir = @prefix@/share/@PACKAGE@/config_plugins
 config_plugin_DATA = tsig_keys.py tsig_keys.spec
 config_plugin_DATA = tsig_keys.py tsig_keys.spec

+ 96 - 0
src/bin/cfgmgr/plugins/b10logging.py

@@ -0,0 +1,96 @@
+# 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.
+
+# This is the configuration plugin for logging options
+# The name is 'b10logging' because logging.py is an existing module
+#
+# For a technical background, see
+# http://bind10.isc.org/wiki/LoggingCppApiDesign
+#
+
+from isc.config.module_spec import module_spec_from_file
+from isc.util.file import path_search
+from bind10_config import PLUGIN_PATHS
+spec = module_spec_from_file(path_search('logging.spec', PLUGIN_PATHS))
+
+ALLOWED_SEVERITIES = [ 'default',
+                       'debug',
+                       'info',
+                       'warn',
+                       'error',
+                       'fatal',
+                       'none' ]
+ALLOWED_DESTINATIONS = [ 'console',
+                         'file',
+                         'syslog' ]
+ALLOWED_STREAMS = [ 'stdout',
+                    'stderr' ]
+
+def check(config):
+    # Check the data layout first
+    errors=[]
+    if not spec.validate_config(False, config, errors):
+        return ' '.join(errors)
+    # The 'layout' is ok, now check for specific values
+    if 'loggers' in config:
+        for logger in config['loggers']:
+            # name should always be present
+            name = logger['name']
+
+            if 'severity' in logger and\
+               logger['severity'].lower() not in ALLOWED_SEVERITIES:
+                errors.append("bad severity value for logger " + name +
+                              ": " + logger['severity'])
+            if 'output_options' in logger:
+                for output_option in logger['output_options']:
+                    if 'destination' in output_option:
+                        destination = output_option['destination'].lower()
+                        if destination not in ALLOWED_DESTINATIONS:
+                            errors.append("bad destination for logger " +
+                            name + ": " + output_option['destination'])
+                        else:
+                            # if left to default, output is stdout, and
+                            # it will not show in the updated config,
+                            # so 1. we only need to check it if present,
+                            # and 2. if destination is changed, so should
+                            # output. So first check checks 'in', and the
+                            # others 'not in' for 'output'
+                            if destination == "console" and\
+                               'output' in output_option and\
+                               output_option['output'] not in ALLOWED_STREAMS:
+                                errors.append("bad output for logger " + name +
+                                              ": " + output_option['stream'] +
+                                              ", must be stdout or stderr")
+                            elif destination == "file" and\
+                                 'output' not in output_option or\
+                                 output_option['output'] == "":
+                                    errors.append("destination set to file but "
+                                                  "output not set to any "
+                                                  "filename for logger "
+                                                  + name)
+                            elif destination == "syslog" and\
+                                 'output' not in output_option or\
+                                 output_option['output'] == "":
+                                    errors.append("destination set to syslog but "
+                                                  "output not set to any facility"
+                                                  " for logger " + name)
+
+    if errors:
+        return ', '.join(errors)
+    return None
+
+def load():
+    return (spec, check)
+

+ 81 - 0
src/bin/cfgmgr/plugins/logging.spec

@@ -0,0 +1,81 @@
+{
+    "module_spec": {
+        "module_name": "Logging",
+        "module_description": "Logging options",
+        "config_data": [
+            {
+                "item_name": "loggers",
+                "item_type": "list",
+                "item_optional": false,
+                "item_default": [],
+                "list_item_spec": {
+                  "item_name": "logger",
+                  "item_type": "map",
+                  "item_optional": false,
+                  "item_default": {},
+                  "map_item_spec": [
+                  {  "item_name": "name",
+                     "item_type": "string",
+                     "item_optional": false,
+                     "item_default": ""
+                  },
+                  {  "item_name": "severity",
+                     "item_type": "string",
+                     "item_optional": false,
+                     "item_default": "INFO"
+                  },
+                  {  "item_name": "debuglevel",
+                     "item_type": "integer",
+                     "item_optional": false,
+                     "item_default": 0
+                  },
+                  {  "item_name": "additive",
+                     "item_type": "boolean",
+                     "item_optional": false,
+                     "item_default": false
+                  },
+                  { "item_name": "output_options",
+                    "item_type": "list",
+                    "item_optional": false,
+                    "item_default": [],
+                    "list_item_spec": {
+                      "item_name": "output_option",
+                      "item_type": "map",
+                      "item_optional": false,
+                      "item_default": {},
+                      "map_item_spec": [
+                      { "item_name": "destination",
+                        "item_type": "string",
+                        "item_optional": false,
+                        "item_default": "console"
+                      },
+                      { "item_name": "output",
+                        "item_type": "string",
+                        "item_optional": false,
+                        "item_default": "stdout"
+                      },
+                      { "item_name": "flush",
+                        "item_type": "boolean",
+                        "item_optional": false,
+                        "item_default": false
+                      },
+                      { "item_name": "maxsize",
+                        "item_type": "integer",
+                        "item_optional": false,
+                        "item_default": 0
+                      },
+                      { "item_name": "maxver",
+                        "item_type": "integer",
+                        "item_optional": false,
+                        "item_default": 0
+                      }
+                      ]
+                    }
+                  }
+                  ]
+                }
+            }
+        ],
+        "commands": []
+    }
+}

+ 1 - 1
src/bin/cfgmgr/plugins/tests/tsig_keys_test.py

@@ -86,7 +86,7 @@ class TSigKeysTest(unittest.TestCase):
         self.assertEqual("TSIG: Invalid TSIG key string: invalid.key",
         self.assertEqual("TSIG: Invalid TSIG key string: invalid.key",
                          tsig_keys.check({'keys': ['invalid.key']}))
                          tsig_keys.check({'keys': ['invalid.key']}))
         self.assertEqual(
         self.assertEqual(
-            "TSIG: attempt to decode a value not in base64 char set",
+            "TSIG: Unexpected end of input in BASE decoder",
             tsig_keys.check({'keys': ['invalid.key:123']}))
             tsig_keys.check({'keys': ['invalid.key:123']}))
 
 
     def test_bad_format(self):
     def test_bad_format(self):

+ 3 - 2
src/bin/cmdctl/Makefile.am

@@ -40,12 +40,13 @@ b10-cmdctl: cmdctl.py
 
 
 if INSTALL_CONFIGURATIONS
 if INSTALL_CONFIGURATIONS
 
 
-# TODO: permissions handled later
+# Below we intentionally use ${INSTALL} -m 640 instead of $(INSTALL_DATA)
+# because these file will contain sensitive information.
 install-data-local:
 install-data-local:
 	$(mkinstalldirs) $(DESTDIR)/@sysconfdir@/@PACKAGE@   
 	$(mkinstalldirs) $(DESTDIR)/@sysconfdir@/@PACKAGE@   
 	for f in $(CMDCTL_CONFIGURATIONS) ; do	\
 	for f in $(CMDCTL_CONFIGURATIONS) ; do	\
 	  if test ! -f $(DESTDIR)$(sysconfdir)/@PACKAGE@/$$f; then	\
 	  if test ! -f $(DESTDIR)$(sysconfdir)/@PACKAGE@/$$f; then	\
-	    $(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ;	\
+	    ${INSTALL} -m 640 $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ;	\
 	  fi ;	\
 	  fi ;	\
 	done
 	done
 
 

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

@@ -18,10 +18,12 @@ endif
 
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 
-CLEANFILES = *.gcno *.gcda resolver.spec spec_config.h
+CLEANFILES  = *.gcno *.gcda
+CLEANFILES += resolver.spec spec_config.h
+CLEANFILES += resolverdef.cc resolverdef.h
 
 
 man_MANS = b10-resolver.8
 man_MANS = b10-resolver.8
-EXTRA_DIST = $(man_MANS) b10-resolver.xml
+EXTRA_DIST = $(man_MANS) b10-resolver.xml resolverdef.mes
 
 
 if ENABLE_MAN
 if ENABLE_MAN
 
 
@@ -36,13 +38,24 @@ resolver.spec: resolver.spec.pre
 spec_config.h: spec_config.h.pre
 spec_config.h: spec_config.h.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
 
 
-BUILT_SOURCES = spec_config.h 
+# Define rule to build logging source files from message file
+resolverdef.h resolverdef.cc: resolverdef.mes
+	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/resolver/resolverdef.mes
+
+
+BUILT_SOURCES = spec_config.h resolverdef.cc resolverdef.h
+
 pkglibexec_PROGRAMS = b10-resolver
 pkglibexec_PROGRAMS = b10-resolver
 b10_resolver_SOURCES = resolver.cc resolver.h
 b10_resolver_SOURCES = resolver.cc resolver.h
+b10_resolver_SOURCES += resolver_log.cc resolver_log.h
 b10_resolver_SOURCES += response_scrubber.cc response_scrubber.h
 b10_resolver_SOURCES += response_scrubber.cc response_scrubber.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/change_user.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/change_user.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/common.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/common.h
 b10_resolver_SOURCES += main.cc
 b10_resolver_SOURCES += main.cc
+
+nodist_b10_resolver_SOURCES = resolverdef.cc resolverdef.h
+
+
 b10_resolver_LDADD =  $(top_builddir)/src/lib/dns/libdns++.la
 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/config/libcfgclient.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libcc.la

+ 26 - 20
src/bin/resolver/main.cc

@@ -52,13 +52,14 @@
 #include <cache/resolver_cache.h>
 #include <cache/resolver_cache.h>
 #include <nsas/nameserver_address_store.h>
 #include <nsas/nameserver_address_store.h>
 
 
-#include <log/dummylog.h>
+#include <log/logger_support.h>
+#include <log/logger_level.h>
+#include "resolver_log.h"
 
 
 using namespace std;
 using namespace std;
 using namespace isc::cc;
 using namespace isc::cc;
 using namespace isc::config;
 using namespace isc::config;
 using namespace isc::data;
 using namespace isc::data;
-using isc::log::dlog;
 using namespace isc::asiodns;
 using namespace isc::asiodns;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
 
 
@@ -79,7 +80,7 @@ my_command_handler(const string& command, ConstElementPtr args) {
     ConstElementPtr answer = createAnswer();
     ConstElementPtr answer = createAnswer();
 
 
     if (command == "print_message") {
     if (command == "print_message") {
-        cout << args << endl;
+        LOG_INFO(resolver_logger, RESOLVER_PRINTMSG).arg(args);
         /* let's add that message to our answer as well */
         /* let's add that message to our answer as well */
         answer = createAnswer(0, args);
         answer = createAnswer(0, args);
     } else if (command == "shutdown") {
     } else if (command == "shutdown") {
@@ -100,7 +101,7 @@ usage() {
 
 
 int
 int
 main(int argc, char* argv[]) {
 main(int argc, char* argv[]) {
-    isc::log::dprefix = "b10-resolver";
+    bool verbose = false;
     int ch;
     int ch;
     const char* uid = NULL;
     const char* uid = NULL;
 
 
@@ -110,7 +111,7 @@ main(int argc, char* argv[]) {
             uid = optarg;
             uid = optarg;
             break;
             break;
         case 'v':
         case 'v':
-            isc::log::denabled = true;
+            verbose = true;
             break;
             break;
         case '?':
         case '?':
         default:
         default:
@@ -122,13 +123,18 @@ main(int argc, char* argv[]) {
         usage();
         usage();
     }
     }
 
 
-    if (isc::log::denabled) { // Show the command line
+    // Until proper logging comes along, initialize the logging with the
-        string cmdline("Command line:");
+    // temporary initLogger() code.  If verbose, we'll use maximum verbosity.
-        for (int i = 0; i < argc; ++ i) {
+    isc::log::initLogger("b10-resolver",
-            cmdline = cmdline + " " + argv[i];
+                         (verbose ? isc::log::DEBUG : isc::log::INFO),
-        }
+                         isc::log::MAX_DEBUG_LEVEL, NULL);
-        dlog(cmdline);
+
+    // Print the starting message
+    string cmdline = argv[0];
+    for (int i = 1; i < argc; ++ i) {
+        cmdline = cmdline + " " + argv[i];
     }
     }
+    LOG_INFO(resolver_logger, RESOLVER_STARTING).arg(cmdline);
 
 
     int ret = 0;
     int ret = 0;
 
 
@@ -144,7 +150,7 @@ main(int argc, char* argv[]) {
         }
         }
 
 
         resolver = boost::shared_ptr<Resolver>(new Resolver());
         resolver = boost::shared_ptr<Resolver>(new Resolver());
-        dlog("Server created.");
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CREATED);
 
 
         SimpleCallback* checkin = resolver->getCheckinProvider();
         SimpleCallback* checkin = resolver->getCheckinProvider();
         DNSLookup* lookup = resolver->getDNSLookupProvider();
         DNSLookup* lookup = resolver->getDNSLookupProvider();
@@ -197,15 +203,14 @@ main(int argc, char* argv[]) {
         
         
         DNSService dns_service(io_service, checkin, lookup, answer);
         DNSService dns_service(io_service, checkin, lookup, answer);
         resolver->setDNSService(dns_service);
         resolver->setDNSService(dns_service);
-        dlog("IOService created.");
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_SERVICE);
 
 
         cc_session = new Session(io_service.get_io_service());
         cc_session = new Session(io_service.get_io_service());
-        dlog("Configuration session channel created.");
-
         config_session = new ModuleCCSession(specfile, *cc_session,
         config_session = new ModuleCCSession(specfile, *cc_session,
                                              my_config_handler,
                                              my_config_handler,
-                                             my_command_handler);
+                                             my_command_handler,
-        dlog("Configuration channel established.");
+                                             true, true);
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CONFIGCHAN);
 
 
         // FIXME: This does not belong here, but inside Boss
         // FIXME: This does not belong here, but inside Boss
         if (uid != NULL) {
         if (uid != NULL) {
@@ -213,17 +218,18 @@ main(int argc, char* argv[]) {
         }
         }
 
 
         resolver->setConfigSession(config_session);
         resolver->setConfigSession(config_session);
-        dlog("Config loaded");
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CONFIGLOAD);
 
 
-        dlog("Server started.");
+        LOG_INFO(resolver_logger, RESOLVER_STARTED);
         io_service.run();
         io_service.run();
     } catch (const std::exception& ex) {
     } catch (const std::exception& ex) {
-        dlog(string("Server failed: ") + ex.what(),true);
+        LOG_FATAL(resolver_logger, RESOLVER_FAILED).arg(ex.what());
         ret = 1;
         ret = 1;
     }
     }
 
 
     delete config_session;
     delete config_session;
     delete cc_session;
     delete cc_session;
 
 
+    LOG_INFO(resolver_logger, RESOLVER_SHUTDOWN);
     return (ret);
     return (ret);
 }
 }

+ 86 - 45
src/bin/resolver/resolver.cc

@@ -45,9 +45,8 @@
 
 
 #include <resolve/recursive_query.h>
 #include <resolve/recursive_query.h>
 
 
-#include <log/dummylog.h>
+#include "resolver.h"
-
+#include "resolver_log.h"
-#include <resolver/resolver.h>
 
 
 using namespace std;
 using namespace std;
 
 
@@ -56,7 +55,6 @@ using namespace isc::util;
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace isc::data;
 using namespace isc::data;
 using namespace isc::config;
 using namespace isc::config;
-using isc::log::dlog;
 using namespace isc::asiodns;
 using namespace isc::asiodns;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
 using namespace isc::server_common::portconfig;
 using namespace isc::server_common::portconfig;
@@ -85,7 +83,7 @@ public:
                     isc::cache::ResolverCache& cache)
                     isc::cache::ResolverCache& cache)
     {
     {
         assert(!rec_query_); // queryShutdown must be called first
         assert(!rec_query_); // queryShutdown must be called first
-        dlog("Query setup");
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_QUSETUP);
         rec_query_ = new RecursiveQuery(dnss, 
         rec_query_ = new RecursiveQuery(dnss, 
                                         nsas, cache,
                                         nsas, cache,
                                         upstream_,
                                         upstream_,
@@ -101,7 +99,7 @@ public:
         // (this is not a safety check, just to prevent logging of
         // (this is not a safety check, just to prevent logging of
         // actions that are not performed
         // actions that are not performed
         if (rec_query_) {
         if (rec_query_) {
-            dlog("Query shutdown");
+            LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_QUSHUT);
             delete rec_query_;
             delete rec_query_;
             rec_query_ = NULL;
             rec_query_ = NULL;
         }
         }
@@ -113,13 +111,12 @@ public:
         upstream_ = upstream;
         upstream_ = upstream;
         if (dnss) {
         if (dnss) {
             if (!upstream_.empty()) {
             if (!upstream_.empty()) {
-                dlog("Setting forward addresses:");
                 BOOST_FOREACH(const AddressPair& address, upstream) {
                 BOOST_FOREACH(const AddressPair& address, upstream) {
-                    dlog(" " + address.first + ":" +
+                    LOG_INFO(resolver_logger, RESOLVER_FWDADDR)
-                        boost::lexical_cast<string>(address.second));
+                             .arg(address.first).arg(address.second);
                 }
                 }
             } else {
             } else {
-                dlog("No forward addresses, running in recursive mode");
+                LOG_INFO(resolver_logger, RESOLVER_RECURSIVE);
             }
             }
         }
         }
     }
     }
@@ -130,13 +127,12 @@ public:
         upstream_root_ = upstream_root;
         upstream_root_ = upstream_root;
         if (dnss) {
         if (dnss) {
             if (!upstream_root_.empty()) {
             if (!upstream_root_.empty()) {
-                dlog("Setting root addresses:");
                 BOOST_FOREACH(const AddressPair& address, upstream_root) {
                 BOOST_FOREACH(const AddressPair& address, upstream_root) {
-                    dlog(" " + address.first + ":" +
+                    LOG_INFO(resolver_logger, RESOLVER_ROOTADDR)
-                        boost::lexical_cast<string>(address.second));
+                             .arg(address.first).arg(address.second);
                 }
                 }
             } else {
             } else {
-                dlog("No root addresses");
+                LOG_WARN(resolver_logger, RESOLVER_NOROOTADDR);
             }
             }
         }
         }
     }
     }
@@ -186,8 +182,6 @@ class QuestionInserter {
 public:
 public:
     QuestionInserter(MessagePtr message) : message_(message) {}
     QuestionInserter(MessagePtr message) : message_(message) {}
     void operator()(const QuestionPtr question) {
     void operator()(const QuestionPtr question) {
-        dlog(string("Adding question ") + question->getName().toText() +
-            " to message");
         message_->addQuestion(question);
         message_->addQuestion(question);
     }
     }
     MessagePtr message_;
     MessagePtr message_;
@@ -234,10 +228,6 @@ makeErrorMessage(MessagePtr message, MessagePtr answer_message,
     message->setRcode(rcode);
     message->setRcode(rcode);
     MessageRenderer renderer(*buffer);
     MessageRenderer renderer(*buffer);
     message->toWire(renderer);
     message->toWire(renderer);
-
-    dlog(string("Sending an error response (") +
-        boost::lexical_cast<string>(renderer.getLength()) + " bytes):\n" +
-        message->toText());
 }
 }
 
 
 // This is a derived class of \c DNSLookup, to serve as a
 // This is a derived class of \c DNSLookup, to serve as a
@@ -312,9 +302,8 @@ public:
 
 
         answer_message->toWire(renderer);
         answer_message->toWire(renderer);
 
 
-        dlog(string("sending a response (") +
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_DETAIL, RESOLVER_DNSMSGSENT)
-            boost::lexical_cast<string>(renderer.getLength()) + "bytes): \n" +
+                  .arg(renderer.getLength()).arg(*answer_message);
-            answer_message->toText());
     }
     }
 };
 };
 
 
@@ -335,9 +324,12 @@ private:
 
 
 Resolver::Resolver() :
 Resolver::Resolver() :
     impl_(new ResolverImpl()),
     impl_(new ResolverImpl()),
+    dnss_(NULL),
     checkin_(new ConfigCheck(this)),
     checkin_(new ConfigCheck(this)),
     dns_lookup_(new MessageLookup(this)),
     dns_lookup_(new MessageLookup(this)),
     dns_answer_(new MessageAnswer),
     dns_answer_(new MessageAnswer),
+    nsas_(NULL),
+    cache_(NULL),
     configured_(false)
     configured_(false)
 {}
 {}
 
 
@@ -391,21 +383,26 @@ Resolver::processMessage(const IOMessage& io_message,
                          OutputBufferPtr buffer,
                          OutputBufferPtr buffer,
                          DNSServer* server)
                          DNSServer* server)
 {
 {
-    dlog("Got a DNS message");
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
     // First, check the header part.  If we fail even for the base header,
     // First, check the header part.  If we fail even for the base header,
     // just drop the message.
     // just drop the message.
+
+    // In the following code, the debug output is such that there should only be
+    // one debug message if packet processing failed.  There could be two if
+    // it succeeded.
     try {
     try {
         query_message->parseHeader(request_buffer);
         query_message->parseHeader(request_buffer);
 
 
         // Ignore all responses.
         // Ignore all responses.
         if (query_message->getHeaderFlag(Message::HEADERFLAG_QR)) {
         if (query_message->getHeaderFlag(Message::HEADERFLAG_QR)) {
-            dlog("Received unexpected response, ignoring");
+            LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_UNEXRESP);
             server->resume(false);
             server->resume(false);
             return;
             return;
         }
         }
+
     } catch (const Exception& ex) {
     } catch (const Exception& ex) {
-        dlog(string("DNS packet exception: ") + ex.what(),true);
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_HDRERR)
+                  .arg(ex.what());
         server->resume(false);
         server->resume(false);
         return;
         return;
     }
     }
@@ -414,37 +411,49 @@ Resolver::processMessage(const IOMessage& io_message,
     try {
     try {
         query_message->fromWire(request_buffer);
         query_message->fromWire(request_buffer);
     } catch (const DNSProtocolError& error) {
     } catch (const DNSProtocolError& error) {
-        dlog(string("returning ") + error.getRcode().toText() + ": " + 
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_PROTERR)
-            error.what());
+                  .arg(error.what()).arg(error.getRcode());
         makeErrorMessage(query_message, answer_message,
         makeErrorMessage(query_message, answer_message,
                          buffer, error.getRcode());
                          buffer, error.getRcode());
         server->resume(true);
         server->resume(true);
         return;
         return;
     } catch (const Exception& ex) {
     } catch (const Exception& ex) {
-        dlog(string("returning SERVFAIL: ") + ex.what());
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_PROTERR)
+                  .arg(ex.what()).arg(Rcode::SERVFAIL());
         makeErrorMessage(query_message, answer_message,
         makeErrorMessage(query_message, answer_message,
                          buffer, Rcode::SERVFAIL());
                          buffer, Rcode::SERVFAIL());
         server->resume(true);
         server->resume(true);
         return;
         return;
-    } // other exceptions will be handled at a higher layer.
+    } // Other exceptions will be handled at a higher layer.
 
 
-    dlog("received a message:\n" + query_message->toText());
+    // Note:  there appears to be no LOG_DEBUG for a successfully-received
+    // message.  This is not an oversight - it is handled below.  In the
+    // meantime, output the full message for debug purposes (if requested).
+    LOG_DEBUG(resolver_logger, RESOLVER_DBG_DETAIL, RESOLVER_DNSMSGRCVD)
+              .arg(*query_message);
 
 
     // Perform further protocol-level validation.
     // Perform further protocol-level validation.
     bool sendAnswer = true;
     bool sendAnswer = true;
     if (query_message->getOpcode() == Opcode::NOTIFY()) {
     if (query_message->getOpcode() == Opcode::NOTIFY()) {
+
         makeErrorMessage(query_message, answer_message,
         makeErrorMessage(query_message, answer_message,
                          buffer, Rcode::NOTAUTH());
                          buffer, Rcode::NOTAUTH());
-        dlog("Notify arrived, but we are not authoritative");
+        // Notify arrived, but we are not authoritative.
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NFYNOTAUTH);
+
     } else if (query_message->getOpcode() != Opcode::QUERY()) {
     } else if (query_message->getOpcode() != Opcode::QUERY()) {
-        dlog("Unsupported opcode (got: " + query_message->getOpcode().toText() +
+
-            ", expected: " + Opcode::QUERY().toText());
+        // Unsupported opcode.
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_OPCODEUNS)
+                  .arg(query_message->getOpcode());
         makeErrorMessage(query_message, answer_message,
         makeErrorMessage(query_message, answer_message,
                          buffer, Rcode::NOTIMP());
                          buffer, Rcode::NOTIMP());
+
     } else if (query_message->getRRCount(Message::SECTION_QUESTION) != 1) {
     } else if (query_message->getRRCount(Message::SECTION_QUESTION) != 1) {
-        dlog("The query contained " +
+
-            boost::lexical_cast<string>(query_message->getRRCount(
+        // Not one question
-            Message::SECTION_QUESTION) + " questions, exactly one expected"));
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NOTONEQUES)
+                  .arg(query_message->getRRCount(Message::SECTION_QUESTION));
         makeErrorMessage(query_message, answer_message,
         makeErrorMessage(query_message, answer_message,
                          buffer, Rcode::FORMERR());
                          buffer, Rcode::FORMERR());
     } else {
     } else {
@@ -452,16 +461,32 @@ Resolver::processMessage(const IOMessage& io_message,
         const RRType &qtype = question->getType();
         const RRType &qtype = question->getType();
         if (qtype == RRType::AXFR()) {
         if (qtype == RRType::AXFR()) {
             if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
             if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
+
+                // Can't process AXFR request receoved over UDP
+                LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
+                          RESOLVER_AXFRUDP);
                 makeErrorMessage(query_message, answer_message,
                 makeErrorMessage(query_message, answer_message,
                                  buffer, Rcode::FORMERR());
                                  buffer, Rcode::FORMERR());
             } else {
             } else {
+
+                // ... or over TCP for that matter
+                LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
+                          RESOLVER_AXFRTCP);
                 makeErrorMessage(query_message, answer_message,
                 makeErrorMessage(query_message, answer_message,
                                  buffer, Rcode::NOTIMP());
                                  buffer, Rcode::NOTIMP());
             }
             }
         } else if (qtype == RRType::IXFR()) {
         } else if (qtype == RRType::IXFR()) {
+
+            // Can't process IXFR request
+            LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_IXFR);
             makeErrorMessage(query_message, answer_message,
             makeErrorMessage(query_message, answer_message,
                              buffer, Rcode::NOTIMP());
                              buffer, Rcode::NOTIMP());
+
         } else if (question->getClass() != RRClass::IN()) {
         } else if (question->getClass() != RRClass::IN()) {
+
+            // Non-IN message received, refuse it.
+            LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NOTIN)
+                      .arg(question->getClass());
             makeErrorMessage(query_message, answer_message,
             makeErrorMessage(query_message, answer_message,
                              buffer, Rcode::REFUSED());
                              buffer, Rcode::REFUSED());
         } else {
         } else {
@@ -492,18 +517,23 @@ ResolverImpl::processNormalQuery(ConstMessagePtr query_message,
                                  DNSServer* server)
                                  DNSServer* server)
 {
 {
     if (upstream_.empty()) {
     if (upstream_.empty()) {
-        dlog("Processing normal query");
+        // Processing normal query
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_NORMQUERY);
         ConstQuestionPtr question = *query_message->beginQuestion();
         ConstQuestionPtr question = *query_message->beginQuestion();
         rec_query_->resolve(*question, answer_message, buffer, server);
         rec_query_->resolve(*question, answer_message, buffer, server);
+
     } else {
     } else {
-        dlog("Processing forward query");
+
+        // Processing forward query
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_FWDQUERY);
         rec_query_->forward(query_message, answer_message, buffer, server);
         rec_query_->forward(query_message, answer_message, buffer, server);
     }
     }
 }
 }
 
 
 ConstElementPtr
 ConstElementPtr
 Resolver::updateConfig(ConstElementPtr config) {
 Resolver::updateConfig(ConstElementPtr config) {
-    dlog("New config comes: " + config->toWire());
+    LOG_DEBUG(resolver_logger, RESOLVER_DBG_CONFIG, RESOLVER_CONFIGUPD)
+              .arg(*config);
 
 
     try {
     try {
         // Parse forward_addresses
         // Parse forward_addresses
@@ -530,6 +560,7 @@ Resolver::updateConfig(ConstElementPtr config) {
             // check for us
             // check for us
             qtimeout = qtimeoutE->intValue();
             qtimeout = qtimeoutE->intValue();
             if (qtimeout < -1) {
             if (qtimeout < -1) {
+                LOG_ERROR(resolver_logger, RESOLVER_QUTMOSMALL).arg(qtimeout);
                 isc_throw(BadValue, "Query timeout too small");
                 isc_throw(BadValue, "Query timeout too small");
             }
             }
             set_timeouts = true;
             set_timeouts = true;
@@ -537,6 +568,7 @@ Resolver::updateConfig(ConstElementPtr config) {
         if (ctimeoutE) {
         if (ctimeoutE) {
             ctimeout = ctimeoutE->intValue();
             ctimeout = ctimeoutE->intValue();
             if (ctimeout < -1) {
             if (ctimeout < -1) {
+                LOG_ERROR(resolver_logger, RESOLVER_CLTMOSMALL).arg(ctimeout);
                 isc_throw(BadValue, "Client timeout too small");
                 isc_throw(BadValue, "Client timeout too small");
             }
             }
             set_timeouts = true;
             set_timeouts = true;
@@ -544,12 +576,18 @@ Resolver::updateConfig(ConstElementPtr config) {
         if (ltimeoutE) {
         if (ltimeoutE) {
             ltimeout = ltimeoutE->intValue();
             ltimeout = ltimeoutE->intValue();
             if (ltimeout < -1) {
             if (ltimeout < -1) {
+                LOG_ERROR(resolver_logger, RESOLVER_LKTMOSMALL).arg(ltimeout);
                 isc_throw(BadValue, "Lookup timeout too small");
                 isc_throw(BadValue, "Lookup timeout too small");
             }
             }
             set_timeouts = true;
             set_timeouts = true;
         }
         }
         if (retriesE) {
         if (retriesE) {
+            // Do the assignment from "retriesE->intValue()" to "retries"
+            // _after_ the comparison (as opposed to before it for the timeouts)
+            // because "retries" is unsigned.
             if (retriesE->intValue() < 0) {
             if (retriesE->intValue() < 0) {
+                LOG_ERROR(resolver_logger, RESOLVER_RETRYNEG)
+                          .arg(retriesE->intValue());
                 isc_throw(BadValue, "Negative number of retries");
                 isc_throw(BadValue, "Negative number of retries");
             }
             }
             retries = retriesE->intValue();
             retries = retriesE->intValue();
@@ -591,8 +629,11 @@ Resolver::updateConfig(ConstElementPtr config) {
         }
         }
         setConfigured();
         setConfigured();
         return (isc::config::createAnswer());
         return (isc::config::createAnswer());
+
     } catch (const isc::Exception& error) {
     } catch (const isc::Exception& error) {
-        dlog(string("error in config: ") + error.what(),true);
+
+        // Configuration error
+        LOG_ERROR(resolver_logger, RESOLVER_CONFIGERR).arg(error.what());
         return (isc::config::createAnswer(1, error.what()));
         return (isc::config::createAnswer(1, error.what()));
     }
     }
 }
 }
@@ -632,10 +673,10 @@ Resolver::setListenAddresses(const AddressList& addresses) {
 void
 void
 Resolver::setTimeouts(int query_timeout, int client_timeout,
 Resolver::setTimeouts(int query_timeout, int client_timeout,
                       int lookup_timeout, unsigned retries) {
                       int lookup_timeout, unsigned retries) {
-    dlog("Setting query timeout to " + boost::lexical_cast<string>(query_timeout) +
+    LOG_DEBUG(resolver_logger, RESOLVER_DBG_CONFIG, RESOLVER_SETPARAM)
-         ", client timeout to " + boost::lexical_cast<string>(client_timeout) +
+              .arg(query_timeout).arg(client_timeout).arg(lookup_timeout)
-         ", lookup timeout to " + boost::lexical_cast<string>(lookup_timeout) +
+              .arg(retries);
-         " and retry count to " + boost::lexical_cast<string>(retries));
+
     impl_->query_timeout_ = query_timeout;
     impl_->query_timeout_ = query_timeout;
     impl_->client_timeout_ = client_timeout;
     impl_->client_timeout_ = client_timeout;
     impl_->lookup_timeout_ = lookup_timeout;
     impl_->lookup_timeout_ = lookup_timeout;

+ 19 - 0
src/bin/resolver/resolver_log.cc

@@ -0,0 +1,19 @@
+// Copyright (C) 2011  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.
+
+/// Defines the logger used by the NSAS
+
+#include "resolver_log.h"
+
+isc::log::Logger resolver_logger("resolver");

+ 49 - 0
src/bin/resolver/resolver_log.h

@@ -0,0 +1,49 @@
+// Copyright (C) 2011  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 __RESOLVER_LOG__H
+#define __RESOLVER_LOG__H
+
+#include <log/macros.h>
+#include "resolverdef.h"
+
+/// \brief Resolver Logging
+///
+/// Defines the levels used to output debug messages in the resolver.  Note that
+/// higher numbers equate to more verbose (and detailed) output.
+
+// Initialization
+const int RESOLVER_DBG_INIT = 10;
+
+// Configuration messages
+const int RESOLVER_DBG_CONFIG = 30;
+
+// Trace sending and receiving of messages
+const int RESOLVER_DBG_IO = 50;
+
+// Trace processing of messages
+const int RESOLVER_DBG_PROCESS = 70;
+
+// Detailed message information
+const int RESOLVER_DBG_DETAIL = 90;
+
+
+/// \brief Resolver Logger
+///
+/// Define the logger used to log messages.  We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger resolver_logger;
+
+#endif // __RESOLVER_LOG__H

+ 193 - 0
src/bin/resolver/resolverdef.mes

@@ -0,0 +1,193 @@
+# Copyright (C) 2011  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.
+
+$PREFIX RESOLVER_
+# No namespace declaration - these constants go in the global namespace
+# along with the resolver methods.
+
+% AXFRTCP       AXFR request received over TCP
+A debug message, the resolver received a NOTIFY message over TCP.  The server
+cannot process it and will return an error message to the sender with the
+RCODE set to NOTIMP.
+
+% AXFRUDP       AXFR request received over UDP
+A debug message, the resolver received a NOTIFY message over UDP.  The server
+cannot process it (and in any case, an AXFR request should be sent over TCP)
+and will return an error message to the sender with the RCODE set to FORMERR.
+
+% CONFIGCHAN    configuration channel created
+A debug message, output when the resolver has successfully established a
+connection to the configuration channel.
+
+% CONFIGERR     error in configuration: %1
+An error was detected in a configuration update received by the resolver. This
+may be in the format of the configuration message (in which case this is a
+programming error) or it may be in the data supplied (in which case it is
+a user error).  The reason for the error, given as a parameter in the message,
+will give more details.
+
+% CONFIGLOAD    configuration loaded
+A debug message, output when the resolver configuration has been successfully
+loaded.
+
+% CONFIGUPD     configuration updated: %1
+A debug message, the configuration has been updated with the specified
+information.
+
+% DNSMSGRCVD    DNS message received: %1
+A debug message, this always precedes some other logging message and is the
+formatted contents of the DNS packet that the other message refers to.
+
+% DNSMSGSENT    DNS message of %1 bytes sent: %2
+A debug message, this contains details of the response sent back to the querying
+system.
+
+% CLTMOSMALL    client timeout of %1 is too small
+An error indicating that the configuration value specified for the query
+timeout is too small.
+
+% CREATED       main resolver object created
+A debug message, output when the Resolver() object has been created.
+
+% FAILED        resolver failed, reason: %1
+This is an error message output when an unhandled exception is caught by the
+resolver.  All it can do is to shut down.
+
+% FWDADDR       setting forward address %1(%2)
+This message may appear multiple times during startup, and it lists the
+forward addresses used by the resolver when running in forwarding mode.
+
+% FWDQUERY      processing forward query
+The received query has passed all checks and is being forwarded to upstream
+servers.
+
+% HDRERR        message received, exception when processing header: %1
+A debug message noting that an exception occurred during the processing of
+a received packet.  The packet has been dropped.
+
+% IXFR          IXFR request received
+The resolver received a NOTIFY message over TCP.  The server cannot process it
+and will return an error message to the sender with the RCODE set to NOTIMP.
+
+% LKTMOSMALL    lookup timeout of %1 is too small
+An error indicating that the configuration value specified for the lookup
+timeout is too small.
+
+% NFYNOTAUTH    NOTIFY arrived but server is not authoritative
+The resolver received a NOTIFY message.  As the server is not authoritative it
+cannot process it, so it returns an error message to the sender with the RCODE
+set to NOTAUTH.
+
+% NORMQUERY     processing normal query
+The received query has passed all checks and is being processed by the resolver.
+
+% NOTIN         non-IN class request received, returning REFUSED message
+A debug message, the resolver has received a DNS packet that was not IN class.
+The resolver cannot handle such packets, so is returning a REFUSED response to
+the sender.
+
+% NOROOTADDR    no root addresses available
+A warning message during startup, indicates that no root addresses have been
+set.  This may be because the resolver will get them from a priming query.
+
+% NOTONEQUES    query contained %1 questions, exactly one question was expected
+A debug message, the resolver received a query that contained the number of
+entires in the question section detailed in the message.  This is a malformed
+message, as a DNS query must contain only one question.  The resolver will
+return a message to the sender with the RCODE set to FORMERR.
+
+% OPCODEUNS     opcode %1 not supported by the resolver
+A debug message, the resolver received a message with an unsupported opcode
+(it can only process QUERY opcodes).  It will return a message to the sender
+with the RCODE set to NOTIMP.
+
+% PARSEERR      error parsing received message: %1 - returning %2
+A debug message noting that the resolver received a message and the parsing
+of the body of the message failed due to some non-protocol related reason
+(although the parsing of the header succeeded).  The message parameters give
+a textual description of the problem and the RCODE returned.
+
+% PRINTMSG      print message command, aeguments are: %1
+This message is logged when a "print_message" command is received over the
+command channel.
+
+% PROTERR       protocol error parsing received message: %1 - returning %2
+A debug message noting that the resolver received a message and the parsing
+of the body of the message failed due to some protocol error (although the
+parsing of the header succeeded).  The message parameters give a textual
+description of the problem and the RCODE returned.
+
+% QUSETUP       query setup
+A debug message noting that the resolver is creating a RecursiveQuery object.
+
+% QUSHUT        query shutdown
+A debug message noting that the resolver is destroying a RecursiveQuery object.
+
+% QUTMOSMALL    query timeout of %1 is too small
+An error indicating that the configuration value specified for the query
+timeout is too small.
+
+% RECURSIVE     running in recursive mode
+This is an informational message that appears at startup noting that the
+resolver is running in recursive mode.
+
+% RECVMSG       resolver has received a DNS message
+A debug message indicating that the resolver has received a message.  Depending
+on the debug settings, subsequent log output will indicate the nature of the
+message.
+
+% RETRYNEG      negative number of retries (%1) specified in the configuration
+An error message indicating that the resolver configuration has specified a
+negative retry count.  Only zero or positive values are valid.
+
+% ROOTADDR      setting root address %1(%2)
+This message may appear multiple times during startup; it lists the root
+addresses used by the resolver.
+
+% SERVICE       service object created
+A debug message, output when the main service object (which handles the
+received queries) is created.
+
+% SETPARAM      query timeout: %1, client timeout: %2, lookup timeout: %3, retry count: %4
+A debug message, lists the parameters associated with the message.  These are:
+query timeout: the timeout (in ms) used for queries originated by the resolver
+to upstream servers.  Client timeout: the interval to resolver a query by
+a client: after this time, the resolver sends back a SERVFAIL to the client
+whilst continuing to resolver the query. Lookup timeout: the time at which the
+resolver gives up trying to resolve a query.  Retry count: the number of times
+the resolver will retry a query to an upstream server if it gets a timeout.
+
+The client and lookup timeouts require a bit more explanation. The
+resolution of the clent query might require a large number of queries to
+upstream nameservers.  Even if none of these queries timeout, the total time
+taken to perform all the queries may exceed the client timeout.  When this
+happens, a SERVFAIL is returned to the client, but the resolver continues
+with the resolution process. Data received is added to the cache.  However,
+there comes a time - the lookup timeout - when even the resolve gives up.
+At this point it will wait for pending upstream queries to complete or
+timeout and drop the query.
+
+% SHUTDOWN      resolver shutdown complete
+This information message is output when the resolver has shut down.
+
+% STARTED       resolver started
+This informational message is output by the resolver when all initialization
+has been completed and it is entering its main loop.
+
+% STARTING      starting resolver with command line '%1'
+An informational message, this is output when the resolver starts up.
+
+% UNEXRESP      received unexpected response, ignoring
+A debug message noting that the server has received a response instead of a
+query and is ignoring it.

+ 10 - 4
src/bin/resolver/tests/Makefile.am

@@ -1,6 +1,7 @@
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_builddir)/src/bin/resolver
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(top_srcdir)/src/lib/testutils/testdata\"
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(top_srcdir)/src/lib/testutils/testdata\"
 AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
 AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -16,20 +17,24 @@ CLEANFILES = *.gcno *.gcda
 TESTS =
 TESTS =
 if HAVE_GTEST
 if HAVE_GTEST
 TESTS += run_unittests
 TESTS += run_unittests
+
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += ../resolver.h ../resolver.cc
 run_unittests_SOURCES += ../resolver.h ../resolver.cc
+run_unittests_SOURCES += ../resolver_log.h ../resolver_log.cc
 run_unittests_SOURCES += ../response_scrubber.h ../response_scrubber.cc
 run_unittests_SOURCES += ../response_scrubber.h ../response_scrubber.cc
 run_unittests_SOURCES += resolver_unittest.cc
 run_unittests_SOURCES += resolver_unittest.cc
 run_unittests_SOURCES += resolver_config_unittest.cc
 run_unittests_SOURCES += resolver_config_unittest.cc
 run_unittests_SOURCES += response_scrubber_unittest.cc
 run_unittests_SOURCES += response_scrubber_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_SOURCES += run_unittests.cc
+
+nodist_run_unittests_SOURCES = ../resolverdef.h ../resolverdef.cc
+
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+
-run_unittests_LDADD += $(SQLITE_LIBS)
+run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
 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/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.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/asiolink/libasiolink.la
@@ -42,6 +47,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.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/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
 run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 
 
 # Note the ordering matters: -Wno-... must follow -Wextra (defined in
 # Note the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS
 # B10_CXXFLAGS

+ 4 - 1
src/bin/resolver/tests/run_unittests.cc

@@ -13,6 +13,8 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <log/logger_support.h>
+#include <util/unittests/run_all.h>
 
 
 #include <dns/tests/unittest_util.h>
 #include <dns/tests/unittest_util.h>
 
 
@@ -21,6 +23,7 @@ main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::InitGoogleTest(&argc, argv);
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
+    isc::log::initLogger();
 
 
-    return (RUN_ALL_TESTS());
+    return (isc::util::unittests::run_all());
 }
 }

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

@@ -16,10 +16,9 @@ run_unittests_SOURCES += run_unittests.cc
 
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+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/libutil_io.la
-run_unittests_LDADD += \
-	$(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 noinst_PROGRAMS = $(TESTS)

+ 2 - 1
src/bin/sockcreator/tests/run_unittests.cc

@@ -13,10 +13,11 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
 
 
 int
 int
 main(int argc, char *argv[]) {
 main(int argc, char *argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::InitGoogleTest(&argc, argv);
 
 
-    return RUN_ALL_TESTS();
+    return isc::util::unittests::run_all();
 }
 }

+ 51 - 1
src/bin/xfrin/tests/xfrin_test.py

@@ -15,6 +15,7 @@
 
 
 import unittest
 import unittest
 import socket
 import socket
+import io
 from isc.testutils.tsigctx_mock import MockTSIGContext
 from isc.testutils.tsigctx_mock import MockTSIGContext
 from xfrin import *
 from xfrin import *
 
 
@@ -78,7 +79,7 @@ class MockXfrin(Xfrin):
 
 
     def _get_db_file(self):
     def _get_db_file(self):
         pass
         pass
-    
+
     def _cc_check_command(self):
     def _cc_check_command(self):
         self._shutdown_event.set()
         self._shutdown_event.set()
         if MockXfrin.check_command_hook:
         if MockXfrin.check_command_hook:
@@ -207,6 +208,18 @@ class TestXfrinConnection(unittest.TestCase):
         mock_ctx.error = error
         mock_ctx.error = error
         return mock_ctx
         return mock_ctx
 
 
+    def __match_exception(self, expected_exception, expected_msg, expression):
+        # This helper method is a higher-granularity version of assertRaises().
+        # If it's not sufficient to check the exception class (e.g., when
+        # the same type of exceptions can be thrown from many places), this
+        # method can be used to check it with the exception argument.
+        try:
+            expression()
+        except expected_exception as ex:
+            self.assertEqual(str(ex), expected_msg)
+        else:
+            self.assertFalse('exception is expected, but not raised')
+
     def test_close(self):
     def test_close(self):
         # we shouldn't be using the global asyncore map.
         # we shouldn't be using the global asyncore map.
         self.assertEqual(len(asyncore.socket_map), 0)
         self.assertEqual(len(asyncore.socket_map), 0)
@@ -293,6 +306,31 @@ class TestXfrinConnection(unittest.TestCase):
         self.conn.reply_data = self.conn.create_response_data(bad_qid = True)
         self.conn.reply_data = self.conn.create_response_data(bad_qid = True)
         self.assertRaises(XfrinException, self._handle_xfrin_response)
         self.assertRaises(XfrinException, self._handle_xfrin_response)
 
 
+    def test_response_error_code_bad_sig(self):
+        self.conn._tsig_key = TSIG_KEY
+        self.conn._tsig_ctx_creator = \
+            lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
+        self.conn._send_query(RRType.AXFR())
+        self.conn.reply_data = self.conn.create_response_data(
+                rcode=Rcode.SERVFAIL())
+        # xfrin should check TSIG before other part of incoming message
+        # validate log message for XfrinException
+        self.__match_exception(XfrinException,
+                               "TSIG verify fail: BADSIG",
+                               self._handle_xfrin_response)
+
+    def test_response_bad_qid_bad_key(self):
+        self.conn._tsig_key = TSIG_KEY
+        self.conn._tsig_ctx_creator = \
+            lambda key: self.__create_mock_tsig(key, TSIGError.BAD_KEY)
+        self.conn._send_query(RRType.AXFR())
+        self.conn.reply_data = self.conn.create_response_data(bad_qid=True)
+        # xfrin should check TSIG before other part of incoming message
+        # validate log message for XfrinException
+        self.__match_exception(XfrinException,
+                               "TSIG verify fail: BADKEY",
+                               self._handle_xfrin_response)
+
     def test_response_non_response(self):
     def test_response_non_response(self):
         self.conn._send_query(RRType.AXFR())
         self.conn._send_query(RRType.AXFR())
         self.conn.reply_data = self.conn.create_response_data(response = False)
         self.conn.reply_data = self.conn.create_response_data(response = False)
@@ -337,6 +375,18 @@ class TestXfrinConnection(unittest.TestCase):
         self.conn.response_generator = self._create_soa_response_data
         self.conn.response_generator = self._create_soa_response_data
         self.assertRaises(XfrinException, self.conn._check_soa_serial)
         self.assertRaises(XfrinException, self.conn._check_soa_serial)
 
 
+    def test_soacheck_bad_qid_bad_sig(self):
+        self.conn._tsig_key = TSIG_KEY
+        self.conn._tsig_ctx_creator = \
+            lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
+        self.soa_response_params['bad_qid'] = True
+        self.conn.response_generator = self._create_soa_response_data
+        # xfrin should check TSIG before other part of incoming message
+        # validate log message for XfrinException
+        self.__match_exception(XfrinException,
+                               "TSIG verify fail: BADSIG",
+                               self.conn._check_soa_serial)
+
     def test_soacheck_non_response(self):
     def test_soacheck_non_response(self):
         self.soa_response_params['response'] = False
         self.soa_response_params['response'] = False
         self.conn.response_generator = self._create_soa_response_data
         self.conn.response_generator = self._create_soa_response_data

+ 7 - 5
src/bin/xfrin/xfrin.py.in

@@ -243,13 +243,13 @@ class XfrinConnection(asyncore.dispatcher):
         msg = Message(Message.PARSE)
         msg = Message(Message.PARSE)
         msg.from_wire(soa_response)
         msg.from_wire(soa_response)
 
 
+        # TSIG related checks, including an unexpected signed response
+        self._check_response_tsig(msg, soa_response)
+
         # perform some minimal level validation.  It's an open issue how
         # perform some minimal level validation.  It's an open issue how
         # strict we should be (see the comment in _check_response_header())
         # strict we should be (see the comment in _check_response_header())
         self._check_response_header(msg)
         self._check_response_header(msg)
 
 
-        # TSIG related checks, including an unexpected signed response
-        self._check_response_tsig(msg, soa_response)
-
         # TODO, need select soa record from data source then compare the two
         # TODO, need select soa record from data source then compare the two
         # serial, current just return OK, since this function hasn't been used
         # serial, current just return OK, since this function hasn't been used
         # now.
         # now.
@@ -311,7 +311,7 @@ class XfrinConnection(asyncore.dispatcher):
             raise XfrinException('error response: %s' % msg_rcode.to_text())
             raise XfrinException('error response: %s' % msg_rcode.to_text())
 
 
         if not msg.get_header_flag(Message.HEADERFLAG_QR):
         if not msg.get_header_flag(Message.HEADERFLAG_QR):
-            raise XfrinException('response is not a response ')
+            raise XfrinException('response is not a response')
 
 
         if msg.get_qid() != self._query_id:
         if msg.get_qid() != self._query_id:
             raise XfrinException('bad query id')
             raise XfrinException('bad query id')
@@ -362,11 +362,13 @@ class XfrinConnection(asyncore.dispatcher):
             recvdata = self._get_request_response(msg_len)
             recvdata = self._get_request_response(msg_len)
             msg = Message(Message.PARSE)
             msg = Message(Message.PARSE)
             msg.from_wire(recvdata)
             msg.from_wire(recvdata)
-            self._check_response_status(msg)
 
 
             # TSIG related checks, including an unexpected signed response
             # TSIG related checks, including an unexpected signed response
             self._check_response_tsig(msg, recvdata)
             self._check_response_tsig(msg, recvdata)
 
 
+            # Perform response status validation
+            self._check_response_status(msg)
+
             answer_section = msg.get_section(Message.SECTION_ANSWER)
             answer_section = msg.get_section(Message.SECTION_ANSWER)
             for rr in self._handle_answer_section(answer_section):
             for rr in self._handle_answer_section(answer_section):
                 yield rr
                 yield rr

+ 210 - 6
src/bin/xfrout/tests/xfrout_test.py.in

@@ -18,11 +18,14 @@
 
 
 import unittest
 import unittest
 import os
 import os
+from isc.testutils.tsigctx_mock import MockTSIGContext
 from isc.cc.session import *
 from isc.cc.session import *
 from pydnspp import *
 from pydnspp import *
 from xfrout import *
 from xfrout import *
 import xfrout
 import xfrout
 
 
+TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
+
 # our fake socket, where we can read and insert messages
 # our fake socket, where we can read and insert messages
 class MySocket():
 class MySocket():
     def __init__(self, family, type):
     def __init__(self, family, type):
@@ -85,10 +88,36 @@ class TestXfroutSession(unittest.TestCase):
         msg.from_wire(self.mdata)
         msg.from_wire(self.mdata)
         return msg
         return msg
 
 
+    def create_mock_tsig_ctx(self, error):
+        # This helper function creates a MockTSIGContext for a given key
+        # and TSIG error to be used as a result of verify (normally faked
+        # one)
+        mock_ctx = MockTSIGContext(TSIG_KEY)
+        mock_ctx.error = error
+        return mock_ctx
+
+    def message_has_tsig(self, msg):
+        return msg.get_tsig_record() is not None
+
+    def create_request_data_with_tsig(self):
+        msg = Message(Message.RENDER)
+        query_id = 0x1035
+        msg.set_qid(query_id)
+        msg.set_opcode(Opcode.QUERY())
+        msg.set_rcode(Rcode.NOERROR())
+        query_question = Question(Name("example.com."), RRClass.IN(), RRType.AXFR())
+        msg.add_question(query_question)
+
+        renderer = MessageRenderer()
+        tsig_ctx = MockTSIGContext(TSIG_KEY)
+        msg.to_wire(renderer, tsig_ctx)
+        reply_data = renderer.get_data()
+        return reply_data
+
     def setUp(self):
     def setUp(self):
         self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
         self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
         self.log = isc.log.NSLogger('xfrout', '',  severity = 'critical', log_to_console = False )
         self.log = isc.log.NSLogger('xfrout', '',  severity = 'critical', log_to_console = False )
-        self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), self.log)
+        self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), self.log, TSIGKeyRing())
         self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
         self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
         self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200')
         self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200')
 
 
@@ -96,6 +125,18 @@ class TestXfroutSession(unittest.TestCase):
         [get_rcode, get_msg] = self.xfrsess._parse_query_message(self.mdata)
         [get_rcode, get_msg] = self.xfrsess._parse_query_message(self.mdata)
         self.assertEqual(get_rcode.to_text(), "NOERROR")
         self.assertEqual(get_rcode.to_text(), "NOERROR")
 
 
+        # tsig signed query message
+        request_data = self.create_request_data_with_tsig()
+        # BADKEY
+        [rcode, msg] = self.xfrsess._parse_query_message(request_data)
+        self.assertEqual(rcode.to_text(), "NOTAUTH")
+        self.assertTrue(self.xfrsess._tsig_ctx is not None)
+        # NOERROR
+        self.xfrsess._tsig_key_ring.add(TSIG_KEY)
+        [rcode, msg] = self.xfrsess._parse_query_message(request_data)
+        self.assertEqual(rcode.to_text(), "NOERROR")
+        self.assertTrue(self.xfrsess._tsig_ctx is not None)
+
     def test_get_query_zone_name(self):
     def test_get_query_zone_name(self):
         msg = self.getmsg()
         msg = self.getmsg()
         self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.")
         self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.")
@@ -111,6 +152,14 @@ class TestXfroutSession(unittest.TestCase):
         get_msg = self.sock.read_msg()
         get_msg = self.sock.read_msg()
         self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
         self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
 
 
+        # tsig signed message
+        msg = self.getmsg()
+        self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+        self.xfrsess._reply_query_with_error_rcode(msg, self.sock, Rcode(3))
+        get_msg = self.sock.read_msg()
+        self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN")
+        self.assertTrue(self.message_has_tsig(get_msg))
+
     def test_send_message(self):
     def test_send_message(self):
         msg = self.getmsg()
         msg = self.getmsg()
         msg.make_response()
         msg.make_response()
@@ -152,6 +201,14 @@ class TestXfroutSession(unittest.TestCase):
         get_msg = self.sock.read_msg()
         get_msg = self.sock.read_msg()
         self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR")
         self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR")
 
 
+        # tsig signed message
+        msg = self.getmsg()
+        self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+        self.xfrsess._reply_query_with_format_error(msg, self.sock)
+        get_msg = self.sock.read_msg()
+        self.assertEqual(get_msg.get_rcode().to_text(), "FORMERR")
+        self.assertTrue(self.message_has_tsig(get_msg))
+
     def test_create_rrset_from_db_record(self):
     def test_create_rrset_from_db_record(self):
         rrset = self.xfrsess._create_rrset_from_db_record(self.soa_record)
         rrset = self.xfrsess._create_rrset_from_db_record(self.soa_record)
         self.assertEqual(rrset.get_name().to_text(), "example.com.")
         self.assertEqual(rrset.get_name().to_text(), "example.com.")
@@ -162,11 +219,16 @@ class TestXfroutSession(unittest.TestCase):
 
 
     def test_send_message_with_last_soa(self):
     def test_send_message_with_last_soa(self):
         rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
         rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
-
         msg = self.getmsg()
         msg = self.getmsg()
         msg.make_response()
         msg.make_response()
-        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 0)
+
+        # packet number less than TSIG_SIGN_EVERY_NTH
+        packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+                                                 0, packet_neet_not_sign)
         get_msg = self.sock.read_msg()
         get_msg = self.sock.read_msg()
+        # tsig context is not exist
+        self.assertFalse(self.message_has_tsig(get_msg))
 
 
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
@@ -180,6 +242,42 @@ class TestXfroutSession(unittest.TestCase):
         rdata = answer.get_rdata()
         rdata = answer.get_rdata()
         self.assertEqual(rdata[0].to_text(), self.soa_record[7])
         self.assertEqual(rdata[0].to_text(), self.soa_record[7])
 
 
+        # msg is the TSIG_SIGN_EVERY_NTH one
+        # sending the message with last soa together
+        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+                                                 0, TSIG_SIGN_EVERY_NTH)
+        get_msg = self.sock.read_msg()
+        # tsig context is not exist
+        self.assertFalse(self.message_has_tsig(get_msg))
+
+    def test_send_message_with_last_soa_with_tsig(self):
+        # create tsig context
+        self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+
+        rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+        msg = self.getmsg()
+        msg.make_response()
+
+        # packet number less than TSIG_SIGN_EVERY_NTH
+        packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+        # msg is not the TSIG_SIGN_EVERY_NTH one
+        # sending the message with last soa together
+        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+                                                 0, packet_neet_not_sign)
+        get_msg = self.sock.read_msg()
+        self.assertTrue(self.message_has_tsig(get_msg))
+
+        self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
+        self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+        self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
+
+        # msg is the TSIG_SIGN_EVERY_NTH one
+        # sending the message with last soa together
+        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa,
+                                                 0, TSIG_SIGN_EVERY_NTH)
+        get_msg = self.sock.read_msg()
+        self.assertTrue(self.message_has_tsig(get_msg))
+
     def test_trigger_send_message_with_last_soa(self):
     def test_trigger_send_message_with_last_soa(self):
         rrset_a = RRset(Name("example.com"), RRClass.IN(), RRType.A(), RRTTL(3600))
         rrset_a = RRset(Name("example.com"), RRClass.IN(), RRType.A(), RRTTL(3600))
         rrset_a.add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.1"))
         rrset_a.add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.1"))
@@ -187,15 +285,21 @@ class TestXfroutSession(unittest.TestCase):
 
 
         msg = self.getmsg()
         msg = self.getmsg()
         msg.make_response()
         msg.make_response()
-
         msg.add_rrset(Message.SECTION_ANSWER, rrset_a)
         msg.add_rrset(Message.SECTION_ANSWER, rrset_a)
-        # give the function a value that is larger than MAX-len(rrset)
-        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 65520)
 
 
+        # length larger than MAX-len(rrset)
+        length_need_split = xfrout.XFROUT_MAX_MESSAGE_SIZE - get_rrset_len(rrset_soa) + 1
+        # packet number less than TSIG_SIGN_EVERY_NTH
+        packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+
+        # give the function a value that is larger than MAX-len(rrset)
         # this should have triggered the sending of two messages
         # this should have triggered the sending of two messages
         # (1 with the rrset we added manually, and 1 that triggered
         # (1 with the rrset we added manually, and 1 that triggered
         # the sending in _with_last_soa)
         # the sending in _with_last_soa)
+        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, length_need_split,
+                                                 packet_neet_not_sign)
         get_msg = self.sock.read_msg()
         get_msg = self.sock.read_msg()
+        self.assertFalse(self.message_has_tsig(get_msg))
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 1)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
@@ -208,6 +312,7 @@ class TestXfroutSession(unittest.TestCase):
         self.assertEqual(rdata[0].to_text(), "192.0.2.1")
         self.assertEqual(rdata[0].to_text(), "192.0.2.1")
 
 
         get_msg = self.sock.read_msg()
         get_msg = self.sock.read_msg()
+        self.assertFalse(self.message_has_tsig(get_msg))
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 0)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_QUESTION), 0)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_ANSWER), 1)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
         self.assertEqual(get_msg.get_rr_count(Message.SECTION_AUTHORITY), 0)
@@ -223,6 +328,45 @@ class TestXfroutSession(unittest.TestCase):
         # and it should not have sent anything else
         # and it should not have sent anything else
         self.assertEqual(0, len(self.sock.sendqueue))
         self.assertEqual(0, len(self.sock.sendqueue))
 
 
+    def test_trigger_send_message_with_last_soa_with_tsig(self):
+        self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+        rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+        msg = self.getmsg()
+        msg.make_response()
+        msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
+
+        # length larger than MAX-len(rrset)
+        length_need_split = xfrout.XFROUT_MAX_MESSAGE_SIZE - get_rrset_len(rrset_soa) + 1
+        # packet number less than TSIG_SIGN_EVERY_NTH
+        packet_neet_not_sign = xfrout.TSIG_SIGN_EVERY_NTH - 1
+
+        # give the function a value that is larger than MAX-len(rrset)
+        # this should have triggered the sending of two messages
+        # (1 with the rrset we added manually, and 1 that triggered
+        # the sending in _with_last_soa)
+        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, length_need_split,
+                                                 packet_neet_not_sign)
+        get_msg = self.sock.read_msg()
+        # msg is not the TSIG_SIGN_EVERY_NTH one, it shouldn't be tsig signed
+        self.assertFalse(self.message_has_tsig(get_msg))
+        # the last packet should be tsig signed
+        get_msg = self.sock.read_msg()
+        self.assertTrue(self.message_has_tsig(get_msg))
+        # and it should not have sent anything else
+        self.assertEqual(0, len(self.sock.sendqueue))
+
+
+        # msg is the TSIG_SIGN_EVERY_NTH one, it should be tsig signed
+        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, length_need_split,
+                                                 xfrout.TSIG_SIGN_EVERY_NTH)
+        get_msg = self.sock.read_msg()
+        self.assertTrue(self.message_has_tsig(get_msg))
+        # the last packet should be tsig signed
+        get_msg = self.sock.read_msg()
+        self.assertTrue(self.message_has_tsig(get_msg))
+        # and it should not have sent anything else
+        self.assertEqual(0, len(self.sock.sendqueue))
+
     def test_get_rrset_len(self):
     def test_get_rrset_len(self):
         rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
         rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
         self.assertEqual(82, get_rrset_len(rrset_soa))
         self.assertEqual(82, get_rrset_len(rrset_soa))
@@ -313,6 +457,51 @@ class TestXfroutSession(unittest.TestCase):
         reply_msg = self.sock.read_msg()
         reply_msg = self.sock.read_msg()
         self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 2)
         self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 2)
 
 
+    def test_reply_xfrout_query_noerror_with_tsig(self):
+        rrset_data = (4, 3, 'a.example.com.', 'com.example.', 3600, 'A', None, '192.168.1.1')
+        global sqlite3_ds
+        global xfrout
+        def get_zone_soa(zonename, file):
+            return self.soa_record
+
+        def get_zone_datas(zone, file):
+            zone_rrsets = []
+            for i in range(0, 100):
+                zone_rrsets.insert(i, rrset_data)
+            return zone_rrsets
+
+        def get_rrset_len(rrset):
+            return 65520
+
+        sqlite3_ds.get_zone_soa = get_zone_soa
+        sqlite3_ds.get_zone_datas = get_zone_datas
+        xfrout.get_rrset_len = get_rrset_len
+
+        self.xfrsess._tsig_ctx = self.create_mock_tsig_ctx(TSIGError.NOERROR)
+        self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock, "example.com.")
+
+        # tsig signed first package
+        reply_msg = self.sock.read_msg()
+        self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 1)
+        self.assertTrue(self.message_has_tsig(reply_msg))
+        # (TSIG_SIGN_EVERY_NTH - 1) packets have no tsig
+        for i in range(0, xfrout.TSIG_SIGN_EVERY_NTH - 1):
+            reply_msg = self.sock.read_msg()
+            self.assertFalse(self.message_has_tsig(reply_msg))
+        # TSIG_SIGN_EVERY_NTH packet has tsig
+        reply_msg = self.sock.read_msg()
+        self.assertTrue(self.message_has_tsig(reply_msg))
+
+        for i in range(0, 100 - TSIG_SIGN_EVERY_NTH):
+            reply_msg = self.sock.read_msg()
+            self.assertFalse(self.message_has_tsig(reply_msg))
+        # tsig signed last package
+        reply_msg = self.sock.read_msg()
+        self.assertTrue(self.message_has_tsig(reply_msg))
+
+        # and it should not have sent anything else
+        self.assertEqual(0, len(self.sock.sendqueue))
+
 class MyCCSession():
 class MyCCSession():
     def __init__(self):
     def __init__(self):
         pass
         pass
@@ -347,8 +536,23 @@ class TestUnixSockServer(unittest.TestCase):
         self.assertEqual(recv_msg, send_msg)
         self.assertEqual(recv_msg, send_msg)
 
 
     def test_updata_config_data(self):
     def test_updata_config_data(self):
+        tsig_key_str = 'example.com:SFuWd/q99SzF8Yzd1QbB9g=='
+        tsig_key_list = [tsig_key_str]
+        bad_key_list = ['bad..example.com:SFuWd/q99SzF8Yzd1QbB9g==']
         self.unix.update_config_data({'transfers_out':10 })
         self.unix.update_config_data({'transfers_out':10 })
         self.assertEqual(self.unix._max_transfers_out, 10)
         self.assertEqual(self.unix._max_transfers_out, 10)
+        self.assertTrue(self.unix.tsig_key_ring is not None)
+
+        self.unix.update_config_data({'transfers_out':9, 'tsig_key_ring':tsig_key_list})
+        self.assertEqual(self.unix._max_transfers_out, 9)
+        self.assertEqual(self.unix.tsig_key_ring.size(), 1)
+        self.unix.tsig_key_ring.remove(Name("example.com."))
+        self.assertEqual(self.unix.tsig_key_ring.size(), 0)
+
+        # bad tsig key
+        config_data = {'transfers_out':9, 'tsig_key_ring': bad_key_list}
+        self.assertRaises(None, self.unix.update_config_data(config_data))
+        self.assertEqual(self.unix.tsig_key_ring.size(), 0)
 
 
     def test_get_db_file(self):
     def test_get_db_file(self):
         self.assertEqual(self.unix.get_db_file(), "initdb.file")
         self.assertEqual(self.unix.get_db_file(), "initdb.file")

+ 90 - 20
src/bin/xfrout/xfrout.py.in

@@ -74,10 +74,12 @@ SPECFILE_LOCATION = SPECFILE_PATH + "/xfrout.spec"
 AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
 AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
 MAX_TRANSFERS_OUT = 10
 MAX_TRANSFERS_OUT = 10
 VERBOSE_MODE = False
 VERBOSE_MODE = False
-
+# tsig sign every N axfr packets.
+TSIG_SIGN_EVERY_NTH = 96
 
 
 XFROUT_MAX_MESSAGE_SIZE = 65535
 XFROUT_MAX_MESSAGE_SIZE = 65535
 
 
+
 def get_rrset_len(rrset):
 def get_rrset_len(rrset):
     """Returns the wire length of the given RRset"""
     """Returns the wire length of the given RRset"""
     bytes = bytearray()
     bytes = bytearray()
@@ -86,15 +88,22 @@ def get_rrset_len(rrset):
 
 
 
 
 class XfroutSession():
 class XfroutSession():
-    def __init__(self, sock_fd, request_data, server, log):
+    def __init__(self, sock_fd, request_data, server, log, tsig_key_ring):
         # The initializer for the superclass may call functions
         # The initializer for the superclass may call functions
         # that need _log to be set, so we set it first
         # that need _log to be set, so we set it first
         self._sock_fd = sock_fd
         self._sock_fd = sock_fd
         self._request_data = request_data
         self._request_data = request_data
         self._server = server
         self._server = server
         self._log = log
         self._log = log
+        self._tsig_key_ring = tsig_key_ring
+        self._tsig_ctx = None
+        self._tsig_len = 0
         self.handle()
         self.handle()
 
 
+    def create_tsig_ctx(self, tsig_record, tsig_key_ring):
+        return TSIGContext(tsig_record.get_name(), tsig_record.get_rdata().get_algorithm(),
+                           tsig_key_ring)
+
     def handle(self):
     def handle(self):
         ''' Handle a xfrout query, send xfrout response '''
         ''' Handle a xfrout query, send xfrout response '''
         try:
         try:
@@ -105,17 +114,33 @@ class XfroutSession():
 
 
         os.close(self._sock_fd)
         os.close(self._sock_fd)
 
 
+    def _check_request_tsig(self, msg, request_data):
+        ''' If request has a tsig record, perform tsig related checks '''
+        tsig_record = msg.get_tsig_record()
+        if tsig_record is not None:
+            self._tsig_len = tsig_record.get_length()
+            self._tsig_ctx = self.create_tsig_ctx(tsig_record, self._tsig_key_ring)
+            tsig_error = self._tsig_ctx.verify(tsig_record, request_data)
+            if tsig_error != TSIGError.NOERROR:
+                return Rcode.NOTAUTH()
+
+        return Rcode.NOERROR()
+
     def _parse_query_message(self, mdata):
     def _parse_query_message(self, mdata):
         ''' parse query message to [socket,message]'''
         ''' parse query message to [socket,message]'''
         #TODO, need to add parseHeader() in case the message header is invalid
         #TODO, need to add parseHeader() in case the message header is invalid
         try:
         try:
             msg = Message(Message.PARSE)
             msg = Message(Message.PARSE)
             Message.from_wire(msg, mdata)
             Message.from_wire(msg, mdata)
+
+            # TSIG related checks
+            rcode = self._check_request_tsig(msg, mdata)
+
         except Exception as err:
         except Exception as err:
             self._log.log_message("error", str(err))
             self._log.log_message("error", str(err))
             return Rcode.FORMERR(), None
             return Rcode.FORMERR(), None
 
 
-        return Rcode.NOERROR(), msg
+        return rcode, msg
 
 
     def _get_query_zone_name(self, msg):
     def _get_query_zone_name(self, msg):
         question = msg.get_question()[0]
         question = msg.get_question()[0]
@@ -130,13 +155,20 @@ class XfroutSession():
             total_count += count
             total_count += count
 
 
 
 
-    def _send_message(self, sock_fd, msg):
+    def _send_message(self, sock_fd, msg, tsig_ctx=None):
         render = MessageRenderer()
         render = MessageRenderer()
         # As defined in RFC5936 section3.4, perform case-preserving name
         # As defined in RFC5936 section3.4, perform case-preserving name
         # compression for AXFR message.
         # compression for AXFR message.
         render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
         render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
         render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
         render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
-        msg.to_wire(render)
+
+        # XXX Currently, python wrapper doesn't accept 'None' parameter in this case,
+        # we should remove the if statement and use a universal interface later.
+        if tsig_ctx is not None:
+            msg.to_wire(render, tsig_ctx)
+        else:
+            msg.to_wire(render)
+
         header_len = struct.pack('H', socket.htons(render.get_length()))
         header_len = struct.pack('H', socket.htons(render.get_length()))
         self._send_data(sock_fd, header_len)
         self._send_data(sock_fd, header_len)
         self._send_data(sock_fd, render.get_data())
         self._send_data(sock_fd, render.get_data())
@@ -145,7 +177,7 @@ class XfroutSession():
     def _reply_query_with_error_rcode(self, msg, sock_fd, rcode_):
     def _reply_query_with_error_rcode(self, msg, sock_fd, rcode_):
         msg.make_response()
         msg.make_response()
         msg.set_rcode(rcode_)
         msg.set_rcode(rcode_)
-        self._send_message(sock_fd, msg)
+        self._send_message(sock_fd, msg, self._tsig_ctx)
 
 
 
 
     def _reply_query_with_format_error(self, msg, sock_fd):
     def _reply_query_with_format_error(self, msg, sock_fd):
@@ -155,7 +187,7 @@ class XfroutSession():
 
 
         msg.make_response()
         msg.make_response()
         msg.set_rcode(Rcode.FORMERR())
         msg.set_rcode(Rcode.FORMERR())
-        self._send_message(sock_fd, msg)
+        self._send_message(sock_fd, msg, self._tsig_ctx)
 
 
     def _zone_has_soa(self, zone):
     def _zone_has_soa(self, zone):
         '''Judge if the zone has an SOA record.'''
         '''Judge if the zone has an SOA record.'''
@@ -204,7 +236,9 @@ class XfroutSession():
     def dns_xfrout_start(self, sock_fd, msg_query):
     def dns_xfrout_start(self, sock_fd, msg_query):
         rcode_, msg = self._parse_query_message(msg_query)
         rcode_, msg = self._parse_query_message(msg_query)
         #TODO. create query message and parse header
         #TODO. create query message and parse header
-        if rcode_ != Rcode.NOERROR():
+        if rcode_ == Rcode.NOTAUTH():
+            return self._reply_query_with_error_rcode(msg, sock_fd, rcode_)
+        elif rcode_ != Rcode.NOERROR():
             return self._reply_query_with_format_error(msg, sock_fd)
             return self._reply_query_with_format_error(msg, sock_fd)
 
 
         zone_name = self._get_query_zone_name(msg)
         zone_name = self._get_query_zone_name(msg)
@@ -248,37 +282,43 @@ class XfroutSession():
         rrset_.add_rdata(rdata_)
         rrset_.add_rdata(rdata_)
         return rrset_
         return rrset_
 
 
-    def _send_message_with_last_soa(self, msg, sock_fd, rrset_soa, message_upper_len):
+    def _send_message_with_last_soa(self, msg, sock_fd, rrset_soa, message_upper_len,
+                                    count_since_last_tsig_sign):
         '''Add the SOA record to the end of message. If it can't be
         '''Add the SOA record to the end of message. If it can't be
         added, a new message should be created to send out the last soa .
         added, a new message should be created to send out the last soa .
         '''
         '''
         rrset_len = get_rrset_len(rrset_soa)
         rrset_len = get_rrset_len(rrset_soa)
 
 
-        if message_upper_len + rrset_len < XFROUT_MAX_MESSAGE_SIZE:
+        if (count_since_last_tsig_sign == TSIG_SIGN_EVERY_NTH and
-            msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
+            message_upper_len + rrset_len >= XFROUT_MAX_MESSAGE_SIZE):
-        else:
+            # If tsig context exist, sign the packet with serial number TSIG_SIGN_EVERY_NTH
+            self._send_message(sock_fd, msg, self._tsig_ctx)
+            msg = self._clear_message(msg)
+        elif (count_since_last_tsig_sign != TSIG_SIGN_EVERY_NTH and
+              message_upper_len + rrset_len + self._tsig_len >= XFROUT_MAX_MESSAGE_SIZE):
             self._send_message(sock_fd, msg)
             self._send_message(sock_fd, msg)
             msg = self._clear_message(msg)
             msg = self._clear_message(msg)
-            msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
 
 
-        self._send_message(sock_fd, msg)
+        # If tsig context exist, sign the last packet
+        msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
+        self._send_message(sock_fd, msg, self._tsig_ctx)
 
 
 
 
     def _reply_xfrout_query(self, msg, sock_fd, zone_name):
     def _reply_xfrout_query(self, msg, sock_fd, zone_name):
         #TODO, there should be a better way to insert rrset.
         #TODO, there should be a better way to insert rrset.
+        count_since_last_tsig_sign = TSIG_SIGN_EVERY_NTH
         msg.make_response()
         msg.make_response()
         msg.set_header_flag(Message.HEADERFLAG_AA)
         msg.set_header_flag(Message.HEADERFLAG_AA)
         soa_record = sqlite3_ds.get_zone_soa(zone_name, self._server.get_db_file())
         soa_record = sqlite3_ds.get_zone_soa(zone_name, self._server.get_db_file())
         rrset_soa = self._create_rrset_from_db_record(soa_record)
         rrset_soa = self._create_rrset_from_db_record(soa_record)
         msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
         msg.add_rrset(Message.SECTION_ANSWER, rrset_soa)
 
 
-        message_upper_len = get_rrset_len(rrset_soa)
+        message_upper_len = get_rrset_len(rrset_soa) + self._tsig_len
 
 
         for rr_data in sqlite3_ds.get_zone_datas(zone_name, self._server.get_db_file()):
         for rr_data in sqlite3_ds.get_zone_datas(zone_name, self._server.get_db_file()):
             if  self._server._shutdown_event.is_set(): # Check if xfrout is shutdown
             if  self._server._shutdown_event.is_set(): # Check if xfrout is shutdown
                 self._log.log_message("info", "xfrout process is being shutdown")
                 self._log.log_message("info", "xfrout process is being shutdown")
                 return
                 return
-
             # TODO: RRType.SOA() ?
             # TODO: RRType.SOA() ?
             if RRType(rr_data[5]) == RRType("SOA"): #ignore soa record
             if RRType(rr_data[5]) == RRType("SOA"): #ignore soa record
                 continue
                 continue
@@ -294,12 +334,25 @@ class XfroutSession():
                 message_upper_len += rrset_len
                 message_upper_len += rrset_len
                 continue
                 continue
 
 
-            self._send_message(sock_fd, msg)
+            # If tsig context exist, sign every N packets
+            if count_since_last_tsig_sign == TSIG_SIGN_EVERY_NTH:
+                count_since_last_tsig_sign = 0
+                self._send_message(sock_fd, msg, self._tsig_ctx)
+            else:
+                self._send_message(sock_fd, msg)
+
+            count_since_last_tsig_sign += 1
             msg = self._clear_message(msg)
             msg = self._clear_message(msg)
             msg.add_rrset(Message.SECTION_ANSWER, rrset_) # Add the rrset to the new message
             msg.add_rrset(Message.SECTION_ANSWER, rrset_) # Add the rrset to the new message
-            message_upper_len = rrset_len
 
 
-        self._send_message_with_last_soa(msg, sock_fd, rrset_soa, message_upper_len)
+            # Reserve tsig space for signed packet
+            if count_since_last_tsig_sign == TSIG_SIGN_EVERY_NTH:
+                message_upper_len = rrset_len + self._tsig_len
+            else:
+                message_upper_len = rrset_len
+
+        self._send_message_with_last_soa(msg, sock_fd, rrset_soa, message_upper_len,
+                                         count_since_last_tsig_sign)
 
 
 class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
 class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
     '''The unix domain socket server which accept xfr query sent from auth server.'''
     '''The unix domain socket server which accept xfr query sent from auth server.'''
@@ -403,7 +456,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
 
 
     def finish_request(self, sock_fd, request_data):
     def finish_request(self, sock_fd, request_data):
         '''Finish one request by instantiating RequestHandlerClass.'''
         '''Finish one request by instantiating RequestHandlerClass.'''
-        self.RequestHandlerClass(sock_fd, request_data, self, self._log)
+        self.RequestHandlerClass(sock_fd, request_data, self, self._log, self.tsig_key_ring)
 
 
     def _remove_unused_sock_file(self, sock_file):
     def _remove_unused_sock_file(self, sock_file):
         '''Try to remove the socket file. If the file is being used
         '''Try to remove the socket file. If the file is being used
@@ -449,10 +502,27 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
         self._log.log_message('info', 'update config data start.')
         self._log.log_message('info', 'update config data start.')
         self._lock.acquire()
         self._lock.acquire()
         self._max_transfers_out = new_config.get('transfers_out')
         self._max_transfers_out = new_config.get('transfers_out')
+        self.set_tsig_key_ring(new_config.get('tsig_key_ring'))
         self._log.log_message('info', 'max transfer out : %d', self._max_transfers_out)
         self._log.log_message('info', 'max transfer out : %d', self._max_transfers_out)
         self._lock.release()
         self._lock.release()
         self._log.log_message('info', 'update config data complete.')
         self._log.log_message('info', 'update config data complete.')
 
 
+    def set_tsig_key_ring(self, key_list):
+        """Set the tsig_key_ring , given a TSIG key string list representation. """
+
+        # XXX add values to configure zones/tsig options
+        self.tsig_key_ring = TSIGKeyRing()
+        # If key string list is empty, create a empty tsig_key_ring
+        if not key_list:
+            return
+
+        for key_item in key_list:
+            try:
+                self.tsig_key_ring.add(TSIGKey(key_item))
+            except InvalidParameter as ipe:
+                errmsg = "bad TSIG key string: " + str(key_item)
+                self._log.log_message('error', '%s' % errmsg)
+
     def get_db_file(self):
     def get_db_file(self):
         file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
         file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
         # this too should be unnecessary, but currently the
         # this too should be unnecessary, but currently the

+ 12 - 0
src/bin/xfrout/xfrout.spec.pre.in

@@ -37,6 +37,18 @@
     	 "item_type": "integer",
     	 "item_type": "integer",
          "item_optional": false,
          "item_optional": false,
     	 "item_default": 1048576
     	 "item_default": 1048576
+       },
+       {
+         "item_name": "tsig_key_ring",
+         "item_type": "list",
+         "item_optional": true,
+         "item_default": [],
+         "list_item_spec" :
+         {
+             "item_name": "tsig_key",
+             "item_type": "string",
+             "item_optional": true
+         }
        }
        }
       ],
       ],
       "commands": [
       "commands": [

+ 1 - 6
src/cppcheck-suppress.lst

@@ -4,12 +4,7 @@ debug
 missingInclude
 missingInclude
 // This is a template, and should be excluded from the check
 // This is a template, and should be excluded from the check
 unreadVariable:src/lib/dns/rdata/template.cc:60
 unreadVariable:src/lib/dns/rdata/template.cc:60
-// These three trigger warnings due to the incomplete implementation.  This is
-// our problem, but we need to suppress the warnings for now.
-functionConst:src/lib/cache/resolver_cache.h
-functionConst:src/lib/cache/message_cache.h
-functionConst:src/lib/cache/rrset_cache.h
 // Intentional self assignment tests.  Suppress warning about them.
 // Intentional self assignment tests.  Suppress warning about them.
 selfAssignment:src/lib/dns/tests/name_unittest.cc:293
 selfAssignment:src/lib/dns/tests/name_unittest.cc:293
 selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
 selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
-selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:125
+selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:137

+ 1 - 1
src/lib/Makefile.am

@@ -1,3 +1,3 @@
 SUBDIRS = exceptions util log cryptolink dns cc config python xfr \
 SUBDIRS = exceptions util log cryptolink dns cc config python xfr \
           bench asiolink asiodns nsas cache resolve testutils datasrc \
           bench asiolink asiodns nsas cache resolve testutils datasrc \
-          server_common
+          server_common acl

+ 6 - 0
src/lib/acl/Makefile.am

@@ -0,0 +1,6 @@
+SUBDIRS = tests
+
+EXTRA_DIST = check.h
+
+# TODO: Once we have some cc file we are able to compile, create the library.
+# For now, we have only header files, not creating empty library.

+ 195 - 0
src/lib/acl/check.h

@@ -0,0 +1,195 @@
+// Copyright (C) 2011  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 ACL_CHECK_H
+#define ACL_CHECK_H
+
+#include <vector>
+#include <typeinfo>
+#include <sstream>
+
+namespace isc {
+namespace acl {
+
+/**
+ * \brief ACL check base class.
+ *
+ * It is intended that all ACL checks are inherited (maybe indirectly) from
+ * this base class. This will allow us to define new types of checks without
+ * changing any of the code that is using it and with the correct
+ * implementation even without changing the thing that parses configuration
+ * and creates instances of the checks.
+ *
+ * It is implemented as a template. This allows easy reuse of the code for
+ * checking of different types of things (packets of different protocols, etc).
+ * We'll implement the loader and compound checks as templates as well (
+ * and just make sure they are instantiated for each type of thing we want
+ * to check). While most of concrete checks will be specific for one protocol
+ * (or whatever the entity we check is), it makes sense to implement some of
+ * these as templates as well (for example the IP address check, for whatever
+ * context that contains member called ip and has the right methods).
+ *
+ * The Context carries whatever information might be checked for that protocol
+ * (eg. the packet, information where it came from, to what port, ...).
+ */
+template<typename Context> class Check {
+protected:
+    /// \brief Constructor.
+    ///
+    /// Just to make sure this thing is not directly instantiated.
+    Check() { }
+public:
+    /**
+     * \brief The check itself.
+     *
+     * The actual check will be performed here. Every concrete child class
+     * will reimplement it and decide based on the context passed if it
+     * matches.
+     *
+     * The caller should expect this method can throw. The list of exceptions
+     * isn't restricted, as we don't know what kind of checks will be needed.
+     * An exception should be considered as it is impossible to check the
+     * condition. It should lead to either blackholing the packet or returning
+     * some 500-like error (ServFail).
+     *
+     * \param context The thing we are trying to match against this check.
+     * \return true if the context satisfies the check, false otherwise.
+     */
+    virtual bool matches(const Context& context) const = 0;
+
+    /**
+     * \brief Cost for unknown cost estimate.
+     *
+     * This indicates that the estimate for cost is not provided. This
+     * is arbitrary large value, meaning "somehow longish time". To be
+     * on the safe side, we guess more and be just happily suprirised
+     * if it turns out to run faster.
+     */
+    static const unsigned UNKNOWN_COST;
+
+    /**
+     * \brief The expected cost of single match.
+     *
+     * This is here to provide some kind of cost information to optimising
+     * routines. It is in units without any real size, just bigger number
+     * means the check takes longer time. It is expected to be linear scale.
+     * It doesn't need to be exact, but better accuracy might lead to better
+     * optimisations. As of writing this, no optimisations exist yet, but
+     * are expected to exist in future.
+     *
+     * The default is UNKNOWN_COST.
+     */
+    virtual unsigned cost() const {
+        return (UNKNOWN_COST);
+    }
+
+    /// \brief Virtual destructor, as we're virtual
+    virtual ~ Check() { }
+
+    /**
+     * \brief Conversion to text.
+     *
+     * This is meant for debugging purposes, it doesn't have to
+     * serialise the whole information stored in this Check.
+     *
+     * If the check is compound, it should not include the subexpressions
+     * (while we're able to build whatever treeish representation using
+     * CompoundCheck::subexpressions, we're not able to separate them
+     * automatically, as this may produce any kind of free-form string).
+     */
+    virtual std::string toText() const {
+        std::stringstream output;
+        output << typeid(*this).name() << "@" << this;
+        return (output.rdbuf()->str());
+    }
+};
+
+// This seems to be the propper way for static template members
+template<typename Context> const unsigned Check<Context>::UNKNOWN_COST = 10000;
+
+/**
+ * \brief Base class for compound checks.
+ *
+ * While some checks will be a match against some property of the information
+ * passed (eg. the sender's IP address must be in some range), others will
+ * combine results of more checks together to get their own. This is base class
+ * for the second type, allowing listing of the subexpressions (mostly for
+ * debugging purposes to print the whole tree of matches and possible future
+ * optimisations which would like to crawl the expression tree).
+ */
+template<typename Context> class CompoundCheck : public Check<Context> {
+public:
+    /// \brief Abbreviated name for list of subexpressions
+    typedef std::vector<const Check<Context>*> Checks;
+
+    /**
+     * \brief Get the list of subexpressions.
+     *
+     * The result contains pointers to the all subexpressions this check holds
+     * (and therefore might call during its own match() function).
+     *
+     * Using shared pointers looks an overkill here. All the checks must be
+     * alive for the whole life of this one and this check will hold their
+     * ownership. Therefore the only thing the caller needs to do is to make
+     * sure this check is not deleted while it's still using the ones from the
+     * result.
+     *
+     * This method must not throw except for the standard allocation exceptions
+     * to allocate the result.
+     */
+    virtual Checks getSubexpressions() const = 0;
+
+    /**
+     * \brief If the result depends only on results of subexpressions.
+     *
+     * Some optimisations might use the fact that a compound expression is
+     * a function of results of its subexpressions (subchecks) only. But
+     * some compound checks might want to look into the provided context in
+     * their match() as well as looking at the results of the subexpressions.
+     *
+     * This function informs the optimisation routines if it is safe to use
+     * these optimisations.
+     *
+     * \return true if the check depends only on results of subexpressions
+     *    only, false if it examines the context itself as well.
+     * \note The default implementation returns true, as it is expected to
+     *    be the case in majority of cases.
+     */
+    virtual bool pure() const { return (true); }
+
+    /**
+     * \brief Default compound cost function.
+     *
+     * It is simply sum of all subexpressions, as an expected upper bound
+     * on the cost. This expects that the combining itself is cheap relatively
+     * to the checks performed by the subexpressions. In most cases, this
+     * should be good enough, but it can be reimplemented in situations
+     * where most of the subexpressions will be avoided in usual situations.
+     * Replacing the default of 10000 from Check.
+     */
+    virtual unsigned cost() const {
+        Checks checks(getSubexpressions());
+        unsigned result(0);
+        for (typename Checks::const_iterator i(checks.begin());
+             i != checks.end(); ++ i) {
+            result += (*i)->cost();
+        }
+        return (result);
+    }
+};
+
+}
+}
+
+#endif

+ 15 - 0
src/lib/acl/tests/Makefile.am

@@ -0,0 +1,15 @@
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += check_test.cc
+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
+endif
+
+noinst_PROGRAMS = $(TESTS)

+ 70 - 0
src/lib/acl/tests/check_test.cc

@@ -0,0 +1,70 @@
+// Copyright (C) 2011  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 <gtest/gtest.h>
+#include <acl/check.h>
+
+using namespace isc::acl;
+
+namespace {
+
+// This test has two function. For one, it checks the default implementations
+// do what they should and it makes sure the template actually compiles
+// (as templates are syntax-checked upon instantiation).
+
+// This is a test check that just passes the boolean it gets.
+class Pass : public Check<bool> {
+public:
+    virtual bool matches(const bool& value) const { return (value); }
+};
+
+// This is a simple test compound check. It contains two Pass checks
+// and passes result of the first one.
+
+class First : public CompoundCheck<bool> {
+public:
+    // The internal checks are public, so we can check the addresses
+    Pass first, second;
+    virtual Checks getSubexpressions() const {
+        Checks result;
+        result.push_back(&first);
+        result.push_back(&second);
+        return (result);
+    }
+    virtual bool matches(const bool& value) const {
+        return (first.matches(value));
+    }
+};
+
+TEST(Check, defaultCheckValues) {
+    Pass p;
+    EXPECT_EQ(Check<bool>::UNKNOWN_COST, p.cost());
+    EXPECT_TRUE(p.matches(true));
+    EXPECT_FALSE(p.matches(false));
+    // The exact text is compiler dependant, but we check it returns something
+    // and can be compiled
+    EXPECT_FALSE(p.toText().empty());
+}
+
+TEST(Check, defaultCompoundValues) {
+    First f;
+    EXPECT_EQ(2 * Check<bool>::UNKNOWN_COST, f.cost());
+    EXPECT_TRUE(f.pure());
+    First::Checks c(f.getSubexpressions());
+    ASSERT_EQ(2, c.size());
+    EXPECT_EQ(&f.first, c[0]);
+    EXPECT_EQ(&f.second, c[1]);
+}
+
+}

+ 4 - 3
src/lib/util/io/tests/run_unittests.cc

@@ -13,10 +13,11 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
 
 
 int
 int
-main(int argc, char *argv[]) {
+main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::InitGoogleTest(&argc, argv);
-
+    return (isc::util::unittests::run_all());
-    return RUN_ALL_TESTS();
 }
 }
+

+ 3 - 10
src/lib/asiodns/io_fetch.cc

@@ -209,16 +209,6 @@ IOFetch::IOFetch(Protocol protocol, IOService& service,
     msg->setHeaderFlag(Message::HEADERFLAG_CD,
     msg->setHeaderFlag(Message::HEADERFLAG_CD,
                        query_message->getHeaderFlag(Message::HEADERFLAG_CD));
                        query_message->getHeaderFlag(Message::HEADERFLAG_CD));
 
 
-    ConstEDNSPtr edns(query_message->getEDNS());
-    const bool dnssec_ok = edns && edns->getDNSSECAwareness();
-    if (edns) {
-        EDNSPtr edns_response(new EDNS());
-        edns_response->setDNSSECAwareness(dnssec_ok);
-        // TODO: We should make our own edns bufsize length configurable
-        edns_response->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
-        msg->setEDNS(edns_response);
-    }
-
     initIOFetch(msg, protocol, service,
     initIOFetch(msg, protocol, service,
                 **(query_message->beginQuestion()),
                 **(query_message->beginQuestion()),
                 address, port, buff, cb, wait);
                 address, port, buff, cb, wait);
@@ -238,6 +228,9 @@ IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& servic
     query_msg->setRcode(Rcode::NOERROR());
     query_msg->setRcode(Rcode::NOERROR());
     query_msg->setHeaderFlag(Message::HEADERFLAG_RD);
     query_msg->setHeaderFlag(Message::HEADERFLAG_RD);
     query_msg->addQuestion(question);
     query_msg->addQuestion(question);
+    EDNSPtr edns_query(new EDNS());
+    edns_query->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
+    query_msg->setEDNS(edns_query);
     MessageRenderer renderer(*data_->msgbuf);
     MessageRenderer renderer(*data_->msgbuf);
     query_msg->toWire(renderer);
     query_msg->toWire(renderer);
 }
 }

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

@@ -25,15 +25,15 @@ run_unittests_SOURCES += io_fetch_unittest.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 
 
 run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD  = $(GTEST_LDADD)
-run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.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/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.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/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.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/asiodns/libasiodns.la
 
 
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) 
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 
 
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 # B10_CXXFLAGS)

+ 3 - 0
src/lib/asiodns/tests/io_fetch_unittest.cc

@@ -130,6 +130,9 @@ public:
         msg.setRcode(Rcode::NOERROR());
         msg.setRcode(Rcode::NOERROR());
         msg.setHeaderFlag(Message::HEADERFLAG_RD);
         msg.setHeaderFlag(Message::HEADERFLAG_RD);
         msg.addQuestion(question_);
         msg.addQuestion(question_);
+        EDNSPtr msg_edns(new EDNS());
+        msg_edns->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
+        msg.setEDNS(msg_edns);
         MessageRenderer renderer(*msgbuf_);
         MessageRenderer renderer(*msgbuf_);
         msg.toWire(renderer);
         msg.toWire(renderer);
         MessageRenderer renderer2(*expected_buffer_);
         MessageRenderer renderer2(*expected_buffer_);

+ 4 - 3
src/lib/asiodns/tests/run_unittests.cc

@@ -13,16 +13,17 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
 
 
-#include <log/root_logger_name.h>
+#include <log/logger_manager.h>
 #include <dns/tests/unittest_util.h>
 #include <dns/tests/unittest_util.h>
 
 
 int
 int
 main(int argc, char* argv[])
 main(int argc, char* argv[])
 {
 {
     ::testing::InitGoogleTest(&argc, argv);         // Initialize Google test
     ::testing::InitGoogleTest(&argc, argv);         // Initialize Google test
-    isc::log::setRootLoggerName("unittest");        // Set a root logger name
+    isc::log::LoggerManager::init("unittest");      // Set a root logger name
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);  // Add location of test data
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);  // Add location of test data
 
 
-    return (RUN_ALL_TESTS());
+    return (isc::util::unittests::run_all());
 }
 }

+ 39 - 24
src/lib/asiolink/interval_timer.cc

@@ -14,11 +14,9 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
-#include <unistd.h>             // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
-
 #include <boost/bind.hpp>
 #include <boost/bind.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/shared_ptr.hpp>
 
 
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
@@ -29,7 +27,16 @@
 namespace isc {
 namespace isc {
 namespace asiolink {
 namespace asiolink {
 
 
-class IntervalTimerImpl {
+/// This class holds a call back function of asynchronous operations.
+/// To ensure the object is alive while an asynchronous operation refers
+/// to it, we use shared_ptr and enable_shared_from_this.
+/// The object will be destructed in case IntervalTimer has been destructed
+/// and no asynchronous operation refers to it.
+/// Please follow the link to get an example:
+/// http://think-async.com/asio/asio-1.4.8/doc/asio/tutorial/tutdaytime3.html#asio.tutorial.tutdaytime3.the_tcp_connection_class
+class IntervalTimerImpl :
+    public boost::enable_shared_from_this<IntervalTimerImpl>
+{
 private:
 private:
     // prohibit copy
     // prohibit copy
     IntervalTimerImpl(const IntervalTimerImpl& source);
     IntervalTimerImpl(const IntervalTimerImpl& source);
@@ -53,14 +60,18 @@ private:
     long interval_;
     long interval_;
     // asio timer
     // asio timer
     asio::deadline_timer timer_;
     asio::deadline_timer timer_;
+    // interval_ will be set to this value in destructor in order to detect
+    // use-after-free type of bugs.
+    static const long INVALIDATED_INTERVAL = -1;
 };
 };
 
 
 IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
 IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
     interval_(0), timer_(io_service.get_io_service())
     interval_(0), timer_(io_service.get_io_service())
 {}
 {}
 
 
-IntervalTimerImpl::~IntervalTimerImpl()
+IntervalTimerImpl::~IntervalTimerImpl() {
-{}
+    interval_ = INVALIDATED_INTERVAL;
+}
 
 
 void
 void
 IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
 IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
@@ -81,42 +92,46 @@ IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
     // At this point the timer is not running yet and will not expire.
     // At this point the timer is not running yet and will not expire.
     // After calling IOService::run(), the timer will expire.
     // After calling IOService::run(), the timer will expire.
     update();
     update();
-    return;
 }
 }
 
 
 void
 void
 IntervalTimerImpl::update() {
 IntervalTimerImpl::update() {
-    if (interval_ == 0) {
-        // timer has been canceled.  Do nothing.
-        return;
-    }
     try {
     try {
         // Update expire time to (current time + interval_).
         // Update expire time to (current time + interval_).
         timer_.expires_from_now(boost::posix_time::millisec(interval_));
         timer_.expires_from_now(boost::posix_time::millisec(interval_));
+        // Reset timer.
+        // Pass a function bound with a shared_ptr to this.
+        timer_.async_wait(boost::bind(&IntervalTimerImpl::callback,
+                                      shared_from_this(),
+                                      asio::placeholders::error));
     } catch (const asio::system_error& e) {
     } catch (const asio::system_error& e) {
-        isc_throw(isc::Unexpected, "Failed to update timer");
+        isc_throw(isc::Unexpected, "Failed to update timer: " << e.what());
+    } catch (const boost::bad_weak_ptr&) {
+        // Can't happen. It means a severe internal bug.
+        assert(0);
     }
     }
-    // Reset timer.
-    timer_.async_wait(boost::bind(&IntervalTimerImpl::callback, this, _1));
 }
 }
 
 
 void
 void
-IntervalTimerImpl::callback(const asio::error_code& cancelled) {
+IntervalTimerImpl::callback(const asio::error_code& ec) {
-    // Do not call cbfunc_ in case the timer was cancelled.
+    assert(interval_ != INVALIDATED_INTERVAL);
-    // The timer will be canelled in the destructor of asio::deadline_timer.
+    if (interval_ == 0 || ec) {
-    if (!cancelled) {
+        // timer has been canceled. Do nothing.
-        cbfunc_();
+    } else {
         // Set next expire time.
         // Set next expire time.
         update();
         update();
+        // Invoke the call back function.
+        cbfunc_();
     }
     }
 }
 }
 
 
-IntervalTimer::IntervalTimer(IOService& io_service) {
+IntervalTimer::IntervalTimer(IOService& io_service) :
-    impl_ = new IntervalTimerImpl(io_service);
+    impl_(new IntervalTimerImpl(io_service))
-}
+{}
 
 
 IntervalTimer::~IntervalTimer() {
 IntervalTimer::~IntervalTimer() {
-    delete impl_;
+    // Cancel the timer to make sure cbfunc_() will not be called any more.
+    cancel();
 }
 }
 
 
 void
 void

+ 4 - 6
src/lib/asiolink/interval_timer.h

@@ -16,6 +16,7 @@
 #define __ASIOLINK_INTERVAL_TIMER_H 1
 #define __ASIOLINK_INTERVAL_TIMER_H 1
 
 
 #include <boost/function.hpp>
 #include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
 
 
 #include <asiolink/io_service.h>
 #include <asiolink/io_service.h>
 
 
@@ -42,9 +43,6 @@ class IntervalTimerImpl;
 /// The call back function will not be called if the instance of this class is
 /// The call back function will not be called if the instance of this class is
 /// destroyed before the timer is expired.
 /// destroyed before the timer is expired.
 ///
 ///
-/// Note: Destruction of an instance of this class while call back is pending
-/// causes throwing an exception from \c IOService.
-///
 /// Sample code:
 /// Sample code:
 /// \code
 /// \code
 ///  void function_to_call_back() {
 ///  void function_to_call_back() {
@@ -100,12 +98,12 @@ public:
     /// \param interval Interval in milliseconds (greater than 0)
     /// \param interval Interval in milliseconds (greater than 0)
     ///
     ///
     /// Note: IntervalTimer will not pass \c asio::error_code to
     /// Note: IntervalTimer will not pass \c asio::error_code to
-    /// call back function. In case the timer is cancelled, the function
+    /// call back function. In case the timer is canceled, the function
     /// will not be called.
     /// will not be called.
     ///
     ///
     /// \throw isc::InvalidParameter cbfunc is empty
     /// \throw isc::InvalidParameter cbfunc is empty
     /// \throw isc::BadValue interval is less than or equal to 0
     /// \throw isc::BadValue interval is less than or equal to 0
-    /// \throw isc::Unexpected ASIO library error
+    /// \throw isc::Unexpected internal runtime error
     void setup(const Callback& cbfunc, const long interval);
     void setup(const Callback& cbfunc, const long interval);
 
 
     /// Cancel the timer.
     /// Cancel the timer.
@@ -127,7 +125,7 @@ public:
     long getInterval() const;
     long getInterval() const;
 
 
 private:
 private:
-    IntervalTimerImpl* impl_;
+    boost::shared_ptr<IntervalTimerImpl> impl_;
 };
 };
 
 
 } // namespace asiolink
 } // namespace asiolink

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

@@ -34,13 +34,12 @@ run_unittests_SOURCES += udp_socket_unittest.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 
 
 run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD  = $(GTEST_LDADD)
-run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.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/libexceptions.la
 
 
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) 
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 
 
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 # B10_CXXFLAGS)

+ 4 - 6
src/lib/asiolink/tests/run_unittests.cc

@@ -13,15 +13,13 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
-
+#include <util/unittests/run_all.h>
-#include <log/root_logger_name.h>
+#include <log/logger_manager.h>
-#include <dns/tests/unittest_util.h>
 
 
 int
 int
 main(int argc, char* argv[])
 main(int argc, char* argv[])
 {
 {
     ::testing::InitGoogleTest(&argc, argv);         // Initialize Google test
     ::testing::InitGoogleTest(&argc, argv);         // Initialize Google test
-    isc::log::setRootLoggerName("unittest");        // Set a root logger name
+    isc::log::LoggerManager::init("unittest");      // Set a root logger name
-
+    return (isc::util::unittests::run_all());
-    return (RUN_ALL_TESTS());
 }
 }

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

@@ -14,10 +14,10 @@ run_unittests_SOURCES += loadquery_unittest.cc
 
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+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/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/bench/libbench.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(GTEST_LDADD)
 run_unittests_LDADD += $(GTEST_LDADD)
 endif
 endif
 
 

+ 2 - 1
src/lib/bench/tests/run_unittests.cc

@@ -13,10 +13,11 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
 
 
 int
 int
 main(int argc, char* argv[]) {
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::InitGoogleTest(&argc, argv);
 
 
-    return (RUN_ALL_TESTS());
+    return (isc::util::unittests::run_all());
 }
 }

+ 3 - 2
src/lib/cache/TODO

@@ -12,7 +12,8 @@
 * When the rrset beging updated is an NS rrset, NSAS should be updated
 * When the rrset beging updated is an NS rrset, NSAS should be updated
   together.
   together.
 * Share the NXDOMAIN info between different type queries. current implementation
 * Share the NXDOMAIN info between different type queries. current implementation
-  can only cache for the type that user quired, for example, if user query A 
+  can only cache for the type that user queried, for example, if user query A
   record of a.example. and the server replied with NXDOMAIN, this should be
   record of a.example. and the server replied with NXDOMAIN, this should be
   cached for all the types queries of a.example.
   cached for all the types queries of a.example.
-
+* Add the interfaces for resizing and serialization (loading and dumping) to
+  cache.

+ 0 - 18
src/lib/cache/message_cache.cc

@@ -97,24 +97,6 @@ MessageCache::update(const Message& msg) {
     return (message_table_.add(msg_entry, entry_key, true));
     return (message_table_.add(msg_entry, entry_key, true));
 }
 }
 
 
-#if 0
-void
-MessageCache::dump(const std::string&) {
-    //TODO
-}
-
-void
-MessageCache::load(const std::string&) {
-    //TODO
-}
-
-bool
-MessageCache::resize(uint32_t) {
-    //TODO
-    return (true);
-}
-#endif
-
 } // namespace cache
 } // namespace cache
 } // namespace isc
 } // namespace isc
 
 

+ 2 - 14
src/lib/cache/message_cache.h

@@ -30,6 +30,8 @@ namespace cache {
 /// The object of MessageCache represents the cache for class-specific
 /// The object of MessageCache represents the cache for class-specific
 /// messages.
 /// messages.
 ///
 ///
+/// \todo The message cache class should provide the interfaces for
+///       loading, dumping and resizing.
 class MessageCache {
 class MessageCache {
 // Noncopyable
 // Noncopyable
 private:
 private:
@@ -64,20 +66,6 @@ public:
     /// If the message doesn't exist in the cache, it will be added
     /// If the message doesn't exist in the cache, it will be added
     /// directly.
     /// directly.
     bool update(const isc::dns::Message& msg);
     bool update(const isc::dns::Message& msg);
-
-#if 0
-    /// \brief Dump the message cache to specified file.
-    /// \todo It should can be dumped to one configured database.
-    void dump(const std::string& file_name);
-
-    /// \brief Load the cache from one file.
-    /// \todo It should can be loaded from one configured database.
-    void load(const std::string& file_name);
-
-    /// \brief Resize the size of message cache in runtime.
-    bool resize(uint32_t size);
-#endif
-
 protected:
 protected:
     /// \brief Get the hash key for the message entry in the cache.
     /// \brief Get the hash key for the message entry in the cache.
     /// \param name query name of the message.
     /// \param name query name of the message.

+ 0 - 10
src/lib/cache/resolver_cache.cc

@@ -227,16 +227,6 @@ ResolverCache::update(const isc::dns::ConstRRsetPtr& rrset_ptr) {
     }
     }
 }
 }
 
 
-void
-ResolverCache::dump(const std::string&) {
-    //TODO
-}
-
-void
-ResolverCache::load(const std::string&) {
-    //TODO
-}
-
 ResolverClassCache*
 ResolverClassCache*
 ResolverCache::getClassCache(const isc::dns::RRClass& cache_class) const {
 ResolverCache::getClassCache(const isc::dns::RRClass& cache_class) const {
     for (int i = 0; i < class_caches_.size(); ++i) {
     for (int i = 0; i < class_caches_.size(); ++i) {

+ 3 - 17
src/lib/cache/resolver_cache.h

@@ -76,6 +76,9 @@ public:
 ///
 ///
 /// \note Public interaction with the cache should be through ResolverCache,
 /// \note Public interaction with the cache should be through ResolverCache,
 /// not directly with this one. (TODO: make this private/hidden/local to the .cc?)
 /// not directly with this one. (TODO: make this private/hidden/local to the .cc?)
+///
+/// \todo The resolver cache class should provide the interfaces for
+///       loading, dumping and resizing.
 class ResolverClassCache {
 class ResolverClassCache {
 public:
 public:
     /// \brief Default Constructor.
     /// \brief Default Constructor.
@@ -300,23 +303,6 @@ public:
     ///
     ///
     bool update(const isc::dns::ConstRRsetPtr& rrset_ptr);
     bool update(const isc::dns::ConstRRsetPtr& rrset_ptr);
 
 
-    /// \name Cache Serialization
-    //@{
-    /// \brief Dump the cache content to one file.
-    ///
-    /// \param file_name file to write to
-    ///
-    /// \todo It should can be dumped to one configured database.
-    void dump(const std::string& file_name);
-
-    /// \brief Load the cache from one file.
-    ///
-    /// \param file to load from
-    ///
-    /// \todo It should can be loaded from one configured database.
-    void load(const std::string& file_name);
-    //@}
-
 private:
 private:
     /// \brief Returns the class-specific subcache
     /// \brief Returns the class-specific subcache
     ///
     ///

+ 0 - 18
src/lib/cache/rrset_cache.cc

@@ -79,24 +79,6 @@ RRsetCache::update(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) {
     return (entry_ptr);
     return (entry_ptr);
 }
 }
 
 
-#if 0
-void
-RRsetCache::dump(const std::string&) {
-    //TODO
-}
-
-void
-RRsetCache::load(const std::string&) {
-    //TODO
-}
-
-bool
-RRsetCache::resize(uint32_t) {
-    //TODO
-    return (true);
-}
-#endif
-
 } // namespace cache
 } // namespace cache
 } // namespace isc
 } // namespace isc
 
 

+ 3 - 22
src/lib/cache/rrset_cache.h

@@ -30,6 +30,9 @@ class RRsetEntry;
 /// \brief RRset Cache
 /// \brief RRset Cache
 /// The object of RRsetCache represented the cache for class-specific
 /// The object of RRsetCache represented the cache for class-specific
 /// RRsets.
 /// RRsets.
+///
+/// \todo The rrset cache class should provide the interfaces for
+///       loading, dumping and resizing.
 class RRsetCache{
 class RRsetCache{
     ///
     ///
     /// \name Constructors and Destructor
     /// \name Constructors and Destructor
@@ -73,28 +76,6 @@ public:
     RRsetEntryPtr update(const isc::dns::RRset& rrset,
     RRsetEntryPtr update(const isc::dns::RRset& rrset,
                          const RRsetTrustLevel& level);
                          const RRsetTrustLevel& level);
 
 
-#if 0
-    /// \brief Dump the rrset cache to specified file.
-    ///
-    /// \param file_name The file to write to
-    ///
-    /// \todo It should can be dumped to one configured database.
-    void dump(const std::string& file_name);
-
-    /// \brief Load the cache from one file.
-    ///
-    /// \param file_name The file to read from
-    ///
-    /// \todo It should can be loaded from one configured database.
-    void load(const std::string& file_name);
-
-    /// \brief Resize the size of rrset cache in runtime.
-    ///
-    /// \param The size to resize to
-    /// \return true
-    bool resize(uint32_t size);
-#endif
-
     /// \short Protected memebers, so they can be accessed by tests.
     /// \short Protected memebers, so they can be accessed by tests.
 protected:
 protected:
     uint16_t class_; // The class of the rrset cache.
     uint16_t class_; // The class of the rrset cache.

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

@@ -32,20 +32,20 @@ TESTS =
 if HAVE_GTEST
 if HAVE_GTEST
 TESTS += run_unittests
 TESTS += run_unittests
 run_unittests_SOURCES  = run_unittests.cc
 run_unittests_SOURCES  = run_unittests.cc
-run_unittests_SOURCES  += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
+run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
-run_unittests_SOURCES  += rrset_entry_unittest.cc
+run_unittests_SOURCES += rrset_entry_unittest.cc
-run_unittests_SOURCES  += rrset_cache_unittest.cc
+run_unittests_SOURCES += rrset_cache_unittest.cc
-run_unittests_SOURCES  += message_cache_unittest.cc
+run_unittests_SOURCES += message_cache_unittest.cc
-run_unittests_SOURCES  += message_entry_unittest.cc
+run_unittests_SOURCES += message_entry_unittest.cc
-run_unittests_SOURCES  += local_zone_data_unittest.cc
+run_unittests_SOURCES += local_zone_data_unittest.cc
-run_unittests_SOURCES  += resolver_cache_unittest.cc
+run_unittests_SOURCES += resolver_cache_unittest.cc
-run_unittests_SOURCES  += negative_cache_unittest.cc
+run_unittests_SOURCES += negative_cache_unittest.cc
-run_unittests_SOURCES  += cache_test_messagefromfile.h
+run_unittests_SOURCES += cache_test_messagefromfile.h
-run_unittests_SOURCES  += cache_test_sectioncount.h
+run_unittests_SOURCES += cache_test_sectioncount.h
 
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS  = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD    = $(GTEST_LDADD)
 
 
 # NOTE: we may have to clean up this hack later (see the note in configure.ac)
 # NOTE: we may have to clean up this hack later (see the note in configure.ac)
 if NEED_LIBBOOST_THREAD
 if NEED_LIBBOOST_THREAD
@@ -55,14 +55,14 @@ endif
 run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.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/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.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/asiolink/libasiolink.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/libexceptions.la
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 noinst_PROGRAMS = $(TESTS)
 
 
-EXTRA_DIST = testdata/message_cname_referral.wire
+EXTRA_DIST  = testdata/message_cname_referral.wire
 EXTRA_DIST += testdata/message_example_com_soa.wire
 EXTRA_DIST += testdata/message_example_com_soa.wire
 EXTRA_DIST += testdata/message_fromWire1
 EXTRA_DIST += testdata/message_fromWire1
 EXTRA_DIST += testdata/message_fromWire2
 EXTRA_DIST += testdata/message_fromWire2

+ 2 - 1
src/lib/cache/tests/run_unittests.cc

@@ -15,6 +15,7 @@
 #include <config.h>
 #include <config.h>
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
 
 
 #include <dns/tests/unittest_util.h>
 #include <dns/tests/unittest_util.h>
 
 
@@ -24,5 +25,5 @@ main(int argc, char* argv[]) {
     isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
 
 
-    return (RUN_ALL_TESTS());
+    return (isc::util::unittests::run_all());
 }
 }

+ 1 - 0
src/lib/cc/tests/Makefile.am

@@ -26,6 +26,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 
 
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD +=  $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/cc/libcc.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/libexceptions.la
 
 
 endif
 endif

+ 2 - 1
src/lib/cc/tests/run_unittests.cc

@@ -13,9 +13,10 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
 
 
 int
 int
 main(int argc, char* argv[]) {
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::InitGoogleTest(&argc, argv);
-    return (RUN_ALL_TESTS());
+    return (isc::util::unittests::run_all());
 }
 }

+ 182 - 31
src/lib/config/ccsession.cc

@@ -35,6 +35,10 @@
 #include <config/config_log.h>
 #include <config/config_log.h>
 #include <config/ccsession.h>
 #include <config/ccsession.h>
 
 
+#include <log/logger_support.h>
+#include <log/logger_specification.h>
+#include <log/logger_manager.h>
+
 using namespace std;
 using namespace std;
 
 
 using isc::data::Element;
 using isc::data::Element;
@@ -151,6 +155,115 @@ parseCommand(ConstElementPtr& arg, ConstElementPtr command) {
     }
     }
 }
 }
 
 
+namespace {
+// Temporary workaround functions for missing functionality in
+// getValue() (main problem described in ticket #993)
+// This returns either the value set for the given relative id,
+// or its default value
+// (intentially defined here so this interface does not get
+// included in ConfigData as it is)
+ConstElementPtr getValueOrDefault(ConstElementPtr config_part,
+                                  const std::string& relative_id,
+                                  const ConfigData& config_data,
+                                  const std::string& full_id) {
+    if (config_part->contains(relative_id)) {
+        return config_part->get(relative_id);
+    } else {
+        return config_data.getDefaultValue(full_id);
+    }
+}
+
+// Reads a output_option subelement of a logger configuration,
+// and sets the values thereing to the given OutputOption struct,
+// or defaults values if they are not provided (from config_data).
+void
+readOutputOptionConf(isc::log::OutputOption& output_option,
+                     ConstElementPtr output_option_el,
+                     const ConfigData& config_data)
+{
+    ConstElementPtr destination_el = getValueOrDefault(output_option_el,
+                                    "destination", config_data,
+                                    "loggers/output_options/destination");
+    output_option.destination = isc::log::getDestination(destination_el->stringValue());
+    ConstElementPtr output_el = getValueOrDefault(output_option_el,
+                                    "output", config_data,
+                                    "loggers/output_options/output");
+    if (output_option.destination == isc::log::OutputOption::DEST_CONSOLE) {
+        output_option.stream = isc::log::getStream(output_el->stringValue());
+    } else if (output_option.destination == isc::log::OutputOption::DEST_FILE) {
+        output_option.filename = output_el->stringValue();
+    } else if (output_option.destination == isc::log::OutputOption::DEST_SYSLOG) {
+        output_option.facility = output_el->stringValue();
+    }
+    output_option.flush = getValueOrDefault(output_option_el,
+                              "flush", config_data,
+                              "loggers/output_options/flush")->boolValue();
+    output_option.maxsize = getValueOrDefault(output_option_el,
+                                "maxsize", config_data,
+                                "loggers/output_options/maxsize")->intValue();
+    output_option.maxver = getValueOrDefault(output_option_el,
+                               "maxver", config_data,
+                               "loggers/output_options/maxver")->intValue();
+}
+
+// Reads a full 'loggers' configuration, and adds the loggers therein
+// to the given vector, fills in blanks with defaults from config_data
+void
+readLoggersConf(std::vector<isc::log::LoggerSpecification>& specs,
+                ConstElementPtr logger,
+                const ConfigData& config_data)
+{
+    const std::string lname = logger->get("name")->stringValue();
+    ConstElementPtr severity_el = getValueOrDefault(logger,
+                                      "severity", config_data,
+                                      "loggers/severity");
+    isc::log::Severity severity = isc::log::getSeverity(
+                                      severity_el->stringValue());
+    int dbg_level = getValueOrDefault(logger, "debuglevel",
+                                      config_data,
+                                      "loggers/debuglevel")->intValue();
+    bool additive = getValueOrDefault(logger, "additive", config_data,
+                                      "loggers/additive")->boolValue();
+
+    isc::log::LoggerSpecification logger_spec(
+        lname, severity, dbg_level, additive
+    );
+
+    if (logger->contains("output_options")) {
+        BOOST_FOREACH(ConstElementPtr output_option_el,
+                      logger->get("output_options")->listValue()) {
+            // create outputoptions
+            isc::log::OutputOption output_option;
+            readOutputOptionConf(output_option,
+                                 output_option_el,
+                                 config_data);
+            logger_spec.addOutputOption(output_option);
+        }
+    }
+
+    specs.push_back(logger_spec);
+}
+
+} // end anonymous namespace
+
+void
+my_logconfig_handler(const std::string&n, ConstElementPtr new_config, const ConfigData& config_data) {
+    config_data.getModuleSpec().validateConfig(new_config, true);
+
+    std::vector<isc::log::LoggerSpecification> specs;
+
+    if (new_config->contains("loggers")) {
+        BOOST_FOREACH(ConstElementPtr logger,
+                      new_config->get("loggers")->listValue()) {
+            readLoggersConf(specs, logger, config_data);
+        }
+    }
+
+    isc::log::LoggerManager logger_manager;
+    logger_manager.process(specs.begin(), specs.end());
+}
+
+
 ModuleSpec
 ModuleSpec
 ModuleCCSession::readModuleSpecification(const std::string& filename) {
 ModuleCCSession::readModuleSpecification(const std::string& filename) {
     std::ifstream file;
     std::ifstream file;
@@ -192,8 +305,11 @@ ModuleCCSession::ModuleCCSession(
     isc::data::ConstElementPtr(*config_handler)(
     isc::data::ConstElementPtr(*config_handler)(
         isc::data::ConstElementPtr new_config),
         isc::data::ConstElementPtr new_config),
     isc::data::ConstElementPtr(*command_handler)(
     isc::data::ConstElementPtr(*command_handler)(
-        const std::string& command, isc::data::ConstElementPtr args)
+        const std::string& command, isc::data::ConstElementPtr args),
+    bool start_immediately,
+    bool handle_logging
     ) :
     ) :
+    started_(false),
     session_(session)
     session_(session)
 {
 {
     module_specification_ = readModuleSpecification(spec_file_name);
     module_specification_ = readModuleSpecification(spec_file_name);
@@ -205,10 +321,8 @@ ModuleCCSession::ModuleCCSession(
 
 
     session_.establish(NULL);
     session_.establish(NULL);
     session_.subscribe(module_name_, "*");
     session_.subscribe(module_name_, "*");
-    //session_.subscribe("Boss", "*");
-    //session_.subscribe("statistics", "*");
-    // send the data specification
 
 
+    // send the data specification
     ConstElementPtr spec_msg = createCommand("module_spec",
     ConstElementPtr spec_msg = createCommand("module_spec",
                                              module_specification_.getFullSpec());
                                              module_specification_.getFullSpec());
     unsigned int seq = session_.group_sendmsg(spec_msg, "ConfigManager");
     unsigned int seq = session_.group_sendmsg(spec_msg, "ConfigManager");
@@ -237,8 +351,27 @@ ModuleCCSession::ModuleCCSession(
         }
         }
     }
     }
 
 
+    // Keep track of logging settings automatically
+    if (handle_logging) {
+        addRemoteConfig("Logging", my_logconfig_handler, false);
+    }
+
+    if (start_immediately) {
+        start();
+    }
+
+}
+
+void
+ModuleCCSession::start() {
+    if (started_) {
+        isc_throw(CCSessionError, "Module CC session already started");
+    }
+
     // register callback for asynchronous read
     // register callback for asynchronous read
     session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
     session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
+
+    started_ = true;
 }
 }
 
 
 /// Validates the new config values, if they are correct,
 /// Validates the new config values, if they are correct,
@@ -346,6 +479,11 @@ ModuleCCSession::checkCommand() {
             }
             }
         } catch (const CCSessionError& re) {
         } catch (const CCSessionError& re) {
             LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG).arg(re.what());
             LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG).arg(re.what());
+        } catch (const std::exception& stde) {
+            // No matter what unexpected error happens, we do not want
+            // to crash because of an incoming event, so we log the
+            // exception and continue to run
+            LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG_INTERNAL).arg(stde.what());
         }
         }
         if (!isNull(answer)) {
         if (!isNull(answer)) {
             session_.reply(routing, answer);
             session_.reply(routing, answer);
@@ -355,45 +493,55 @@ ModuleCCSession::checkCommand() {
     return (0);
     return (0);
 }
 }
 
 
-std::string
+ModuleSpec
-ModuleCCSession::addRemoteConfig(const std::string& spec_name,
+ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) {
-                                 void (*handler)(const std::string& module,
+    if (is_filename) {
-                                          ConstElementPtr),
+        // It is a filename, simply load it.
-                                 bool spec_is_filename)
+        return (readModuleSpecification(module));
-{
-    std::string module_name;
-    ModuleSpec rmod_spec;
-    if (spec_is_filename) {
-        // It's a file name, so load it
-        rmod_spec = readModuleSpecification(spec_name);
-        module_name =
-            rmod_spec.getFullSpec()->get("module_name")->stringValue();
     } else {
     } else {
         // It's module name, request it from config manager
         // It's module name, request it from config manager
-        ConstElementPtr cmd = Element::fromJSON("{ \"command\": ["
+
-                                                "\"get_module_spec\","
+        // Send the command
-                                                "{\"module_name\": \"" +
+        ConstElementPtr cmd(createCommand("get_module_spec",
-                                                module_name + "\"} ] }");
+                            Element::fromJSON("{\"module_name\": \"" + module +
+                                              "\"}")));
         unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
         unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
         ConstElementPtr env, answer;
         ConstElementPtr env, answer;
         session_.group_recvmsg(env, answer, false, seq);
         session_.group_recvmsg(env, answer, false, seq);
         int rcode;
         int rcode;
         ConstElementPtr spec_data = parseAnswer(rcode, answer);
         ConstElementPtr spec_data = parseAnswer(rcode, answer);
         if (rcode == 0 && spec_data) {
         if (rcode == 0 && spec_data) {
-            rmod_spec = ModuleSpec(spec_data);
+            // received OK, construct the spec out of it
-            module_name = spec_name;
+            ModuleSpec spec = ModuleSpec(spec_data);
-            if (module_name != rmod_spec.getModuleName()) {
+            if (module != spec.getModuleName()) {
+                // It's a different module!
                 isc_throw(CCSessionError, "Module name mismatch");
                 isc_throw(CCSessionError, "Module name mismatch");
             }
             }
+            return (spec);
         } else {
         } else {
-            isc_throw(CCSessionError, "Error getting config for " + module_name + ": " + answer->str());
+            isc_throw(CCSessionError, "Error getting config for " +
+                      module + ": " + answer->str());
         }
         }
     }
     }
-    ConfigData rmod_config = ConfigData(rmod_spec);
+}
 
 
-    // Get the current configuration values for that module
+std::string
-    ConstElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
+ModuleCCSession::addRemoteConfig(const std::string& spec_name,
-    unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
+                                 void (*handler)(const std::string& module,
+                                                 ConstElementPtr,
+                                                 const ConfigData&),
+                                 bool spec_is_filename)
+{
+    // First get the module name, specification and default config
+    const ModuleSpec rmod_spec(fetchRemoteSpec(spec_name, spec_is_filename));
+    const std::string module_name(rmod_spec.getModuleName());
+    ConfigData rmod_config(rmod_spec);
+
+    // Get the current configuration values from config manager
+    ConstElementPtr cmd(createCommand("get_config",
+                        Element::fromJSON("{\"module_name\": \"" +
+                                          module_name + "\"}")));
+    const unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
 
 
     ConstElementPtr env, answer;
     ConstElementPtr env, answer;
     session_.group_recvmsg(env, answer, false, seq);
     session_.group_recvmsg(env, answer, false, seq);
@@ -401,6 +549,7 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_name,
     ConstElementPtr new_config = parseAnswer(rcode, answer);
     ConstElementPtr new_config = parseAnswer(rcode, answer);
     ElementPtr local_config;
     ElementPtr local_config;
     if (rcode == 0 && new_config) {
     if (rcode == 0 && new_config) {
+        // Merge the received config into existing local config
         local_config = rmod_config.getLocalConfig();
         local_config = rmod_config.getLocalConfig();
         isc::data::merge(local_config, new_config);
         isc::data::merge(local_config, new_config);
         rmod_config.setLocalConfig(local_config);
         rmod_config.setLocalConfig(local_config);
@@ -412,8 +561,10 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_name,
     remote_module_configs_[module_name] = rmod_config;
     remote_module_configs_[module_name] = rmod_config;
     if (handler) {
     if (handler) {
         remote_module_handlers_[module_name] = handler;
         remote_module_handlers_[module_name] = handler;
-        handler(module_name, local_config);
+        handler(module_name, local_config, rmod_config);
     }
     }
+
+    // Make sure we get updates in future
     session_.subscribe(module_name);
     session_.subscribe(module_name);
     return (module_name);
     return (module_name);
 }
 }
@@ -458,7 +609,7 @@ ModuleCCSession::updateRemoteConfig(const std::string& module_name,
         std::map<std::string, RemoteHandler>::iterator hit =
         std::map<std::string, RemoteHandler>::iterator hit =
             remote_module_handlers_.find(module_name);
             remote_module_handlers_.find(module_name);
         if (hit != remote_module_handlers_.end()) {
         if (hit != remote_module_handlers_.end()) {
-            hit->second(module_name, new_config);
+            hit->second(module_name, new_config, it->second);
         }
         }
     }
     }
 }
 }

+ 35 - 5
src/lib/config/ccsession.h

@@ -161,6 +161,7 @@ public:
      * configuration of the local module needs to be updated.
      * configuration of the local module needs to be updated.
      * This must refer to a valid object of a concrete derived class of
      * This must refer to a valid object of a concrete derived class of
      * AbstractSession without establishing the session.
      * AbstractSession without establishing the session.
+     *
      * Note: the design decision on who is responsible for establishing the
      * Note: the design decision on who is responsible for establishing the
      * session is in flux, and may change in near future.
      * session is in flux, and may change in near future.
      *
      *
@@ -171,6 +172,14 @@ public:
      * @param command_handler A callback function pointer to be called when
      * @param command_handler A callback function pointer to be called when
      * a control command from a remote agent needs to be performed on the
      * a control command from a remote agent needs to be performed on the
      * local module.
      * local module.
+     * @param start_immediately If true (default), start listening to new commands
+     * and configuration changes asynchronously at the end of the constructor;
+     * if false, it will be delayed until the start() method is explicitly
+     * called. (This is a short term workaround for an initialization trouble.
+     * We'll need to develop a cleaner solution, and then remove this knob)
+     * @param handle_logging If true, the ModuleCCSession will automatically
+     * take care of logging configuration through the virtual Logging config
+     * module.
      */
      */
     ModuleCCSession(const std::string& spec_file_name,
     ModuleCCSession(const std::string& spec_file_name,
                     isc::cc::AbstractSession& session,
                     isc::cc::AbstractSession& session,
@@ -178,9 +187,21 @@ public:
                         isc::data::ConstElementPtr new_config) = NULL,
                         isc::data::ConstElementPtr new_config) = NULL,
                     isc::data::ConstElementPtr(*command_handler)(
                     isc::data::ConstElementPtr(*command_handler)(
                         const std::string& command,
                         const std::string& command,
-                        isc::data::ConstElementPtr args) = NULL
+                        isc::data::ConstElementPtr args) = NULL,
+                    bool start_immediately = true,
+                    bool handle_logging = false
                     );
                     );
 
 
+    /// Start receiving new commands and configuration changes asynchronously.
+    ///
+    /// This method must be called only once, and only when the ModuleCCSession
+    /// was constructed with start_immediately being false.  Otherwise
+    /// CCSessionError will be thrown.
+    ///
+    /// As noted in the constructor, this method should be considered a short
+    /// term workaround and will be removed in future.
+    void start();
+
     /**
     /**
      * Optional optimization for checkCommand loop; returns true
      * Optional optimization for checkCommand loop; returns true
      * if there are unhandled queued messages in the cc session.
      * if there are unhandled queued messages in the cc session.
@@ -240,12 +261,16 @@ public:
      * for those changes. This function will subscribe to the relevant module
      * for those changes. This function will subscribe to the relevant module
      * channel.
      * channel.
      *
      *
+     * This method must be called before calling the \c start() method on the
+     * ModuleCCSession (it also implies the ModuleCCSession must have been
+     * constructed with start_immediately being false).
+     *
      * \param spec_name This specifies the module to add. It is either a
      * \param spec_name This specifies the module to add. It is either a
      *                  filename of the spec file to use or a name of module
      *                  filename of the spec file to use or a name of module
      *                  (in case it's a module name, the spec data is
      *                  (in case it's a module name, the spec data is
      *                  downloaded from the configuration manager, therefore
      *                  downloaded from the configuration manager, therefore
      *                  the configuration manager must know it). If
      *                  the configuration manager must know it). If
-     *                  spec_is_filenabe is true (the default), then a
+     *                  spec_is_filename is true (the default), then a
      *                  filename is assumed, otherwise a module name.
      *                  filename is assumed, otherwise a module name.
      * \param handler The handler function called whenever there's a change.
      * \param handler The handler function called whenever there's a change.
      *                Called once initally from this function. May be NULL
      *                Called once initally from this function. May be NULL
@@ -263,7 +288,8 @@ public:
     std::string addRemoteConfig(const std::string& spec_name,
     std::string addRemoteConfig(const std::string& spec_name,
                                 void (*handler)(const std::string& module_name,
                                 void (*handler)(const std::string& module_name,
                                                 isc::data::ConstElementPtr
                                                 isc::data::ConstElementPtr
-                                                update) = NULL,
+                                                update,
+                                                const ConfigData& config_data) = NULL,
                                 bool spec_is_filename = true);
                                 bool spec_is_filename = true);
 
 
     /**
     /**
@@ -293,7 +319,8 @@ public:
 private:
 private:
     ModuleSpec readModuleSpecification(const std::string& filename);
     ModuleSpec readModuleSpecification(const std::string& filename);
     void startCheck();
     void startCheck();
-    
+
+    bool started_;
     std::string module_name_;
     std::string module_name_;
     isc::cc::AbstractSession& session_;
     isc::cc::AbstractSession& session_;
     ModuleSpec module_specification_;
     ModuleSpec module_specification_;
@@ -316,12 +343,15 @@ private:
         isc::data::ConstElementPtr args);
         isc::data::ConstElementPtr args);
 
 
     typedef void (*RemoteHandler)(const std::string&,
     typedef void (*RemoteHandler)(const std::string&,
-                                  isc::data::ConstElementPtr);
+                                  isc::data::ConstElementPtr,
+                                  const ConfigData&);
     std::map<std::string, ConfigData> remote_module_configs_;
     std::map<std::string, ConfigData> remote_module_configs_;
     std::map<std::string, RemoteHandler> remote_module_handlers_;
     std::map<std::string, RemoteHandler> remote_module_handlers_;
 
 
     void updateRemoteConfig(const std::string& module_name,
     void updateRemoteConfig(const std::string& module_name,
                             isc::data::ConstElementPtr new_config);
                             isc::data::ConstElementPtr new_config);
+
+    ModuleSpec fetchRemoteSpec(const std::string& module, bool is_filename);
 };
 };
 
 
 }
 }

+ 94 - 42
src/lib/config/config_data.cc

@@ -21,6 +21,63 @@
 
 
 using namespace isc::data;
 using namespace isc::data;
 
 
+namespace {
+
+// Returns the '_spec' part of a list or map specification (recursively,
+// i.e. if it is a list of lists or maps, will return the spec of the
+// inner-most list or map).
+//
+// \param spec_part the list or map specification (part)
+// \return the value of spec_part's "list_item_spec" or "map_item_spec",
+//         or the original spec_part, if it is not a MapElement or does
+//         not contain "list_item_spec" or "map_item_spec"
+ConstElementPtr findListOrMapSubSpec(ConstElementPtr spec_part) {
+    while (spec_part->getType() == Element::map &&
+           (spec_part->contains("list_item_spec") ||
+            spec_part->contains("map_item_spec"))) {
+        if (spec_part->contains("list_item_spec")) {
+            spec_part = spec_part->get("list_item_spec");
+        } else {
+            spec_part = spec_part->get("map_item_spec");
+        }
+    }
+    return spec_part;
+}
+
+// Returns a specific Element in a given specification ListElement
+//
+// \exception DataNotFoundError if the given identifier does not
+// point to an existing element. Since we are dealing with the
+// specification here, and not the config data itself, this should
+// not happen, and is a code bug.
+//
+// \param spec_part ListElement to find the element in
+// \param id_part the name of the element to find (must match the value
+//                "item_name" in the list item
+// \param id_full the full identifier id_part is a part of, this is
+//                used to better report any errors
+ConstElementPtr findItemInSpecList(ConstElementPtr spec_part,
+                                   const std::string& id_part,
+                                   const std::string& id_full)
+{
+    bool found = false;
+    BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
+        if (list_el->getType() == Element::map &&
+            list_el->contains("item_name") &&
+            list_el->get("item_name")->stringValue() == id_part) {
+            spec_part = list_el;
+            found = true;
+        }
+    }
+    if (!found) {
+        isc_throw(isc::config::DataNotFoundError,
+                  id_part + " in " + id_full + " not found");
+    }
+    return (spec_part);
+}
+
+} // anonymous namespace
+
 namespace isc {
 namespace isc {
 namespace config {
 namespace config {
 
 
@@ -36,11 +93,10 @@ namespace config {
 // validated and conforms to the specification.
 // validated and conforms to the specification.
 static ConstElementPtr
 static ConstElementPtr
 find_spec_part(ConstElementPtr spec, const std::string& identifier) {
 find_spec_part(ConstElementPtr spec, const std::string& identifier) {
-    //std::cout << "[XX] find_spec_part for " << identifier << std::endl;
     if (!spec) {
     if (!spec) {
         isc_throw(DataNotFoundError, "Empty specification");
         isc_throw(DataNotFoundError, "Empty specification");
     }
     }
-    //std::cout << "in: " << std::endl << spec << std::endl;
+
     ConstElementPtr spec_part = spec;
     ConstElementPtr spec_part = spec;
     if (identifier == "") {
     if (identifier == "") {
         isc_throw(DataNotFoundError, "Empty identifier");
         isc_throw(DataNotFoundError, "Empty identifier");
@@ -49,59 +105,44 @@ find_spec_part(ConstElementPtr spec, const std::string& identifier) {
     size_t sep = id.find('/');
     size_t sep = id.find('/');
     while(sep != std::string::npos) {
     while(sep != std::string::npos) {
         std::string part = id.substr(0, sep);
         std::string part = id.substr(0, sep);
-        //std::cout << "[XX] id part: " << part << std::endl;
+
         if (spec_part->getType() == Element::list) {
         if (spec_part->getType() == Element::list) {
-            bool found = false;
+            spec_part = findItemInSpecList(spec_part, part, identifier);
-            BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
+        } else {
-                if (list_el->getType() == Element::map &&
+            isc_throw(DataNotFoundError,
-                    list_el->contains("item_name") &&
+                      "Not a list of spec items: " + spec_part->str());
-                    list_el->get("item_name")->stringValue() == part) {
-                    spec_part = list_el;
-                    found = true;
-                }
-            }
-            if (!found) {
-                isc_throw(DataNotFoundError, identifier);
-            }
         }
         }
         id = id.substr(sep + 1);
         id = id.substr(sep + 1);
         sep = id.find("/");
         sep = id.find("/");
+
+        // As long as we are not in the 'final' element as specified
+        // by the identifier, we want to automatically traverse list
+        // and map specifications
+        if (id != "" && id != "/") {
+            spec_part = findListOrMapSubSpec(spec_part);
+        }
     }
     }
     if (id != "" && id != "/") {
     if (id != "" && id != "/") {
         if (spec_part->getType() == Element::list) {
         if (spec_part->getType() == Element::list) {
-            bool found = false;
+            spec_part = findItemInSpecList(spec_part, id, identifier);
-            BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
-                if (list_el->getType() == Element::map &&
-                    list_el->contains("item_name") &&
-                    list_el->get("item_name")->stringValue() == id) {
-                    spec_part = list_el;
-                    found = true;
-                }
-            }
-            if (!found) {
-                isc_throw(DataNotFoundError, identifier);
-            }
         } else if (spec_part->getType() == Element::map) {
         } else if (spec_part->getType() == Element::map) {
             if (spec_part->contains("map_item_spec")) {
             if (spec_part->contains("map_item_spec")) {
-                bool found = false;
+                spec_part = findItemInSpecList(
-                BOOST_FOREACH(ConstElementPtr list_el,
+                                spec_part->get("map_item_spec"),
-                              spec_part->get("map_item_spec")->listValue()) {
+                                id, identifier);
-                    if (list_el->getType() == Element::map &&
-                        list_el->contains("item_name") &&
-                        list_el->get("item_name")->stringValue() == id) {
-                        spec_part = list_el;
-                        found = true;
-                    }
-                }
-                if (!found) {
-                    isc_throw(DataNotFoundError, identifier);
-                }
             } else {
             } else {
-                isc_throw(DataNotFoundError, identifier);
+                // Either we already have the element we are looking
+                // for, or we are trying to reach something that does
+                // not exist (i.e. the code does not match the spec)
+                if (!spec_part->contains("item_name") ||
+                    spec_part->get("item_name")->stringValue() != id) {
+                    isc_throw(DataNotFoundError, "Element above " + id +
+                                                 " in " + identifier +
+                                                 " is not a map: " + spec_part->str());
+                }
             }
             }
         }
         }
     }
     }
-    //std::cout << "[XX] found spec part: " << std::endl << spec_part << std::endl;
     return (spec_part);
     return (spec_part);
 }
 }
 
 
@@ -164,6 +205,17 @@ ConfigData::getValue(bool& is_default, const std::string& identifier) const {
     return (value);
     return (value);
 }
 }
 
 
+ConstElementPtr
+ConfigData::getDefaultValue(const std::string& identifier) const {
+    ConstElementPtr spec_part =
+        find_spec_part(_module_spec.getConfigSpec(), identifier);
+    if (spec_part->contains("item_default")) {
+        return spec_part->get("item_default");
+    } else {
+        isc_throw(DataNotFoundError, "No default for " + identifier);
+    }
+}
+
 /// Returns an ElementPtr pointing to a ListElement containing
 /// Returns an ElementPtr pointing to a ListElement containing
 /// StringElements with the names of the options at the given
 /// StringElements with the names of the options at the given
 /// identifier. If recurse is true, maps will be expanded as well
 /// identifier. If recurse is true, maps will be expanded as well

+ 10 - 0
src/lib/config/config_data.h

@@ -57,6 +57,16 @@ public:
     ///        value that is to be returned
     ///        value that is to be returned
     isc::data::ConstElementPtr getValue(const std::string& identifier) const;
     isc::data::ConstElementPtr getValue(const std::string& identifier) const;
 
 
+    /// Returns the default value for the given identifier.
+    ///
+    /// \exception DataNotFoundError if the given identifier does not
+    ///            exist, or if the given value has no specified default
+    ///
+    /// \param identifier The identifier pointing to the configuration
+    ///        value for which the default is to be returned
+    /// \return ElementPtr containing the default value
+    isc::data::ConstElementPtr getDefaultValue(const std::string& identifier) const;
+
     /// Returns the value currently set for the given identifier
     /// Returns the value currently set for the given identifier
     /// If no value is set, the default value (as specified by the
     /// If no value is set, the default value (as specified by the
     /// .spec file) is returned. If there is no value and no default,
     /// .spec file) is returned. If there is no value and no default,

+ 7 - 0
src/lib/config/configdef.mes

@@ -48,3 +48,10 @@ channel. The message does not appear to be a valid command, and is
 missing a required element or contains an unknown data format. This
 missing a required element or contains an unknown data format. This
 most likely means that another BIND10 module is sending a bad message.
 most likely means that another BIND10 module is sending a bad message.
 The message itself is ignored by this module.
 The message itself is ignored by this module.
+
+% CCSESSION_MSG_INTERNAL error handling CC session message: %1
+There was an internal problem handling an incoming message on the
+command and control channel. An unexpected exception was thrown. This
+most likely points to an internal inconsistency in the module code. The
+exception message is appended to the log error, and the module will
+continue to run, but will not send back an answer.

+ 3 - 2
src/lib/config/tests/Makefile.am

@@ -22,11 +22,12 @@ run_unittests_SOURCES = ccsession_unittests.cc module_spec_unittests.cc config_d
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD =  $(GTEST_LDADD)
 run_unittests_LDADD =  $(GTEST_LDADD)
+run_unittests_LDADD += libfake_session.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += libfake_session.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
 
 
 endif
 endif
 
 

+ 89 - 3
src/lib/config/tests/ccsession_unittests.cc

@@ -160,6 +160,10 @@ TEST_F(CCSessionTest, session1) {
     EXPECT_EQ("ConfigManager", group);
     EXPECT_EQ("ConfigManager", group);
     EXPECT_EQ("*", to);
     EXPECT_EQ("*", to);
     EXPECT_EQ(0, session.getMsgQueue()->size());
     EXPECT_EQ(0, session.getMsgQueue()->size());
+
+    // without explicit argument, the session should not automatically
+    // subscribe to logging config
+    EXPECT_FALSE(session.haveSubscription("Logging", "*"));
 }
 }
 
 
 TEST_F(CCSessionTest, session2) {
 TEST_F(CCSessionTest, session2) {
@@ -351,7 +355,9 @@ int remote_item1(0);
 ConstElementPtr remote_config;
 ConstElementPtr remote_config;
 ModuleCCSession *remote_mccs(NULL);
 ModuleCCSession *remote_mccs(NULL);
 
 
-void remoteHandler(const std::string& module_name, ConstElementPtr config) {
+void remoteHandler(const std::string& module_name,
+                   ConstElementPtr config,
+                   const ConfigData&) {
     remote_module_name = module_name;
     remote_module_name = module_name;
     remote_item1 = remote_mccs->getRemoteConfigValue("Spec2", "item1")->
     remote_item1 = remote_mccs->getRemoteConfigValue("Spec2", "item1")->
         intValue();
         intValue();
@@ -362,7 +368,7 @@ TEST_F(CCSessionTest, remoteConfig) {
     std::string module_name;
     std::string module_name;
     int item1;
     int item1;
     
     
-    ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL);
+    ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL, false);
     EXPECT_TRUE(session.haveSubscription("Spec1", "*"));
     EXPECT_TRUE(session.haveSubscription("Spec1", "*"));
     
     
     // first simply connect, with no config values, and see we get
     // first simply connect, with no config values, and see we get
@@ -415,7 +421,16 @@ TEST_F(CCSessionTest, remoteConfig) {
         EXPECT_NO_THROW(module_name = mccs.addRemoteConfig("Spec2", NULL,
         EXPECT_NO_THROW(module_name = mccs.addRemoteConfig("Spec2", NULL,
                                                            false));
                                                            false));
 
 
+        const size_t qsize(session.getMsgQueue()->size());
+        EXPECT_TRUE(session.getMsgQueue()->get(qsize - 2)->equals(*el(
+            "[ \"ConfigManager\", \"*\", { \"command\": ["
+            "\"get_module_spec\", { \"module_name\": \"Spec2\" } ] } ]")));
+        EXPECT_TRUE(session.getMsgQueue()->get(qsize - 1)->equals(*el(
+            "[ \"ConfigManager\", \"*\", { \"command\": [ \"get_config\","
+            "{ \"module_name\": \"Spec2\" } ] } ]")));
         EXPECT_EQ("Spec2", module_name);
         EXPECT_EQ("Spec2", module_name);
+        // Since we returned an empty local config above, the default value
+        // for "item1", which is 1, should be used.
         EXPECT_NO_THROW(item1 =
         EXPECT_NO_THROW(item1 =
                         mccs.getRemoteConfigValue(module_name,
                         mccs.getRemoteConfigValue(module_name,
                                                   "item1")->intValue());
                                                   "item1")->intValue());
@@ -425,6 +440,18 @@ TEST_F(CCSessionTest, remoteConfig) {
     }
     }
 
 
     {
     {
+        SCOPED_TRACE("With bad module name");
+        // It is almost the same as above, but we supply wrong module name.
+        // It should fail.
+        // Try adding it with downloading the spec from config manager
+        ModuleSpec spec(moduleSpecFromFile(ccspecfile("spec2.spec")));
+        session.getMessages()->add(createAnswer(0, spec.getFullSpec()));
+
+        EXPECT_THROW(module_name = mccs.addRemoteConfig("Spec1", NULL, false),
+                     CCSessionError);
+    }
+
+    {
         // Try adding it with a handler.
         // Try adding it with a handler.
         // Pass non-default value to see the handler is called after
         // Pass non-default value to see the handler is called after
         // downloading the configuration, not too soon.
         // downloading the configuration, not too soon.
@@ -496,7 +523,8 @@ TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
     session.getMessages()->add(createAnswer(0, el("{  }")));
     session.getMessages()->add(createAnswer(0, el("{  }")));
 
 
     EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
     EXPECT_FALSE(session.haveSubscription("Spec29", "*"));
-    ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler, my_command_handler);
+    ModuleCCSession mccs(ccspecfile("spec29.spec"), session, my_config_handler,
+                         my_command_handler, false);
     EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
     EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
 
 
     EXPECT_EQ(2, session.getMsgQueue()->size());
     EXPECT_EQ(2, session.getMsgQueue()->size());
@@ -546,4 +574,62 @@ TEST_F(CCSessionTest, initializationFail) {
     EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
     EXPECT_TRUE(session.haveSubscription("Spec29", "*"));
 }
 }
 
 
+// Test it throws when we try to start it twice (once from the constructor)
+TEST_F(CCSessionTest, doubleStartImplicit) {
+    ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL);
+    EXPECT_THROW(mccs.start(), CCSessionError);
+}
+
+// The same, but both starts are explicit
+TEST_F(CCSessionTest, doubleStartExplicit) {
+    ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL,
+                         false);
+    mccs.start();
+    EXPECT_THROW(mccs.start(), CCSessionError);
+}
+
+// Test we can request synchronous receive before we start the session,
+// and check there's the mechanism if we do it after
+TEST_F(CCSessionTest, delayedStart) {
+    ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL, false);
+    session.getMessages()->add(createAnswer());
+    ConstElementPtr env, answer;
+    EXPECT_NO_THROW(session.group_recvmsg(env, answer, false, 3));
+    mccs.start();
+    session.getMessages()->add(createAnswer());
+    EXPECT_THROW(session.group_recvmsg(env, answer, false, 3),
+                 FakeSession::DoubleRead);
+}
+
+TEST_F(CCSessionTest, loggingStart) {
+    // provide the logging module spec
+    ConstElementPtr log_spec = moduleSpecFromFile(LOG_SPEC_FILE).getFullSpec();
+    session.getMessages()->add(createAnswer(0, log_spec));
+    // just give an empty config
+    session.getMessages()->add(createAnswer(0, el("{}")));
+    ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
+                         true, true);
+    EXPECT_TRUE(session.haveSubscription("Logging", "*"));
+}
+
+TEST_F(CCSessionTest, loggingStartBadSpec) {
+    // provide the logging module spec
+    session.getMessages()->add(createAnswer(0, el("{}")));
+    // just give an empty config
+    session.getMessages()->add(createAnswer(0, el("{}")));
+    EXPECT_THROW(new ModuleCCSession(ccspecfile("spec2.spec"), session,
+                 NULL, NULL, true, true), ModuleSpecError);
+    EXPECT_FALSE(session.haveSubscription("Logging", "*"));
+}
+
+// Similar to the above, but more implicitly by calling addRemoteConfig().
+// We should construct ModuleCCSession with start_immediately being false
+// if we need to call addRemoteConfig().
+// The correct cases are covered in remoteConfig test.
+TEST_F(CCSessionTest, doubleStartWithAddRemoteConfig) {
+    ModuleCCSession mccs(ccspecfile("spec29.spec"), session, NULL, NULL);
+    session.getMessages()->add(createAnswer(0, el("{}")));
+    EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")),
+                 FakeSession::DoubleRead);
+}
 }
 }

+ 17 - 3
src/lib/config/tests/config_data_unittests.cc

@@ -64,21 +64,35 @@ TEST(ConfigData, getValue) {
     EXPECT_EQ("{  }", cd.getValue(is_default, "value6/")->str());
     EXPECT_EQ("{  }", cd.getValue(is_default, "value6/")->str());
     EXPECT_TRUE(is_default);
     EXPECT_TRUE(is_default);
     EXPECT_EQ("[  ]", cd.getValue("value8")->str());
     EXPECT_EQ("[  ]", cd.getValue("value8")->str());
+    EXPECT_EQ("[  ]", cd.getDefaultValue("value8")->str());
+    EXPECT_EQ("empty", cd.getValue("value8/a")->stringValue());
 
 
     EXPECT_THROW(cd.getValue("")->str(), DataNotFoundError);
     EXPECT_THROW(cd.getValue("")->str(), DataNotFoundError);
     EXPECT_THROW(cd.getValue("/")->str(), DataNotFoundError);
     EXPECT_THROW(cd.getValue("/")->str(), DataNotFoundError);
     EXPECT_THROW(cd.getValue("no_such_item")->str(), DataNotFoundError);
     EXPECT_THROW(cd.getValue("no_such_item")->str(), DataNotFoundError);
     EXPECT_THROW(cd.getValue("value6/a")->str(), DataNotFoundError);
     EXPECT_THROW(cd.getValue("value6/a")->str(), DataNotFoundError);
     EXPECT_THROW(cd.getValue("value6/no_such_item")->str(), DataNotFoundError);
     EXPECT_THROW(cd.getValue("value6/no_such_item")->str(), DataNotFoundError);
-    EXPECT_THROW(cd.getValue("value8/a")->str(), DataNotFoundError);
+    EXPECT_THROW(cd.getValue("value8/b")->str(), DataNotFoundError);
-    EXPECT_THROW(cd.getValue("value8/a")->str(), DataNotFoundError);
-    EXPECT_THROW(cd.getValue("value8/a")->str(), DataNotFoundError);
 
 
     ModuleSpec spec1 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec1.spec");
     ModuleSpec spec1 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec1.spec");
     ConfigData cd1 = ConfigData(spec1);
     ConfigData cd1 = ConfigData(spec1);
     EXPECT_THROW(cd1.getValue("anything")->str(), DataNotFoundError);
     EXPECT_THROW(cd1.getValue("anything")->str(), DataNotFoundError);
 }
 }
 
 
+TEST(ConfigData, getDefaultValue) {
+    ModuleSpec spec31 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec31.spec");
+    ConfigData cd = ConfigData(spec31);
+    EXPECT_EQ("[  ]", cd.getDefaultValue("first_list_items")->str());
+    EXPECT_EQ("\"foo\"", cd.getDefaultValue("first_list_items/foo")->str());
+    EXPECT_EQ("{  }", cd.getDefaultValue("first_list_items/second_list_items/map_element")->str());
+    EXPECT_EQ("[  ]", cd.getDefaultValue("first_list_items/second_list_items/map_element/list1")->str());
+    EXPECT_EQ("1", cd.getDefaultValue("first_list_items/second_list_items/map_element/list1/number")->str());
+
+    EXPECT_THROW(cd.getDefaultValue("doesnotexist")->str(), DataNotFoundError);
+    EXPECT_THROW(cd.getDefaultValue("first_list_items/second_list_items/map_element/list1/doesnotexist")->str(), DataNotFoundError);
+}
+
+
 TEST(ConfigData, setLocalConfig) {
 TEST(ConfigData, setLocalConfig) {
     ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
     ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
     ConfigData cd = ConfigData(spec2);
     ConfigData cd = ConfigData(spec2);

+ 1 - 0
src/lib/config/tests/data_def_unittests_config.h.in

@@ -13,3 +13,4 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #define TEST_DATA_PATH "@abs_srcdir@/testdata"
 #define TEST_DATA_PATH "@abs_srcdir@/testdata"
+#define LOG_SPEC_FILE "@abs_top_srcdir@/src/bin/cfgmgr/plugins/logging.spec"

+ 19 - 3
src/lib/config/tests/fake_session.cc

@@ -71,7 +71,8 @@ FakeSession::FakeSession(isc::data::ElementPtr initial_messages,
                          isc::data::ElementPtr msg_queue) :
                          isc::data::ElementPtr msg_queue) :
     messages_(initial_messages),
     messages_(initial_messages),
     subscriptions_(subscriptions),
     subscriptions_(subscriptions),
-    msg_queue_(msg_queue)
+    msg_queue_(msg_queue),
+    started_(false)
 {
 {
 }
 }
 
 
@@ -84,6 +85,7 @@ FakeSession::disconnect() {
 
 
 void
 void
 FakeSession::startRead(boost::function<void()>) {
 FakeSession::startRead(boost::function<void()>) {
+    started_ = true;
 }
 }
 
 
 void
 void
@@ -91,7 +93,13 @@ FakeSession::establish(const char*) {
 }
 }
 
 
 bool
 bool
-FakeSession::recvmsg(ConstElementPtr& msg, bool, int) {
+FakeSession::recvmsg(ConstElementPtr& msg, bool nonblock, int) {
+    if (started_ && !nonblock) {
+        // This would schedule another read for length, leading to
+        // corputed data
+        isc_throw(DoubleRead, "Second read scheduled from recvmsg");
+    }
+
     //cout << "[XX] client asks for message " << endl;
     //cout << "[XX] client asks for message " << endl;
     if (messages_ &&
     if (messages_ &&
         messages_->getType() == Element::list &&
         messages_->getType() == Element::list &&
@@ -105,7 +113,15 @@ FakeSession::recvmsg(ConstElementPtr& msg, bool, int) {
 }
 }
 
 
 bool
 bool
-FakeSession::recvmsg(ConstElementPtr& env, ConstElementPtr& msg, bool, int) {
+FakeSession::recvmsg(ConstElementPtr& env, ConstElementPtr& msg, bool nonblock,
+                     int)
+{
+    if (started_ && !nonblock) {
+        // This would schedule another read for length, leading to
+        // corputed data
+        isc_throw(DoubleRead, "Second read scheduled from recvmsg");
+    }
+
     //cout << "[XX] client asks for message and env" << endl;
     //cout << "[XX] client asks for message and env" << endl;
     env = ElementPtr();
     env = ElementPtr();
     if (messages_ &&
     if (messages_ &&

+ 9 - 0
src/lib/config/tests/fake_session.h

@@ -42,6 +42,14 @@ public:
                 isc::data::ElementPtr msg_queue);
                 isc::data::ElementPtr msg_queue);
     virtual ~FakeSession();
     virtual ~FakeSession();
 
 
+    // This is thrown if two reads for length at once are scheduled at once.
+    // Such thing does bad things currently (see discussion in ticket #931).
+    class DoubleRead : public Exception {
+    public:
+        DoubleRead(const char* file, size_t line, const char* what) :
+            Exception(file, line, what) {}
+    };
+
     virtual void startRead(boost::function<void()> read_callback);
     virtual void startRead(boost::function<void()> read_callback);
 
 
     virtual void establish(const char* socket_file = NULL);
     virtual void establish(const char* socket_file = NULL);
@@ -89,6 +97,7 @@ private:
     const isc::data::ElementPtr messages_;
     const isc::data::ElementPtr messages_;
     isc::data::ElementPtr subscriptions_;
     isc::data::ElementPtr subscriptions_;
     isc::data::ElementPtr msg_queue_;
     isc::data::ElementPtr msg_queue_;
+    bool started_;
 };
 };
 } // namespace cc
 } // namespace cc
 } // namespace isc
 } // namespace isc

+ 3 - 7
src/lib/config/tests/run_unittests.cc

@@ -13,16 +13,12 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
 #include <log/logger_support.h>
 #include <log/logger_support.h>
 
 
 int
 int
 main(int argc, char* argv[]) {
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::InitGoogleTest(&argc, argv);
-
+    isc::log::initLogger();
-    // TODO: UNCOMMENT ON MERGE
+    return (isc::util::unittests::run_all());
-    // (this is the call we want in master, but branch point does not
-    // have this yet)
-    //isc::log::initLogger();
-
-    return (RUN_ALL_TESTS());
 }
 }

+ 2 - 0
src/lib/config/tests/testdata/Makefile.am

@@ -51,3 +51,5 @@ EXTRA_DIST += spec26.spec
 EXTRA_DIST += spec27.spec
 EXTRA_DIST += spec27.spec
 EXTRA_DIST += spec28.spec
 EXTRA_DIST += spec28.spec
 EXTRA_DIST += spec29.spec
 EXTRA_DIST += spec29.spec
+EXTRA_DIST += spec30.spec
+EXTRA_DIST += spec31.spec

+ 45 - 0
src/lib/config/tests/testdata/spec30.spec

@@ -0,0 +1,45 @@
+{
+    "module_spec": {
+        "module_name": "lists",
+        "module_description": "Logging options",
+        "config_data": [
+            {
+                "item_name": "first_list_items",
+                "item_type": "list",
+                "item_optional": false,
+                "item_default": [],
+                "list_item_spec": {
+                  "item_name": "first_list_item",
+                  "item_type": "map",
+                  "item_optional": false,
+                  "item_default": {},
+                  "map_item_spec": [
+                  {  "item_name": "foo",
+                     "item_type": "string",
+                     "item_optional": false,
+                     "item_default": "foo"
+                  },
+                  { "item_name": "second_list_items",
+                    "item_type": "list",
+                    "item_optional": false,
+                    "item_default": [],
+                    "list_item_spec": {
+                      "item_name": "second_list_item",
+                      "item_type": "map",
+                      "item_optional": false,
+                      "item_default": {},
+                      "map_item_spec": [
+                      { "item_name": "final_element",
+                        "item_type": "string",
+                        "item_optional": false,
+                        "item_default": "hello"
+                      }
+                      ]
+                    }
+                  }
+                  ]
+                }
+            }
+        ]
+    }
+}

+ 63 - 0
src/lib/config/tests/testdata/spec31.spec

@@ -0,0 +1,63 @@
+{
+    "module_spec": {
+        "module_name": "lists",
+        "module_description": "Logging options",
+        "config_data": [
+            {
+                "item_name": "first_list_items",
+                "item_type": "list",
+                "item_optional": false,
+                "item_default": [],
+                "list_item_spec": {
+                  "item_name": "first_list_item",
+                  "item_type": "map",
+                  "item_optional": false,
+                  "item_default": {},
+                  "map_item_spec": [
+                  {  "item_name": "foo",
+                     "item_type": "string",
+                     "item_optional": false,
+                     "item_default": "foo"
+                  },
+                  { "item_name": "second_list_items",
+                    "item_type": "list",
+                    "item_optional": false,
+                    "item_default": [],
+                    "list_item_spec": {
+                      "item_name": "second_list_item",
+                      "item_type": "map",
+                      "item_optional": false,
+                      "item_default": {},
+                      "map_item_spec": [
+                      { "item_name": "map_element",
+                        "item_type": "map",
+                        "item_optional": false,
+                        "item_default": {},
+                        "map_item_spec": [
+                        { "item_name": "list1",
+                          "item_type": "list",
+                          "item_optional": false,
+                          "item_default": [],
+                          "list_item_spec":
+                          { "item_name": "list2",
+                            "item_type": "list",
+                            "item_optional": false,
+                            "item_default": [],
+                            "list_item_spec":
+                            { "item_name": "number",
+                              "item_type": "integer",
+                              "item_optional": false,
+                              "item_default": 1
+                            }
+                          }
+                        }]
+                      }
+                      ]
+                    }
+                  }
+                  ]
+                }
+            }
+        ]
+    }
+}

+ 15 - 5
src/lib/cryptolink/crypto_hmac.cc

@@ -36,6 +36,15 @@ getBotanHashAlgorithmName(isc::cryptolink::HashAlgorithm algorithm) {
     case isc::cryptolink::SHA256:
     case isc::cryptolink::SHA256:
         return ("SHA-256");
         return ("SHA-256");
         break;
         break;
+    case isc::cryptolink::SHA224:
+        return ("SHA-224");
+        break;
+    case isc::cryptolink::SHA384:
+        return ("SHA-384");
+        break;
+    case isc::cryptolink::SHA512:
+        return ("SHA-512");
+        break;
     case isc::cryptolink::UNKNOWN_HASH:
     case isc::cryptolink::UNKNOWN_HASH:
         return ("Unknown");
         return ("Unknown");
         break;
         break;
@@ -61,7 +70,8 @@ public:
                 getBotanHashAlgorithmName(hash_algorithm));
                 getBotanHashAlgorithmName(hash_algorithm));
         } catch (const Botan::Algorithm_Not_Found&) {
         } catch (const Botan::Algorithm_Not_Found&) {
             isc_throw(isc::cryptolink::UnsupportedAlgorithm,
             isc_throw(isc::cryptolink::UnsupportedAlgorithm,
-                      "Unknown hash algorithm: " + hash_algorithm);
+                      "Unknown hash algorithm: " <<
+                      static_cast<int>(hash_algorithm));
         } catch (const Botan::Exception& exc) {
         } catch (const Botan::Exception& exc) {
             isc_throw(isc::cryptolink::LibraryError, exc.what());
             isc_throw(isc::cryptolink::LibraryError, exc.what());
         }
         }
@@ -173,9 +183,9 @@ public:
         try {
         try {
             Botan::SecureVector<Botan::byte> our_mac = hmac_->final();
             Botan::SecureVector<Botan::byte> our_mac = hmac_->final();
             if (len < getOutputLength()) {
             if (len < getOutputLength()) {
-                // Currently we don't support truncated signature.  To avoid
+                // Currently we don't support truncated signature in TSIG (see
-                // validating too short signature accidently, we enforce the
+                // #920).  To avoid validating too short signature accidently,
-                // standard signature size for the moment.
+                // we enforce the standard signature size for the moment.
                 // Once we support truncation correctly, this if-clause should
                 // Once we support truncation correctly, this if-clause should
                 // (and the capitalized comment above) be removed.
                 // (and the capitalized comment above) be removed.
                 return (false);
                 return (false);
@@ -236,7 +246,7 @@ HMAC::verify(const void* sig, const size_t len) {
 }
 }
 
 
 void
 void
-signHMAC(const void* data, size_t data_len, const void* secret,
+signHMAC(const void* data, const size_t data_len, const void* secret,
          size_t secret_len, const HashAlgorithm hash_algorithm,
          size_t secret_len, const HashAlgorithm hash_algorithm,
          isc::util::OutputBuffer& result, size_t len)
          isc::util::OutputBuffer& result, size_t len)
 {
 {

+ 8 - 4
src/lib/cryptolink/cryptolink.h

@@ -29,15 +29,19 @@ namespace cryptolink {
 
 
 /// \brief Hash algorithm identifiers
 /// \brief Hash algorithm identifiers
 enum HashAlgorithm {
 enum HashAlgorithm {
-    MD5 = 0,            ///< MD5
+    UNKNOWN_HASH = 0,   ///< This value can be used in conversion
-    SHA1 = 1,           ///< SHA-1
-    SHA256 = 2,         ///< SHA-256
-    UNKNOWN_HASH = 3    ///< This value can be used in conversion
                         ///  functions, to be returned when the
                         ///  functions, to be returned when the
                         ///  input is unknown (but a value MUST be
                         ///  input is unknown (but a value MUST be
                         ///  returned), for instance when the input
                         ///  returned), for instance when the input
                         ///  is a Name or a string, and the return
                         ///  is a Name or a string, and the return
                         ///  value is a HashAlgorithm.
                         ///  value is a HashAlgorithm.
+    MD5 = 1,            ///< MD5
+    SHA1 = 2,           ///< SHA-1
+    SHA256 = 3,         ///< SHA-256
+    SHA224 = 4,         ///< SHA-224
+    SHA384 = 5,         ///< SHA-384
+    SHA512 = 6          ///< SHA-512
+
 };
 };
 
 
 // Forward declaration for createHMAC()
 // Forward declaration for createHMAC()

+ 3 - 2
src/lib/cryptolink/tests/Makefile.am

@@ -16,10 +16,11 @@ TESTS += run_unittests
 run_unittests_SOURCES = run_unittests.cc
 run_unittests_SOURCES = run_unittests.cc
 run_unittests_SOURCES += crypto_unittests.cc
 run_unittests_SOURCES += crypto_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS = ${BOTAN_LDFLAGS} $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.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/libexceptions.la
 endif
 endif
 
 

+ 160 - 69
src/lib/cryptolink/tests/crypto_unittests.cc

@@ -13,8 +13,16 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <config.h>
 #include <config.h>
+
+#include <string>
+#include <vector>
+
+#include <boost/lexical_cast.hpp>
+
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
+#include <util/encode/hex.h>
+
 #include <cryptolink/cryptolink.h>
 #include <cryptolink/cryptolink.h>
 #include <cryptolink/crypto_hmac.h>
 #include <cryptolink/crypto_hmac.h>
 
 
@@ -23,7 +31,9 @@
 
 
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 
 
+using namespace boost;
 using namespace isc::util;
 using namespace isc::util;
+using namespace isc::util::encode;
 using namespace isc::cryptolink;
 using namespace isc::cryptolink;
 
 
 namespace {
 namespace {
@@ -340,77 +350,158 @@ TEST(CryptoLinkTest, DISABLED_HMAC_SHA1_RFC2202_SIGN_TRUNCATED) {
 //
 //
 // Test values taken from RFC 4231
 // Test values taken from RFC 4231
 //
 //
-TEST(CryptoLinkTest, HMAC_SHA256_RFC2202_SIGN) {
+//  Test data from RFC4231, including secret key
-    const uint8_t secret[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+//  and source data, they are common for sha224/256/384/512
-                               0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+//  so put them together within the separate function.
-                               0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b };
+void
-    const uint8_t hmac_expected[] = { 0xb0, 0x34, 0x4c, 0x61, 0xd8,
+doRFC4231Tests(HashAlgorithm hash_algorithm,
-                                      0xdb, 0x38, 0x53, 0x5c, 0xa8,
+               const std::vector<std::vector<uint8_t> >& hmac_list)
-                                      0xaf, 0xce, 0xaf, 0x0b, 0xf1,
+{
-                                      0x2b, 0x88, 0x1d, 0xc2, 0x00,
+    std::vector<std::string> data_list;
-                                      0xc9, 0x83, 0x3d, 0xa7, 0x26,
+    std::vector<std::string> secret_list;
-                                      0xe9, 0x37, 0x6c, 0x2e, 0x32,
+
-                                      0xcf, 0xf7 };
+    data_list.push_back("Hi There");
-    doHMACTest("Hi There", secret, 20, SHA256, hmac_expected, 32);
+    data_list.push_back("what do ya want for nothing?");
-
+    data_list.push_back(std::string(50, 0xdd));
-    const uint8_t hmac_expected2[] = { 0x5b, 0xdc, 0xc1, 0x46, 0xbf,
+    data_list.push_back(std::string(50, 0xcd));
-                                       0x60, 0x75, 0x4e, 0x6a, 0x04,
+    data_list.push_back("Test With Truncation");
-                                       0x24, 0x26, 0x08, 0x95, 0x75,
+    data_list.push_back("Test Using Larger Than Block-Size Key - "
-                                       0xc7, 0x5a, 0x00, 0x3f, 0x08,
+                        "Hash Key First");
-                                       0x9d, 0x27, 0x39, 0x83, 0x9d,
+    data_list.push_back("This is a test using a larger than block-size "
-                                       0xec, 0x58, 0xb9, 0x64, 0xec,
+                        "key and a larger than block-size data. The key "
-                                       0x38, 0x43 };
+                        "needs to be hashed before being used by the HMAC "
-    doHMACTest("what do ya want for nothing?", "Jefe", 4, SHA256,
+                        "algorithm.");
-               hmac_expected2, 32);
+
+    secret_list.push_back(std::string(20, 0x0b));
+    secret_list.push_back("Jefe");
+    secret_list.push_back(std::string(20, 0xaa));
+    const uint8_t secret_array[] = {
+        0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+        0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+        0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+        0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+        0x19 
+    };
+    secret_list.push_back(std::string(secret_array,
+                                      secret_array + sizeof(secret_array)));
+    secret_list.push_back(std::string(20, 0x0c));
+    secret_list.push_back(std::string(131, 0xaa));
+    secret_list.push_back(std::string(131, 0xaa));
+
+    // Make sure we provide a consistent size of test data
+    ASSERT_EQ(secret_list.size(), data_list.size());
+    ASSERT_EQ(secret_list.size(), hmac_list.size());
+
+    for (int i = 0; i < data_list.size(); ++i) {
+        SCOPED_TRACE("RFC4231 HMAC test for algorithm ID: " +
+                     lexical_cast<std::string>(hash_algorithm) +
+                     ", data ID: " + lexical_cast<std::string>(i));
+        // Until #920 is resolved we have to skip truncation cases.
+        if (data_list[i] == "Test With Truncation") {
+            continue;
+        }
+        doHMACTest(data_list[i], secret_list[i].c_str(), secret_list[i].size(),
+                   hash_algorithm, &hmac_list[i][0], hmac_list[i].size());
+    }
+}
 
 
-    const uint8_t secret3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+TEST(CryptoLinkTest, HMAC_SHA256_RFC4231_SIGN) {
-                                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    std::vector<std::vector<uint8_t> > hmac_expected_list(7);
-                                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+
-                                0xaa, 0xaa };
+    int i = 0;
-    const uint8_t hmac_expected3[] = { 0x77, 0x3e, 0xa9, 0x1e, 0x36,
+    decodeHex(
-                                       0x80, 0x0e, 0x46, 0x85, 0x4d,
+        "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
-                                       0xb8, 0xeb, 0xd0, 0x91, 0x81,
+        hmac_expected_list[i++]);
-                                       0xa7, 0x29, 0x59, 0x09, 0x8b,
+    decodeHex(
-                                       0x3e, 0xf8, 0xc1, 0x22, 0xd9,
+        "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
-                                       0x63, 0x55, 0x14, 0xce, 0xd5,
+        hmac_expected_list[i++]);
-                                       0x65, 0xfe };
+    decodeHex(
-    doHMACTest(std::string(50, 0xdd), secret3, 20, SHA256, hmac_expected3, 32);
+        "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
+        hmac_expected_list[i++]);
+    decodeHex(
+        "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
+        hmac_expected_list[i++]);
+    decodeHex("a3b6167473100ee06e0c796c2955552b", hmac_expected_list[i++]);
+    decodeHex(
+        "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54",
+        hmac_expected_list[i++]);
+    decodeHex(
+        "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2",
+        hmac_expected_list[i++]);
+
+    doRFC4231Tests(SHA256, hmac_expected_list);
+}
 
 
-    const uint8_t secret4[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+//
-                                0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+// Test values taken from RFC 4231, test optional algorithm 224,384,512
-                                0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+//
-                                0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+TEST(CryptoLinkTest, HMAC_SHA224_RFC4231_SIGN) {
-                                0x19 };
+    std::vector<std::vector<uint8_t> > hmac_expected_list(7);
-    const uint8_t hmac_expected4[] = { 0x82, 0x55, 0x8a, 0x38, 0x9a,
+
-                                       0x44, 0x3c, 0x0e, 0xa4, 0xcc,
+    int i = 0;
-                                       0x81, 0x98, 0x99, 0xf2, 0x08,
+    decodeHex("896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22",
-                                       0x3a, 0x85, 0xf0, 0xfa, 0xa3,
+              hmac_expected_list[i++]);
-                                       0xe5, 0x78, 0xf8, 0x07, 0x7a,
+    decodeHex("a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44",
-                                       0x2e, 0x3f, 0xf4, 0x67, 0x29,
+              hmac_expected_list[i++]);
-                                       0x66, 0x5b };
+    decodeHex("7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea",
-    doHMACTest(std::string(50, 0xcd), secret4, 25, SHA256, hmac_expected4, 32);
+              hmac_expected_list[i++]);
-
+    decodeHex("6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a",
-    const uint8_t hmac_expected6[] = { 0x60, 0xe4, 0x31, 0x59, 0x1e,
+              hmac_expected_list[i++]);
-                                       0xe0, 0xb6, 0x7f, 0x0d, 0x8a,
+    decodeHex("0e2aea68a90c8d37c988bcdb9fca6fa8", hmac_expected_list[i++]);
-                                       0x26, 0xaa, 0xcb, 0xf5, 0xb7,
+    decodeHex("95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e",
-                                       0x7f, 0x8e, 0x0b, 0xc6, 0x21,
+              hmac_expected_list[i++]);
-                                       0x37, 0x28, 0xc5, 0x14, 0x05,
+    decodeHex("3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1",
-                                       0x46, 0x04, 0x0f, 0x0e, 0xe3,
+              hmac_expected_list[i++]);
-                                       0x7f, 0x54 };
+
-    doHMACTest("Test Using Larger Than Block-Size Key - Hash Key First",
+    doRFC4231Tests(SHA224, hmac_expected_list);
-               std::string(131, 0xaa).c_str(), 131, SHA256, hmac_expected6, 32);
+}
-
+
-    const uint8_t hmac_expected7[] = { 0x9b, 0x09, 0xff, 0xa7, 0x1b,
+TEST(CryptoLinkTest, HMAC_SHA384_RFC4231_SIGN) {
-                                       0x94, 0x2f, 0xcb, 0x27, 0x63,
+    std::vector<std::vector<uint8_t> > hmac_expected_list(7);
-                                       0x5f, 0xbc, 0xd5, 0xb0, 0xe9,
+
-                                       0x44, 0xbf, 0xdc, 0x63, 0x64,
+    int i = 0;
-                                       0x4f, 0x07, 0x13, 0x93, 0x8a,
+    decodeHex("afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc5"
-                                       0x7f, 0x51, 0x53, 0x5c, 0x3a,
+              "9cfaea9ea9076ede7f4af152e8b2fa9cb6", hmac_expected_list[i++]);
-                                       0x35, 0xe2 };
+    decodeHex("af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec373632244"
-    doHMACTest("This is a test using a larger than block-size key and a"
+              "5e8e2240ca5e69e2c78b3239ecfab21649", hmac_expected_list[i++]);
-               " larger than block-size data. The key needs to be hashe"
+    decodeHex("88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e5596614"
-               "d before being used by the HMAC algorithm.",
+              "4b2a5ab39dc13814b94e3ab6e101a34f27", hmac_expected_list[i++]);
-               std::string(131, 0xaa).c_str(), 131, SHA256, hmac_expected7, 32);
+    decodeHex("3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b"
+              "4e6801dd23c4a7d679ccf8a386c674cffb", hmac_expected_list[i++]);
+    decodeHex("3abf34c3503b2a23a46efc619baef897", hmac_expected_list[i++]);
+    decodeHex("4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4"
+              "c60c2ef6ab4030fe8296248df163f44952", hmac_expected_list[i++]);
+    decodeHex("6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99"
+              "c5a678cc31e799176d3860e6110c46523e", hmac_expected_list[i++]);
+
+    doRFC4231Tests(SHA384, hmac_expected_list);
+}
+
+TEST(CryptoLinkTest, HMAC_SHA512_RFC4231_SIGN) {
+    std::vector<std::vector<uint8_t> > hmac_expected_list(7);
+
+    int i = 0;
+    decodeHex("87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17c"
+              "dedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a12"
+              "6854", hmac_expected_list[i++]);
+    decodeHex("164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505"
+              "549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bc"
+              "e737", hmac_expected_list[i++]);
+    decodeHex("fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d"
+              "39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e132"
+              "92fb", hmac_expected_list[i++]);
+    decodeHex("b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3"
+              "dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a2"
+              "98dd", hmac_expected_list[i++]);
+    decodeHex("415fad6271580a531d4179bc891d87a6", hmac_expected_list[i++]);
+    decodeHex("80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3"
+              "526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d78"
+              "6598", hmac_expected_list[i++]);
+    decodeHex("e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc9"
+              "44b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c"
+              "6a58", hmac_expected_list[i++]);
+
+    doRFC4231Tests(SHA512, hmac_expected_list);
 }
 }
 
 
 TEST(CryptoLinkTest, DISABLED_HMAC_SHA256_RFC2202_SIGN_TRUNCATED) {
 TEST(CryptoLinkTest, DISABLED_HMAC_SHA256_RFC2202_SIGN_TRUNCATED) {

+ 2 - 1
src/lib/cryptolink/tests/run_unittests.cc

@@ -13,10 +13,11 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
 
 
 int
 int
 main(int argc, char* argv[]) {
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::InitGoogleTest(&argc, argv);
 
 
-    return (RUN_ALL_TESTS());
+    return (isc::util::unittests::run_all());
 }
 }

+ 5 - 2
src/lib/datasrc/tests/Makefile.am

@@ -28,9 +28,11 @@ run_unittests_SOURCES += rbtree_unittest.cc
 run_unittests_SOURCES += zonetable_unittest.cc
 run_unittests_SOURCES += zonetable_unittest.cc
 run_unittests_SOURCES += memory_datasrc_unittest.cc
 run_unittests_SOURCES += memory_datasrc_unittest.cc
 run_unittests_SOURCES += logger_unittest.cc
 run_unittests_SOURCES += logger_unittest.cc
+
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+
+run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.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/dns/libdns++.la
@@ -38,6 +40,7 @@ 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/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
 run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 noinst_PROGRAMS = $(TESTS)

+ 5 - 1
src/lib/datasrc/tests/run_unittests.cc

@@ -13,6 +13,8 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_support.h>
 
 
 #include <dns/tests/unittest_util.h>
 #include <dns/tests/unittest_util.h>
 
 
@@ -21,5 +23,7 @@ main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::InitGoogleTest(&argc, argv);
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
 
 
-    return (RUN_ALL_TESTS());
+    isc::log::initLogger();
+
+    return (isc::util::unittests::run_all());
 }
 }

+ 3 - 0
src/lib/dns/python/tests/tsigkey_python_test.py

@@ -25,6 +25,9 @@ class TSIGKeyTest(unittest.TestCase):
                          TSIGKey.HMACMD5_NAME)
                          TSIGKey.HMACMD5_NAME)
         self.assertEqual(Name('hmac-sha1'), TSIGKey.HMACSHA1_NAME)
         self.assertEqual(Name('hmac-sha1'), TSIGKey.HMACSHA1_NAME)
         self.assertEqual(Name('hmac-sha256'), TSIGKey.HMACSHA256_NAME)
         self.assertEqual(Name('hmac-sha256'), TSIGKey.HMACSHA256_NAME)
+        self.assertEqual(Name('hmac-sha224'), TSIGKey.HMACSHA224_NAME)
+        self.assertEqual(Name('hmac-sha384'), TSIGKey.HMACSHA384_NAME)
+        self.assertEqual(Name('hmac-sha512'), TSIGKey.HMACSHA512_NAME)
 
 
     def test_init(self):
     def test_init(self):
         key = TSIGKey(self.key_name, TSIGKey.HMACMD5_NAME, self.secret)
         key = TSIGKey(self.key_name, TSIGKey.HMACMD5_NAME, self.secret)

+ 6 - 0
src/lib/dns/python/tsigkey_python.cc

@@ -256,6 +256,12 @@ initModulePart_TSIGKey(PyObject* mod) {
                              createNameObject(TSIGKey::HMACSHA1_NAME()));
                              createNameObject(TSIGKey::HMACSHA1_NAME()));
         installClassVariable(tsigkey_type, "HMACSHA256_NAME",
         installClassVariable(tsigkey_type, "HMACSHA256_NAME",
                              createNameObject(TSIGKey::HMACSHA256_NAME()));
                              createNameObject(TSIGKey::HMACSHA256_NAME()));
+        installClassVariable(tsigkey_type, "HMACSHA224_NAME",
+                             createNameObject(TSIGKey::HMACSHA224_NAME()));
+        installClassVariable(tsigkey_type, "HMACSHA384_NAME",
+                             createNameObject(TSIGKey::HMACSHA384_NAME()));
+        installClassVariable(tsigkey_type, "HMACSHA512_NAME",
+                             createNameObject(TSIGKey::HMACSHA512_NAME()));
     } catch (const exception& ex) {
     } catch (const exception& ex) {
         const string ex_what =
         const string ex_what =
             "Unexpected failure in TSIGKey initialization: " +
             "Unexpected failure in TSIGKey initialization: " +

+ 5 - 2
src/lib/dns/tests/Makefile.am

@@ -53,12 +53,15 @@ run_unittests_SOURCES += tsigkey_unittest.cc
 run_unittests_SOURCES += tsigrecord_unittest.cc
 run_unittests_SOURCES += tsigrecord_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+# We shouldn't need to include BOTAN_LDFLAGS here, but there
+# is one test system where the path for GTEST_LDFLAGS contains
+# an older version of botan, and somehow that version gets
+# linked if we don't
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(BOTAN_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.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/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 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/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 endif
 
 

+ 2 - 1
src/lib/dns/tests/run_unittests.cc

@@ -13,6 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
 
 
 #include <util/unittests/testdata.h>
 #include <util/unittests/testdata.h>
 #include <dns/tests/unittest_util.h>
 #include <dns/tests/unittest_util.h>
@@ -25,5 +26,5 @@ main(int argc, char* argv[]) {
     isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
     isc::util::unittests::addTestDataPath(TEST_DATA_BUILDDIR);
     isc::util::unittests::addTestDataPath(TEST_DATA_BUILDDIR);
 
 
-    return (RUN_ALL_TESTS());
+    return (isc::util::unittests::run_all());
 }
 }

+ 23 - 0
src/lib/dns/tests/tsig_unittest.cc

@@ -425,6 +425,29 @@ TEST_F(TSIGTest, signUsingHMACSHA1) {
     }
     }
 }
 }
 
 
+TEST_F(TSIGTest, signUsingHMACSHA224) {
+    isc::util::detail::gettimeFunction = testGetTime<0x4dae7d5f>;
+
+    secret.clear();
+    decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret);
+    TSIGContext sha1_ctx(TSIGKey(test_name, TSIGKey::HMACSHA224_NAME(),
+                                 &secret[0], secret.size()));
+
+    const uint16_t sha1_qid = 0x0967;
+    const uint8_t expected_mac[] = {
+        0x3b, 0x93, 0xd3, 0xc5, 0xf9, 0x64, 0xb9, 0xc5, 0x00, 0x35, 
+        0x02, 0x69, 0x9f, 0xfc, 0x44, 0xd6, 0xe2, 0x66, 0xf4, 0x08, 
+        0xef, 0x33, 0xa2, 0xda, 0xa1, 0x48, 0x71, 0xd3
+    };
+    {
+        SCOPED_TRACE("Sign test using HMAC-SHA1");
+        commonSignChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx),
+                         sha1_qid, 0x4dae7d5f, expected_mac,
+                         sizeof(expected_mac), 0, 0, NULL,
+                         TSIGKey::HMACSHA224_NAME());
+    }
+}
+
 // The first part of this test checks verifying the signed query used for
 // The first part of this test checks verifying the signed query used for
 // the "sign" test.
 // the "sign" test.
 // The second part of this test generates a signed response to the signed
 // The second part of this test generates a signed response to the signed

+ 12 - 0
src/lib/dns/tests/tsigkey_unittest.cc

@@ -40,6 +40,9 @@ TEST_F(TSIGKeyTest, algorithmNames) {
     EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), TSIGKey::HMACMD5_NAME());
     EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), TSIGKey::HMACMD5_NAME());
     EXPECT_EQ(Name("hmac-sha1"), TSIGKey::HMACSHA1_NAME());
     EXPECT_EQ(Name("hmac-sha1"), TSIGKey::HMACSHA1_NAME());
     EXPECT_EQ(Name("hmac-sha256"), TSIGKey::HMACSHA256_NAME());
     EXPECT_EQ(Name("hmac-sha256"), TSIGKey::HMACSHA256_NAME());
+    EXPECT_EQ(Name("hmac-sha224"), TSIGKey::HMACSHA224_NAME());
+    EXPECT_EQ(Name("hmac-sha384"), TSIGKey::HMACSHA384_NAME());
+    EXPECT_EQ(Name("hmac-sha512"), TSIGKey::HMACSHA512_NAME());
 
 
     // Also check conversion to cryptolink definitions
     // Also check conversion to cryptolink definitions
     EXPECT_EQ(isc::cryptolink::MD5, TSIGKey(key_name, TSIGKey::HMACMD5_NAME(),
     EXPECT_EQ(isc::cryptolink::MD5, TSIGKey(key_name, TSIGKey::HMACMD5_NAME(),
@@ -49,6 +52,15 @@ TEST_F(TSIGKeyTest, algorithmNames) {
     EXPECT_EQ(isc::cryptolink::SHA256, TSIGKey(key_name,
     EXPECT_EQ(isc::cryptolink::SHA256, TSIGKey(key_name,
                                                TSIGKey::HMACSHA256_NAME(),
                                                TSIGKey::HMACSHA256_NAME(),
                                                NULL, 0).getAlgorithm());
                                                NULL, 0).getAlgorithm());
+    EXPECT_EQ(isc::cryptolink::SHA224, TSIGKey(key_name,
+                                               TSIGKey::HMACSHA224_NAME(),
+                                               NULL, 0).getAlgorithm());
+    EXPECT_EQ(isc::cryptolink::SHA384, TSIGKey(key_name,
+                                               TSIGKey::HMACSHA384_NAME(),
+                                               NULL, 0).getAlgorithm());
+    EXPECT_EQ(isc::cryptolink::SHA512, TSIGKey(key_name,
+                                               TSIGKey::HMACSHA512_NAME(),
+                                               NULL, 0).getAlgorithm());
 }
 }
 
 
 TEST_F(TSIGKeyTest, construct) {
 TEST_F(TSIGKeyTest, construct) {

+ 28 - 0
src/lib/dns/tsigkey.cc

@@ -42,6 +42,16 @@ namespace {
         if (name == TSIGKey::HMACSHA256_NAME()) {
         if (name == TSIGKey::HMACSHA256_NAME()) {
             return (isc::cryptolink::SHA256);
             return (isc::cryptolink::SHA256);
         }
         }
+        if (name == TSIGKey::HMACSHA224_NAME()) {
+            return (isc::cryptolink::SHA224);
+        }
+        if (name == TSIGKey::HMACSHA384_NAME()) {
+            return (isc::cryptolink::SHA384);
+        }
+        if (name == TSIGKey::HMACSHA512_NAME()) {
+            return (isc::cryptolink::SHA512);
+        }
+ 
         return (isc::cryptolink::UNKNOWN_HASH);
         return (isc::cryptolink::UNKNOWN_HASH);
     }
     }
 }
 }
@@ -207,6 +217,24 @@ Name& TSIGKey::HMACSHA256_NAME() {
     return (alg_name);
     return (alg_name);
 }
 }
 
 
+const
+Name& TSIGKey::HMACSHA224_NAME() {
+    static Name alg_name("hmac-sha224");
+    return (alg_name);
+}
+
+const
+Name& TSIGKey::HMACSHA384_NAME() {
+    static Name alg_name("hmac-sha384");
+    return (alg_name);
+}
+
+const
+Name& TSIGKey::HMACSHA512_NAME() {
+    static Name alg_name("hmac-sha512");
+    return (alg_name);
+}
+
 struct TSIGKeyRing::TSIGKeyRingImpl {
 struct TSIGKeyRing::TSIGKeyRingImpl {
     typedef map<Name, TSIGKey> TSIGKeyMap;
     typedef map<Name, TSIGKey> TSIGKeyMap;
     typedef pair<Name, TSIGKey> NameAndKey;
     typedef pair<Name, TSIGKey> NameAndKey;

+ 3 - 0
src/lib/dns/tsigkey.h

@@ -206,6 +206,9 @@ public:
     static const Name& HMACMD5_NAME();    ///< HMAC-MD5 (RFC2845)
     static const Name& HMACMD5_NAME();    ///< HMAC-MD5 (RFC2845)
     static const Name& HMACSHA1_NAME();   ///< HMAC-SHA1 (RFC4635)
     static const Name& HMACSHA1_NAME();   ///< HMAC-SHA1 (RFC4635)
     static const Name& HMACSHA256_NAME(); ///< HMAC-SHA256 (RFC4635)
     static const Name& HMACSHA256_NAME(); ///< HMAC-SHA256 (RFC4635)
+    static const Name& HMACSHA224_NAME(); ///< HMAC-SHA256 (RFC4635)
+    static const Name& HMACSHA384_NAME(); ///< HMAC-SHA256 (RFC4635)
+    static const Name& HMACSHA512_NAME(); ///< HMAC-SHA256 (RFC4635)
     //@}
     //@}
 
 
 private:
 private:

+ 3 - 0
src/lib/exceptions/tests/run_unittests.cc

@@ -17,5 +17,8 @@
 int
 int
 main(int argc, char* argv[]) {
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::InitGoogleTest(&argc, argv);
+
+    // Unlike other tests we cannot use our wrapper for RUN_ALL_TESTS()
+    // due to dependency.
     return (RUN_ALL_TESTS());
     return (RUN_ALL_TESTS());
 }
 }

+ 15 - 10
src/lib/log/Makefile.am

@@ -2,32 +2,36 @@ SUBDIRS = . compiler tests
 
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
 
 
 CLEANFILES = *.gcno *.gcda
 CLEANFILES = *.gcno *.gcda
 
 
 lib_LTLIBRARIES = liblog.la
 lib_LTLIBRARIES = liblog.la
 liblog_la_SOURCES  =
 liblog_la_SOURCES  =
-liblog_la_SOURCES += debug_levels.h logger_levels.h
 liblog_la_SOURCES += dummylog.h dummylog.cc
 liblog_la_SOURCES += dummylog.h dummylog.cc
+liblog_la_SOURCES += impldef.cc impldef.h
+liblog_la_SOURCES += log_formatter.h log_formatter.cc
 liblog_la_SOURCES += logger.cc logger.h
 liblog_la_SOURCES += logger.cc logger.h
 liblog_la_SOURCES += logger_impl.cc logger_impl.h
 liblog_la_SOURCES += logger_impl.cc logger_impl.h
+liblog_la_SOURCES += logger_level.h
+liblog_la_SOURCES += logger_level.cc logger_level.h
+liblog_la_SOURCES += logger_level_impl.cc logger_level_impl.h
+liblog_la_SOURCES += logger_manager.cc logger_manager.h
+liblog_la_SOURCES += logger_manager_impl.cc logger_manager_impl.h
+liblog_la_SOURCES += logger_name.cc logger_name.h
+liblog_la_SOURCES += logger_specification.h
 liblog_la_SOURCES += logger_support.cc logger_support.h
 liblog_la_SOURCES += logger_support.cc logger_support.h
+liblog_la_SOURCES += macros.h
 liblog_la_SOURCES += messagedef.cc messagedef.h
 liblog_la_SOURCES += messagedef.cc messagedef.h
 liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
 liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
 liblog_la_SOURCES += message_exception.h
 liblog_la_SOURCES += message_exception.h
 liblog_la_SOURCES += message_initializer.cc message_initializer.h
 liblog_la_SOURCES += message_initializer.cc message_initializer.h
 liblog_la_SOURCES += message_reader.cc message_reader.h
 liblog_la_SOURCES += message_reader.cc message_reader.h
 liblog_la_SOURCES += message_types.h
 liblog_la_SOURCES += message_types.h
-liblog_la_SOURCES += root_logger_name.cc root_logger_name.h
+liblog_la_SOURCES += output_option.cc output_option.h
-liblog_la_SOURCES += log_formatter.h log_formatter.cc
-liblog_la_SOURCES += macros.h
 
 
 EXTRA_DIST  = README
 EXTRA_DIST  = README
+EXTRA_DIST += impldef.mes
 EXTRA_DIST += messagedef.mes
 EXTRA_DIST += messagedef.mes
-EXTRA_DIST += logger_impl_log4cxx.cc logger_impl_log4cxx.h
-EXTRA_DIST += xdebuglevel.cc xdebuglevel.h
 
 
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 # B10_CXXFLAGS)
@@ -39,5 +43,6 @@ if USE_CLANGPP
 # Same for clang++, but we need to turn off -Werror completely.
 # Same for clang++, but we need to turn off -Werror completely.
 liblog_la_CXXFLAGS += -Wno-error
 liblog_la_CXXFLAGS += -Wno-error
 endif
 endif
-liblog_la_CPPFLAGS = $(AM_CPPFLAGS)
+liblog_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
-liblog_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+liblog_la_LDFLAGS  = $(LOG4CPLUS_LDFLAGS)
+liblog_la_LIBADD   = $(top_builddir)/src/lib/util/libutil.la

+ 26 - 73
src/lib/log/README

@@ -117,7 +117,7 @@ Points to note:
 * Lines starting $ are directives.  At present, two directives are recognised:
 * Lines starting $ are directives.  At present, two directives are recognised:
 
 
   * $PREFIX, which has one optional argument: the string used to prefix symbols.
   * $PREFIX, which has one optional argument: the string used to prefix symbols.
-    If absent, there is no prefix to the symbols. (Prefixes are explained below.)
+    If absent, there is no prefix to the symbols (prefixes are explained below).
 
 
   * $NAMESPACE, which has one argument: the namespace in which the symbols are
   * $NAMESPACE, which has one argument: the namespace in which the symbols are
     created.  In the absence of a $NAMESPACE directive, symbols will be put in
     created.  In the absence of a $NAMESPACE directive, symbols will be put in
@@ -135,7 +135,7 @@ Points to note:
   * The replacement tokens are the strings "%1", "%2" etc.  When a message
   * The replacement tokens are the strings "%1", "%2" etc.  When a message
     is logged, these are replaced with the arguments passed to the logging
     is logged, these are replaced with the arguments passed to the logging
     call: %1 refers to the first argument, %2 to the second etc.  Within the
     call: %1 refers to the first argument, %2 to the second etc.  Within the
-    message text, the placeholders can appear in any order, and placeholders
+    message text, the placeholders can appear in any order and placeholders
     can be repeated.
     can be repeated.
      
      
 * Remaining lines indicate an explanation for the preceding message.  These
 * Remaining lines indicate an explanation for the preceding message.  These
@@ -215,33 +215,9 @@ To use the current version of the logging:
 
 
 1. Build message header file and source file as describe above.
 1. Build message header file and source file as describe above.
 
 
-2. In the main module of the program, declare an instance of the
+2. The main program unit should include a call to isc::log::initLogger()
-   RootLoggerName class to define the name of the program's root logger, e.g.
-
-       #include <log/root_logger_name.h>
-
-       isc::log::RootLoggerName("b10-auth");
-
-   This can be declared inside or outside an execution unit.
-
-2. In the code that needs to do logging, declare a logger with a given name,
-   e.g.
-
-       #include <log/logger.h>
-            :
-       isc::log::Logger logger("myname");   // "myname" can be anything
-
-   The above example assumes declaration outside a function.  If declaring
-   non-statically within a function, declare it as:
-
-       isc::log::Logger logger("myname", true);
-
-   (The argument is required to support a possible future implementation of
-   logging.  Currently it has no effect.)
-
-3. The main program unit should include a call to isc::log::initLogger()
    (defined in logger_support.h) to set the logging severity, debug log level,
    (defined in logger_support.h) to set the logging severity, debug log level,
-   and external message file.
+   and external message file:
 
 
    a) The logging severity is one of the enum defined in logger.h, i.e.
    a) The logging severity is one of the enum defined in logger.h, i.e.
 
 
@@ -262,56 +238,43 @@ To use the current version of the logging:
       directive of a particular type will be ignored; multiple directives will
       directive of a particular type will be ignored; multiple directives will
       cause the read of the file to fail with an error.)
       cause the read of the file to fail with an error.)
 
 
-4. Issue logging calls using methods on logger, e.g.
+   The settings remain in effect until the logging configuration is read, and
+   so provide the default logging during program initialization.
 
 
-       logger.error(DPS_NSTIMEOUT).arg("isc.org");
+3. Issue logging calls using supplied macros in "log/macros.h", e.g.
 
 
-   (where, in the example above we might have defined the symbol in the message
+       LOG_ERROR(logger, DPS_NSTIMEOUT).arg("isc.org");
+
+   (The macros are more efficient that calls to the methods on the logger class:
+   they avoid the overhead of evaluating the parameters to arg() if the
+   settings are such that the message is not going to be output.)
+
+   Note: in the example above we might have defined the symbol in the message
    file with something along the lines of:
    file with something along the lines of:
 
 
        $PREFIX DPS_
        $PREFIX DPS_
            :
            :
        NSTIMEOUT  queries to all nameservers for %1 have timed out
        NSTIMEOUT  queries to all nameservers for %1 have timed out
 
 
-   At present, the only logging is to the console.
-
-
-Efficiency Considerations
--------------------------
-A common pattern in logging is a debug call of the form:
-
-   logger.debug(dbglevel, MSGID).arg(expensive_call()).arg(...
-
-... where "expensive_call()" is a function call to obtain logging information
-that may be computationally intensive.  Although the cost may be justified
-when debugging is enabled, the cost is still incurred even if debugging is
-disabled and the debug() method returns without outputting anything.  (The
-same may be true of other logging levels, although there are likely to be
-fewer calls to logger.info(), logger.error() etc. throughout the code and
-they are less likely to be disabled.)
-
-For this reason, a set of macros is provided and are called using the
-construct:
-
-   LOG_DEBUG(logger, dbglevel, MSGID).arg(expensive_call()).arg(...
-   LOG_INFO(logger, MSGID).arg(expensive_call()...)
-
-If these are used, the arguments passed to the arg() method are not evaluated
-if the relevant logging level is disabled.
-
-
 Severity Guidelines
 Severity Guidelines
 ===================
 ===================
 When using logging, the question arises, what severity should a message be
 When using logging, the question arises, what severity should a message be
 logged at?  The following is a suggestion - as always, the decision must be
 logged at?  The following is a suggestion - as always, the decision must be
 made in the context of which the message is logged.
 made in the context of which the message is logged.
 
 
+One thing that should always be borne in mind is whether the logging could
+be used as a vector for a DOS attack.  For example, if a warning message is
+logged every time an invalid packet is received, an attacker could simply send
+large numbers of invalid packets.  (Of course, warnings could be disabled (or
+just warnings for that that particular logger), but nevertheless the message
+is an attack vector.)
+
 FATAL
 FATAL
 -----
 -----
-The program has encountered an error that is so severe that it cannot
+The program has encountered an error that is so severe that it cannot continue
-continue (or there is no point in continuing).  When a fatal error has been
+(or there is no point in continuing).  When a fatal error has been logged,
-logged, the program will usually exit immediately (via a call to abort()) or
+the program will usually exit immediately (or shortly afterwards) after
-shortly afterwards, after dumping some diagnostic information.
+dumping some diagnostic information.
 
 
 ERROR
 ERROR
 -----
 -----
@@ -342,7 +305,7 @@ are distributed between the levels is up to the developer.  So if debugging
 the NSAS (for example), a level 0 message might record the creation of a new
 the NSAS (for example), a level 0 message might record the creation of a new
 zone, a level 10 recording a timeout when trying to get a nameserver address,
 zone, a level 10 recording a timeout when trying to get a nameserver address,
 but a level 50 would record every query for an address. (And we might add
 but a level 50 would record every query for an address. (And we might add
-level 51 to record every update of the RTT.)
+level 70 to record every update of the RTT.)
 
 
 Note that like severities, levels are cumulative; so if level 25 is set as the
 Note that like severities, levels are cumulative; so if level 25 is set as the
 debug level, all debug levels from 0 to 25 will be output.  In fact, it is
 debug level, all debug levels from 0 to 25 will be output.  In fact, it is
@@ -397,13 +360,3 @@ is only one piece of code that does this functionality.
 Outstanding Issues
 Outstanding Issues
 ==================
 ==================
 * Ability to configure system according to configuration database.
 * Ability to configure system according to configuration database.
-
-
-log4cxx Issues
-==============
-Some experimental code to utilise log4cxx as an underlying implementation
-is present in the source code directory although it is not currently used.
-The files are:
-
-   logger_impl_log4cxx.{cc,h}
-   xdebuglevel.{cc,h}

+ 3 - 5
src/lib/log/compiler/Makefile.am

@@ -1,8 +1,6 @@
 SUBDIRS = .
 SUBDIRS = .
 
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -14,7 +12,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 CLEANFILES = *.gcno *.gcda
 
 
 noinst_PROGRAMS = message
 noinst_PROGRAMS = message
-message_SOURCES = message.cc
-message_LDADD  = $(top_builddir)/src/lib/log/liblog.la
-message_LDADD += $(top_builddir)/src/lib/util/libutil.la
 
 
+message_SOURCES = message.cc
+message_LDADD   = $(top_builddir)/src/lib/log/liblog.la
+message_LDADD  += $(top_builddir)/src/lib/util/libutil.la

+ 157 - 109
src/lib/log/compiler/message.cc

@@ -35,6 +35,8 @@
 
 
 #include <log/logger.h>
 #include <log/logger.h>
 
 
+#include <boost/foreach.hpp>
+
 using namespace std;
 using namespace std;
 using namespace isc::log;
 using namespace isc::log;
 using namespace isc::util;
 using namespace isc::util;
@@ -78,10 +80,11 @@ version() {
 void
 void
 usage() {
 usage() {
     cout <<
     cout <<
-        "Usage: message [-h] [-v] <message-file>\n" <<
+        "Usage: message [-h] [-v] [-p] <message-file>\n" <<
         "\n" <<
         "\n" <<
         "-h       Print this message and exit\n" <<
         "-h       Print this message and exit\n" <<
         "-v       Print the program version and exit\n" <<
         "-v       Print the program version and exit\n" <<
+        "-p       Output python source instead of C++ ones\n" <<
         "\n" <<
         "\n" <<
         "<message-file> is the name of the input message file.\n";
         "<message-file> is the name of the input message file.\n";
 }
 }
@@ -237,6 +240,44 @@ writeClosingNamespace(ostream& output, const vector<string>& ns) {
     }
     }
 }
 }
 
 
+/// \breif Write python file
+///
+/// Writes the python file containing the symbol definitions as module level
+/// constants. These are objects which register themself at creation time,
+/// so they can be replaced by dictionary later.
+///
+/// \param file Name of the message file. The source code is written to a file
+///     file of the same name but with a .py suffix.
+/// \param dictionary The dictionary holding the message definitions.
+///
+/// \note We don't use the namespace as in C++. We don't need it, because
+///     python file/module works as implicit namespace as well.
+
+void
+writePythonFile(const string& file, MessageDictionary& dictionary) {
+    Filename message_file(file);
+    Filename python_file(Filename(message_file.name()).useAsDefault(".py"));
+
+    // Open the file for writing
+    ofstream pyfile(python_file.fullName().c_str());
+
+    // Write the comment and imports
+    pyfile <<
+        "# File created from " << message_file.fullName() << " on " <<
+            currentTime() << "\n" <<
+        "\n" <<
+        "import isc.log.message\n" <<
+        "\n";
+
+    vector<string> idents(sortedIdentifiers(dictionary));
+    BOOST_FOREACH(const string& ident, idents) {
+        pyfile << ident << " = isc.log.message.create(\"" <<
+            ident << "\", \"" << quoteString(dictionary.getText(ident)) <<
+            "\")\n";
+    }
+
+    pyfile.close();
+}
 
 
 /// \brief Write Header File
 /// \brief Write Header File
 ///
 ///
@@ -264,52 +305,46 @@ writeHeaderFile(const string& file, const vector<string>& ns_components,
     // Open the output file for writing
     // Open the output file for writing
     ofstream hfile(header_file.fullName().c_str());
     ofstream hfile(header_file.fullName().c_str());
 
 
-    try {
+    if (hfile.fail()) {
-        if (hfile.fail()) {
+        throw MessageException(MSG_OPENOUT, header_file.fullName(),
-            throw MessageException(MSG_OPENOUT, header_file.fullName(),
+            strerror(errno));
-                strerror(errno));
+    }
-        }
-
-        // Write the header preamble.  If there is an error, we'll pick it up
-        // after the last write.
-
-        hfile <<
-            "// File created from " << message_file.fullName() << " on " <<
-                currentTime() << "\n" <<
-             "\n" <<
-             "#ifndef " << sentinel_text << "\n" <<
-             "#define "  << sentinel_text << "\n" <<
-             "\n" <<
-             "#include <log/message_types.h>\n" <<
-             "\n";
-
-        // Write the message identifiers, bounded by a namespace declaration
-        writeOpeningNamespace(hfile, ns_components);
-
-        vector<string> idents = sortedIdentifiers(dictionary);
-        for (vector<string>::const_iterator j = idents.begin();
-            j != idents.end(); ++j) {
-            hfile << "extern const isc::log::MessageID " << *j << ";\n";
-        }
-        hfile << "\n";
 
 
-        writeClosingNamespace(hfile, ns_components);
+    // Write the header preamble.  If there is an error, we'll pick it up
+    // after the last write.
+
+    hfile <<
+        "// File created from " << message_file.fullName() << " on " <<
+            currentTime() << "\n" <<
+         "\n" <<
+         "#ifndef " << sentinel_text << "\n" <<
+         "#define "  << sentinel_text << "\n" <<
+         "\n" <<
+         "#include <log/message_types.h>\n" <<
+         "\n";
+
+    // Write the message identifiers, bounded by a namespace declaration
+    writeOpeningNamespace(hfile, ns_components);
+
+    vector<string> idents = sortedIdentifiers(dictionary);
+    for (vector<string>::const_iterator j = idents.begin();
+        j != idents.end(); ++j) {
+        hfile << "extern const isc::log::MessageID " << *j << ";\n";
+    }
+    hfile << "\n";
 
 
-        // ... and finally the postamble
+    writeClosingNamespace(hfile, ns_components);
-        hfile << "#endif // " << sentinel_text << "\n";
 
 
-        // Report errors (if any) and exit
+    // ... and finally the postamble
-        if (hfile.fail()) {
+    hfile << "#endif // " << sentinel_text << "\n";
-            throw MessageException(MSG_WRITERR, header_file.fullName(),
-                strerror(errno));
-        }
 
 
-        hfile.close();
+    // Report errors (if any) and exit
-    }
+    if (hfile.fail()) {
-    catch (MessageException&) {
+        throw MessageException(MSG_WRITERR, header_file.fullName(),
-        hfile.close();
+            strerror(errno));
-        throw;
     }
     }
+
+    hfile.close();
 }
 }
 
 
 
 
@@ -357,76 +392,71 @@ writeProgramFile(const string& file, const vector<string>& ns_components,
 
 
     // Open the output file for writing
     // Open the output file for writing
     ofstream ccfile(program_file.fullName().c_str());
     ofstream ccfile(program_file.fullName().c_str());
-    try {
-        if (ccfile.fail()) {
-            throw MessageException(MSG_OPENOUT, program_file.fullName(),
-                strerror(errno));
-        }
 
 
-        // Write the preamble.  If there is an error, we'll pick it up after
+    if (ccfile.fail()) {
-        // the last write.
+        throw MessageException(MSG_OPENOUT, program_file.fullName(),
+            strerror(errno));
+    }
 
 
-        ccfile <<
+    // Write the preamble.  If there is an error, we'll pick it up after
-            "// File created from " << message_file.fullName() << " on " <<
+    // the last write.
-                currentTime() << "\n" <<
-             "\n" <<
-             "#include <cstddef>\n" <<
-             "#include <log/message_types.h>\n" <<
-             "#include <log/message_initializer.h>\n" <<
-             "\n";
 
 
-        // Declare the message symbols themselves.
+    ccfile <<
+        "// File created from " << message_file.fullName() << " on " <<
+            currentTime() << "\n" <<
+         "\n" <<
+         "#include <cstddef>\n" <<
+         "#include <log/message_types.h>\n" <<
+         "#include <log/message_initializer.h>\n" <<
+         "\n";
 
 
-        writeOpeningNamespace(ccfile, ns_components);
+    // Declare the message symbols themselves.
 
 
-        vector<string> idents = sortedIdentifiers(dictionary);
+    writeOpeningNamespace(ccfile, ns_components);
-        for (vector<string>::const_iterator j = idents.begin();
-            j != idents.end(); ++j) {
-            ccfile << "extern const isc::log::MessageID " << *j <<
-                " = \"" << *j << "\";\n";
-        }
-        ccfile << "\n";
 
 
-        writeClosingNamespace(ccfile, ns_components);
+    vector<string> idents = sortedIdentifiers(dictionary);
+    for (vector<string>::const_iterator j = idents.begin();
+        j != idents.end(); ++j) {
+        ccfile << "extern const isc::log::MessageID " << *j <<
+            " = \"" << *j << "\";\n";
+    }
+    ccfile << "\n";
 
 
-        // Now the code for the message initialization.
+    writeClosingNamespace(ccfile, ns_components);
 
 
-        ccfile <<
+    // Now the code for the message initialization.
-             "namespace {\n" <<
-             "\n" <<
-             "const char* values[] = {\n";
 
 
-        // Output the identifiers and the associated text.
+    ccfile <<
-        idents = sortedIdentifiers(dictionary);
+         "namespace {\n" <<
-        for (vector<string>::const_iterator i = idents.begin();
+         "\n" <<
-            i != idents.end(); ++i) {
+         "const char* values[] = {\n";
-                ccfile << "    \"" << *i << "\", \"" <<
-                    quoteString(dictionary.getText(*i)) << "\",\n";
-        }
 
 
+    // Output the identifiers and the associated text.
+    idents = sortedIdentifiers(dictionary);
+    for (vector<string>::const_iterator i = idents.begin();
+        i != idents.end(); ++i) {
+            ccfile << "    \"" << *i << "\", \"" <<
+                quoteString(dictionary.getText(*i)) << "\",\n";
+    }
 
 
-        // ... and the postamble
-        ccfile <<
-            "    NULL\n" <<
-            "};\n" <<
-            "\n" <<
-            "const isc::log::MessageInitializer initializer(values);\n" <<
-            "\n" <<
-            "} // Anonymous namespace\n" <<
-            "\n";
-
-        // Report errors (if any) and exit
-        if (ccfile.fail()) {
-            throw MessageException(MSG_WRITERR, program_file.fullName(),
-                strerror(errno));
-        }
 
 
-        ccfile.close();
+    // ... and the postamble
-    }
+    ccfile <<
-    catch (MessageException&) {
+        "    NULL\n" <<
-        ccfile.close();
+        "};\n" <<
-        throw;
+        "\n" <<
+        "const isc::log::MessageInitializer initializer(values);\n" <<
+        "\n" <<
+        "} // Anonymous namespace\n" <<
+        "\n";
+
+    // Report errors (if any) and exit
+    if (ccfile.fail()) {
+        throw MessageException(MSG_WRITERR, program_file.fullName(),
+            strerror(errno));
     }
     }
+
+    ccfile.close();
 }
 }
 
 
 
 
@@ -466,13 +496,19 @@ warnDuplicates(MessageReader& reader) {
 int
 int
 main(int argc, char* argv[]) {
 main(int argc, char* argv[]) {
 
 
-    const char* soptions = "hv";               // Short options
+    const char* soptions = "hvp";               // Short options
 
 
     optind = 1;             // Ensure we start a new scan
     optind = 1;             // Ensure we start a new scan
     int  opt;               // Value of the option
     int  opt;               // Value of the option
 
 
+    bool doPython = false;
+
     while ((opt = getopt(argc, argv, soptions)) != -1) {
     while ((opt = getopt(argc, argv, soptions)) != -1) {
         switch (opt) {
         switch (opt) {
+            case 'p':
+                doPython = true;
+                break;
+
             case 'h':
             case 'h':
                 usage();
                 usage();
                 return 0;
                 return 0;
@@ -508,15 +544,27 @@ main(int argc, char* argv[]) {
         MessageReader reader(&dictionary);
         MessageReader reader(&dictionary);
         reader.readFile(message_file);
         reader.readFile(message_file);
 
 
-        // Get the namespace into which the message definitions will be put and
+        if (doPython) {
-        // split it into components.
+            // Warn in case of ignored directives
-        vector<string> ns_components = splitNamespace(reader.getNamespace());
+            if (!reader.getNamespace().empty()) {
-
+                cerr << "Python mode, ignoring the $NAMESPACE directive" <<
-        // Write the header file.
+                    endl;
-        writeHeaderFile(message_file, ns_components, dictionary);
+            }
-
+
-        // Write the file that defines the message symbols and text
+            // Write the whole python file
-        writeProgramFile(message_file, ns_components, dictionary);
+            writePythonFile(message_file, dictionary);
+        } else {
+            // Get the namespace into which the message definitions will be put and
+            // split it into components.
+            vector<string> ns_components =
+                splitNamespace(reader.getNamespace());
+
+            // Write the header file.
+            writeHeaderFile(message_file, ns_components, dictionary);
+
+            // Write the file that defines the message symbols and text
+            writeProgramFile(message_file, ns_components, dictionary);
+        }
 
 
         // Finally, warn of any duplicates encountered.
         // Finally, warn of any duplicates encountered.
         warnDuplicates(reader);
         warnDuplicates(reader);

+ 29 - 0
src/lib/log/impldef.cc

@@ -0,0 +1,29 @@
+// File created from impldef.mes on Wed Jun  1 10:32:57 2011
+
+#include <cstddef>
+#include <log/message_types.h>
+#include <log/message_initializer.h>
+
+namespace isc {
+namespace log {
+
+extern const isc::log::MessageID LOGIMPL_ABOVEDBGMAX = "LOGIMPL_ABOVEDBGMAX";
+extern const isc::log::MessageID LOGIMPL_BADDEBUG = "LOGIMPL_BADDEBUG";
+extern const isc::log::MessageID LOGIMPL_BELOWDBGMIN = "LOGIMPL_BELOWDBGMIN";
+
+} // namespace log
+} // namespace isc
+
+namespace {
+
+const char* values[] = {
+    "LOGIMPL_ABOVEDBGMAX", "debug level of %1 is too high and will be set to the maximum of %2",
+    "LOGIMPL_BADDEBUG", "debug string is '%1': must be of the form DEBUGn",
+    "LOGIMPL_BELOWDBGMIN", "debug level of %1 is too low and will be set to the minimum of %2",
+    NULL
+};
+
+const isc::log::MessageInitializer initializer(values);
+
+} // Anonymous namespace
+

+ 18 - 0
src/lib/log/impldef.h

@@ -0,0 +1,18 @@
+// File created from impldef.mes on Wed Jun  1 10:32:57 2011
+
+#ifndef __IMPLDEF_H
+#define __IMPLDEF_H
+
+#include <log/message_types.h>
+
+namespace isc {
+namespace log {
+
+extern const isc::log::MessageID LOGIMPL_ABOVEDBGMAX;
+extern const isc::log::MessageID LOGIMPL_BADDEBUG;
+extern const isc::log::MessageID LOGIMPL_BELOWDBGMIN;
+
+} // namespace log
+} // namespace isc
+
+#endif // __IMPLDEF_H

+ 0 - 0
src/lib/log/impldef.mes


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