Browse Source

merge trunk into trac289


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac289@2731 e5f2f494-b856-4b98-b285-d166d9295462
Jerry 14 years ago
parent
commit
c88bf74e9c
80 changed files with 1969 additions and 254 deletions
  1. 41 1
      ChangeLog
  2. 2 2
      Makefile.am
  3. 13 8
      configure.ac
  4. 1 1
      doc/Doxyfile
  5. 18 26
      doc/guide/bind10-guide.html
  6. 6 24
      doc/guide/bind10-guide.xml
  7. 5 0
      ext/asio/README
  8. 11 6
      src/bin/auth/Makefile.am
  9. 1 0
      src/bin/auth/asio_link.cc
  10. 6 4
      src/bin/auth/auth_srv.cc
  11. 31 5
      src/bin/auth/b10-auth.8
  12. 46 3
      src/bin/auth/b10-auth.xml
  13. 55 0
      src/bin/auth/change_user.cc
  14. 59 0
      src/bin/auth/change_user.h
  15. 17 2
      src/bin/auth/main.cc
  16. 12 6
      src/bin/auth/tests/Makefile.am
  17. 7 1
      src/bin/auth/tests/asio_link_unittest.cc
  18. 4 2
      src/bin/auth/tests/auth_srv_unittest.cc
  19. 67 0
      src/bin/auth/tests/change_user_unittest.cc
  20. 31 6
      src/bin/bind10/bind10.8
  21. 2 0
      src/bin/bind10/bind10.py.in
  22. 54 9
      src/bin/bind10/bind10.xml
  23. 19 10
      src/bin/bindctl/bindcmd.py
  24. 5 0
      src/bin/bindctl/tests/bindctl_test.py
  25. 8 3
      src/bin/cfgmgr/b10-cfgmgr.py.in
  26. 6 2
      src/bin/host/Makefile.am
  27. 6 7
      src/bin/msgq/b10-msgq.8
  28. 10 8
      src/bin/msgq/msgq.xml
  29. 2 2
      src/bin/usermgr/b10-cmdctl-usermgr.py.in
  30. 1 1
      src/bin/xfrin/tests/Makefile.am
  31. 1 1
      src/bin/xfrout/tests/Makefile.am
  32. 1 1
      src/lib/Makefile.am
  33. 10 0
      src/lib/bench/Makefile.am
  34. 403 0
      src/lib/bench/benchmark.h
  35. 116 0
      src/lib/bench/benchmark_util.cc
  36. 149 0
      src/lib/bench/benchmark_util.h
  37. 9 0
      src/lib/bench/example/Makefile.am
  38. 144 0
      src/lib/bench/example/search_bench.cc
  39. 24 0
      src/lib/bench/tests/Makefile.am
  40. 143 0
      src/lib/bench/tests/benchmark_unittest.cc
  41. 198 0
      src/lib/bench/tests/loadquery_unittest.cc
  42. 24 0
      src/lib/bench/tests/run_unittests.cc
  43. 6 0
      src/lib/bench/tests/testdata/query.txt
  44. 2 17
      src/lib/cc/Makefile.am
  45. 1 1
      src/lib/cc/session.cc
  46. 31 0
      src/lib/cc/tests/Makefile.am
  47. 1 1
      src/lib/cc/data_unittests.cc
  48. 0 0
      src/lib/cc/tests/run_unittests.cc
  49. 2 1
      src/lib/cc/session_unittests.cc
  50. 1 0
      src/lib/config/Makefile.am
  51. 1 1
      src/lib/config/testdata/b10-config-bad1.db
  52. 1 0
      src/lib/config/testdata/b10-config-bad4.db
  53. 1 1
      src/lib/config/testdata/b10-config.db
  54. 4 0
      src/lib/config/tests/Makefile.am
  55. 3 0
      src/lib/datasrc/static_datasrc.cc
  56. 8 4
      src/lib/datasrc/tests/Makefile.am
  57. 0 1
      src/lib/datasrc/tests/datasrc_unittest.cc
  58. 3 0
      src/lib/datasrc/tests/static_unittest.cc
  59. 1 1
      src/lib/dns/message.h
  60. 2 0
      src/lib/dns/python/libdns_python.cc
  61. 16 16
      src/lib/dns/python/message_python.cc
  62. 13 13
      src/lib/dns/python/name_python.cc
  63. 3 3
      src/lib/dns/python/question_python.cc
  64. 1 1
      src/lib/dns/python/rrclass_python.cc
  65. 3 3
      src/lib/dns/python/rrset_python.cc
  66. 2 2
      src/lib/dns/python/rrttl_python.cc
  67. 2 2
      src/lib/dns/python/rrtype_python.cc
  68. 1 0
      src/lib/dns/rdata/generic/txt_16.cc
  69. 6 2
      src/lib/dns/tests/Makefile.am
  70. 2 12
      src/lib/exceptions/Makefile.am
  71. 23 0
      src/lib/exceptions/tests/Makefile.am
  72. 0 0
      src/lib/exceptions/tests/exceptions_unittest.cc
  73. 0 0
      src/lib/exceptions/tests/run_unittests.cc
  74. 2 2
      src/lib/python/isc/config/ccsession.py
  75. 30 17
      src/lib/python/isc/config/cfgmgr.py
  76. 7 0
      src/lib/python/isc/config/config_data.py
  77. 5 4
      src/lib/python/isc/config/tests/ccsession_test.py
  78. 10 6
      src/lib/python/isc/config/tests/cfgmgr_test.py
  79. 5 1
      src/lib/python/isc/config/tests/config_data_test.py
  80. 2 1
      src/lib/xfr/xfrout_client.cc

+ 41 - 1
ChangeLog

@@ -1,3 +1,39 @@
+  85.	[build]*	jinmei
+	Build programs using dynamic link by default.  A new configure
+	option --enable-static-link is provided to force static link for
+	executable programs.  Statically linked programs can be run on a
+	debugger more easily and would be convenient for developers.
+	(Trac #309, svn r2723)
+
+bind10-devel-20100812 released on August 12, 2010
+
+  84.	[bug]		jinmei, jerry
+	This is a quick fix patch for the issue: AXFR fails half the 
+	time because of connection problems. xfrout client will make
+	a new connection every time. (Trac #299, svn r2697)
+
+  83.	[build]*	jreed
+	The configure --with-boost-lib option is removed. It was not
+	used since the build included ASIO. (svn r2684)
+
+  82.	[func]		jinmei
+	bin/auth: Added -u option to change the effective process user
+	of the authoritative server after invocation.  The same option to
+	the boss process will be propagated to b10-auth, too.
+	(Trac #268, svn r2675)
+
+  81.	[func]		jinmei
+	Added a C++ framework for micro benchmark tests.  A supplemental
+	library functions to build query data for the tests were also
+	provided. (Trac #241, svn r2664)
+
+  80.	[bug]		jelte
+	bindctl no longer accepts configuration changes for unknown or
+	non-running modules (for the latter, this is until we have a
+	way to verify those options, at which point it'll be allowed
+	again).
+	(Trac #99, r2657)
+
   79.	[func]		feng, jinmei
 	Refactored the ASIO link interfaces to move incoming XFR and
 	NOTIFY processing to the auth server class.  Wrapper classes for
@@ -196,7 +232,11 @@ bind10-devel-20100602 released on June 2, 2010
 	a remote server. (Trac #218, svn r2038)
 
   49.	[func]*		jelte
-	Use unix domain sockets for msgq. (Trac #183, svn r2009)
+	Use unix domain sockets for msgq. For b10-msgq, the command
+	line options --msgq-port and -m were removed. For bind10,
+	the -msgq-port option was removed, and the -m command line
+	option was changed to be a filename (instead of port number).
+	(Trac #183, svn r2009)
 
   48.	[func]		jelte
 	bin/auth: Use asio's io_service for the msgq handling.

+ 2 - 2
Makefile.am

@@ -41,8 +41,8 @@ report-coverage:
 coverage: clean-coverage perform-coverage report-coverage
 
 #### include external sources in the distributed tarball:
-# EXTRA_DIST = ext/asio/README
-EXTRA_DIST = ext/asio/asio/local/stream_protocol.hpp
+EXTRA_DIST = ext/asio/README
+EXTRA_DIST += ext/asio/asio/local/stream_protocol.hpp
 EXTRA_DIST += ext/asio/asio/local/basic_endpoint.hpp
 EXTRA_DIST += ext/asio/asio/local/datagram_protocol.hpp
 EXTRA_DIST += ext/asio/asio/local/connect_pair.hpp

+ 13 - 8
configure.ac

@@ -32,6 +32,14 @@ AC_TRY_LINK([],[],
 	])
 LDFLAGS=$LDFLAGS_SAVED
 
+# allow building programs with static link.  we need to make it selective
+# because loadable modules cannot be statically linked.
+AC_ARG_ENABLE([static-link],
+AC_HELP_STRING([--enable-static-link],
+  [build programs with static link [[default=no]]]),
+  [enable_static_link=yes], [enable_static_link=no])
+AM_CONDITIONAL(USE_STATIC_LINK, test $enable_static_link = yes)
+
 # OS dependent compiler flags
 case "$host" in
 *-solaris*)
@@ -256,14 +264,6 @@ if test "${boost_include_path}" ; then
 fi
 AC_SUBST(BOOST_INCLUDES)
 
-AC_ARG_WITH([boost-lib],
-AC_HELP_STRING([--with-boost-lib=PATH],
-  [specify exact directory for Boost libraries]),
-  [if test "$withval" != "yes" -a "$withval" != "no"; then
-   BOOST_LDFLAGS="-L$withval"
-   fi])
-AC_SUBST(BOOST_LDFLAGS)
-
 #
 # Check availability of gtest, which will be used for unit tests.
 #
@@ -415,7 +415,11 @@ AC_CONFIG_FILES([Makefile
                  src/bin/zonemgr/tests/Makefile
                  src/bin/usermgr/Makefile
                  src/lib/Makefile
+                 src/lib/bench/Makefile
+                 src/lib/bench/example/Makefile
+                 src/lib/bench/tests/Makefile
                  src/lib/cc/Makefile
+                 src/lib/cc/tests/Makefile
                  src/lib/python/Makefile
                  src/lib/python/isc/Makefile
                  src/lib/python/isc/datasrc/Makefile
@@ -434,6 +438,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/dns/python/Makefile
                  src/lib/dns/python/tests/Makefile
                  src/lib/exceptions/Makefile
+                 src/lib/exceptions/tests/Makefile
                  src/lib/datasrc/Makefile
                  src/lib/datasrc/tests/Makefile
                  src/lib/xfr/Makefile

+ 1 - 1
doc/Doxyfile

@@ -568,7 +568,7 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth
+INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/lib/bench
 
 # 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

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


+ 6 - 24
doc/guide/bind10-guide.xml

@@ -75,9 +75,8 @@
 	data source backend is SQLite3. The authoritative server
 	requires SQLite 3.3.9 or newer.
         The <command>b10-xfrin</command> and <command>b10-xfrout</command>
-	modules require the libboost library,
-        libpython3 library,
-	and the Python _sqlite3.so module.
+        modules require the libpython3 library and the Python
+        _sqlite3.so module.
       </para></note>
 <!-- TODO: this will change ... -->
 
@@ -272,9 +271,8 @@ var/
         </para>
 
         <para>
-	  The Boost Library, Python Library,
-	  and Python _sqlite3 module are required to enable the
-	  Xfrout and Xfrin support.
+	  The Python Library and Python _sqlite3 module are required to
+          enable the Xfrout and Xfrin support.
         </para>
 
         <note><simpara>
@@ -487,14 +485,6 @@ var/
           </varlistentry>
 
           <varlistentry>
-            <term>--with-boost-lib</term>
-            <listitem> 
-              <simpara>Define the path to find the Boost library.
-              </simpara>
-            </listitem> 
-          </varlistentry>
-
-          <varlistentry>
             <term>--with-pythonpath</term>
             <listitem> 
               <simpara>Define the path to Python 3.1 if it is not in the
@@ -520,10 +510,10 @@ var/
 
         <para>
           For example, the following configures it to
-    find the Boost headers and library, find the
+    find the Boost headers, find the
     Python interpreter, and sets the installation location:
 
-          <screen>$ <userinput>./configure --with-boost-lib=/usr/pkg/lib \
+          <screen>$ <userinput>./configure \
       --with-boost-include=/usr/pkg/include \
       --with-pythonpath=/usr/pkg/bin/python3.1 \
       --prefix=/opt/bind10</userinput></screen>
@@ -557,14 +547,6 @@ var/
           <para>The install step may require superuser privileges.</para>
         </note>
 
-<!-- Trac #148 -->
-        <note><simpara>
-	  Depending on your system and the location of your Boost
-	  Python and Python shared libraries, you may need to
-	  configure your run-time linker to find them (such as
-	  setting LD_LIBRARY_PATH).
-        </simpara></note>
-
       </section>
 
   <!-- TODO: tests -->

+ 5 - 0
ext/asio/README

@@ -0,0 +1,5 @@
+ASIO library header files
+Version 1.4.5 (2010-05-12)
+Downloaded from http://sourceforge.net/projects/asio/files
+Project page: http://think-async.com/Asio
+No local modifications.

+ 11 - 6
src/bin/auth/Makefile.am

@@ -8,6 +8,10 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 CLEANFILES = *.gcno *.gcda auth.spec spec_config.h
@@ -47,15 +51,16 @@ libasio_link_a_CPPFLAGS = $(AM_CPPFLAGS)
 BUILT_SOURCES = spec_config.h 
 pkglibexec_PROGRAMS = b10-auth
 b10_auth_SOURCES = auth_srv.cc auth_srv.h
+b10_auth_SOURCES += change_user.cc change_user.h
 b10_auth_SOURCES += common.h
 b10_auth_SOURCES += main.cc
-b10_auth_LDADD =  $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a
-b10_auth_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns++.a
-b10_auth_LDADD += $(top_builddir)/src/lib/config/.libs/libcfgclient.a
-b10_auth_LDADD += $(top_builddir)/src/lib/cc/.libs/libcc.a
-b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
+b10_auth_LDADD =  $(top_builddir)/src/lib/datasrc/libdatasrc.la
+b10_auth_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+b10_auth_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_auth_LDADD += $(top_builddir)/src/bin/auth/libasio_link.a
-b10_auth_LDADD += $(top_builddir)/src/lib/xfr/.libs/libxfr.a
+b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 b10_auth_LDADD += $(SQLITE_LIBS)
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir

+ 1 - 0
src/bin/auth/asio_link.cc

@@ -368,6 +368,7 @@ public:
         dns_message_(Message::PARSE),
         custom_callback_(NULL)
     {
+        socket_.set_option(socket_base::reuse_address(true));
         // Set v6-only (we use a different instantiation for v4,
         // otherwise asio will bind to both v4 and v6
         if (addr.is_v6()) {

+ 6 - 4
src/bin/auth/auth_srv.cc

@@ -342,10 +342,8 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, Message& message,
     }
 
     try {
-        if (!xfrout_connected_) {
-            xfrout_client_.connect();
-            xfrout_connected_ = true;
-        }
+        xfrout_client_.connect();
+        xfrout_connected_ = true;
         xfrout_client_.sendXfroutRequestInfo(
             io_message.getSocket().getNative(),
             io_message.getData(),
@@ -368,6 +366,10 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, Message& message,
                          verbose_mode_);
         return (true);
     }
+
+    xfrout_client_.disconnect();
+    xfrout_connected_ = false;
+
     return (false);
 }
 

+ 31 - 5
src/bin/auth/b10-auth.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-auth
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: March 16, 2010
+.\"      Date: July 29, 2010
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-AUTH" "8" "March 16, 2010" "BIND10" "BIND10"
+.TH "B10\-AUTH" "8" "July 29, 2010" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
 b10-auth \- Authoritative DNS server
 .SH "SYNOPSIS"
 .HP \w'\fBb10\-auth\fR\ 'u
-\fBb10\-auth\fR [\fB\-4\fR] [\fB\-6\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-v\fR]
+\fBb10\-auth\fR [\fB\-4\fR] [\fB\-6\fR] [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-u\ \fR\fB\fIusername\fR\fR] [\fB\-v\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -64,13 +64,30 @@ The arguments are as follows:
 \fB\-4\fR
 .RS 4
 Enables IPv4 only mode\&. This switch may not be used with
-\fB\-6\fR\&. By default, it listens on both IPv4 and IPv6 (if capable)\&.
+\fB\-6\fR
+nor
+\fB\-a\fR\&. By default, it listens on both IPv4 and IPv6 (if capable)\&.
 .RE
 .PP
 \fB\-6\fR
 .RS 4
 Enables IPv6 only mode\&. This switch may not be used with
-\fB\-4\fR\&. By default, it listens on both IPv4 and IPv6 (if capable)\&.
+\fB\-4\fR
+nor
+\fB\-a\fR\&. By default, it listens on both IPv4 and IPv6 (if capable)\&.
+.RE
+.PP
+\fB\-a \fR\fB\fIaddress\fR\fR
+.RS 4
+The IPv4 or IPv6 address to listen on\&. This switch may not be used with
+\fB\-4\fR
+nor
+\fB\-6\fR\&. The default is to listen on all addresses\&. (This is a short term workaround\&. This argument may change\&.)
+.RE
+.PP
+\fB\-n\fR
+.RS 4
+Do not cache answers in memory\&. The default is to use the cache for faster responses\&. The cache keeps the most recent 30,000 answers (positive and negative) in memory for 30 seconds (instead of querying the data source, such as SQLite3 database, each time)\&.
 .RE
 .PP
 \fB\-p \fR\fB\fInumber\fR\fR
@@ -93,6 +110,15 @@ The Y1 prototype runs on all interfaces and on this nonstandard port\&.
 .RE
 .RE
 .PP
+\fB\-u \fR\fB\fIusername\fR\fR
+.RS 4
+The user name of the
+\fBb10\-auth\fR
+daemon\&. If specified, the daemon changes the process owner to the specified user\&. The
+\fIusername\fR
+must be either a valid numeric user ID or a valid user name\&. By default the daemon runs as the user who invokes it\&.
+.RE
+.PP
 \fB\-v\fR
 .RS 4
 Enabled verbose mode\&. This enables diagnostic messages to STDERR\&.

+ 46 - 3
src/bin/auth/b10-auth.xml

@@ -21,7 +21,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>March 16, 2010</date>
+    <date>July 29, 2010</date>
   </refentryinfo>
 
   <refmeta>
@@ -47,7 +47,10 @@
       <command>b10-auth</command>
       <arg><option>-4</option></arg>
       <arg><option>-6</option></arg>
+      <arg><option>-a <replaceable>address</replaceable></option></arg>
+      <arg><option>-n</option></arg>
       <arg><option>-p <replaceable>number</replaceable></option></arg>
+      <arg><option>-u <replaceable>username</replaceable></option></arg>
       <arg><option>-v</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -95,7 +98,8 @@
         <term><option>-4</option></term>
         <listitem><para>
           Enables IPv4 only mode.
-          This switch may not be used with <option>-6</option>.
+          This switch may not be used with <option>-6</option> nor
+          <option>-a</option>.
           By default, it listens on both IPv4 and IPv6 (if capable).
         </para></listitem>
       </varlistentry>
@@ -104,12 +108,37 @@
         <term><option>-6</option></term>
         <listitem><para>
           Enables IPv6 only mode.
-          This switch may not be used with <option>-4</option>.
+          This switch may not be used with <option>-4</option> nor
+          <option>-a</option>.
           By default, it listens on both IPv4 and IPv6 (if capable).
         </para></listitem>
       </varlistentry>
 
       <varlistentry>
+        <term><option>-a <replaceable>address</replaceable></option></term>
+
+        <listitem>
+          <para>The IPv4 or IPv6 address to listen on.
+            This switch may not be used with <option>-4</option> nor
+            <option>-6</option>.
+            The default is to listen on all addresses.
+            (This is a short term workaround. This argument may change.)   
+          </para>                      
+         </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-n</option></term>
+        <listitem><para>
+          Do not cache answers in memory.
+          The default is to use the cache for faster responses.
+	  The cache keeps the most recent 30,000 answers (positive
+	  and negative) in memory for 30 seconds (instead of querying
+	  the data source, such as SQLite3 database, each time).
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-p <replaceable>number</replaceable></option></term>
         <listitem><para>
           The port number it listens on.
@@ -120,6 +149,20 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>-u <replaceable>username</replaceable></option></term>
+        <listitem>
+	  <para>
+	    The user name of the <command>b10-auth</command> daemon.
+	    If specified, the daemon changes the process owner to the
+	    specified user.
+	    The <replaceable>username</replaceable> must be either a
+	    valid numeric user ID or a valid user name.
+	    By default the daemon runs as the user who invokes it.
+	  </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-v</option></term>
         <listitem><para>
           Enabled verbose mode. This enables diagnostic messages to

+ 55 - 0
src/bin/auth/change_user.cc

@@ -0,0 +1,55 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#include <errno.h>
+#include <string.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <auth/common.h>
+
+using namespace boost;
+
+void
+changeUser(const char* const username) {
+    const struct passwd *runas_pw = NULL;
+
+    runas_pw = getpwnam(username);
+    endpwent();
+    if (runas_pw == NULL) {
+        try {
+            runas_pw = getpwuid(lexical_cast<uid_t>(username));
+            endpwent();
+        } catch (const bad_lexical_cast&) {
+            ;                   // fall through to isc_throw below.
+        }
+    }
+    if (runas_pw == NULL) {
+        isc_throw(FatalError, "Unknown user name or UID:" << username);
+    }
+
+    if (setgid(runas_pw->pw_gid) < 0) {
+        isc_throw(FatalError, "setgid() failed: " << strerror(errno));
+    }
+
+    if (setuid(runas_pw->pw_uid) < 0) {
+        isc_throw(FatalError, "setuid() failed: " << strerror(errno));
+    }
+}

+ 59 - 0
src/bin/auth/change_user.h

@@ -0,0 +1,59 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#ifndef __CHANGE_USER_H
+#define __CHANGE_USER_H 1
+
+/// \brief Change the run time user.
+///
+/// This function changes the user and its group of the authoritative server
+/// process.
+///
+/// On success the user ID of the process is changed to the specified user,
+/// and the group is changed to that of the new user.
+///
+/// This is considered a short term workaround until we develop clearer
+/// privilege separation, where the server won't even have to open privileged
+/// ports and can be started by a non privileged user from the beginning.
+/// This function therefore ignores some corner case problems (see below)
+/// which we would address otherwise.
+///
+/// \c username can be either a textual user name or its numeric ID.
+/// If the specified user name (or ID) doesn't specify a local user ID
+/// or the user originally starting the process doesn't have a permission
+/// of changing the user to \c username, this function throws an exception
+/// of class \c FatalError.
+///
+/// This function internally uses system libraries that do not guarantee
+/// reentrancy.  In fact, it doesn't even expect to be called more than once.
+/// The behavior is undefined if this function is called from multiple threads
+/// simultaneously or more generally called multiple times.
+///
+/// This function only offers the basic exception guarantee, that is, if
+/// an exception is thrown from this function, it's possible that an exception
+/// is thrown after changing the group ID.  This function doesn't recover
+/// from that situation.  In practice, the process is expected to consider
+/// this event a fatal error and will immediately exit, and shouldn't cause
+/// a real trouble.
+///
+/// \param username User name or ID of the new effective user.
+void changeUser(const char* const username);
+
+#endif // __CHANGE_USER_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 17 - 2
src/bin/auth/main.cc

@@ -41,6 +41,7 @@
 
 #include <auth/spec_config.h>
 #include <auth/common.h>
+#include <auth/change_user.h>
 #include <auth/auth_srv.h>
 #include <auth/asio_link.h>
 
@@ -97,9 +98,10 @@ main(int argc, char* argv[]) {
     int ch;
     const char* port = DNSPORT;
     const char* address = NULL;
+    const char* uid = NULL;
     bool use_ipv4 = true, use_ipv6 = true, cache = true;
 
-    while ((ch = getopt(argc, argv, "46a:np:v")) != -1) {
+    while ((ch = getopt(argc, argv, "46a:np:u:v")) != -1) {
         switch (ch) {
         case '4':
             // Note that -4 means "ipv4 only", we need to set "use_ipv6" here,
@@ -121,6 +123,9 @@ main(int argc, char* argv[]) {
         case 'p':
             port = optarg;
             break;
+        case 'u':
+            uid = optarg;
+            break;
         case 'v':
             verbose_mode = true;
             break;
@@ -151,7 +156,13 @@ main(int argc, char* argv[]) {
     Session* xfrin_session = NULL;
     bool xfrin_session_established = false; // XXX (see Trac #287)
     ModuleCCSession* config_session = NULL;
-    XfroutClient xfrout_client(UNIX_SOCKET_FILE);
+    string xfrout_socket_path;
+    if (getenv("B10_FROM_BUILD") != NULL) {
+        xfrout_socket_path = string(getenv("B10_FROM_BUILD")) + "/auth_xfrout_conn";
+    } else {
+        xfrout_socket_path = UNIX_SOCKET_FILE;
+    }
+    XfroutClient xfrout_client(xfrout_socket_path);
     try {
         string specfile;
         if (getenv("B10_FROM_BUILD")) {
@@ -188,6 +199,10 @@ main(int argc, char* argv[]) {
                                              my_command_handler);
         cout << "[b10-auth] Configuration channel established." << endl;
 
+        if (uid != NULL) {
+            changeUser(uid);
+        }
+
         xfrin_session = new Session(io_service->get_io_service());
         cout << "[b10-auth] Xfrin session channel created." << endl;
         xfrin_session->establish(NULL);

+ 12 - 6
src/bin/auth/tests/Makefile.am

@@ -5,6 +5,10 @@ AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
 CLEANFILES = *.gcno *.gcda
 
 TESTS =
@@ -13,20 +17,22 @@ 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.cc
 run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
+run_unittests_SOURCES += ../change_user.h ../change_user.cc
 run_unittests_SOURCES += auth_srv_unittest.cc
+run_unittests_SOURCES += change_user_unittest.cc
 run_unittests_SOURCES += asio_link_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
-run_unittests_LDADD +=  $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a
-run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/.libs/libdns++.a
-run_unittests_LDADD += $(top_builddir)/src/lib/config/.libs/libcfgclient.a
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/.libs/libcc.a
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
+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/config/libcfgclient.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/bin/auth/libasio_link.a
-run_unittests_LDADD += $(top_builddir)/src/lib/xfr/.libs/libxfr.a
+run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 endif
 
 noinst_PROGRAMS = $(TESTS)

+ 7 - 1
src/bin/auth/tests/asio_link_unittest.cc

@@ -108,8 +108,14 @@ TEST(IOServiceTest, badAddress) {
 TEST(IOServiceTest, unavailableAddress) {
     // These addresses should generally be unavailable as a valid local
     // address, although there's no guarantee in theory.
-    EXPECT_THROW(IOService(NULL, *TEST_PORT, *"ffff:ffff::"), IOError);
     EXPECT_THROW(IOService(NULL, *TEST_PORT, *"255.255.0.0"), IOError);
+
+    // Some OSes would simply reject binding attempt for an AF_INET6 socket
+    // to an IPv4-mapped IPv6 address.  Even if those that allow it, since
+    // the corresponding IPv4 address is the same as the one used in the
+    // AF_INET socket case above, it should at least show the same result
+    // as the previous one.
+    EXPECT_THROW(IOService(NULL, *TEST_PORT, *"::ffff:255.255.0.0"), IOError);
 }
 
 TEST(IOServiceTest, duplicateBind) {

+ 4 - 2
src/bin/auth/tests/auth_srv_unittest.cc

@@ -469,7 +469,7 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
     // so we shouldn't have to respond.
     EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                            response_renderer));
-    EXPECT_TRUE(xfrout.isConnected());
+    EXPECT_FALSE(xfrout.isConnected());
 }
 
 TEST_F(AuthSrvTest, AXFRConnectFail) {
@@ -481,6 +481,8 @@ TEST_F(AuthSrvTest, AXFRConnectFail) {
                                       response_renderer));
     headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+    // For a shot term workaround with xfrout we currently close the connection
+    // for each AXFR attempt
     EXPECT_FALSE(xfrout.isConnected());
 }
 
@@ -490,7 +492,7 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                         RRType::AXFR(), IPPROTO_TCP);
     server.processMessage(*io_message, parse_message, response_renderer);
-    EXPECT_TRUE(xfrout.isConnected());
+    EXPECT_FALSE(xfrout.isConnected()); // see above
 
     xfrout.disableSend();
     parse_message.clear(Message::PARSE);

+ 67 - 0
src/bin/auth/tests/change_user_unittest.cc

@@ -0,0 +1,67 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#include <stdlib.h>
+#include <unistd.h>             // for getuid
+
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <gtest/gtest.h>
+
+#include <auth/common.h>
+#include <auth/change_user.h>
+
+using namespace std;
+
+namespace {
+TEST(ChangeUserTest, changeToTheSameUser) {
+    const char* const my_username = getenv("USER");
+
+    // normally the USER environment variable should be set to the name
+    // of the local user running this test, but it's not always the case.
+    if (my_username == NULL) {
+        cerr << "Environment variable USER is undefined, skipping the test"
+             << endl;
+        return;
+    }
+
+    // changing to the run time user should succeed.
+    EXPECT_NO_THROW(changeUser(my_username));
+}
+
+TEST(ChangeUserTest, changeToTheSameUserId) {
+    // same as above, but using numeric user ID
+    EXPECT_NO_THROW(changeUser(
+                        (boost::lexical_cast<string>(getuid())).c_str()));
+}
+
+TEST(ChangeUserTest, badUID) {
+    // -1 should be an invalid numeric UID, and (hopefully) shouldn't be
+    // a valid textual username.
+    EXPECT_THROW(changeUser("-1"), FatalError);
+}
+
+TEST(ChangeUserTest, promotionAttempt) {
+    // change to root should fail unless the running user is a super user.
+    if (getuid() == 0) {
+        cerr << "Already a super user, skipping the test" << endl;
+        return;
+    }
+    EXPECT_THROW(changeUser("root"), FatalError);
+}
+}

+ 31 - 6
src/bin/bind10/bind10.8

@@ -2,12 +2,12 @@
 .\"     Title: bind10
 .\"    Author: [see the "AUTHORS" section]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: April 19, 2010
+.\"      Date: July 29, 2010
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "BIND10" "8" "April 19, 2010" "BIND10" "BIND10"
+.TH "BIND10" "8" "July 29, 2010" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
 bind10 \- BIND 10 boss process
 .SH "SYNOPSIS"
 .HP \w'\fBbind10\fR\ 'u
-\fBbind10\fR [\fB\-m\ \fR\fB\fInumber\fR\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-v\fR] [\fB\-msgq\-port\ \fR\fB\fInumber\fR\fR] [\fB\-port\ \fR\fB\fInumber\fR\fR] [\fB\-verbose\fR]
+\fBbind10\fR [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-address\ \fR\fB\fIaddress\fR\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-verbose\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -32,11 +32,26 @@ daemon starts up other BIND 10 required daemons\&. It handles restarting of exit
 .PP
 The arguments are as follows:
 .PP
-\fB\-m\fR \fInumber\fR, \fB\-\-msgq\-port\fR \fInumber\fR
+\fB\-a\fR \fIaddress\fR, \fB\-\-address\fR \fIaddress\fR
 .RS 4
-The port number for the
+The IPv4 or IPv6 address for the
+\fBb10-auth\fR(8)
+daemon to listen on\&. The default is to listen on all addresses\&. (This is a short term workaround\&. This argument may change\&.)
+.RE
+.PP
+\fB\-m\fR \fIfile\fR, \fB\-\-msgq\-socket\-file\fR \fIfile\fR
+.RS 4
+The UNIX domain socket file for the
 \fBb10-msgq\fR(8)
-daemon to listen on\&. The default is 9912\&.
+daemon to use\&. The default is
+/usr/local/var/bind10\-devel/msg_socket\&.
+.RE
+.PP
+\fB\-n\fR, \fB\-\-no\-cache\fR
+.RS 4
+Disables the hot\-spot caching used by the
+\fBb10-auth\fR(8)
+daemon\&.
 .RE
 .PP
 \fB\-p\fR \fInumber\fR, \fB\-\-port\fR \fInumber\fR
@@ -61,6 +76,16 @@ The Y1 prototype release uses a non\-default port for domain service\&.
 .RE
 .RE
 .PP
+\fB\-u\fR \fIuser\fR, \fB\-\-user\fR \fIname\fR
+.RS 4
+The username for
+\fBbind10\fR
+to run as\&.
+
+\fBbind10\fR
+must be initially ran as the root user to use this option\&. The default is to run as the current user\&.
+.RE
+.PP
 \fB\-v\fR, \fB\-\-verbose\fR
 .RS 4
 Display more about what is going on for

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

@@ -336,6 +336,8 @@ class BoB:
             authargs += ['-a', str(self.address)]
         if self.nocache:
             authargs += ['-n']
+        if self.uid:
+            authargs += ['-u', str(self.uid)]
         if self.verbose:
             authargs += ['-v']
             sys.stdout.write("Starting b10-auth using port %d" %

+ 54 - 9
src/bin/bind10/bind10.xml

@@ -21,7 +21,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>April 19, 2010</date>
+    <date>July 29, 2010</date>
   </refentryinfo>
 
   <refmeta>
@@ -45,12 +45,18 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>bind10</command>    
-      <arg><option>-m <replaceable>number</replaceable></option></arg>
+      <arg><option>-a <replaceable>address</replaceable></option></arg>
+      <arg><option>-m <replaceable>file</replaceable></option></arg>
+      <arg><option>-n</option></arg>
       <arg><option>-p <replaceable>number</replaceable></option></arg>
+      <arg><option>-u <replaceable>user</replaceable></option></arg>
       <arg><option>-v</option></arg>
-      <arg><option>-msgq-port <replaceable>number</replaceable></option></arg>
-      <arg><option>-port <replaceable>number</replaceable></option></arg>
-      <arg><option>-verbose</option></arg>
+      <arg><option>--address <replaceable>address</replaceable></option></arg>
+      <arg><option>--msgq-socket-file <replaceable>file</replaceable></option></arg>
+      <arg><option>--no-cache</option></arg>
+      <arg><option>--port <replaceable>number</replaceable></option></arg>
+      <arg><option>--user <replaceable>user</replaceable></option></arg>
+      <arg><option>--verbose</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -80,17 +86,43 @@
     <variablelist>
 
       <varlistentry>
-        <term><option>-m</option> <replaceable>number</replaceable>, <option>--msgq-port</option> <replaceable>number</replaceable></term>
+        <term><option>-a</option> <replaceable>address</replaceable>, <option>--address</option> <replaceable>address</replaceable></term>
 
         <listitem>
-          <para>The port number for the
-	    <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+	  <para>The IPv4 or IPv6 address for the
+	    <citerefentry><refentrytitle>b10-auth</refentrytitle><manvolnum>8</manvolnum></citerefentry>
             daemon to listen on.
-            The default is 9912.</para>
+            The default is to listen on all addresses. 
+            (This is a short term workaround. This argument may change.)
+          </para>
+         </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-m</option> <replaceable>file</replaceable>,
+           <option>--msgq-socket-file</option> <replaceable>file</replaceable></term>
+
+        <listitem>
+          <para>The UNIX domain socket file for the
+	    <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+            daemon to use.
+            The default is
+            <filename>/usr/local/var/bind10-devel/msg_socket</filename>.
+<!-- @localstatedir@/@PACKAGE_NAME@/msg_socket -->
+           </para>
          </listitem>
       </varlistentry>
 
       <varlistentry>
+        <term><option>-n</option>, <option>--no-cache</option></term>
+        <listitem>
+	  <para>Disables the hot-spot caching used by the
+	    <citerefentry><refentrytitle>b10-auth</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+	  daemon.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-p</option> <replaceable>number</replaceable>, <option>--port</option> <replaceable>number</replaceable></term>
 
         <listitem>
@@ -105,6 +137,18 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>-u</option> <replaceable>user</replaceable>, <option>--user</option> <replaceable>name</replaceable></term>
+
+        <listitem>
+          <para>The username for <command>bind10</command> to run as.
+<!-- TODO: example more detail. -->
+            <command>bind10</command> must be initially ran as the
+            root user to use this option.
+            The default is to run as the current user.</para>
+         </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-v</option>, <option>--verbose</option></term>
         <listitem>
 	  <para>Display more about what is going on for
@@ -112,6 +156,7 @@
 <!-- TODO: not true about all children yet -->
         </listitem>
       </varlistentry>
+
     </variablelist>
   </refsect1>
 

+ 19 - 10
src/bin/bindctl/bindcmd.py

@@ -38,7 +38,6 @@ import csv
 import json
 import pwd
 import getpass
-import traceback
 
 try:
     from collections import OrderedDict
@@ -123,7 +122,6 @@ class BindCmdInterpreter(Cmd):
         except FailToLogin as err:
             print(err)
             print(FAIL_TO_CONNECT_WITH_CMDCTL)
-            traceback.print_exc()
         except KeyboardInterrupt:
             print('\nExit from bindctl')
 
@@ -142,8 +140,8 @@ class BindCmdInterpreter(Cmd):
             users_info = csv.reader(csvfile)
             for row in users_info:
                 users.append([row[0], row[1]])
-        except (IOError, IndexError) as e:
-            pass
+        except (IOError, IndexError) as err:
+            print("Error reading saved username and password from %s%s: %s" % (dir, file_name, err))
         finally:
             if csvfile:
                 csvfile.close()
@@ -162,8 +160,9 @@ class BindCmdInterpreter(Cmd):
             writer = csv.writer(csvfile)
             writer.writerow([username, passwd])
             csvfile.close()
-        except Exception as e:
-            print(e, "\nCannot write %s%s; default user is not stored" % (dir, file_name))
+        except IOError as err:
+            print("Error saving user information:", err)
+            print("user info file name: %s%s" % (dir, file_name))
             return False
 
         return True
@@ -183,8 +182,8 @@ class BindCmdInterpreter(Cmd):
             try:
                 response = self.send_POST('/login', param)
                 data = response.read().decode()
-            except socket.error:
-                traceback.print_exc()
+            except socket.error as err:
+                print("Socket error while sending login information:", err)
                 raise FailToLogin()
 
             if response.status == http.client.OK:
@@ -206,8 +205,8 @@ class BindCmdInterpreter(Cmd):
                 response = self.send_POST('/login', param)
                 data = response.read().decode()
                 print(data)
-            except socket.error as e:
-                traceback.print_exc()
+            except socket.error as err:
+                print("Socket error while sending login information:", err)
                 raise FailToLogin()
 
             if response.status == http.client.OK:
@@ -543,6 +542,16 @@ class BindCmdInterpreter(Cmd):
                     identifier = cmd.params['identifier']
                 else:
                     identifier += cmd.params['identifier']
+
+                # Check if the module is known; for unknown modules
+                # we currently deny setting preferences, as we have
+                # no way yet to determine if they are ok.
+                module_name = identifier.split('/')[1]
+                if self.config_data is None or \
+                   not self.config_data.have_specification(module_name):
+                    print("Error: Module '" + module_name + "' unknown or not running")
+                    return
+
             if cmd.command == "show":
                 values = self.config_data.get_value_maps(identifier)
                 for value_map in values:

+ 5 - 0
src/bin/bindctl/tests/bindctl_test.py

@@ -237,6 +237,11 @@ class TestNameSequence(unittest.TestCase):
             assert self.random_names[i] == cmd_names[i+1]
             assert self.random_names[i] == module_names[i+1]
             i = i + 1
+
+    def test_apply_cfg_command(self):
+        self.tool.location = '/'
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/bar\" value=\"5\"")
+        self.tool.apply_config_cmd(cmd)
     
 class FakeBindCmdInterpreter(bindcmd.BindCmdInterpreter):
     def __init__(self):

+ 8 - 3
src/bin/cfgmgr/b10-cfgmgr.py.in

@@ -19,7 +19,7 @@
 
 import sys; sys.path.append ('@@PYTHONPATH@@')
 
-from isc.config.cfgmgr import ConfigManager
+from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError
 from isc.cc import SessionError
 import signal
 import os
@@ -52,10 +52,15 @@ def main():
     except SessionError as se:
         print("[b10-cfgmgr] Error creating config manager, "
               "is the command channel daemon running?")
+        return 1
     except KeyboardInterrupt as kie:
         print("[b10-cfgmgr] Interrupted, exiting")
+    except ConfigManagerDataReadError as cmdre:
+        print("[b10-cfgmgr] " + str(cmdre))
+        return 2
     if cm:
-        cm.write_config()
+        return cm.write_config()
+    return 0
 
 if __name__ == "__main__":
-    main()
+    sys.exit(main())

+ 6 - 2
src/bin/host/Makefile.am

@@ -3,12 +3,16 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
 CLEANFILES = *.gcno *.gcda
 
 bin_PROGRAMS = host
 host_SOURCES = host.cc
-host_LDADD = $(top_builddir)/src/lib/dns/.libs/libdns++.a
-host_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
+host_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
+host_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 
 #man_MANS = host.1
 #EXTRA_DIST = $(man_MANS) host.xml

+ 6 - 7
src/bin/msgq/b10-msgq.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-msgq
 .\"    Author: [see the "AUTHORS" section]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: April 19, 2010
+.\"      Date: August 4, 2010
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-MSGQ" "8" "April 19, 2010" "BIND10" "BIND10"
+.TH "B10\-MSGQ" "8" "August 4, 2010" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
 b10-msgq \- message routing daemon for the Command Channel
 .SH "SYNOPSIS"
 .HP \w'\fBb10\-msgq\fR\ 'u
-\fBb10\-msgq\fR [\fB\-m\ \fR\fB\fInumber\fR\fR] [\fB\-v\fR] [\fB\-\-msgq\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-verbose\fR]
+\fBb10\-msgq\fR [\fB\-s\ \fR\fB\fIfile\fR\fR] [\fB\-v\fR] [\fB\-\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-verbose\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -96,11 +96,10 @@ daemon may be cleanly stopped by sending the SIGTERM signal to the process\&. Th
 .PP
 The arguments are as follows:
 .PP
-\fB\-m \fR\fB\fInumber\fR\fR, \fB\-\-msgq\-port \fR\fB\fInumber\fR\fR
+\fB\-s \fR\fB\fIfile\fR\fR, \fB\-\-socket\-file \fR\fB\fIfile\fR\fR
 .RS 4
-The port number that
-\fBb10\-msgq\fR
-will listen on\&. The default is 9912\&.
+The UNIX domain socket file this daemon will use\&. The default is
+/usr/local/var/bind10\-devel/msg_socket\&.
 .RE
 .PP
 \fB\-v\fR, \fB\-\-verbose\fR

+ 10 - 8
src/bin/msgq/msgq.xml

@@ -21,7 +21,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>April 19, 2010</date>
+    <date>August 4, 2010</date>
   </refentryinfo>
 
   <refmeta>
@@ -45,9 +45,9 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>b10-msgq</command>
-      <arg><option>-m <replaceable>number</replaceable></option></arg>
+      <arg><option>-s <replaceable>file</replaceable></option></arg>
       <arg><option>-v</option></arg>
-      <arg><option>--msgq-port <replaceable>number</replaceable></option></arg>
+      <arg><option>--socket-file <replaceable>file</replaceable></option></arg>
       <arg><option>--verbose</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -111,12 +111,14 @@
 
     <variablelist>
       <varlistentry>
-        <term><option>-m <replaceable>number</replaceable></option>,
-          <option>--msgq-port <replaceable>number</replaceable></option></term>
+        <term><option>-s <replaceable>file</replaceable></option>,
+          <option>--socket-file <replaceable>file</replaceable></option></term>
         <listitem><para>
-          The port number that <command>b10-msgq</command> will listen on.
-          The default is 9912.</para>
-        </listitem>
+          The UNIX domain socket file this daemon will use.
+          The default is
+          <filename>/usr/local/var/bind10-devel/msg_socket</filename>.
+<!-- @localstatedir@/@PACKAGE_NAME@/msg_socket -->
+          </para></listitem>
       </varlistentry>
 
       <varlistentry>

+ 2 - 2
src/bin/usermgr/b10-cmdctl-usermgr.py.in

@@ -69,8 +69,8 @@ def usage():
 def main():
     filename = DEFAULT_FILE
     try: 
-        opts, args = getopt.getopt(sys.argv[1:], 'hvf:', 
-                                   ['help', 'file=', 'version=']) 
+        opts, args = getopt.getopt(sys.argv[1:], 'f:hv', 
+                                   ['file=', 'help', 'version']) 
     except getopt.GetoptError as err: 
         print(err) 
         usage() 

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

@@ -7,6 +7,6 @@ PYCOVERAGE = $(PYTHON)
 check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+	env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
 	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
 	done

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

@@ -7,6 +7,6 @@ PYCOVERAGE = $(PYTHON)
 check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
+	env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
 	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
 	done

+ 1 - 1
src/lib/Makefile.am

@@ -1 +1 @@
-SUBDIRS = exceptions dns cc config datasrc python xfr
+SUBDIRS = exceptions dns cc config datasrc python xfr bench

+ 10 - 0
src/lib/bench/Makefile.am

@@ -0,0 +1,10 @@
+SUBDIRS = . tests example
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+CLEANFILES = *.gcno *.gcda
+
+lib_LTLIBRARIES = libbench.la
+libbench_la_SOURCES = benchmark_util.h benchmark_util.cc
+EXTRA_DIST = benchmark.h

+ 403 - 0
src/lib/bench/benchmark.h

@@ -0,0 +1,403 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#ifndef __BENCHMARK_H
+#define __BENCHMARK_H 1
+
+#include <sys/time.h>
+
+#include <iostream>
+#include <ios>
+
+namespace isc {
+namespace bench {
+
+/// \brief Templated micro benchmark framework.
+///
+/// "Premature optimization is the root of all evil."
+/// But programmers are often tempted to focus on performance optimization
+/// too early.
+/// Likewise, it's not uncommon for an engineer to introduce a minor
+/// optimization with a lot of complicated code logic that actually improves
+/// performance only marginally.
+/// Making benchmark easier will help avoid such common pitfalls.
+/// Of course, it also helps when we really need to introduce optimization
+/// to identify where is the bottleneck and see how a particular optimization
+/// improves the performance.
+///
+/// The BenchMark class template provides a simple framework for so-called
+/// "stopwatch" micro benchmarking.
+/// It just encapsulates the common operations for this type of benchmark:
+/// repeat a specified operation for a given number of times,
+/// record the start and end times of the operations,
+/// and provide major accumulated results such as the number of iterations
+/// per second.
+/// The main goal of this class is to help developers write benchmark test
+/// cases with fewer strokes of typing.
+///
+/// The constructors of a \c BenchMark class is to take the number of
+/// iterations (which is referred to as \c niter below)
+/// and an object (or reference to it) of the type of the template
+/// parameter, \c T.  Class \c T implements the benchmark target code via
+/// its \c run() method, whose signature is as follows:
+/// \code  unsigned int T::run(); \endcode
+///
+/// A BenchMark class object, via its own \c run() method, calls \c T::run()
+/// for \c niter times.
+/// In the simplest form \c T::run() would perform a single operation to
+/// be benchmarked and returns 1.
+/// In some cases, however, the operation is very lightweight (e.g. performing
+/// a binary search on a moderate length of integer vector), and it may be
+/// desirable to have an internal iterations within \c T::run() to avoid
+/// the overhead of function calls to \c T::run().
+/// In that case, \c T::run() would return the number of internal iterations
+/// instead of 1.
+///
+/// The \c BenchMark::run() method records some statistics %data on the
+/// benchmarking, including the start and end times and the total number of
+/// iterations (which is the sum of the return value of \c T::run(), and,
+/// is equal to \c niter in the simplest case where \c T::run() always
+/// returns 1).
+/// This %data can be retried via other methods of \c BenchMark, but in
+/// the primarily intended use case the \c BenchMark object would calls its
+/// \c run() method at the end of its construction, and prints summarized
+/// statistics to the standard output.
+/// This way, the developer can only write a single line of code to perform
+/// everything other than the benchmark target code (see the example below).
+///
+/// \b Example
+///
+/// Suppose that we want to measure performance of the search (find)
+/// operation on STL set objects.  We'd first define the implementation
+/// class (to be the template parameter of the \c BenchMark class) as follows:
+///
+/// \code class SetSearchBenchMark {
+/// public:
+///    SetSearchBenchMark(const set<int>& data, const vector<int>& keys) :
+///        data_(data), keys_(keys)
+///    {}
+///    unsigned int run() {
+///        vector<int>::const_iterator iter;
+///        vector<int>::const_iterator end_key = keys_.end();
+///        for (iter = keys_.begin(); iter != end_key; ++iter) {
+///            data_.find(*iter);
+///        }        
+///        return (keys_.size());
+///    }
+///    const set<int>& data_;
+///    const vector<int>& keys_;
+/// }; \endcode
+///
+/// In its constructor the \c SetSearchBenchMark class takes a set of
+/// integers (\c %data) and a vector of integers (\c keys).  \c %data is
+/// the STL set to be searched, and \c keys stores the search keys.
+/// The constructor keeps local references to these objects.
+///
+/// The \c SetSearchBenchMark::run() method, which is called via
+/// \c BenchMark::run(), iterates over the key vector, and performs the
+/// \c find() method of the set class for each key.
+/// (This is only for performance measurement, so the result is ignored).
+/// Note that this \c run() method has its own internal iterations.
+/// This is because each invocation of \c find() would be pretty lightweight,
+/// and the function call overhead may be relatively heavier.
+/// Due to the internal iterations, this method returns the number of
+/// \c find() calls, which is equal to the size of the key vector.
+///
+/// Next, we should prepare test %data.  In this simple example, let's assume
+/// we use a fixed size: a set of 10,000 integers (from 0 to 9999), and use
+/// the same number of search keys randomly chosen from that range:
+/// \code
+///    set<int> data_set;
+///    vector<int> keys;
+///    for (int i = 0; i < 10000; ++i) {
+///        data_set.insert(i);
+///        keys.push_back(rand() % 10000);
+///    } \endcode
+///
+/// Then construct a \c BenchMark<SetSearchBenchMark> object with the
+/// test %data:
+/// \code
+///    BenchMark<SetSearchBenchMark>(100, SetSearchBenchMark(data_set, keys));
+/// \endcode
+/// Here we specify 100 for the number of iterations, which would cause
+/// 1 million search attempts in total.
+///
+/// That's it.  If we put these in a C++ source file with usual necessary
+/// stuff (such as \c %main()), compile it, and run the executable, then
+/// we'll see something like this:
+///
+/// \code Performed 1000000 iterations in 0.180172s (5550251.98ips) \endcode
+///
+/// It should be obvious what this means (ips stands for "iterations
+///  per second").
+///
+/// A complete example program of this measurement scenario (with some
+/// additional test cases and configurable parameters) can be found in
+/// example/search_bench.cc.
+///
+/// \b Customization
+///
+/// The above simple usage should be sufficient in many benchmark cases,
+/// but the \c BenchMark class provides some customization points by
+/// specializing some of its (templated) public methods.
+/// For example, assume you want to customize the output of benchmark result.
+/// It can be done by specializing \c BenchMark::printResult():
+/// \code namespace isc {
+/// namespace bench {
+/// template<>
+/// void
+/// BenchMark<SetSearchBenchMark>::printResult() const {
+///     cout << "Searched for " << target_.keys_.size() << " keys "
+///         << getIteration() << " times in " << getDuration() << "s" << endl;
+/// }
+/// }
+/// } \endcode
+///
+/// Then the Result would be something like this:
+///
+/// \code Searched for 10000 keys 1000000 times in 0.21s \endcode
+///
+/// Note that the specialization must be defined in the same namespace as
+/// that of the \c BenchMark class, that is, \c isc::bench.
+/// It should also be noted that the corresponding \c SetSearchBenchMark
+/// object can be accessed (through its public interfaces) via the \c target_
+/// member variable of \c BenchMark.
+///
+/// <b>Future Plans and Compatibility Notes</b>
+///
+/// Currently, benchmark developers need to write supplemental code that is
+/// not directly related to benchmarks (such as \c %main()) by hand.
+/// It would be better if we could minimize such development overhead.
+/// In future versions we may provide a common \c %main() function and
+/// option parsers, thereby allowing the developer to only write the benchmark
+/// classes and invoke them, just like what various unit test frameworks do.
+///
+/// If and when we implement it, some existing benchmark cases may need to be
+/// adjusted.
+template <typename T>
+class BenchMark {
+    ///
+    /// \name Constructors
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    /// We use the default destructor.
+    //@{
+private:
+    BenchMark(const BenchMark& source);
+    BenchMark& operator=(const BenchMark& source);
+public:
+    /// \bench Constructor for immediate run.
+    ///
+    /// This is the constructor that is expected to be used normally.
+    /// It runs the benchmark within the constructor and prints the result,
+    /// thereby making it possible to do everything with a single line of
+    /// code (see the above example).
+    ///
+    /// \param iterations The number of iterations.  The \c run() method will
+    /// be called this number of times.
+    /// \param target The templated class object that
+    /// implements the code to be benchmarked.
+    BenchMark(const int iterations, T target) :
+        iterations_(iterations), sub_iterations_(0), target_(target)
+    {
+        initialize(true);
+    }
+
+    /// \bench Constructor for finer-grained control.
+    ///
+    /// This constructor takes the third parameter, \c immediate, to control
+    /// whether to run the benchmark within the constructor.
+    /// It also takes a reference to the templated class object rather than
+    /// an object copy, so if the copy overhead isn't acceptable this
+    /// constructor must be used.
+    ///
+    /// \param iterations The number of iterations.  The \c run() method will
+    /// be called this number of times.
+    /// \param target A reference to the templated class object that
+    /// implements the code to be benchmarked.
+    /// \param immediate If \c true the benchmark will be performed within
+    /// the constructor; otherwise it only does initialization.
+    BenchMark(const int iterations, T& target, const bool immediate) :
+        iterations_(iterations), sub_iterations_(0), target_(target)
+    {
+        initialize(immediate);
+    }
+    //@}
+
+    /// \brief Hook to be called before starting benchmark.
+    ///
+    /// This method will be called from \c run() before starting the benchmark.
+    /// By default it's empty, but can be customized via template
+    /// specialization.
+    void setUp() {}
+
+    /// \brief Hook to be called after benchmark.
+    ///
+    /// This method will be called from \c run() when the benchmark completes.
+    /// By default it's empty, but can be customized via template
+    /// specialization.
+    void tearDown() {}
+
+    /// \brief Perform benchmark.
+    ///
+    /// This method first calls \c setUp().
+    /// It then records the current time, calls \c T::run() for the number
+    /// of times specified on construction, and records the time on completion.
+    /// Finally, it calls \c tearDown().
+    void run() {
+        setUp();
+
+        struct timeval beg, end;
+        gettimeofday(&beg, NULL);
+        for (int i = 0; i < iterations_; ++i) {
+            sub_iterations_ += target_.run();
+        }
+        gettimeofday(&end, NULL);
+        tv_diff_ = tv_subtract(end, beg);
+
+        tearDown();
+    }
+
+    /// \brief Print the benchmark result.
+    ///
+    /// This method prints the benchmark result in a common style to the
+    /// standard out.  The result contains the number of total iterations,
+    /// the duration of the test, and the number of iterations per second
+    /// calculated from the previous two parameters.
+    ///
+    /// A call to this method is only meaningful after the completion of
+    /// \c run().  The behavior is undefined in other cases.
+    void printResult() const {
+        std::cout.precision(6);
+        std::cout << "Performed " << getIteration() << " iterations in "
+                  << std::fixed << getDuration() << "s";
+        std::cout.precision(2);
+        std::cout << " (" << std::fixed << getIterationPerSecond() << "ips)"
+                  << std::endl;
+    }
+
+    /// \brief Return the number of iterations.
+    ///
+    /// It returns the total iterations of benchmark, which is the sum
+    /// of the return value of \c T::run() over all calls to it
+    /// (note that it may not equal to the number of calls to \c T::run(),
+    /// which was specified on construction of this class).
+    ///
+    /// A call to this method is only meaningful after the completion of
+    /// \c run().  The behavior is undefined in other cases.
+    unsigned int getIteration() const { return (sub_iterations_); }
+
+    /// \brief Return the duration of benchmark in seconds.
+    ///
+    /// The highest possible precision of this value is microseconds.
+    ///
+    /// A call to this method is only meaningful after the completion of
+    /// \c run().  The behavior is undefined in other cases.
+    double getDuration() const {
+        return (tv_diff_.tv_sec +
+                static_cast<double>(tv_diff_.tv_usec) / ONE_MILLION);
+    }
+
+    /// \brief Return the average duration per iteration in seconds.
+    ///
+    /// The highest possible precision of this value is microseconds.
+    /// The iteration is the sum of the return value of \c T::run() over
+    /// all calls to it (note that it may not equal to the number of calls
+    /// to \c T::run()).
+    ///
+    /// If it cannot calculate the average, it returns \c TIME_FAILURE.
+    ///
+    /// A call to this method is only meaningful after the completion of
+    /// \c run().  The behavior is undefined in other cases.
+    double getAverageTime() const {
+        if (sub_iterations_ == 0) {
+            return (TIME_FAILURE);
+        }
+        return ((tv_diff_.tv_sec +
+                 static_cast<double>(tv_diff_.tv_usec) / ONE_MILLION ) /
+                sub_iterations_);
+    }
+
+    /// \brief Return the number of possible iterations per second based on
+    /// the benchmark result.
+    ///
+    /// If it cannot calculate that number (e.g. because the duration is
+    /// too small) it returns \c ITERATION_FAILURE.
+    /// A call to this method is only meaningful after the completion of
+    /// \c run().  The behavior is undefined in other cases.
+    double getIterationPerSecond() const {
+        const double duration_usec = tv_diff_.tv_sec +
+            static_cast<double>(tv_diff_.tv_usec) / ONE_MILLION;
+        if (duration_usec == 0) {
+            return (ITERATION_FAILURE);
+        }
+        return (sub_iterations_ / duration_usec);
+    }
+public:
+    /// \brief A constant that indicates a failure in \c getAverageTime().
+    ///
+    /// This constant be used as double but is defined as int so that it can
+    /// be initialized within the class definition.  Type conversion will be
+    /// performed implicitly.
+    static const int TIME_FAILURE = -1;
+
+    /// \brief A constant that indicates a failure in
+    /// \c getIterationPerSecond().
+    ///
+    /// This constant be used as double but is defined as int so that it can
+    /// be initialized within the class definition.  Type conversion will be
+    /// performed implicitly.
+    static const int ITERATION_FAILURE = -1;
+private:
+    void initialize(const bool immediate) {
+        if (immediate) {
+            run();
+            printResult();
+        }
+    }
+private:
+    // return t1 - t2
+    struct timeval tv_subtract(const struct timeval& t1,
+                               const struct timeval& t2)
+    {
+        struct timeval result;
+
+        result.tv_sec = t1.tv_sec - t2.tv_sec;
+        if (t1.tv_usec >= t2.tv_usec) {
+            result.tv_usec = t1.tv_usec- t2.tv_usec;
+        } else {
+            result.tv_usec = ONE_MILLION + t1.tv_usec - t2.tv_usec;
+            --result.tv_sec;
+        }
+
+        return (result);
+    }
+private:
+    static const int ONE_MILLION = 1000000;
+    const unsigned int iterations_;
+    unsigned int sub_iterations_;
+    T& target_;
+    struct timeval tv_diff_;
+};
+
+}
+}
+#endif  // __BENCHMARK_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 116 - 0
src/lib/bench/benchmark_util.cc

@@ -0,0 +1,116 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/buffer.h>
+#include <dns/exceptions.h>
+#include <dns/name.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/rrtype.h>
+#include <dns/rrclass.h>
+#include <dns/question.h>
+
+#include <bench/benchmark_util.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dns;
+
+namespace isc {
+namespace bench {
+void
+loadQueryData(const char* const input_file, BenchQueries& queries,
+              const RRClass& qclass, const bool strict)
+{
+    ifstream ifs;
+
+    ifs.open(input_file, ios_base::in);
+    if ((ifs.rdstate() & istream::failbit) != 0) {
+        isc_throw(BenchMarkError, "failed to load query data file: " +
+                  string(input_file));
+    }
+    loadQueryData(ifs, queries, qclass, strict);
+    ifs.close();
+}
+
+void
+loadQueryData(istream& input, BenchQueries& queries, const RRClass& qclass,
+              const bool strict)
+{
+    string line;
+    unsigned int linenum = 0;
+    Message query_message(Message::RENDER);
+    OutputBuffer buffer(128); // this should be sufficiently large
+    MessageRenderer renderer(buffer);
+    while (getline(input, line), !input.eof()) {
+        ++linenum;
+        if (input.bad() || input.fail()) {
+            isc_throw(BenchMarkError,
+                      "Unexpected line in query data file around line " <<
+                      linenum);
+        }
+        if (line.empty() || line[0] == '#') {
+            continue;           // skip comment and blank lines
+        }
+
+        istringstream iss(line);
+        string qname_string, qtype_string;
+        iss >> qname_string >> qtype_string;
+        if (iss.bad() || iss.fail()) {
+            if (strict) {
+                isc_throw(BenchMarkError,
+                          "load query: unexpected input around line " <<
+                          linenum);
+            }
+            continue;
+        }
+
+        // We expect broken lines of data, which will be ignored with a
+        // warning message.
+        try {
+            query_message.clear(Message::RENDER);
+            query_message.setQid(0);
+            query_message.setOpcode(Opcode::QUERY());
+            query_message.setRcode(Rcode::NOERROR());
+            query_message.addQuestion(Question(Name(qname_string), qclass,
+                                               RRType(qtype_string)));
+
+            renderer.clear();
+            query_message.toWire(renderer);
+            vector<unsigned char> query_data(
+                static_cast<const unsigned char*>(buffer.getData()),
+                static_cast<const unsigned char*>(buffer.getData()) +
+                buffer.getLength());
+            queries.push_back(query_data);
+        } catch (const Exception& error) {
+            if (strict) {
+                isc_throw(BenchMarkError,
+                          "failed to parse/create query around line " <<
+                          linenum);
+            }
+            continue;
+        }
+    }
+}
+}
+}

+ 149 - 0
src/lib/bench/benchmark_util.h

@@ -0,0 +1,149 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#ifndef __BENCHMARK_UTIL_H
+#define __BENCHMARK_UTIL_H 1
+
+/// \file
+/// Utilities to help write benchmark cases.
+///
+/// The initial version of this library only contains utilities for very
+/// specific benchmark cases, that is, building DNS query data.
+/// It's not clear if we have more utilities including scenario-independent
+/// ones in future, but we have them here for now.
+/// If we find we only need utilities specific to individual benchmark
+/// scenarios, we may move them to more specific places.
+/// For example, the query generator may go to benchmarks for DNS server
+/// implementations.
+
+#include <istream>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace dns {
+class RRClass;
+}
+
+namespace bench {
+/// \brief An exception that is thrown if an error occurs within the benchmark
+/// module.
+class BenchMarkError : public Exception {
+public:
+    BenchMarkError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// \brief A convenient shortcut type to represent a sequence of query %data
+/// in wire format.
+typedef std::vector<std::vector<unsigned char> > BenchQueries; 
+
+/// \brief Load query %data from a file into a vector.
+///
+/// The format of the %data file is a sequence of tuples of query name and
+/// query type.  Each line specifies a single tuple.  Empty lines and
+/// lines beginning with a pound sign (#) are considered comments and will
+/// be ignored.  Example:
+/// \code
+/// # This is a comment line, will be ignored.  same for the next line.
+///
+/// www.example.com AAAA
+/// ftp.example.org NS
+/// text.dot.example TXT \endcode
+///
+/// For those who are familiar with BIND 9's queryperf tool, this is the
+/// same as the simplest form of the input file for queryperf.
+///
+/// For each tuple, this function builds a wire-format non recursive DNS
+/// query message, and appends it to the given vector in a form of
+/// a vector of <code>unsigned char</code>.
+///
+/// The resulting vector can be used, e.g., for benchmarking query processing
+/// code without involving disk access or network I/O.
+/// It would be handier than existing tool such as queryperf and can help
+/// measure the "bare" (or the best possible) performance of the query
+/// processing itself.
+///
+/// If this function fails to open the specified file to read the %data,
+/// an exception of class \c BenchMarkError will be thrown.
+/// If it fails to recognize an input line either as a comment or as
+/// a tuple of strings, an exception of class \c BenchMarkError will be
+/// thrown.
+///
+/// By default, this function does not require the strings be a valid
+/// domain name or a valid textual representation of an RR type.
+/// This is because the input %data may be built from a packet dump of
+/// real query samples without validation, which may contain bogus values.
+/// It would make more sense to just ignore the bogus %data than filter
+/// the sample beforehand.
+/// This behavior can be changed by setting the \c strict argument to
+/// \c true, in which case if this function fails to parse the query name
+/// or the type, it will throw an exception of class \c BenchMarkError.
+///
+/// If memory allocation fails during the processing, a corresponding standard
+/// exception will be thrown.
+///
+/// This function only offers the basic exception guarantee.  That is, if
+/// exception is thrown from this function, it is not guaranteed that
+/// \c queries keeps the content before this function is called.
+/// It is not so difficult to offer a stronger exception guarantee, but
+/// since this function is used in a limited usage, mainly for testing
+/// purposes, its benefit wouldn't outweigh the implementation complexity.
+///
+/// \param input_file A character string specifying the %data file name.
+/// \param queries A vector wherein the query %data is to be stored.
+/// \param qclass The RR class of the resulting queries.  The same RR class
+/// is used for all queries.
+/// \param strict If \c true, apply stricter validation on the query name and
+/// query RR types; otherwise invalid inputs will be ignored.
+void loadQueryData(const char* const input_file, BenchQueries& queries,
+                   const isc::dns::RRClass& qclass, const bool strict = false);
+
+/// \brief Load query %data from an input stream into a vector.
+///
+/// This version of function is same as
+/// loadQueryData(const char*,  BenchQueries&, const isc::dns::RRClass&, const bool)
+/// except it reads the input query sequence from a specified input stream.
+///
+/// This version will be used for a smaller scale test where query %data is
+///  hardcoded in the benchmark source code.  For example, we could build
+/// a sequence of wire-format queries via the following code:
+/// \code
+///    vector<QueryParam> queries;
+///    stringstream qstream;
+///    qstream << "www.example.com AAAA" << endl
+///            << "ftp.example.org NS" << endl
+///            << "text.dot.example TXT" << endl;
+///    loadQueryData(qstream, queries, RRClass::IN()); \endcode
+/// This will result in the same sequence of queries as the example using
+/// a %data file shown in the other version of the function.
+///
+/// \param input An input stream object that is to emit the query sequence.
+/// \param queries A vector wherein the query %data is to be stored.
+/// \param qclass The RR class of the resulting queries.  The same RR class
+/// is used for all queries.
+/// \param strict If \c true, apply stricter validation on the query name and
+/// query RR types; otherwise invalid inputs will be ignored.
+void loadQueryData(std::istream& input, BenchQueries& queries,
+                   const isc::dns::RRClass& qclass, const bool strict = false);
+}
+}
+#endif  // __BENCHMARK_UTIL_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 9 - 0
src/lib/bench/example/Makefile.am

@@ -0,0 +1,9 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+
+CLEANFILES = *.gcno *.gcda
+
+noinst_PROGRAMS = search_bench
+search_bench_SOURCES = search_bench.cc
+
+search_bench_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+

+ 144 - 0
src/lib/bench/example/search_bench.cc

@@ -0,0 +1,144 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#include <unistd.h>             // for getpid
+
+#include <cstdlib>              // for rand
+#include <algorithm>
+#include <iostream>
+#include <vector>
+#include <set>
+
+#include <exceptions/exceptions.h>
+
+#include <bench/benchmark.h>
+
+using namespace std;
+using namespace isc::bench;
+
+namespace {
+template <bool Sorted>
+class VectorSearchBenchMark {
+public:
+    VectorSearchBenchMark(const vector<int>& data,
+                          const vector<int>& keys) :
+        data_(data), keys_(keys)
+    {}
+    unsigned int run() {
+        vector<int>::const_iterator iter;
+        vector<int>::const_iterator end_key = keys_.end();
+        for (iter = keys_.begin(); iter != end_key; ++iter) {
+            if (Sorted) {
+                binary_search(data_.begin(), data_.end(), *iter);
+            } else {
+                find(data_.begin(), data_.end(), *iter);
+            }
+        }
+        return (keys_.size());
+    }
+private:
+    const vector<int>& data_;
+    const vector<int>& keys_;
+};
+
+class SetSearchBenchMark {
+public:
+    SetSearchBenchMark(const set<int>& data, const vector<int>& keys) :
+        data_(data), keys_(keys)
+    {}
+    unsigned int run() {
+        vector<int>::const_iterator iter;
+        vector<int>::const_iterator end_key = keys_.end();
+        for (iter = keys_.begin(); iter != end_key; ++iter) {
+            data_.find(*iter);
+        }        
+        return (keys_.size());
+    }
+public:   // make it visible to the BenchMark class
+    const set<int>& data_;
+private:
+    const vector<int>& keys_;
+};
+}
+
+namespace isc {
+namespace bench {
+template<>
+void
+BenchMark<SetSearchBenchMark>::setUp() {
+    cout << "Benchmark for searching std::set (size="
+         << target_.data_.size() << ")" << endl;    
+}
+}
+}
+
+namespace {
+const int DEFAULT_ITERATION = 100;
+const int DEFAULT_SIZE = 10000;
+
+void
+usage() {
+    cerr << "Usage: search_bench [-n iterations] [-s data_size]" << endl;
+    exit (1);
+}
+}
+
+int
+main(int argc, char* argv[]) {
+    int ch;
+    int iteration = DEFAULT_ITERATION;
+    int size = DEFAULT_SIZE;
+    while ((ch = getopt(argc, argv, "n:s:")) != -1) {
+        switch (ch) {
+        case 'n':
+            iteration = atoi(optarg);
+            break;
+        case 's':
+            size = atoi(optarg);
+            break;
+        case '?':
+        default:
+            usage();
+        }
+    }
+    argc -= optind;
+    argv += optind;
+    if (argc != 0) {
+        usage();
+    }
+
+    srand(getpid());
+    vector<int> data_vector;
+    set<int> data_set;
+    vector<int> keys;
+    for (int i = 0; i < size; ++i) {
+        data_vector.push_back(i);
+        data_set.insert(i);
+        keys.push_back(rand() % size);
+    }
+
+    cout << "Benchmark for linear search" << endl;
+    BenchMark<VectorSearchBenchMark<false> >(iteration,
+                                             VectorSearchBenchMark<false>(
+                                                 data_vector, keys));
+    cout << "Benchmark for binary search" << endl;
+    BenchMark<VectorSearchBenchMark<true> >(iteration,
+                                             VectorSearchBenchMark<true>(
+                                                 data_vector, keys));
+    BenchMark<SetSearchBenchMark>(iteration,
+                                  SetSearchBenchMark(data_set, keys));
+    return (0);
+}

+ 24 - 0
src/lib/bench/tests/Makefile.am

@@ -0,0 +1,24 @@
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += benchmark_unittest.cc
+run_unittests_SOURCES += loadquery_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/bench/libbench.la
+run_unittests_LDADD += $(GTEST_LDADD)
+endif
+
+noinst_PROGRAMS = $(TESTS)
+
+EXTRA_DIST = testdata/query.txt

+ 143 - 0
src/lib/bench/tests/benchmark_unittest.cc

@@ -0,0 +1,143 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#include <unistd.h>             // for usleep
+
+#include <bench/benchmark.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::bench;
+
+namespace {
+// Our "benchmark" simply sleeps for a short period, and reports a faked
+// number of iterations.
+class TestBenchMark {
+public:
+    TestBenchMark(const int sub_iterations, const int sleep_time) :
+        sub_iterations_(sub_iterations), sleep_time_(sleep_time),
+        setup_completed_(false), teardown_completed_(false)
+    {}
+    unsigned int run() {
+        usleep(sleep_time_);
+        return (sub_iterations_);
+    }
+    const int sub_iterations_;
+    const int sleep_time_;
+    bool setup_completed_;
+    bool teardown_completed_;
+};
+}
+
+namespace isc {
+namespace bench {
+template <>
+void
+BenchMark<TestBenchMark>::setUp() {
+    target_.setup_completed_ = true;
+};
+
+template <>
+void
+BenchMark<TestBenchMark>::tearDown() {
+    target_.teardown_completed_ = true;
+};
+
+// XXX: some compilers cannot find class static constants used in
+// EXPECT_xxx macross, for which we need an explicit definition.
+template <typename T>
+const int BenchMark<T>::TIME_FAILURE;
+}
+}
+
+namespace {
+TEST(BenchMarkTest, run) {
+    // use some uncommon iterations for testing purpose:
+    const int sub_iterations = 23;
+    const int sleep_time = 50000; // will sleep for 50ms
+    // we cannot expect particular accuracy on the measured duration, so
+    // we'll include some conservative margin (25%) and perform range
+    // comparison below.
+    const int duration_margin = 12500; // 12.5ms
+    const int ONE_MILLION = 1000000;
+
+    // Prerequisite check: since the tests in this case may depend on subtle
+    // timing, it may result in false positives.  There are reportedly systems
+    // where usleep() doesn't work as this test expects.  So we check the
+    // conditions before the tests, and if it fails skip the tests at the
+    // risk of overlooking possible bugs.
+    struct timeval check_begin, check_end;
+    gettimeofday(&check_begin, NULL);
+    usleep(sleep_time);
+    gettimeofday(&check_end, NULL);
+    check_end.tv_sec -= check_begin.tv_sec;
+    if (check_end.tv_usec >= check_begin.tv_usec) {
+        check_end.tv_usec = check_end.tv_usec - check_begin.tv_usec;
+    } else {
+        check_end.tv_usec = ONE_MILLION + check_begin.tv_usec -
+            check_end.tv_usec;
+        --check_end.tv_sec;
+    }
+    if (check_end.tv_sec != 0 ||
+        sleep_time - duration_margin > check_end.tv_usec ||
+        sleep_time + duration_margin < check_end.tv_usec) {
+        cerr << "Prerequisite check failed.  skipping test" << endl;
+        return;
+    }
+
+    TestBenchMark test_bench(sub_iterations, sleep_time);
+    BenchMark<TestBenchMark> bench(1, test_bench, false);
+    // Check pre-test conditions.
+    EXPECT_FALSE(test_bench.setup_completed_);
+    EXPECT_FALSE(test_bench.teardown_completed_);
+
+    bench.run();
+
+    // Check if specialized setup and teardown were performed.
+    EXPECT_TRUE(test_bench.setup_completed_);
+    EXPECT_TRUE(test_bench.teardown_completed_);
+
+    // Check accuracy of the measured statistics.
+    EXPECT_EQ(sub_iterations, bench.getIteration());
+    EXPECT_LT(sleep_time - duration_margin, bench.getDuration() * ONE_MILLION);
+    EXPECT_GT(sleep_time + duration_margin, bench.getDuration() * ONE_MILLION);
+    EXPECT_LT((sleep_time - duration_margin) /
+              static_cast<double>(sub_iterations),
+              bench.getAverageTime() * ONE_MILLION);
+    EXPECT_GT((sleep_time + duration_margin) /
+              static_cast<double>(sub_iterations),
+              bench.getAverageTime() * ONE_MILLION);
+    EXPECT_LT(static_cast<double>(sub_iterations) /
+              (sleep_time + duration_margin),
+              bench.getIterationPerSecond() / ONE_MILLION);
+    EXPECT_GT(static_cast<double>(sub_iterations) /
+              (sleep_time - duration_margin),
+              bench.getIterationPerSecond() / ONE_MILLION);
+}
+
+TEST(BenchMarkTest, runWithNoIteration) {
+    // we'll lie on the number of iteration (0).  it will result in
+    // meaningless result, but at least it shouldn't crash.
+    TestBenchMark test_bench(0, 0);
+    BenchMark<TestBenchMark> bench(1, test_bench, false);
+    bench.run();
+    EXPECT_EQ(0, bench.getIteration());
+    // Since the reported iteration is 0, naive calculation of the average
+    // time would cause a division by 0 failure.
+    EXPECT_EQ(bench.TIME_FAILURE, bench.getAverageTime());
+}
+}

+ 198 - 0
src/lib/bench/tests/loadquery_unittest.cc

@@ -0,0 +1,198 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include <sstream>
+
+#include <dns/buffer.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <bench/benchmark_util.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::bench;
+using namespace isc::dns;
+
+namespace {
+typedef pair<string, string> QueryParam;
+
+class LoadQueryTest : public ::testing::Test {
+protected:
+    LoadQueryTest() : query_rrclass(RRClass::IN()) {
+        queries.push_back(QueryParam("www.example.org", "AAAA"));
+        queries.push_back(QueryParam("www.example.com", "A"));
+        queries.push_back(QueryParam("test.example", "NS"));
+    }
+    RRClass query_rrclass;
+    BenchQueries result_queries;
+    vector<QueryParam> queries;
+    stringstream query_stream;
+    static const char* const DATA_DIR;
+};
+
+const char* const LoadQueryTest::DATA_DIR = TEST_DATA_DIR;
+
+class QueryInserter {
+public:
+    QueryInserter(stringstream& stream) : stream_(stream) {}
+    void operator()(const QueryParam& query) {
+        stream_ << query.first << " " << query.second << endl;
+    }
+private:
+    stringstream& stream_;
+};
+
+class QueryChecker {
+public:
+    QueryChecker(const vector<QueryParam>* expected, const RRClass& rrclass) :
+        expected_(expected), rrclass_(rrclass)
+    {
+        if (expected != NULL) {
+            iter_ = expected_->begin();
+        }
+    }
+    void operator()(const vector<unsigned char>& actual_data) {
+        InputBuffer buffer(&actual_data[0], actual_data.size());
+        Message message(Message::PARSE);
+        message.fromWire(buffer);
+
+        // Check if the header part indicates an expected standard query.
+        EXPECT_EQ(0, message.getQid());
+        EXPECT_EQ(Opcode::QUERY(), message.getOpcode());
+        EXPECT_EQ(Rcode::NOERROR(), message.getRcode());
+        EXPECT_EQ(Rcode::NOERROR(), message.getRcode());
+        EXPECT_FALSE(message.getHeaderFlag(MessageFlag::QR()));
+        EXPECT_FALSE(message.getHeaderFlag(MessageFlag::AA()));
+        EXPECT_EQ(1, message.getRRCount(Section::QUESTION()));
+        EXPECT_EQ(0, message.getRRCount(Section::ANSWER()));
+        EXPECT_EQ(0, message.getRRCount(Section::AUTHORITY()));
+        EXPECT_EQ(0, message.getRRCount(Section::ADDITIONAL()));
+
+        // Check if the question matches our original data, if the expected
+        // data is given.
+        if (expected_ != NULL) {
+            ConstQuestionPtr question = *message.beginQuestion();;
+            EXPECT_EQ(Name((*iter_).first), question->getName());
+            EXPECT_EQ(RRType((*iter_).second), question->getType());
+            EXPECT_EQ(rrclass_, question->getClass());
+        
+            ++iter_;
+        }
+    }
+private:
+    const vector<QueryParam>* expected_;
+    vector<QueryParam>::const_iterator iter_;
+    const RRClass rrclass_;
+};
+
+TEST_F(LoadQueryTest, load) {
+    for_each(queries.begin(), queries.end(), QueryInserter(query_stream));
+
+    loadQueryData(query_stream, result_queries, query_rrclass);
+
+    EXPECT_EQ(queries.size(), result_queries.size());
+    for_each(result_queries.begin(), result_queries.end(),
+             QueryChecker(&queries, query_rrclass));
+}
+
+TEST_F(LoadQueryTest, loadForCHClass) {
+    for_each(queries.begin(), queries.end(), QueryInserter(query_stream));
+    query_rrclass = RRClass::CH();
+
+    loadQueryData(query_stream, result_queries, query_rrclass);
+
+    EXPECT_EQ(queries.size(), result_queries.size());
+    for_each(result_queries.begin(), result_queries.end(),
+             QueryChecker(&queries, query_rrclass));
+}
+
+TEST_F(LoadQueryTest, loadWithComment) {
+    for_each(queries.begin(), queries.end(), QueryInserter(query_stream));
+    // add a comment line.  this shouldn't change the result.
+    query_stream << "# this is a comment" << endl;
+    query_stream << endl;       // empty line.  should be ignored, too.
+
+    loadQueryData(query_stream, result_queries, query_rrclass);
+    EXPECT_EQ(queries.size(), result_queries.size());
+    for_each(result_queries.begin(), result_queries.end(),
+             QueryChecker(&queries, query_rrclass));
+}
+
+TEST_F(LoadQueryTest, loadWithIncompleteData) {
+    for_each(queries.begin(), queries.end(), QueryInserter(query_stream));
+    // RRType is missing.  It should be ignored by default.
+    query_stream << "type-is-missing" << endl;
+
+    loadQueryData(query_stream, result_queries, query_rrclass);
+    EXPECT_EQ(queries.size(), result_queries.size());
+    for_each(result_queries.begin(), result_queries.end(),
+             QueryChecker(&queries, query_rrclass));
+}
+
+TEST_F(LoadQueryTest, loadWithIncompleteDataToBeRejected) {
+    for_each(queries.begin(), queries.end(), QueryInserter(query_stream));
+    // RRType is missing.  We're going to specify the "strict" check, so
+    // we should receive an exception.
+    query_stream << "type-is-missing" << endl;
+    EXPECT_THROW(loadQueryData(query_stream, result_queries, query_rrclass,
+                               true), BenchMarkError);
+}
+
+TEST_F(LoadQueryTest, loadWithBadData) {
+    for_each(queries.begin(), queries.end(), QueryInserter(query_stream));
+    // invalid RRType.  It should be ignored by default.
+    query_stream << "www.example.com NOSUCHRRTYPE" << endl;
+
+    loadQueryData(query_stream, result_queries, query_rrclass);
+    EXPECT_EQ(queries.size(), result_queries.size());
+    for_each(result_queries.begin(), result_queries.end(),
+             QueryChecker(&queries, query_rrclass));
+}
+
+TEST_F(LoadQueryTest, loadWithBadDataToBeRejected) {
+    for_each(queries.begin(), queries.end(), QueryInserter(query_stream));
+    // invalid RRType, which should trigger an exception.
+    query_stream << "www.example.com NOSUCHRRTYPE" << endl;
+    EXPECT_THROW(loadQueryData(query_stream, result_queries, query_rrclass,
+                               true), BenchMarkError);
+}
+
+TEST_F(LoadQueryTest, loadFromFile) {
+    const string data_file = string(DATA_DIR) + string("/query.txt");
+    loadQueryData(data_file.c_str(), result_queries, query_rrclass);
+    EXPECT_LT(0, result_queries.size());
+
+    // We are going to skip matching the query data; we only check the header.
+    // We could check the data, too, but to do so we need to populate the
+    // expected data from the file (or prepare a consistent copy locally).
+    // Since the implementation is shared with the stringstream case, the
+    // additional setup wouldn't be worthwhile.
+    for_each(result_queries.begin(), result_queries.end(),
+             QueryChecker(NULL, query_rrclass));
+}
+
+TEST_F(LoadQueryTest, loadFromFileNotExist) {
+    EXPECT_THROW(loadQueryData("notexistent/query.data", result_queries,
+                               query_rrclass), BenchMarkError);
+}
+}

+ 24 - 0
src/lib/bench/tests/run_unittests.cc

@@ -0,0 +1,24 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    return (RUN_ALL_TESTS());
+}

+ 6 - 0
src/lib/bench/tests/testdata/query.txt

@@ -0,0 +1,6 @@
+# This is sample query data for benchmark.
+# The format is the same as BIND 9's queryperf.
+
+www.example.com TXT
+www.example.org SOA
+ftp.example.org RRSIG

+ 2 - 17
src/lib/cc/Makefile.am

@@ -1,3 +1,5 @@
+SUBDIRS = . tests
+
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
 
@@ -19,20 +21,3 @@ session_config.h: session_config.h.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" session_config.h.pre >$@
 
 BUILT_SOURCES = session_config.h 
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-# (TODO: these need to be completed and moved to tests/)
-run_unittests_SOURCES = data_unittests.cc session_unittests.cc run_unittests.cc
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-# TODO: remove PTHREAD_LDFLAGS (and from configure too)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(PTHREAD_LDFLAGS)
-
-run_unittests_LDADD = libcc.la $(GTEST_LDADD)
-run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/.libs/libdns++.a
-run_unittests_LDADD +=  $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
-
-endif
-
-noinst_PROGRAMS = $(TESTS)

+ 1 - 1
src/lib/cc/session.cc

@@ -134,7 +134,7 @@ SessionImpl::readData(void* data, size_t datalen) {
     try {
         asio::read(socket_, asio::buffer(data, datalen));
     } catch (const asio::system_error& asio_ex) {
-        // to hide boost specific exceptions, we catch them explicitly
+        // to hide ASIO specific exceptions, we catch them explicitly
         // and convert it to SessionError.
         isc_throw(SessionError, "ASIO read failed: " << asio_ex.what());
     }

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

@@ -0,0 +1,31 @@
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+if USE_GXX			#XXX: see ../Makefile.am
+AM_CXXFLAGS += -Wno-unused-parameter
+endif
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+# (TODO: these need to be completed and moved to tests/)
+run_unittests_SOURCES = data_unittests.cc session_unittests.cc run_unittests.cc
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+# TODO: remove PTHREAD_LDFLAGS (and from configure too)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(PTHREAD_LDFLAGS)
+
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD +=  $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/exceptions/libexceptions.la
+
+endif
+
+noinst_PROGRAMS = $(TESTS)

+ 1 - 1
src/lib/cc/data_unittests.cc

@@ -18,7 +18,7 @@
 #include <boost/foreach.hpp>
 #include <boost/assign/std/vector.hpp>
 
-#include <data.h>
+#include <cc/data.h>
 
 using namespace isc::data;
 

src/lib/cc/run_unittests.cc → src/lib/cc/tests/run_unittests.cc


+ 2 - 1
src/lib/cc/session_unittests.cc

@@ -22,10 +22,11 @@
 #include <asio.hpp>
 
 #include <gtest/gtest.h>
-#include <session.h>
 
 #include <exceptions/exceptions.h>
 
+#include <cc/session.h>
+
 using namespace isc::cc;
 
 TEST(AsioSession, establish) {

+ 1 - 0
src/lib/config/Makefile.am

@@ -15,6 +15,7 @@ endif
 EXTRA_DIST =  testdata/b10-config-bad1.db
 EXTRA_DIST += testdata/b10-config-bad2.db
 EXTRA_DIST += testdata/b10-config-bad3.db
+EXTRA_DIST += testdata/b10-config-bad4.db
 EXTRA_DIST += testdata/b10-config.db
 EXTRA_DIST += testdata/data22_1.data
 EXTRA_DIST += testdata/data22_2.data

+ 1 - 1
src/lib/config/testdata/b10-config-bad1.db

@@ -1 +1 @@
-{'version': 0}
+{"version": 0}

+ 1 - 0
src/lib/config/testdata/b10-config-bad4.db

@@ -0,0 +1 @@
+{'version': 2}

+ 1 - 1
src/lib/config/testdata/b10-config.db

@@ -1 +1 @@
-{"version": 1, "TestModule": {"test": 125}}
+{"version": 2, "TestModule": {"test": 125}}

+ 4 - 0
src/lib/config/tests/Makefile.am

@@ -6,6 +6,10 @@ if USE_GXX
 AM_CXXFLAGS += -Wno-unused-parameter
 endif
 
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
 CLEANFILES = *.gcno *.gcda
 
 lib_LTLIBRARIES = libfake_session.la

+ 3 - 0
src/lib/datasrc/static_datasrc.cc

@@ -70,6 +70,7 @@ StaticDataSrcImpl::StaticDataSrcImpl() :
 {
     authors = RRsetPtr(new RRset(authors_name, RRClass::CH(),
                                  RRType::TXT(), RRTTL(0)));
+    authors->addRdata(generic::TXT("Chen Zhengzhang")); // Jerry
     authors->addRdata(generic::TXT("Evan Hunt"));
     authors->addRdata(generic::TXT("Han Feng"));
     authors->addRdata(generic::TXT("Jelte Jansen"));
@@ -80,6 +81,8 @@ StaticDataSrcImpl::StaticDataSrcImpl() :
     authors->addRdata(generic::TXT("Michael Graff"));
     authors->addRdata(generic::TXT("Naoki Kambe"));
     authors->addRdata(generic::TXT("Shane Kerr"));
+    authors->addRdata(generic::TXT("Shen Tingting"));
+    authors->addRdata(generic::TXT("Stephen Morris"));
     authors->addRdata(generic::TXT("Zhang Likun"));
 
     authors_ns = RRsetPtr(new RRset(authors_name, RRClass::CH(),

+ 8 - 4
src/lib/datasrc/tests/Makefile.am

@@ -4,6 +4,10 @@ AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
 CLEANFILES = *.gcno *.gcda
 
 TESTS =
@@ -22,10 +26,10 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
-run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a 
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns++.a 
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/.libs/libcc.a
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
+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/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 
 noinst_PROGRAMS = $(TESTS)

+ 0 - 1
src/lib/datasrc/tests/datasrc_unittest.cc

@@ -1056,7 +1056,6 @@ TEST_F(DataSrcMatchTest, updateWithNoMatch) {
     EXPECT_EQ(&datasrc1, match.getDataSource());
 }
 
-// This test currently fails.
 TEST_F(DataSrcMatchTest, initialUpdateWithNoMatch) {
     DataSrcMatch match(Name("www.example.com"), RRClass::IN());
 

+ 3 - 0
src/lib/datasrc/tests/static_unittest.cc

@@ -54,6 +54,7 @@ protected:
         version_data.push_back(PACKAGE_STRING);
 
         // XXX: in addition, the order the following items matter.
+        authors_data.push_back("Chen Zhengzhang");
         authors_data.push_back("Evan Hunt");
         authors_data.push_back("Han Feng");
         authors_data.push_back("Jelte Jansen");
@@ -64,6 +65,8 @@ protected:
         authors_data.push_back("Michael Graff");
         authors_data.push_back("Naoki Kambe");
         authors_data.push_back("Shane Kerr");
+        authors_data.push_back("Shen Tingting");
+        authors_data.push_back("Stephen Morris");
         authors_data.push_back("Zhang Likun");
 
         version_ns_data.push_back("version.bind.");

+ 1 - 1
src/lib/dns/message.h

@@ -80,7 +80,7 @@ typedef uint16_t qid_t;
 class InputBuffer;
 class MessageRenderer;
 class Message;
-struct MessageImpl;
+class MessageImpl;
 
 template <typename T>
 struct SectionIteratorImpl;

+ 2 - 0
src/lib/dns/python/libdns_python.cc

@@ -23,6 +23,8 @@
 //
 // And of course care has to be taken that all identifiers be unique
 
+// $Id$
+
 #define PY_SSIZE_T_CLEAN
 #include <Python.h>
 #include <structmember.h>

+ 16 - 16
src/lib/dns/python/message_python.cc

@@ -12,7 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: message_python.cc 1711 2010-04-14 15:14:53Z jelte $
+// $Id$
 
 #include <dns/message.h>
 using namespace isc::dns;
@@ -589,7 +589,7 @@ Rcode_init(s_Rcode* self UNUSED_PARAM, PyObject* args UNUSED_PARAM) {
         try {
             self->rcode = new Rcode(code);
             self->static_code = false;
-        } catch (isc::OutOfRange) {
+        } catch (const isc::OutOfRange&) {
             PyErr_SetString(PyExc_OverflowError,
                             "rcode out of range");
             return (-1);
@@ -1221,7 +1221,7 @@ Message_setHeaderFlag(s_Message* self, PyObject* args) {
     try {
         self->message->setHeaderFlag(*messageflag->messageflag);
         Py_RETURN_NONE;
-    } catch (isc::dns::InvalidMessageOperation imo) {
+    } catch (const InvalidMessageOperation& imo) {
         PyErr_Clear();
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
         return (NULL);
@@ -1238,7 +1238,7 @@ Message_clearHeaderFlag(s_Message* self, PyObject* args) {
     try {
         self->message->clearHeaderFlag(*messageflag->messageflag);
         Py_RETURN_NONE;
-    } catch (isc::dns::InvalidMessageOperation imo) {
+    } catch (const InvalidMessageOperation& imo) {
         PyErr_Clear();
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
         return (NULL);
@@ -1269,7 +1269,7 @@ Message_setDNSSECSupported(s_Message* self, PyObject* args) {
             self->message->setDNSSECSupported(false);
         }
         Py_RETURN_NONE;
-    } catch (isc::dns::InvalidMessageOperation imo) {
+    } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
         return (NULL);
     }
@@ -1289,10 +1289,10 @@ Message_setUDPSize(s_Message* self, PyObject* args) {
     try {
         self->message->setUDPSize(size);
         Py_RETURN_NONE;
-    } catch (isc::dns::InvalidMessageUDPSize imus) {
+    } catch (const InvalidMessageUDPSize& imus) {
         PyErr_SetString(po_InvalidMessageUDPSize, imus.what());
         return (NULL);
-    } catch (isc::dns::InvalidMessageOperation imo) {
+    } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
         return (NULL);
     }
@@ -1312,7 +1312,7 @@ Message_setQid(s_Message* self, PyObject* args) {
     try {
         self->message->setQid(id);
         Py_RETURN_NONE;
-    } catch (InvalidMessageOperation imo) {
+    } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
         return (NULL);
     }
@@ -1344,7 +1344,7 @@ Message_setRcode(s_Message* self, PyObject* args) {
     try {
         self->message->setRcode(*rcode->rcode);
         Py_RETURN_NONE;
-    } catch (InvalidMessageOperation imo) {
+    } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
         return (NULL);
     }
@@ -1379,7 +1379,7 @@ Message_setOpcode(s_Message* self, PyObject* args) {
     try {
         self->message->setOpcode(*opcode->opcode);
         Py_RETURN_NONE;
-    } catch (InvalidMessageOperation imo) {
+    } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
         return (NULL);
     }
@@ -1482,7 +1482,7 @@ Message_addRRset(s_Message* self, PyObject* args) {
             self->message->addRRset(*section->section, rrset->rrset, false);
         }
         Py_RETURN_NONE;
-    } catch (InvalidMessageOperation imo) {
+    } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
         return (NULL);
     }
@@ -1541,7 +1541,7 @@ Message_toWire(s_Message* self, PyObject* args) {
             // If we return NULL it is seen as an error, so use this for
             // None returns
             Py_RETURN_NONE;
-        } catch (isc::dns::InvalidMessageOperation imo) {
+        } catch (const InvalidMessageOperation& imo) {
             PyErr_Clear();
             PyErr_SetString(po_InvalidMessageOperation, imo.what());
             return (NULL);
@@ -1565,16 +1565,16 @@ Message_fromWire(s_Message* self, PyObject* args) {
     try {
         self->message->fromWire(inbuf);
         Py_RETURN_NONE;
-    } catch (isc::dns::InvalidMessageOperation imo) {
+    } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
         return (NULL);
-    } catch (isc::dns::DNSMessageFORMERR dmfe) {
+    } catch (const DNSMessageFORMERR& dmfe) {
         PyErr_SetString(po_DNSMessageFORMERR, dmfe.what());
         return (NULL);
-    } catch (isc::dns::DNSMessageBADVERS dmfe) {
+    } catch (const DNSMessageBADVERS& dmfe) {
         PyErr_SetString(po_DNSMessageBADVERS, dmfe.what());
         return (NULL);
-    } catch (isc::dns::MessageTooShort mts) {
+    } catch (const MessageTooShort& mts) {
         PyErr_SetString(po_MessageTooShort, mts.what());
         return (NULL);
     }

+ 13 - 13
src/lib/dns/python/name_python.cc

@@ -291,26 +291,26 @@ Name_init(s_Name* self, PyObject* args) {
 
             self->name = new Name(n, downcase == Py_True);
             self->position = 0;
-        } catch (EmptyLabel) {
+        } catch (const EmptyLabel&) {
             PyErr_SetString(po_EmptyLabel, "EmptyLabel");
             return (-1);
-        } catch (TooLongLabel) {
+        } catch (const TooLongLabel&) {
             PyErr_SetString(po_TooLongLabel, "TooLongLabel");
             return (-1);
-        } catch (BadLabelType) {
+        } catch (const BadLabelType&) {
             PyErr_SetString(po_BadLabelType, "BadLabelType");
             return (-1);
-        } catch (BadEscape) {
+        } catch (const BadEscape&) {
             PyErr_SetString(po_BadEscape, "BadEscape");
             return (-1);
-        } catch (TooLongName) {
+        } catch (const TooLongName&) {
             PyErr_SetString(po_TooLongName, "TooLongName");
             return (-1);
-        } catch (IncompleteName) {
+        } catch (const IncompleteName&) {
             PyErr_SetString(po_IncompleteName, "IncompleteName");
             return (-1);
 #ifdef CATCHMEMERR
-        } catch (std::bad_alloc) {
+        } catch (const std::bad_alloc&) {
             PyErr_NoMemory();
             return (-1);
 #endif
@@ -338,11 +338,11 @@ Name_init(s_Name* self, PyObject* args) {
             buffer.setPosition(position);
             self->name = new Name(buffer, downcase == Py_True);
             self->position = buffer.getPosition();
-        } catch (InvalidBufferPosition) {
+        } catch (const InvalidBufferPosition&) {
             PyErr_SetString(po_InvalidBufferPosition,
                             "InvalidBufferPosition");
             return (-1);
-        } catch (DNSMessageFORMERR) {
+        } catch (const DNSMessageFORMERR&) {
             PyErr_SetString(po_DNSMessageFORMERR, "DNSMessageFORMERR");
             return (-1);
         } catch (...) {
@@ -373,7 +373,7 @@ Name_at(s_Name* self, PyObject* args) {
     }
     try {
         return (Py_BuildValue("I", self->name->at(pos)));
-    } catch (isc::OutOfRange oor) {
+    } catch (const isc::OutOfRange&) {
         PyErr_SetString(PyExc_IndexError,
                         "name index out of range");
         return (NULL);
@@ -472,7 +472,7 @@ Name_split(s_Name* self, PyObject* args) {
             ret->name = NULL;
             try {
                 ret->name = new Name(self->name->split(first, n));
-            } catch(isc::OutOfRange oor) {
+            } catch(const isc::OutOfRange& oor) {
                 PyErr_SetString(PyExc_IndexError, oor.what());
                 ret->name = NULL;
             }
@@ -487,7 +487,7 @@ Name_split(s_Name* self, PyObject* args) {
             ret->name = NULL;
             try {
                 ret->name = new Name(self->name->split(n));
-            } catch(isc::OutOfRange oor) {
+            } catch(const isc::OutOfRange& oor) {
                 PyErr_SetString(PyExc_IndexError, oor.what());
                 ret->name = NULL;
             }
@@ -572,7 +572,7 @@ Name_concatenate(s_Name* self, PyObject* args) {
     if (ret != NULL) {
         try {
             ret->name = new Name(self->name->concatenate(*other->name));
-        } catch (isc::dns::TooLongName tln) {
+        } catch (const TooLongName& tln) {
             PyErr_SetString(po_TooLongName, tln.what());
             return (NULL);
         }

+ 3 - 3
src/lib/dns/python/question_python.cc

@@ -156,15 +156,15 @@ Question_init(s_Question* self, PyObject* args) {
             self->question = QuestionPtr(new Question(inbuf));
             return (0);
         }
-    } catch (isc::dns::DNSMessageFORMERR dmfe) {
+    } catch (const DNSMessageFORMERR& dmfe) {
         PyErr_Clear();
         PyErr_SetString(po_DNSMessageFORMERR, dmfe.what());
         return (-1);
-    } catch (isc::dns::IncompleteRRClass irc) {
+    } catch (const IncompleteRRClass& irc) {
         PyErr_Clear();
         PyErr_SetString(po_IncompleteRRClass, irc.what());
         return (-1);
-    } catch (isc::dns::IncompleteRRType irt) {
+    } catch (const IncompleteRRType& irt) {
         PyErr_Clear();
         PyErr_SetString(po_IncompleteRRType, irt.what());
         return (-1);

+ 1 - 1
src/lib/dns/python/rrclass_python.cc

@@ -188,7 +188,7 @@ RRClass_init(s_RRClass* self, PyObject* args) {
         }
     // Incomplete is never thrown, a type error would have already been raised
     //when we try to read the 2 bytes above
-    } catch (InvalidRRClass ic) {
+    } catch (const InvalidRRClass& ic) {
         PyErr_Clear();
         PyErr_SetString(po_InvalidRRClass, ic.what());
         return (-1);

+ 3 - 3
src/lib/dns/python/rrset_python.cc

@@ -284,7 +284,7 @@ static PyObject*
 RRset_toText(s_RRset* self) {
     try {
         return (Py_BuildValue("s", self->rrset->toText().c_str()));
-    } catch (EmptyRRset ers) {
+    } catch (const EmptyRRset& ers) {
         PyErr_SetString(po_EmptyRRset, ers.what());
         return (NULL);
     }
@@ -321,7 +321,7 @@ RRset_toWire(s_RRset* self, PyObject* args) {
             // None returns
             Py_RETURN_NONE;
         }
-    } catch (EmptyRRset ers) {
+    } catch (const EmptyRRset& ers) {
         PyErr_Clear();
         PyErr_SetString(po_EmptyRRset, ers.what());
         return (NULL);
@@ -341,7 +341,7 @@ RRset_addRdata(s_RRset* self, PyObject* args) {
     try {
         self->rrset->addRdata(*rdata->rdata);
         Py_RETURN_NONE;
-    } catch (std::bad_cast) {
+    } catch (const std::bad_cast&) {
         PyErr_Clear();
         PyErr_SetString(PyExc_TypeError,
                         "Rdata type to add must match type of RRset");

+ 2 - 2
src/lib/dns/python/rrttl_python.cc

@@ -177,7 +177,7 @@ RRTTL_init(s_RRTTL* self, PyObject* args) {
             PyErr_Clear();
             return (0);
         }
-    } catch (IncompleteRRTTL icc) {
+    } catch (const IncompleteRRTTL& icc) {
         // Ok so one of our functions has thrown a C++ exception.
         // We need to translate that to a Python Exception
         // First clear any existing error that was set
@@ -186,7 +186,7 @@ RRTTL_init(s_RRTTL* self, PyObject* args) {
         PyErr_SetString(po_IncompleteRRTTL, icc.what());
         // And return negative
         return (-1);
-    } catch (InvalidRRTTL ic) {
+    } catch (const InvalidRRTTL& ic) {
         PyErr_Clear();
         PyErr_SetString(po_InvalidRRTTL, ic.what());
         return (-1);

+ 2 - 2
src/lib/dns/python/rrtype_python.cc

@@ -217,7 +217,7 @@ RRType_init(s_RRType* self, PyObject* args) {
             PyErr_Clear();
             return (0);
         }
-    } catch (IncompleteRRType icc) {
+    } catch (const IncompleteRRType& icc) {
         // Ok so one of our functions has thrown a C++ exception.
         // We need to translate that to a Python Exception
         // First clear any existing error that was set
@@ -226,7 +226,7 @@ RRType_init(s_RRType* self, PyObject* args) {
         PyErr_SetString(po_IncompleteRRType, icc.what());
         // And return negative
         return (-1);
-    } catch (InvalidRRType ic) {
+    } catch (const InvalidRRType& ic) {
         PyErr_Clear();
         PyErr_SetString(po_InvalidRRType, ic.what());
         return (-1);

+ 1 - 0
src/lib/dns/rdata/generic/txt_16.cc

@@ -21,6 +21,7 @@
 #include <vector>
 
 #include <dns/buffer.h>
+#include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>

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

@@ -3,6 +3,10 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
 CLEANFILES = *.gcno *.gcda
 
 TESTS =
@@ -40,8 +44,8 @@ run_unittests_SOURCES += run_unittests.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/dns/.libs/libdns++.a
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 
 noinst_PROGRAMS = $(TESTS)

+ 2 - 12
src/lib/exceptions/Makefile.am

@@ -1,3 +1,5 @@
+SUBDIRS = . tests
+
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CXXFLAGS=$(B10_CXXFLAGS)
 
@@ -6,17 +8,5 @@ libexceptions_la_SOURCES = exceptions.h exceptions.cc
 
 CLEANFILES = *.gcno *.gcda
 
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += exceptions_unittest.cc
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(GTEST_LDFLAGS)
-run_unittests_LDADD = .libs/libexceptions.a $(GTEST_LDADD)
-endif
-
-noinst_PROGRAMS = $(TESTS)
-
 libexceptions_includedir = $(includedir)/exceptions
 libexceptions_include_HEADERS = exceptions.h

+ 23 - 0
src/lib/exceptions/tests/Makefile.am

@@ -0,0 +1,23 @@
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += exceptions_unittest.cc
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+endif
+
+noinst_PROGRAMS = $(TESTS)

src/lib/exceptions/exceptions_unittest.cc → src/lib/exceptions/tests/exceptions_unittest.cc


src/lib/exceptions/run_unittests.cc → src/lib/exceptions/tests/run_unittests.cc


+ 2 - 2
src/lib/python/isc/config/ccsession.py

@@ -37,7 +37,7 @@
 """
 
 from isc.cc import Session
-from isc.config.config_data import ConfigData, MultiConfigData
+from isc.config.config_data import ConfigData, MultiConfigData, BIND10_CONFIG_DATA_VERSION
 import isc
 
 class ModuleCCSessionError(Exception): pass
@@ -333,7 +333,7 @@ class UIModuleCCSession(MultiConfigData):
         """Requests the current configuration from the configuration
            manager through b10-cmdctl, and stores those as CURRENT"""
         config = self._conn.send_GET('/config_data')
-        if 'version' not in config or config['version'] != 1:
+        if 'version' not in config or config['version'] != BIND10_CONFIG_DATA_VERSION:
             raise ModuleCCSessionError("Bad config version")
         self._set_current_config(config)
 

+ 30 - 17
src/lib/python/isc/config/cfgmgr.py

@@ -27,7 +27,7 @@ import copy
 import tempfile
 import json
 from isc.cc import data
-from isc.config import ccsession
+from isc.config import ccsession, config_data
 
 class ConfigManagerDataReadError(Exception):
     """This exception is thrown when there is an error while reading
@@ -43,15 +43,13 @@ class ConfigManagerData:
     """This class hold the actual configuration information, and
        reads it from and writes it to persistent storage"""
 
-    CONFIG_VERSION = 1
-
     def __init__(self, data_path, file_name = "b10-config.db"):
         """Initialize the data for the configuration manager, and
            set the version and path for the data store. Initializing
            this does not yet read the database, a call to
            read_from_file is needed for that."""
         self.data = {}
-        self.data['version'] = ConfigManagerData.CONFIG_VERSION
+        self.data['version'] = config_data.BIND10_CONFIG_DATA_VERSION
         self.data_path = data_path
         self.db_filename = data_path + os.sep + file_name
 
@@ -65,21 +63,36 @@ class ConfigManagerData:
            the second exception, the best way is probably to report the
            error and stop loading the system."""
         config = ConfigManagerData(data_path, file_name)
+        file = None
         try:
             file = open(config.db_filename, 'r')
             file_config = json.loads(file.read())
-            if 'version' in file_config and \
-                file_config['version'] == ConfigManagerData.CONFIG_VERSION:
-                config.data = file_config
+            # handle different versions here
+            # If possible, we automatically convert to the new
+            # scheme and update the configuration
+            # If not, we raise an exception
+            if 'version' in file_config:
+                if file_config['version'] == config_data.BIND10_CONFIG_DATA_VERSION:
+                    config.data = file_config
+                elif file_config['version'] == 1:
+                    # only format change, no other changes necessary
+                    file_config['version'] = 2
+                    print("[b10-cfgmgr] Updating configuration database version from 1 to 2")
+                    config.data = file_config
+                else:
+                    if config_data.BIND10_CONFIG_DATA_VERSION > file_config['version']:
+                        raise ConfigManagerDataReadError("Cannot load configuration file: version %d no longer supported" % file_config['version'])
+                    else:
+                        raise ConfigManagerDataReadError("Cannot load configuration file: version %d not yet supported" % file_config['version'])
             else:
-                # We can put in a migration path here for old data
-                raise ConfigManagerDataReadError("[b10-cfgmgr] Old version of data found")
-            file.close()
+                raise ConfigManagerDataReadError("No version information in configuration file " + config.db_filename)
         except IOError as ioe:
-            raise ConfigManagerDataEmpty("No config file found")
-        except:
-            raise ConfigManagerDataReadError("Config file unreadable")
-
+            raise ConfigManagerDataEmpty("No configuration file found")
+        except ValueError:
+            raise ConfigManagerDataReadError("Configuration file out of date or corrupt, please update or remove " + config.db_filename)
+        finally:
+            if file:
+                file.close();
         return config
         
     def write_to_file(self, output_file_name = None):
@@ -102,11 +115,11 @@ class ConfigManagerData:
                 os.rename(filename, self.db_filename)
         except IOError as ioe:
             # TODO: log this (level critical)
-            print("[b10-cfgmgr] Unable to write config file; configuration not stored: " + str(ioe))
+            print("[b10-cfgmgr] Unable to write configuration file; configuration not stored: " + str(ioe))
             # TODO: debug option to keep file?
         except OSError as ose:
             # TODO: log this (level critical)
-            print("[b10-cfgmgr] Unable to write config file; configuration not stored: " + str(ose))
+            print("[b10-cfgmgr] Unable to write configuration file; configuration not stored: " + str(ose))
         try:
             if filename and os.path.exists(filename):
                 os.remove(filename)
@@ -243,7 +256,7 @@ class ConfigManager:
             except data.DataNotFoundError as dnfe:
                 # no data is ok, that means we have nothing that
                 # deviates from default values
-                return ccsession.create_answer(0, { 'version': self.config.CONFIG_VERSION })
+                return ccsession.create_answer(0, { 'version': config_data.BIND10_CONFIG_DATA_VERSION })
         else:
             return ccsession.create_answer(1, "Bad module_name in get_config command")
 

+ 7 - 0
src/lib/python/isc/config/config_data.py

@@ -25,6 +25,8 @@ import isc.config.module_spec
 
 class ConfigDataError(Exception): pass
 
+BIND10_CONFIG_DATA_VERSION = 2
+
 def check_type(spec_part, value):
     """Does nothing if the value is of the correct type given the
        specification part relevant for the value. Raises an
@@ -251,6 +253,11 @@ class MultiConfigData:
         if module_name in self._specifications:
             del self._specifications[module_name]
 
+    def have_specification(self, module_name):
+        """Returns True if we have a specification for the module with the given name.
+           Returns False if we do not."""
+        return module_name in self._specifications
+
     def get_module_spec(self, module):
         """Returns the ModuleSpec for the module with the given name.
            If there is no such module, it returns None"""

+ 5 - 4
src/lib/python/isc/config/tests/ccsession_test.py

@@ -22,6 +22,7 @@
 import unittest
 import os
 from isc.config.ccsession import *
+from isc.config.config_data import BIND10_CONFIG_DATA_VERSION
 from unittest_fakesession import FakeModuleCCSession
 
 class TestHelperFunctions(unittest.TestCase):
@@ -442,20 +443,20 @@ class TestUIModuleCCSession(unittest.TestCase):
     def create_uccs2(self, fake_conn):
         module_spec = isc.config.module_spec_from_file(self.spec_file("spec2.spec"))
         fake_conn.set_get_answer('/module_spec', { module_spec.get_module_name(): module_spec.get_full_spec()})
-        fake_conn.set_get_answer('/config_data', { 'version': 1 })
+        fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION })
         return UIModuleCCSession(fake_conn)
 
     def test_init(self):
         fake_conn = fakeUIConn()
         fake_conn.set_get_answer('/module_spec', {})
-        fake_conn.set_get_answer('/config_data', { 'version': 1 })
+        fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION })
         uccs = UIModuleCCSession(fake_conn)
         self.assertEqual({}, uccs._specifications)
-        self.assertEqual({ 'version': 1}, uccs._current_config)
+        self.assertEqual({ 'version': BIND10_CONFIG_DATA_VERSION}, uccs._current_config)
 
         module_spec = isc.config.module_spec_from_file(self.spec_file("spec2.spec"))
         fake_conn.set_get_answer('/module_spec', { module_spec.get_module_name(): module_spec.get_full_spec()})
-        fake_conn.set_get_answer('/config_data', { 'version': 1 })
+        fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION })
         uccs = UIModuleCCSession(fake_conn)
         self.assertEqual(module_spec._module_spec, uccs._specifications['Spec2']._module_spec)
 

+ 10 - 6
src/lib/python/isc/config/tests/cfgmgr_test.py

@@ -22,6 +22,7 @@
 import unittest
 import os
 from isc.config.cfgmgr import *
+from isc.config import config_data
 from unittest_fakesession import FakeModuleCCSession
 
 class TestConfigManagerData(unittest.TestCase):
@@ -32,7 +33,7 @@ class TestConfigManagerData(unittest.TestCase):
 
     def test_init(self):
         self.assertEqual(self.config_manager_data.data['version'],
-                         ConfigManagerData.CONFIG_VERSION)
+                         config_data.BIND10_CONFIG_DATA_VERSION)
         self.assertEqual(self.config_manager_data.data_path,
                          self.data_path)
         self.assertEqual(self.config_manager_data.db_filename,
@@ -52,6 +53,9 @@ class TestConfigManagerData(unittest.TestCase):
         self.assertRaises(ConfigManagerDataReadError,
                           ConfigManagerData.read_from_file,
                           self.data_path, "b10-config-bad3.db")
+        self.assertRaises(ConfigManagerDataReadError,
+                          ConfigManagerData.read_from_file,
+                          self.data_path, "b10-config-bad4.db")
 
     def test_write_to_file(self):
         output_file_name = "b10-config-write-test";
@@ -161,13 +165,13 @@ class TestConfigManager(unittest.TestCase):
         self.assertEqual(commands_spec['Spec2'], module_spec.get_commands_spec())
 
     def test_read_config(self):
-        self.assertEqual(self.cm.config.data, {'version': 1})
+        self.assertEqual(self.cm.config.data, {'version': config_data.BIND10_CONFIG_DATA_VERSION})
         self.cm.read_config()
         # due to what get written, the value here is what the last set_config command in test_handle_msg does
-        self.assertEqual(self.cm.config.data, {'TestModule': {'test': 125}, 'version': 1})
+        self.assertEqual(self.cm.config.data, {'TestModule': {'test': 125}, 'version': config_data.BIND10_CONFIG_DATA_VERSION})
         self.cm.data_path = "/no_such_path"
         self.cm.read_config()
-        self.assertEqual(self.cm.config.data, {'version': 1})
+        self.assertEqual(self.cm.config.data, {'version': config_data.BIND10_CONFIG_DATA_VERSION})
 
     def test_write_config(self):
         # tested in ConfigManagerData tests
@@ -190,9 +194,9 @@ class TestConfigManager(unittest.TestCase):
                                 {'result': [1, 'Bad get_module_spec command, argument not a dict']})
         self._handle_msg_helper({ "command": [ "get_module_spec", { } ] },
                                 {'result': [1, 'Bad module_name in get_module_spec command']})
-        self._handle_msg_helper({ "command": [ "get_config" ] }, { 'result': [ 0, { 'version': 1} ]})
+        self._handle_msg_helper({ "command": [ "get_config" ] }, { 'result': [ 0, { 'version': config_data.BIND10_CONFIG_DATA_VERSION } ]})
         self._handle_msg_helper({ "command": [ "get_config", { "module_name": "nosuchmodule" } ] },
-                                {'result': [0, { 'version': 1 }]})
+                                {'result': [0, { 'version': config_data.BIND10_CONFIG_DATA_VERSION }]})
         self._handle_msg_helper({ "command": [ "get_config", 1 ] },
                                 {'result': [1, 'Bad get_config command, argument not a dict']})
         self._handle_msg_helper({ "command": [ "get_config", { } ] },

+ 5 - 1
src/lib/python/isc/config/tests/config_data_test.py

@@ -267,12 +267,16 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual({}, self.mcd._current_config)
         self.assertEqual({}, self.mcd._local_changes)
 
-    def test_set_specification(self):
+    def test_set_remove_specification(self):
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
+        self.assertFalse(self.mcd.have_specification(module_spec.get_module_name()))
         self.mcd.set_specification(module_spec)
+        self.assertTrue(self.mcd.have_specification(module_spec.get_module_name()))
         self.assert_(module_spec.get_module_name() in self.mcd._specifications)
         self.assertEquals(module_spec, self.mcd._specifications[module_spec.get_module_name()])
         self.assertRaises(ConfigDataError, self.mcd.set_specification, "asdf")
+        self.mcd.remove_specification(module_spec.get_module_name())
+        self.assertFalse(self.mcd.have_specification(module_spec.get_module_name()))
 
     def test_get_module_spec(self):
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec1.spec")

+ 2 - 1
src/lib/xfr/xfrout_client.cc

@@ -77,7 +77,8 @@ XfroutClient::sendXfroutRequestInfo(const int tcp_sock,
 {
     if (-1 == send_fd(impl_->socket_.native(), tcp_sock)) {
         isc_throw(XfroutError,
-                  "Fail to send the socket file descriptor to xfrout module");
+                  "Failed to send the socket file descriptor "
+                  "to xfrout module");
     }
 
     // XXX: this shouldn't be blocking send, even though it's unlikely to