Browse Source

Merge remote-tracking branch 'origin/trac1976-cont' into work/merge

Conflicts:
	src/bin/auth/tests/datasrc_configurator_unittest.cc
Michal 'vorner' Vaner 12 years ago
parent
commit
9cef6836cd
100 changed files with 4297 additions and 1455 deletions
  1. 29 1
      ChangeLog
  2. 4 7
      README
  3. 1 0
      configure.ac
  4. 42 6
      doc/devel/02-dhcp.dox
  5. 3 0
      doc/devel/mainpage.dox
  6. 358 108
      doc/guide/bind10-guide.html
  7. 518 267
      doc/guide/bind10-guide.txt
  8. 483 137
      doc/guide/bind10-guide.xml
  9. 388 23
      doc/guide/bind10-messages.html
  10. 639 39
      doc/guide/bind10-messages.xml
  11. 14 14
      src/bin/auth/auth_messages.mes
  12. 1 49
      src/bin/auth/auth_srv.cc
  13. 1 80
      src/bin/auth/auth_srv.h
  14. 3 3
      src/bin/auth/b10-auth.8
  15. 3 3
      src/bin/auth/b10-auth.xml
  16. 4 1
      src/bin/auth/command.cc
  17. 4 2
      src/bin/auth/datasrc_configurator.h
  18. 6 1
      src/bin/auth/main.cc
  19. 2 1
      src/bin/auth/tests/Makefile.am
  20. 228 217
      src/bin/auth/tests/auth_srv_unittest.cc
  21. 31 4
      src/bin/auth/tests/command_unittest.cc
  22. 3 2
      src/bin/auth/tests/config_unittest.cc
  23. 15 9
      src/bin/auth/tests/datasrc_configurator_unittest.cc
  24. 0 4
      src/bin/auth/tests/query_unittest.cc
  25. 5 0
      src/bin/bind10/bind10_messages.mes
  26. 7 3
      src/bin/bind10/bind10_src.py.in
  27. 7 8
      src/bin/bindctl/bindctl.1
  28. 10 9
      src/bin/bindctl/bindctl.xml
  29. 3 9
      src/bin/cfgmgr/b10-cfgmgr.8
  30. 3 10
      src/bin/cfgmgr/b10-cfgmgr.xml
  31. 4 1
      src/bin/cfgmgr/plugins/Makefile.am
  32. 26 1
      src/bin/cfgmgr/plugins/datasrc.spec
  33. 17 20
      src/bin/dbutil/b10-dbutil.8
  34. 25 21
      src/bin/dbutil/b10-dbutil.xml
  35. 36 25
      src/bin/ddns/b10-ddns.8
  36. 51 15
      src/bin/ddns/b10-ddns.xml
  37. 2 3
      src/bin/ddns/ddns.py.in
  38. 32 17
      src/bin/ddns/ddns_messages.mes
  39. 4 0
      src/bin/dhcp4/Makefile.am
  40. 159 0
      src/bin/dhcp4/ctrl_dhcp4_srv.cc
  41. 123 0
      src/bin/dhcp4/ctrl_dhcp4_srv.h
  42. 14 2
      src/bin/dhcp4/dhcp4.spec
  43. 10 2
      src/bin/dhcp4/dhcp4_srv.cc
  44. 10 0
      src/bin/dhcp4/dhcp4_srv.h
  45. 27 44
      src/bin/dhcp4/main.cc
  46. 5 2
      src/bin/dhcp4/tests/Makefile.am
  47. 85 0
      src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
  48. 1 1
      src/bin/dhcp6/tests/Makefile.am
  49. 1 1
      src/bin/resolver/tests/Makefile.am
  50. 1 1
      src/bin/sockcreator/tests/Makefile.am
  51. 3 2
      src/bin/stats/b10-stats-httpd.8
  52. 8 6
      src/bin/stats/b10-stats-httpd.xml
  53. 11 10
      src/bin/stats/b10-stats.8
  54. 17 18
      src/bin/stats/b10-stats.xml
  55. 1 1
      src/bin/stats/stats.py.in
  56. 1 1
      src/bin/stats/stats_httpd.py.in
  57. 1 2
      src/bin/xfrin/xfrin.py.in
  58. 66 17
      src/bin/xfrout/tests/xfrout_test.py.in
  59. 42 22
      src/bin/xfrout/xfrout.py.in
  60. 19 8
      src/bin/xfrout/xfrout_messages.mes
  61. 2 1
      src/bin/zonemgr/tests/zonemgr_test.py
  62. 4 5
      src/bin/zonemgr/zonemgr.py.in
  63. 1 1
      src/lib/acl/tests/Makefile.am
  64. 1 1
      src/lib/asiodns/tests/Makefile.am
  65. 1 1
      src/lib/asiolink/tests/Makefile.am
  66. 1 1
      src/lib/bench/tests/Makefile.am
  67. 1 1
      src/lib/cache/tests/Makefile.am
  68. 16 0
      src/lib/cc/session.cc
  69. 5 0
      src/lib/cc/session.h
  70. 1 1
      src/lib/cc/tests/Makefile.am
  71. 13 0
      src/lib/cc/tests/session_unittests.cc
  72. 1 1
      src/lib/config/tests/Makefile.am
  73. 1 1
      src/lib/cryptolink/tests/Makefile.am
  74. 1 0
      src/lib/datasrc/.gitignore
  75. 3 4
      src/lib/datasrc/Makefile.am
  76. 112 18
      src/lib/datasrc/client_list.cc
  77. 21 15
      src/lib/datasrc/client_list.h
  78. 3 3
      src/lib/datasrc/database.cc
  79. 7 6
      src/lib/datasrc/database.h
  80. 0 6
      src/lib/datasrc/memory_datasrc.cc
  81. 0 6
      src/lib/datasrc/memory_datasrc.h
  82. 2 0
      src/lib/datasrc/static.zone.pre
  83. 1 2
      src/lib/datasrc/tests/Makefile.am
  84. 300 26
      src/lib/datasrc/tests/client_list_unittest.cc
  85. 0 34
      src/lib/datasrc/tests/database_unittest.cc
  86. 0 8
      src/lib/datasrc/tests/memory_datasrc_unittest.cc
  87. 0 25
      src/lib/datasrc/zone.h
  88. 1 1
      src/lib/dhcp/Makefile.am
  89. 82 12
      src/lib/dhcp/iface_mgr.cc
  90. 25 5
      src/lib/dhcp/iface_mgr.h
  91. 1 1
      src/lib/dhcp/tests/Makefile.am
  92. 51 2
      src/lib/dhcp/tests/iface_mgr_unittest.cc
  93. 1 1
      src/lib/dns/tests/Makefile.am
  94. 1 1
      src/lib/exceptions/tests/Makefile.am
  95. 1 1
      src/lib/log/Makefile.am
  96. 2 2
      src/lib/log/tests/Makefile.am
  97. 1 1
      src/lib/nsas/tests/Makefile.am
  98. 4 0
      src/lib/python/isc/bind10/special_component.py
  99. 12 1
      src/lib/python/isc/bind10/tests/component_test.py
  100. 0 0
      src/lib/python/isc/bind10/tests/sockcreator_test.py

+ 29 - 1
ChangeLog

@@ -1,3 +1,31 @@
+450.	[func]*		tomek
+	b10-dhcp4: DHCPv4 server component is now integrated into
+	BIND10 framework. It can be started from BIND10 (using bindctl)
+	and can receive commands. The only supported command for now
+	is 'Dhcp4 shutdown'.
+	(Trac #1651, git 7e16a5a50d3311e63d10a224ec6ebcab5f25f62c)
+
+bind10-devel-20120621 released on June 21. 2012
+
+449.	[bug]		muks
+	b10-xfin: fixed a bug where xfrin sent the wrong notification
+	message to zonemgr on successful zone transfer. This also
+	solves other reported problems such as too frequent attempts
+	of zone refreshing (see Trac #1786 and #1834).
+	(Trac #2023, git b5fbf8a408a047a2552e89ef435a609f5df58d8c)
+
+448.	[func]		team
+	b10-ddns is now functional and handles dynamic update requests
+	per RFC 2136.  See BIND 10 guide for configuration and operation
+	details.
+	(Multiple Trac tickets)
+
+447.	[bug]		jinmei
+	Fixed a bug in b10-xfrout where a helper thread could fall into
+	an infinite loop if b10-auth stops while the thread is waiting for
+	forwarded requests from b10-auth.
+	(Trac #988 and #1833, git 95a03bbefb559615f3f6e529d408b749964d390a)
+
 446.	[bug]		muks
 	A number of warnings reported by Python about unclosed file and
 	socket objects were fixed. Some related code was also made safer.
@@ -16,7 +44,7 @@
 	authoritative data even on a delegation point.
 	(Trac #1912, git 7130da883f823ce837c10cbf6e216a15e1996e5d)
 
-443.    [func]*		muks
+443.	[func]*		muks
 	The logger now uses a lockfile named `logger_lockfile' that is
 	created in the local state directory to mutually separate
 	individual logging operations from various processes. This is

+ 4 - 7
README

@@ -2,17 +2,14 @@
 This is the source for the development version of BIND 10.
 
 BIND is the popular implementation of a DNS server, developer
-interfaces, and DNS tools. BIND 10 is a rewrite of BIND 9. BIND 10
-is written in C++ and Python and provides a modular environment
-for serving, maintaining, and developing DNS.
+interfaces, and DNS tools. BIND 10 is a rewrite of BIND 9 and ISC
+DHCP. BIND 10 is written in C++ and Python and provides a modular
+environment for serving, maintaining, and developing DNS and DHCP.
 
 BIND10-devel is new development leading up to the production
 BIND 10 release. It contains prototype code and experimental
 interfaces. Nevertheless it is ready to use now for testing the
-new BIND 10 infrastructure ideas. The Year 3 goals of the five
-year plan are described here:
-
-        http://bind10.isc.org/wiki/Year3Goals
+new BIND 10 infrastructure ideas.
 
 This release includes the bind10 master process, b10-msgq message
 bus, b10-auth authoritative DNS server (with SQLite3 and in-memory

+ 1 - 0
configure.ac

@@ -1137,6 +1137,7 @@ AC_CONFIG_FILES([Makefile
 AC_OUTPUT([doc/version.ent
            src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/cfgmgr/tests/b10-cfgmgr_test.py
+           src/bin/cfgmgr/plugins/datasrc.spec.pre
            src/bin/cmdctl/cmdctl.py
            src/bin/cmdctl/run_b10-cmdctl.sh
            src/bin/cmdctl/tests/cmdctl_test

+ 42 - 6
doc/devel/02-dhcp.dox

@@ -15,11 +15,47 @@
  * only), as support for transmission to hosts without IPv4 address
  * assigned is not implemented in IfaceMgr yet.
  *
- * DHCPv4 server component does not listen to BIND10 message queue.
- *
  * DHCPv4 server component does not use BIND10 logging yet.
  *
- * DHCPv4 server component is not integrated with boss yet.
+ * @section dhcpv4Session BIND10 message queue integration
+ *
+ * DHCPv4 server component is now integrated with BIND10 message queue.
+ * The integration is performed by establishSession() and disconnectSession()
+ * functions in isc::dhcp::ControlledDhcpv4Srv class. main() method deifined
+ * in the src/bin/dhcp4/main.cc file instantiates isc::dhcp::ControlledDhcpv4Srv
+ * class that establishes connection with msgq and install necessary handlers
+ * for receiving commands and configuration updates. It is derived from
+ * a base isc::dhcp::Dhcpv4Srv class that implements DHCPv4 server functionality,
+ * without any controlling mechanisms.
+ *
+ * ControlledDhcpv4Srv instantiates several components to make management
+ * session possible. In particular, isc::cc::Session cc_session
+ * object uses ASIO for establishing connection. It registers its socket
+ * in isc::asiolink::IOService io_service object. Typically, other components
+ * (e.g. auth or resolver) that use ASIO for their communication, register their
+ * other sockets in the
+ * same io_service and then just call io_service.run() method that does
+ * not return, until one of the callback decides that it is time to shut down
+ * the whole component cal calls io_service.stop(). DHCPv4 works in a
+ * different way. It does receive messages using select()
+ * (see isc::dhcp::IfaceMgr::receive4()), which is incompatible with ASIO.
+ * To solve this problem, socket descriptor is extracted from cc_session
+ * object and is passed to IfaceMgr by using isc::dhcp::IfaceMgr::set_session_socket().
+ * IfaceMgr then uses this socket in its select() call. If there is some
+ * data to be read, it calls registered callback that is supposed to
+ * read and process incoming data.
+ *
+ * This somewhat complicated approach is needed for a simple reason. In
+ * embedded deployments there will be no message queue. Not referring directly
+ * to anything related to message queue in isc::dhcp::Dhcpv4Srv and
+ * isc::dhcp::IfaceMgr classes brings in two benefits. First, the can
+ * be used with and without message queue. Second benefit is related to the
+ * first one: \ref libdhcp is supposed to be simple and robust and not require
+ * many dependencies. One notable example of a use case that benefits from
+ * this approach is a perfdhcp tool. Finally, the idea is that it should be
+ * possible to instantiate Dhcpv4Srv object directly, thus getting a server
+ * that does not support msgq. That is useful for embedded environments.
+ * It may also be useful in validation.
  *
  * @page dhcpv6 DHCPv6 Server Component
  *
@@ -42,9 +78,9 @@
  *
  * DHCPv6 server component is not integrated with boss yet.
  *
- * @page libdhcp libdhcp++ library
+ * @page libdhcp libdhcp++
  *
- * @section libdhcpIntro Libdhcp++ Introduction
+ * @section libdhcpIntro Libdhcp++ Library Introduction
  *
  * libdhcp++ is an all-purpose DHCP-manipulation library, written in
  * C++. It offers packet parsing and assembly, DHCPv4 and DHCPv6
@@ -82,7 +118,7 @@
  * isc::dhcp::Option::delOption(), isc::dhcp::Option::getOption() can be used
  * for that purpose.
  *
- * @section lidhcpIfaceMgr Interface Manager
+ * @section libdhcpIfaceMgr Interface Manager
  *
  * Interface Manager (or IfaceMgr) is an abstraction layer about low-level
  * network operations. In particlar, it provides information about existing

+ 3 - 0
doc/devel/mainpage.dox

@@ -19,8 +19,11 @@
  *
  * @section DHCP
  * - @subpage dhcpv4
+ *   - @subpage dhcpv4Session
  * - @subpage dhcpv6
  * - @subpage libdhcp
+ *   - @subpage libdhcpIntro
+ *   - @subpage libdhcpIfaceMgr
  *
  * @section misc Miscellaneous topics
  * - @subpage LoggingApi

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


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


+ 483 - 137
doc/guide/bind10-guide.xml

@@ -36,8 +36,8 @@
     <abstract>
       <para>BIND 10 is a framework that features Domain Name System
       (DNS) suite and Dynamic Host Configuration Protocol (DHCP)
-      servers managed by Internet Systems Consortium (ISC). It
-      includes DNS libraries, modular components for controlling
+      servers with development managed by Internet Systems Consortium (ISC).
+      It includes DNS libraries, modular components for controlling
       authoritative and recursive DNS servers, and experimental DHCPv4
       and DHCPv6 servers.
       </para>
@@ -59,6 +59,8 @@
     <section id="acknowledgements">
       <title>Acknowledgements</title>
 
+<!-- TODO: acknowledge all sponsors and CNNIC and CZNIC too -->
+
       <para>ISC would like to acknowledge generous support for
       BIND 10 development of DHCPv4 and DHCPv6 components provided
       by <ulink url="http://www.comcast.com/">Comcast</ulink>.</para>
@@ -72,11 +74,13 @@
     <para>
       BIND is the popular implementation of a DNS server, developer
       interfaces, and DNS tools.
-      BIND 10 is a rewrite of BIND 9.  BIND 10 is written in C++ and Python
-      and provides a modular environment for serving and maintaining DNS.
+      BIND 10 is a rewrite of BIND 9 and ISC DHCP.
+      BIND 10 is written in C++ and Python and provides a modular
+      environment for serving, maintaining, and developing DNS and DHCP.
       BIND 10 provides a EDNS0- and DNSSEC-capable authoritative
       DNS server and a caching recursive name server which also
       provides forwarding.
+      It also provides experimental DHCPv4 and DHCPv6 servers.
     </para>
 
     <para>
@@ -87,9 +91,10 @@
     <section>
       <title>Supported Platforms</title>
       <para>
-  BIND 10 builds have been tested on Debian GNU/Linux 5 and unstable,
-  Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7 and 8, CentOS
-  Linux 5.3, and MacOS 10.6.
+  BIND 10 builds have been tested on (in no particular order)
+  Debian GNU/Linux 5 and unstable, Ubuntu 9.10, NetBSD 5,
+  Solaris 10 and 11, FreeBSD 7 and 8, CentOS Linux 5.3,
+  MacOS 10.6 and 10.7, and OpenBSD 5.1.
 
   It has been tested on Sparc, i386, and amd64 hardware
   platforms.
@@ -104,7 +109,7 @@
       <para>
         BIND 10 requires at least Python 3.1
         (<ulink url="http://www.python.org/"/>).
-        It has also been tested with Python 3.2.
+        It also works with Python 3.2.
       </para>
 
       <para>
@@ -117,6 +122,7 @@
         BIND 10 uses the log4cplus C++ logging library
         (<ulink url="http://log4cplus.sourceforge.net/"/>).
         It requires at least log4cplus version 1.0.3.
+<!-- TODO: It is recommended to use at least version .... -->
       </para>
 
       <para>
@@ -127,13 +133,11 @@
       </para>
 
       <para>
-        The <command>b10-xfrin</command>, <command>b10-xfrout</command>,
-        and <command>b10-zonemgr</command> components require the
-        libpython3 library and the Python _sqlite3.so module
-        (which is included with Python).
-        The <command>b10-stats-httpd</command> component uses the
-        Python pyexpat.so module.
-        The Python modules need to be built for the corresponding Python 3.
+        The <command>b10-ddns</command>, <command>b10-xfrin</command>,
+        <command>b10-xfrout</command>, and <command>b10-zonemgr</command>
+        components require the libpython3 library and the Python
+        _sqlite3.so module (which is included with Python).
+        Python modules need to be built for the corresponding Python 3.
       </para>
 <!-- TODO: this will change ... -->
 
@@ -198,6 +202,16 @@
 
           <listitem>
             <simpara>
+              <command>b10-ddns</command> &mdash;
+              Dynamic DNS update service.
+              This process is used to handle incoming DNS update
+              requests to allow granted clients to update zones
+	      for which BIND 10 is serving as a primary server.
+            </simpara>
+          </listitem>
+
+          <listitem>
+            <simpara>
               <command>b10-msgq</command> &mdash;
               Message bus daemon.
               This process coordinates communication between all of the other
@@ -209,8 +223,8 @@
             <simpara>
               <command>b10-resolver</command> &mdash;
               Recursive name server.
-              This process handles incoming queries.
-<!-- TODO: -->
+              This process handles incoming DNS queries and provides
+              answers from its cache or by recursively doing remote lookups.
             </simpara>
           </listitem>
 
@@ -253,15 +267,14 @@
               <command>b10-xfrout</command> &mdash;
               Outgoing zone transfer service.
               This process is used to handle transfer requests to
-              send a local zone to a remote secondary server,
-              when acting as a master server.
+              send a local zone to a remote secondary server.
             </simpara>
           </listitem>
 
           <listitem>
             <simpara>
               <command>b10-zonemgr</command> &mdash;
-              Secondary manager.
+              Secondary zone manager.
               This process keeps track of timers and other
               necessary information for BIND 10 to act as a slave server.
             </simpara>
@@ -271,8 +284,8 @@
       </para>
 
       <para>
-        These are ran automatically by <command>bind10</command>
-        and do not need to be run manually.
+        These are ran by <command>bind10</command>
+        and do not need to be manually started independently.
       </para>
 
     </section>
@@ -287,7 +300,7 @@
           <listitem>
             <simpara>
               <command>bindctl</command> &mdash;
-              interactive administration interface.
+              Interactive administration interface.
               This is a low-level command-line tool which allows
               a developer or an experienced administrator to control
               BIND 10.
@@ -296,7 +309,7 @@
           <listitem>
             <simpara>
               <command>b10-loadzone</command> &mdash;
-              zone file loader.
+              Zone file loader.
               This tool will load standard masterfile-format zone files into
               BIND 10.
             </simpara>
@@ -304,7 +317,7 @@
           <listitem>
             <simpara>
               <command>b10-cmdctl-usermgr</command> &mdash;
-              user access control.
+              User access control.
               This tool allows an administrator to authorize additional users
               to manage BIND 10.
             </simpara>
@@ -347,6 +360,7 @@ var/
       for C++ and Python for the message bus, configuration backend,
       and, of course, DNS. These include detailed developer
       documentation and code examples.
+<!-- TODO: DHCP also but no Python yet. -->
 <!-- TODO point to this -->
     </para>
 
@@ -355,12 +369,100 @@ var/
   <chapter id="installation">
     <title>Installation</title>
 
+    <section id="packages">
+      <title>Packages</title>
+
+      <para>
+        Some operating systems or softare package vendors may
+        provide ready-to-use, pre-built software packages for
+        the BIND 10 suite.
+        Installing a pre-built package means you do not need to
+        install build-only prerequisites and do not need to
+        <emphasis>make</emphasis> the software.
+      </para>
+
+      <para>
+        FreeBSD ports, NetBSD pkgsrc, and Debian
+        <emphasis>testing</emphasis> package collections provide
+        all the prerequisite packages.
+      </para>
+    </section>
+
+    <section id="install-hierarchy">
+      <title>Install Hierarchy</title>
+      <para>
+        The following is the standard, common layout of the
+        complete BIND 10 installation:
+        <itemizedlist>
+          <listitem>
+           <simpara>
+              <filename>bin/</filename> &mdash;
+              general tools and diagnostic clients.
+            </simpara>
+          </listitem>
+          <listitem>
+          <simpara>
+            <filename>etc/bind10-devel/</filename> &mdash;
+            configuration files.
+          </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <filename>lib/</filename> &mdash;
+              libraries and python modules.
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <filename>libexec/bind10-devel/</filename> &mdash;
+              executables that a user wouldn't normally run directly and
+              are not run independently.
+              These are the BIND 10 modules which are daemons started by
+              the <command>bind10</command> tool.
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <filename>sbin/</filename> &mdash;
+              commands used by the system administrator.
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <filename>share/bind10-devel/</filename> &mdash;
+              configuration specifications.
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <filename>share/doc/bind10-devel/</filename> &mdash;
+              this guide and other supplementary documentation.
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <filename>share/man/</filename> &mdash;
+              manual pages (online documentation).
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <filename>var/bind10-devel/</filename> &mdash;
+              data source and configuration databases.
+            </simpara>
+          </listitem>
+        </itemizedlist>
+      </para>
+    </section>
+
     <section id="build-requirements">
       <title>Building Requirements</title>
 
         <para>
-          In addition to the run-time requirements, building BIND 10
-          from source code requires various development include headers.
+          In addition to the run-time requirements (listed in
+          <xref linkend="required-software"/>), building BIND 10
+          from source code requires various development include headers and
+          program development tools.
         </para>
 
         <note>
@@ -404,7 +506,7 @@ as a dependency earlier -->
         </para>
 
         <para>
-          Visit the wiki at <ulink
+          Visit the user-contributed wiki at <ulink
           url="http://bind10.isc.org/wiki/SystemSpecificNotes" />
           for system-specific installation tips.
         </para>
@@ -473,7 +575,7 @@ as a dependency earlier -->
         </listitem>
 
         <listitem>
-
+<!-- TODO: this is wrong; b10-auth is not started by default any more -->
          <para>Test it; for example:
             <screen>$ <userinput>dig @127.0.0.1 -c CH -t TXT authors.bind</userinput></screen>
          </para>
@@ -499,10 +601,10 @@ as a dependency earlier -->
       <title>Installation from source</title>
       <para>
         BIND 10 is open source software written in C++ and Python.
-        It is freely available in source code form from ISC via
-        the Git code revision control system or as a downloadable
-        tar file. It may also be available in pre-compiled ready-to-use
-        packages from operating system vendors.
+        It is freely available in source code form from ISC as a
+        downloadable tar file or via BIND 10's Git code revision control
+        service. (It may also be available in pre-compiled ready-to-use
+        packages from operating system vendors.)
       </para>
 
       <section>
@@ -530,7 +632,7 @@ as a dependency earlier -->
 
         <note>
           <para>
-            When using source code retrieved via Git additional
+            When using source code retrieved via Git, additional
             software will be required:  automake (v1.11 or newer),
             libtoolize, and autoconf (2.59 or newer).
             These may need to be installed.
@@ -538,11 +640,12 @@ as a dependency earlier -->
         </note>
 
         <para>
-          The latest development code, including temporary experiments
-          and un-reviewed code, is available via the BIND 10 code revision
+          The latest development code (and temporary experiments
+          and un-reviewed code) is available via the BIND 10 code revision
           control system. This is powered by Git and all the BIND 10
           development is public.
-          The leading development is done in the <quote>master</quote>.
+          The leading development is done in the <quote>master</quote>
+          branch.
         </para>
         <para>
           The code can be checked out from
@@ -555,8 +658,8 @@ as a dependency earlier -->
         <para>
           When checking out the code from
           the code version control system, it doesn't include the
-          generated configure script, Makefile.in files, nor the
-          related configure files.
+          generated configure script, Makefile.in files, nor their
+          related build files.
           They can be created by running <command>autoreconf</command>
           with the <option>--install</option> switch.
           This will run <command>autoconf</command>,
@@ -580,7 +683,7 @@ as a dependency earlier -->
         </para>
         <para>
           Run <command>./configure</command> with the <option>--help</option>
-          switch to view the different options. The commonly-used options are:
+          switch to view the different options. Some commonly-used options are:
 
           <variablelist>
 
@@ -668,65 +771,6 @@ as a dependency earlier -->
 
   <!-- TODO: tests -->
 
-      <section>
-        <title>Install Hierarchy</title>
-        <para>
-          The following is the layout of the complete BIND 10 installation:
-          <itemizedlist>
-            <listitem>
-              <simpara>
-                <filename>bin/</filename> &mdash;
-                general tools and diagnostic clients.
-              </simpara>
-            </listitem>
-            <listitem>
-            <simpara>
-              <filename>etc/bind10-devel/</filename> &mdash;
-              configuration files.
-            </simpara>
-            </listitem>
-            <listitem>
-              <simpara>
-                <filename>lib/</filename> &mdash;
-                libraries and python modules.
-              </simpara>
-            </listitem>
-            <listitem>
-              <simpara>
-                <filename>libexec/bind10-devel/</filename> &mdash;
-                executables that a user wouldn't normally run directly and
-                are not run independently.
-                These are the BIND 10 modules which are daemons started by
-                the <command>bind10</command> tool.
-              </simpara>
-            </listitem>
-            <listitem>
-              <simpara>
-                <filename>sbin/</filename> &mdash;
-                commands used by the system administrator.
-              </simpara>
-            </listitem>
-            <listitem>
-              <simpara>
-                <filename>share/bind10-devel/</filename> &mdash;
-                configuration specifications.
-              </simpara>
-            </listitem>
-            <listitem>
-              <simpara>
-                <filename>share/man/</filename> &mdash;
-                manual pages (online documentation).
-              </simpara>
-            </listitem>
-            <listitem>
-              <simpara>
-                <filename>var/bind10-devel/</filename> &mdash;
-                data source and configuration databases.
-              </simpara>
-            </listitem>
-          </itemizedlist>
-        </para>
-      </section>
     </section>
 
   <!--
@@ -832,6 +876,7 @@ as a dependency earlier -->
         in usual way. This is the list of components that need to be started
         in a special way, with the value of special used for them:
         <table>
+          <title>Special startup components</title>
           <tgroup cols='3' align='left'>
           <colspec colname='component'/>
           <colspec colname='special'/>
@@ -878,7 +923,7 @@ as a dependency earlier -->
         message bus. The special components already know their
         address, but the usual ones don't. The address is by
         convention the thing after <emphasis>b10-</emphasis>, with
-        the first letter capital (eg. <command>b10-stats</command>
+        the first letter capitalized (eg. <command>b10-stats</command>
         would have <quote>Stats</quote> as its address).
 <!-- TODO: this should be simplified so we don't even have to document it -->
       </para>
@@ -1833,7 +1878,6 @@ what if a NOTIFY is sent?
 
   <chapter id="xfrout">
     <title>Outbound Zone Transfers</title>
-
     <para>
       The <command>b10-xfrout</command> process is started by
       <command>bind10</command>.
@@ -1909,6 +1953,325 @@ what is XfroutClient xfr_client??
 
   </chapter>
 
+  <chapter id="ddns">
+    <title>Dynamic DNS Update</title>
+
+    <para>
+      BIND 10 supports the server side of the Dynamic DNS Update
+      (DDNS) protocol as defined in RFC 2136.
+      This service is provided by the <command>b10-ddns</command>
+      component, which is started by the <command>bind10</command>
+      process if configured so.
+    </para>
+
+    <para>
+      When the <command>b10-auth</command> authoritative DNS server
+      receives an UPDATE request, it internally forwards the request
+      to <command>b10-ddns</command>, which handles the rest of
+      request processing.
+      When the processing is completed <command>b10-ddns</command>
+      will send a response to the client with the RCODE set to the
+      value as specified in RFC 2136 (NOERROR for successful update,
+      REFUSED if rejected due to ACL check, etc).
+      If the zone has been changed as a result, it will internally
+      notify <command>b10-xfrout</command> so that other secondary
+      servers will be notified via the DNS notify protocol.
+      In addition, if <command>b10-auth</command> serves the updated
+      zone from its in-memory cache (as described in
+      <xref linkend="in-memory-datasource-with-sqlite3-backend" />),
+      <command>b10-ddns</command> will also
+      notify <command>b10-auth</command> so that <command>b10-auth</command>
+      will re-cache the updated zone content.
+    </para>
+
+    <para>
+      The <command>b10-ddns</command> component supports requests over
+      both UDP and TCP, and both IPv6 and IPv4; for TCP requests,
+      however, it terminates the TCP connection immediately after
+      each single request has been processed.  Clients cannot reuse the
+      same TCP connection for multiple requests. (This is a current
+      implementation limitation of <command>b10-ddns</command>.
+      While RFC 2136 doesn't specify anything about such reuse of TCP
+      connection, there is no reason for disallowing it as RFC 1035
+      generally allows multiple requests sent over a single TCP
+      connection.  BIND 9 supports such reuse.)
+    </para>
+
+    <para>
+      As of this writing <command>b10-ddns</command> does not support
+      update forwarding for secondary zones.
+      If it receives an update request for a secondary zone, it will
+      immediately return a response with an RCODE of NOTIMP.
+      <note><simpara>
+	  For feature completeness update forwarding should be
+	  eventually supported.  But right now it's considered a lower
+	  priority task and there is no specific plan of implementing
+	  this feature.
+<!-- See Trac #2063 -->
+      </simpara></note>
+    </para>
+
+    <section>
+      <title>Enabling Dynamic Update</title>
+      <para>
+        First off, it must be made sure that a few components on which
+        <command>b10-ddns</command> depends are configured to run,
+        which are <command>b10-auth</command>
+        and <command>b10-zonemgr</command>.
+        In addition, <command>b10-xfrout</command> should also be
+        configured to run; otherwise the notification after an update
+        (see above) will fail with a timeout, suspending the DDNS
+        service while <command>b10-ddns</command> waits for the
+        response (see the description of the <ulink
+        url="bind10-messages.html#DDNS_UPDATE_NOTIFY_FAIL">DDNS_UPDATE_NOTIFY_FAIL</ulink>
+        log message for further details).
+        If BIND 10 is already configured to provide authoritative DNS
+        service they should normally be configured to run already.
+      </para>
+
+      <para>
+        Second, for the obvious reason dynamic update requires that the
+        underlying data source storing the zone data be writable.
+        In the current implementation this means the zone must be stored
+        in an SQLite3-based data source.
+        Also, right now, the <command>b10-ddns</command> component
+        configures itself with the data source referring to the
+        <quote>database_file</quote> configuration parameter of
+        <command>b10-auth</command>.
+        So this information must be configured correctly before starting
+        <command>b10-ddns</command>.
+
+        <note><simpara>
+            The way to configure data sources is now being revised.
+            Configuration on the data source for DDNS will be very
+            likely to be changed in a backward incompatible manner in
+            a near future version.
+        </simpara></note>
+      </para>
+
+      <para>
+	In general, if something goes wrong regarding the dependency
+	described above, <command>b10-ddns</command> will log the
+	related event at the warning or error level.
+	It's advisable to check the log message when you first enable
+	DDNS or if it doesn't work as you expect to see if there's any
+	warning or error log message.
+      </para>
+
+      <para>
+        Next, to enable the DDNS service, <command>b10-ddns</command>
+        needs to be explicitly configured to run.
+        It can be done by using the <command>bindctl</command>
+        utility.  For example:
+      <screen>
+&gt; <userinput>config add Boss/components b10-ddns</userinput>
+&gt; <userinput>config set Boss/components/b10-ddns/address DDNS</userinput>
+&gt; <userinput>config set Boss/components/b10-ddns/kind dispensable</userinput>
+&gt; <userinput>config commit</userinput>
+</screen>
+      <note><simpara>
+	  In theory "kind" could be omitted because "dispensable" is its
+	  default.  But there's some peculiar behavior (which should
+	  be a bug and should be fixed eventually; see Trac ticket
+	  #2064) with bindctl and you'll still need to specify that explicitly.
+	  Likewise, "address" may look unnecessary because
+	  <command>b10-ddns</command> would start and work without
+	  specifying it.  But for it to shutdown gracefully this
+	  parameter should also be specified.
+      </simpara></note>
+      </para>
+    </section>
+
+    <section>
+      <title>Access Control</title>
+      <para>
+        By default <command>b10-ddns</command> rejects any update
+        requests from any clients by returning a response with an RCODE
+        of REFUSED.
+        To allow updates to take effect, an access control rule
+        (called update ACL) with a policy allowing updates must explicitly be
+        configured.
+        Update ACL must be configured per zone basis in the
+        <quote>zones</quote> configuration parameter of
+        <command>b10-ddns</command>.
+        This is a list of per-zone configurations regarding DDNS.
+        Each list element consists of the following parameters:
+        <variablelist>
+          <varlistentry>
+            <term>origin</term>
+            <listitem>
+              <simpara>The zone's origin name</simpara>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>class</term>
+            <listitem>
+              <simpara>The RR class of the zone
+                (normally <quote>IN</quote>, and in that case
+		can be omitted in configuration)</simpara>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term>update_acl</term>
+            <listitem>
+              <simpara>List of access control rules (ACL) for the zone</simpara>
+            </listitem>
+          </varlistentry>
+        </variablelist>
+        The syntax of the ACL is the same as ACLs for other
+        components.
+        Specific examples are given below.
+      </para>
+
+      <para>
+        In general, an update ACL rule that allows an update request
+        should be configured with a TSIG key.
+        This is an example update ACL that allows updates to the zone
+        named <quote>example.org</quote> of RR class <quote>IN</quote>
+        from clients that send requests signed with a TSIG whose
+        key name is "key.example.org" (and refuses all others):
+      <screen>
+&gt; <userinput>config add DDNS/zones</userinput>
+&gt; <userinput>config set DDNS/zones[0]/origin example.org</userinput>
+&gt; <userinput>config set DDNS/zones[0]/class IN</userinput>
+(Note: "class" can be omitted)
+&gt; <userinput>config add DDNS/zones[0]/update_acl {"action": "ACCEPT", "key": "key.example.org"}</userinput>
+&gt; <userinput>config commit</userinput>
+</screen>
+      The TSIG key must be configured system wide
+      (see <xref linkend="xfrout"/>.)
+      </para>
+
+      <para>
+        Multiple rules can be specified in the ACL, and an ACL rule
+        can consist of multiple constraints, such as a combination of
+        IP address and TSIG.
+        The following configuration sequence will add a new rule to
+        the ACL created in the above example.  This additional rule
+	allows update requests sent from a client
+        using TSIG key name of "key.example" (different from the
+        key used in the previous example) and has an IPv6 address of ::1.
+      <screen>
+&gt; <userinput>config add DDNS/zones[0]/update_acl {"action": "ACCEPT", "from": "::1", "key": "key.example"}</userinput>
+&gt; <userinput>config show DDNS/zones[0]/update_acl</userinput>
+DDNS/zones[0]/update_acl[0]     {"action": "ACCEPT", "key": "key.example.org"} any (modified)
+DDNS/zones[0]/update_acl[1]     {"action": "ACCEPT", "from": "::1", "key": "key.example"} any (modified)
+&gt; <userinput>config commit</userinput>
+</screen>
+      (Note the "add" in the first line.  Before this sequence, we
+      have had only entry in zones[0]/update_acl.  The "add" command
+      with a value (rule) adds a new entry and sets it to the given rule.
+      Due to a limitation of the current implementation, it doesn't
+      work if you first try to just add a new entry and then set it to
+      a given rule).
+      </para>
+
+      <note><simpara>
+          The <command>b10-ddns</command> component accepts an ACL
+          rule that just allows updates from a specific IP address
+          (i.e., without requiring TSIG), but this is highly
+          discouraged (remember that requests can be made over UDP and
+          spoofing the source address of a UDP packet is often pretty
+          easy).
+          Unless you know what you are doing and that you can accept
+          its consequence, any update ACL rule that allows updates
+          should have a TSIG key in its constraints.
+      </simpara></note>
+
+      <para>
+	The ACL rules will be checked in the listed order, and the
+	first matching one will apply.
+	If none of the rules matches, the default rule will apply,
+	which is rejecting any requests in the case of
+	<command>b10-ddns</command>.
+      </para>
+
+      <para>
+	Other actions than "ACCEPT", namely "REJECT" and "DROP", can be
+	used, too.
+	See <xref linkend="resolverserver"/> about their effects.
+      </para>
+
+      <para>
+        Currently update ACL can only control updates per zone basis;
+        it's not possible to specify access control with higher
+        granularity such as for particular domain names or specific
+        types of RRs.
+<!-- See Trac ticket #2065 -->
+      </para>
+
+      <note><simpara>
+        Contrary to what RFC 2136 (literally) specifies,
+        <command>b10-ddns</command> checks the update ACL before
+        checking the prerequisites of the update request.
+        This is a deliberate implementation decision.
+        This counter intuitive specification has been repeatedly
+        discussed among implementers and in the IETF, and it is now
+        widely agreed that it does not make sense to strictly follow
+        that part of RFC.
+        One known specific bad result of following the RFC is that it
+        could leak information about which name or record exists or does not
+        exist in the zone as a result of prerequisite checks even if a
+        zone is somehow configured to reject normal queries from
+        arbitrary clients.
+        There have been other troubles that could have been avoided if
+        the ACL could be checked before the prerequisite check.
+      </simpara></note>
+    </section>
+
+    <section>
+      <title>Miscellaneous Operational Issues</title>
+      <para>
+        Unlike BIND 9, BIND 10 currently does not support automatic
+        resigning of DNSSEC-signed zone when it's updated via DDNS.
+        It could be possible to resign the updated zone afterwards
+        or make sure the update request also updates related DNSSEC
+        records, but that will be pretty error-prone operation.
+        In general, it's not advisable to allow DDNS for a signed zone
+        at this moment.
+      </para>
+
+      <para>
+        Also unlike BIND 9, it's currently not possible
+        to <quote>freeze</quote> a zone temporarily in order to
+        suspend DDNS while you manually update the zone.
+        If you need to make manual updates to a dynamic zone,
+        you'll need to temporarily reject any updates to the zone via
+        the update ACLs.
+      </para>
+
+      <para>
+        Dynamic updates are only applicable to primary zones.
+        In order to avoid updating secondary zones via DDNS requests,
+        <command>b10-ddns</command> refers to the
+        <quote>secondary_zones</quote> configuration of
+        <command>b10-zonemgr</command>.  Zones listed in
+        <quote>secondary_zones</quote> will never be updated via DDNS
+        regardless of the update ACL configuration;
+	<command>b10-ddns</command> will return a response with an
+	RCODE of NOTAUTH as specified in RFC 2136.
+        If you have a "conceptual" secondary zone whose content is a
+        copy of some external source but is not updated via the
+        standard zone transfers and therefore not listed in
+        <quote>secondary_zones</quote>, be careful not to allow DDNS
+        for the zone; it would be quite likely to lead to inconsistent
+        state between different servers.
+        Normally this should not be a problem because the default
+        update ACL rejects any update requests, but you may want to
+        take an extra care about the configuration if you have such
+        type of secondary zones.
+      </para>
+      <para>
+        The difference of two versions of a zone, before and after a
+        DDNS transaction, is automatically recorded in the underlying
+        data source, and can be retrieved in the form of outbound
+        IXFR.
+        This is done automatically; it does not require specific
+        configuration to make this possible.
+      </para>
+    </section>
+  </chapter>
+
   <chapter id="resolverserver">
     <title>Recursive Name Server</title>
 
@@ -2110,21 +2473,22 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
       </para>
 
       <para>
-        The DHCPv4 server is implemented as <command>b10-dhcp4</command>
-        daemon. As it is not configurable yet, it is fully autonomous,
-        that is it does not interact with <command>b10-cfgmgr</command>.
-        To start DHCPv4 server, simply input:
-
-        <screen>
-#<userinput>cd src/bin/dhcp4</userinput>
-#<userinput>./b10-dhcp4</userinput>
-</screen>
+        <command>b10-dhcp4</command> is a BIND10 component and is being
+        run under BIND10 framework. To add a DHCPv4 process to the set of running
+        BIND10 services, you can use following commands in <command>bindctl</command>:
+        <screen>&gt; <userinput>config add Boss/components b10-dhcp4</userinput>
+&gt; <userinput>config set Boss/components/b10-dhcp4/kind dispensable</userinput>
+&gt; <userinput>config commit</userinput></screen></para>
 
-        Depending on your installation, <command>b10-dhcp4</command>
-        binary may reside in src/bin/dhcp4 in your source code
-        directory, in /usr/local/bin/b10-dhcp4 or other directory
-        you specified during compilation.
+       <para>
+         To shutdown running <command>b10-dhcp4</command>, please use the
+         following command:
+         <screen>&gt; <userinput>Dhcp4 shutdown</userinput></screen>
+         or
+         <screen>&gt; <userinput>config remove Boss/components b10-dhcp4</userinput>
+&gt; <userinput>config commit</userinput></screen></para>
 
+      <para>
         At start, the server will detect available network interfaces
         and will attempt to open UDP sockets on all interfaces that
         are up, running, are not loopback, and have IPv4 address
@@ -2226,13 +2590,7 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
           <listitem>
             <simpara>Upon start, the server will open sockets on all
             interfaces that are not loopback, are up and running and
-            have IPv4 address.  Support for multiple interfaces is not
-            coded in reception routines yet, so if you are running
-            this code on a machine that has many interfaces and
-            <command>b10-dhcp4</command> happens to listen on wrong
-            interface, the easiest way to work around this problem is
-            to turn down other interfaces. This limitation will be
-            fixed shortly.</simpara>
+            have IPv4 address.</simpara>
           </listitem>
           <listitem>
             <simpara>PRL (Parameter Request List, a list of options
@@ -2490,22 +2848,10 @@ const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";</screen>
       currently only supported on Linux systems.</para>
 
       <para>For non-Linux systems, there is currently stub
-      implementation provided. As DHCP servers need to know available
-      addresses, there is a simple mechanism implemented to provide
-      that information. User is expected to create interfaces.txt
-      file. Format of this file is simple. It contains list of
-      interfaces along with available address on each interface. This
-      mechanism is temporary and is going to be removed as soon as
-      interface detection becomes available on non-Linux
-      systems. Here is an example of the interfaces.txt file:
-      <screen>
-# For DHCPv6, please specify link-local address (starts with fe80::)
-# If in doubt, check output of 'ifconfig -a' command.
-eth0 fe80::21e:8cff:fe9b:7349
-
-# For DHCPv4, please use following format:
-#eth0 192.0.2.5</screen>
-      </para>
+      implementation provided. Interface manager detects loopback
+      interfaces only as their name (lo or lo0) can be easily predicted.
+      Please contact BIND10 development team if you are interested
+      in running DHCP components on systems other than Linux.</para>
     </section>
 
     <section id="packet-handling">

+ 388 - 23
doc/guide/bind10-messages.html

@@ -131,6 +131,19 @@ discovered that the memory data source is disabled for the given class.
 </p></dd><dt><a name="AUTH_MEM_DATASRC_ENABLED"></a><span class="term">AUTH_MEM_DATASRC_ENABLED memory data source is enabled for class %1</span></dt><dd><p>
 This is a debug message reporting that the authoritative server has
 discovered that the memory data source is enabled for the given class.
+</p></dd><dt><a name="AUTH_MESSAGE_FORWARD_ERROR"></a><span class="term">AUTH_MESSAGE_FORWARD_ERROR failed to forward %1 request from %2: %3</span></dt><dd><p>
+The authoritative server tried to forward some type DNS request
+message to a separate process (e.g., forwarding dynamic update
+requests to b10-ddns) to handle it, but it failed.  The authoritative
+server returns SERVFAIL to the client on behalf of the separate
+process.  The error could be configuration mismatch between b10-auth
+and the recipient component, or it may be because the requests are
+coming too fast and the receipient process cannot keep up with the
+rate, or some system level failure.  In either case this means the
+BIND 10 system is not working as expected, so the administrator should
+look into the cause and address the issue.  The log message includes
+the client's address (and port), and the error message sent from the
+lower layer that detects the failure.
 </p></dd><dt><a name="AUTH_NOTIFY_QUESTIONS"></a><span class="term">AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY</span></dt><dd><p>
 This debug message is logged by the authoritative server when it receives
 a NOTIFY packet that contains zero or more than one question. (A valid
@@ -395,6 +408,10 @@ so BIND 10 will now shut down. The specific error is printed.
 The boss module is sending a SIGKILL signal to the given process.
 </p></dd><dt><a name="BIND10_SEND_SIGTERM"></a><span class="term">BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2)</span></dt><dd><p>
 The boss module is sending a SIGTERM signal to the given process.
+</p></dd><dt><a name="BIND10_SETGID"></a><span class="term">BIND10_SETGID setting GID to %1</span></dt><dd><p>
+The boss switches the process group ID to the given value.  This happens
+when BIND 10 starts with the -u option, and the group ID will be set to
+that of the specified user.
 </p></dd><dt><a name="BIND10_SETUID"></a><span class="term">BIND10_SETUID setting UID to %1</span></dt><dd><p>
 The boss switches the user it runs as to the given UID.
 </p></dd><dt><a name="BIND10_SHUTDOWN"></a><span class="term">BIND10_SHUTDOWN stopping the server</span></dt><dd><p>
@@ -671,6 +688,11 @@ all messages must contain at least the envelope.
 An older version of the configuration database has been found, from which
 there was an automatic upgrade path to the current version. These changes
 are now applied, and no action from the administrator is necessary.
+</p></dd><dt><a name="CFGMGR_BACKED_UP_CONFIG_FILE"></a><span class="term">CFGMGR_BACKED_UP_CONFIG_FILE Config file %1 was removed; a backup was made at %2</span></dt><dd><p>
+BIND 10 has been started with the command to clear the configuration
+file.  The existing file has been backed up (moved) to the given file
+name. A new configuration file will be created in the original location
+when necessary.
 </p></dd><dt><a name="CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE"></a><span class="term">CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE Unable to parse response from module %1: %2</span></dt><dd><p>
 The configuration manager sent a configuration update to a module, but
 the module responded with an answer that could not be parsed. The answer
@@ -700,10 +722,6 @@ was trying to write the configuration database to disk. The specific
 error is given. The most likely cause is that the system does not have
 write access to the configuration database file. The updated
 configuration is not stored.
-</p></dd><dt><a name="CFGMGR_RENAMED_CONFIG_FILE"></a><span class="term">CFGMGR_RENAMED_CONFIG_FILE renamed configuration file %1 to %2, will create new %1</span></dt><dd><p>
-BIND 10 has been started with the command to clear the configuration file.
-The existing file is backed up to the given file name, so that data is not
-immediately lost if this was done by accident.
 </p></dd><dt><a name="CFGMGR_STOPPED_BY_KEYBOARD"></a><span class="term">CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down</span></dt><dd><p>
 There was a keyboard interrupt signal to stop the cfgmgr daemon. The
 daemon will now shut down.
@@ -891,9 +909,12 @@ in the answer as a result.
 </p></dd><dt><a name="DATASRC_DATABASE_FINDNSEC3"></a><span class="term">DATASRC_DATABASE_FINDNSEC3 Looking for NSEC3 for %1 in %2 mode</span></dt><dd><p>
 Debug information. A search in an database data source for NSEC3 that
 matches or covers the given name is being started.
-</p></dd><dt><a name="DATASRC_DATABASE_FINDNSEC3_COVER"></a><span class="term">DATASRC_DATABASE_FINDNSEC3_COVER found a covering NSEC3 for %1: %2</span></dt><dd><p>
+</p></dd><dt><a name="DATASRC_DATABASE_FINDNSEC3_COVER"></a><span class="term">DATASRC_DATABASE_FINDNSEC3_COVER found a covering NSEC3 for %1 at label count %2: %3</span></dt><dd><p>
 Debug information. An NSEC3 that covers the given name is found and
-being returned.  The found NSEC3 RRset is also displayed.
+being returned.  The found NSEC3 RRset is also displayed. When the shown label
+count is smaller than that of the given name, the matching NSEC3 is for a
+superdomain of the given name (see DATASRC_DATABSE_FINDNSEC3_TRYHASH).  The
+found NSEC3 RRset is also displayed.
 </p></dd><dt><a name="DATASRC_DATABASE_FINDNSEC3_MATCH"></a><span class="term">DATASRC_DATABASE_FINDNSEC3_MATCH found a matching NSEC3 for %1 at label count %2: %3</span></dt><dd><p>
 Debug information. An NSEC3 that matches (a possibly superdomain of)
 the given name is found and being returned.  When the shown label
@@ -954,7 +975,7 @@ name and class, but not for the given type.
 A search in the database for RRs for the specified name, type and class has
 located RRs that match the name and class but not the type.  DNSSEC information
 has been requested and returned.
-</p></dd><dt><a name="DATASRC_DATABASE_FOUND_RRSET"></a><span class="term">DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %5</span></dt><dd><p>
+</p></dd><dt><a name="DATASRC_DATABASE_FOUND_RRSET"></a><span class="term">DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %2</span></dt><dd><p>
 The data returned by the database backend contained data for the given domain
 name, and it either matches the type or has a relevant type. The RRset that is
 returned is printed.
@@ -1053,7 +1074,7 @@ The given wildcard matches the name being sough but it as an empty
 nonterminal (e.g. there's nothing at *.example.com but something like
 subdomain.*.example.org, do exist: so *.example.org exists in the
 namespace but has no RRs assopciated with it). This will produce NXRRSET.
-</p></dd><dt><a name="DATASRC_DATABASE_WILDCARD_MATCH"></a><span class="term">DATASRC_DATABASE_WILDCARD_MATCH search in datasource %1 resulted in wildcard match at %5 with RRset %6</span></dt><dd><p>
+</p></dd><dt><a name="DATASRC_DATABASE_WILDCARD_MATCH"></a><span class="term">DATASRC_DATABASE_WILDCARD_MATCH search in datasource %1 resulted in wildcard match at %2 with RRset %3</span></dt><dd><p>
 The database doesn't contain directly matching name.  When searching
 for a wildcard match, a wildcard record matching the name and type of
 the query was found. The data at this point is returned.
@@ -1526,6 +1547,11 @@ There was a low-level error when we tried to accept an incoming connection
 (probably coming from b10-auth). We continue serving on whatever other
 connections we already have, but this connection is dropped. The reason
 is logged.
+</p></dd><dt><a name="DDNS_AUTH_DBFILE_UPDATE"></a><span class="term">DDNS_AUTH_DBFILE_UPDATE updated auth DB file to %1</span></dt><dd><p>
+b10-ddns was notified of updates to the SQLite3 DB file that b10-auth
+uses for the underlying data source and on which b10-ddns needs to
+make updates.  b10-ddns then updated its internal setup so further
+updates would be made on the new DB.
 </p></dd><dt><a name="DDNS_CC_SESSION_ERROR"></a><span class="term">DDNS_CC_SESSION_ERROR error reading from cc channel: %1</span></dt><dd><p>
 There was a problem reading from the command and control channel. The
 most likely cause is that the msgq process is not running.
@@ -1536,12 +1562,41 @@ configuration manager b10-cfgmgr is not running.
 </p></dd><dt><a name="DDNS_CONFIG_ERROR"></a><span class="term">DDNS_CONFIG_ERROR error found in configuration data: %1</span></dt><dd><p>
 The ddns process encountered an error when installing the configuration at
 startup time.  Details of the error are included in the log message.
+</p></dd><dt><a name="DDNS_CONFIG_HANDLER_ERROR"></a><span class="term">DDNS_CONFIG_HANDLER_ERROR failed to update ddns configuration: %1</span></dt><dd><p>
+An update to b10-ddns configuration was delivered but an error was
+found while applying them.  None of the delivered updates were applied
+to the running b10-ddns system, and the server will keep running with
+the existing configuration.  If this happened in the initial
+configuration setup, the server will be running with the default
+configurations.
 </p></dd><dt><a name="DDNS_DROP_CONN"></a><span class="term">DDNS_DROP_CONN dropping connection on file descriptor %1 because of error %2</span></dt><dd><p>
 There was an error on a connection with the b10-auth server (or whatever
 connects to the ddns daemon). This might be OK, for example when the
 authoritative server shuts down, the connection would get closed. It also
 can mean the system is busy and can't keep up or that the other side got
 confused and sent bad data.
+</p></dd><dt><a name="DDNS_GET_REMOTE_CONFIG_FAIL"></a><span class="term">DDNS_GET_REMOTE_CONFIG_FAIL failed to get %1 module configuration %2 times: %3</span></dt><dd><p>
+b10-ddns tried to get configuration of some remote modules for its
+operation, but it failed.  The most likely cause of this is that the
+remote module has not fully started up and b10-ddns couldn't get the
+configuration in a timely fashion.  b10-ddns attempts to retry it a
+few times, imposing a short delay, hoping it eventually succeeds if
+it's just a timing issue.  The number of total failed attempts is also
+logged.  If it reaches an internal threshold b10-ddns considers it a
+fatal error and terminates.  Even in that case, if b10-ddns is
+configured as a "dispensable" component (which is the default), the
+parent bind10 process will restart it, and there will be another
+chance of getting the remote configuration successfully.  These are
+not the optimal behavior, but it's believed to be sufficient in
+practice (there would normally be no failure in the first place).  If
+it really causes an operational trouble other than having a few of
+these log messages, please submit a bug report; there can be several
+ways to make it more sophisticated.  Another, less likely reason for
+having this error is because the remote modules are not actually
+configured to run.  If that's the case fixing the configuration should
+solve the problem - either by making sure the remote module will run
+or by not running b10-ddns (without these remote modules b10-ddns is
+not functional, so there's no point in running it in this case).
 </p></dd><dt><a name="DDNS_MODULECC_SESSION_ERROR"></a><span class="term">DDNS_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1</span></dt><dd><p>
 There was a problem in the lower level module handling configuration and
 control commands.  This could happen for various reasons, but the most likely
@@ -1553,12 +1608,80 @@ Debug message. We received a connection and we are going to start handling
 requests from it. The file descriptor number and the address where the request
 comes from is logged. The connection is over a unix domain socket and is likely
 coming from a b10-auth process.
+</p></dd><dt><a name="DDNS_RECEIVED_AUTH_UPDATE"></a><span class="term">DDNS_RECEIVED_AUTH_UPDATE received configuration updates from auth server</span></dt><dd><p>
+b10-ddns is notified of updates to b10-auth configuration
+(including a report of the initial configuration) that b10-ddns might
+be interested in.
 </p></dd><dt><a name="DDNS_RECEIVED_SHUTDOWN_COMMAND"></a><span class="term">DDNS_RECEIVED_SHUTDOWN_COMMAND shutdown command received</span></dt><dd><p>
 The ddns process received a shutdown command from the command channel
 and will now shut down.
-</p></dd><dt><a name="DDNS_RUNNING"></a><span class="term">DDNS_RUNNING ddns server is running and listening for updates</span></dt><dd><p>
-The ddns process has successfully started and is now ready to receive commands
-and updates.
+</p></dd><dt><a name="DDNS_RECEIVED_ZONEMGR_UPDATE"></a><span class="term">DDNS_RECEIVED_ZONEMGR_UPDATE received configuration updates from zonemgr</span></dt><dd><p>
+b10-ddns is notified of updates to b10-zonemgr's configuration
+(including a report of the initial configuration).  It may possibly
+contain changes to the secondary zones, in which case b10-ddns will
+update its internal copy of that configuration.
+</p></dd><dt><a name="DDNS_REQUEST_PARSE_FAIL"></a><span class="term">DDNS_REQUEST_PARSE_FAIL failed to parse update request: %1</span></dt><dd><p>
+b10-ddns received an update request via b10-auth, but the received
+data failed to pass minimum validation: it was either broken wire
+format data for a valid DNS message (e.g. it's shorter than the
+fixed-length header), or the opcode is not update, or TSIG is included
+in the request but it fails to validate.  Since b10-auth should have
+performed this level of checks, such an error shouldn't be detected at
+this stage and should rather be considered an internal bug.  This
+event is therefore logged at the error level, and the request is
+simply dropped.  Additional information of the error is also logged.
+</p></dd><dt><a name="DDNS_REQUEST_TCP_QUOTA"></a><span class="term">DDNS_REQUEST_TCP_QUOTA reject TCP update client %1 (%2 running)</span></dt><dd><p>
+b10-ddns received a new update request from a client over TCP, but
+the number of TCP clients being handled by the server already reached
+the configured quota, so the latest client was rejected by closing
+the connection.  The administrator may want to check the status of
+b10-ddns, and if this happens even if the server is not very busy,
+the quota may have to be increased.  Or, if it's more likely to be
+malicious or simply bogus clients that somehow keep the TCP connection
+open for a long period, maybe they should be rejected with an
+appropriate ACL configuration or some lower layer filtering.  The
+number of existing TCP clients are shown in the log, which should be
+identical to the current quota.
+</p></dd><dt><a name="DDNS_RESPONSE_SOCKET_ERROR"></a><span class="term">DDNS_RESPONSE_SOCKET_ERROR failed to send update response to %1: %2</span></dt><dd><p>
+Network I/O error happens in sending an update response.  The
+client's address that caused the error and error details are also
+logged.
+</p></dd><dt><a name="DDNS_RESPONSE_TCP_SOCKET_ERROR"></a><span class="term">DDNS_RESPONSE_TCP_SOCKET_ERROR failed to complete sending update response to %1 over TCP</span></dt><dd><p>
+b10-ddns had tried to send an update response over TCP, and it hadn't
+been completed at that time, and a followup attempt to complete the
+send operation failed due to some network I/O error.  While a network
+error can happen any time, this event is quite unexpected for two
+reasons.  First, since the size of a response to an update request
+should be generally small, it's unlikely that the initial attempt
+didn't fail but wasn't completed.  Second, since the first attempt
+succeeded and the TCP connection had been established in the first
+place, it's more likely for the subsequent attempt to succeed.  In any
+case, there may not be able to do anything to fix it at the server
+side, but the administrator may want to check the general reachability
+with the client address.
+</p></dd><dt><a name="DDNS_SECONDARY_ZONES_UPDATE"></a><span class="term">DDNS_SECONDARY_ZONES_UPDATE updated secondary zone list (%1 zones are listed)</span></dt><dd><p>
+b10-ddns has successfully updated the internal copy of secondary zones
+obtained from b10-zonemgr, based on a latest update to zonemgr's
+configuration.  The number of newly configured (unique) secondary
+zones is logged.
+</p></dd><dt><a name="DDNS_SECONDARY_ZONES_UPDATE_FAIL"></a><span class="term">DDNS_SECONDARY_ZONES_UPDATE_FAIL failed to update secondary zone list: %1</span></dt><dd><p>
+An error message.  b10-ddns was notified of updates to a list of
+secondary zones from b10-zonemgr and tried to update its own internal
+copy of the list, but it failed.  This can happen if the configuration
+contains an error, and b10-zonemgr should also reject that update.
+Unfortunately, in the current implementation there is no way to ensure
+that both zonemgr and ddns have consistent information when an update
+contains an error; further, as of this writing zonemgr has a bug that
+it could partially update the list of secondary zones if part of the
+list has an error (see Trac ticket #2038).  b10-ddns still keeps
+running with the previous configuration, but it's strongly advisable
+to check log messages from zonemgr, and if it indicates there can be
+inconsistent state, it's better to restart the entire BIND 10 system
+(just restarting b10-ddns wouldn't be enough, because zonemgr can have
+partially updated configuration due to bug #2038).  The log message
+contains an error description, but it's intentionally kept simple as
+it's primarily a matter of zonemgr.  To know the details of the error,
+log messages of zonemgr should be consulted.
 </p></dd><dt><a name="DDNS_SESSION"></a><span class="term">DDNS_SESSION session arrived on file descriptor %1</span></dt><dd><p>
 A debug message, informing there's some activity on the given file descriptor.
 It will be either a request or the file descriptor will be closed. See
@@ -1567,6 +1690,9 @@ following log messages to see what of it.
 The ddns process is shutting down. It will no longer listen for new commands
 or updates. Any command or update that is being addressed at this moment will
 be completed, after which the process will exit.
+</p></dd><dt><a name="DDNS_STARTED"></a><span class="term">DDNS_STARTED ddns server is running and listening for updates</span></dt><dd><p>
+The ddns process has successfully started and is now ready to receive commands
+and updates.
 </p></dd><dt><a name="DDNS_STOPPED"></a><span class="term">DDNS_STOPPED ddns server has stopped</span></dt><dd><p>
 The ddns process has successfully stopped and is no longer listening for or
 handling commands or updates, and will now exit.
@@ -1577,6 +1703,212 @@ process will now shut down.
 The b10-ddns process encountered an uncaught exception and will now shut
 down. This is indicative of a programming error and should not happen under
 normal circumstances. The exception type and message are printed.
+</p></dd><dt><a name="DDNS_UPDATE_NOTIFY"></a><span class="term">DDNS_UPDATE_NOTIFY notified %1 of updates to %2</span></dt><dd><p>
+Debug message.  b10-ddns has made updates to a zone based on an update
+request and has successfully notified an external module of the updates.
+The notified module will use that information for updating its own
+state or any necessary protocol action such as zone reloading or sending
+notify messages to secondary servers.
+</p></dd><dt><a name="DDNS_UPDATE_NOTIFY_FAIL"></a><span class="term">DDNS_UPDATE_NOTIFY_FAIL failed to notify %1 of updates to %2: %3</span></dt><dd><p>
+b10-ddns has made updates to a zone based on an update request and
+tried to notify an external component of the updates, but the
+notification fails.  One possible cause of this is that the external
+component is not really running and it times out in waiting for the
+response, although it will be less likely to happen in practice
+because these components will normally be configured to run when the
+server provides the authoritative DNS service; ddns is rather optional
+among them.  If this happens, however, it will suspend b10-ddns for a
+few seconds during which it cannot handle new requests (some may be
+delayed, some may be dropped, depending on the volume of the incoming
+requests).  This is obviously bad, and if this error happens due to
+this reason, the administrator should make sure the component in
+question should be configured to run.  For a longer term, b10-ddns
+should be more robust about this case such as by making this
+notification asynchronously and/or detecting the existence of the
+external components to avoid hopeless notification in the first place.
+Severity of this error for the receiving components depends on the
+type of the component.  If it's b10-xfrout, this means DNS notify
+messages won't be sent to secondary servers of the zone.  It's
+suboptimal, but not necessarily critical as the secondary servers will
+try to check the zone's status periodically.  If it's b10-auth and the
+notification was needed to have it reload the corresponding zone, it's
+more serious because b10-auth won't be able to serve the new version
+of the zone unless some explicit recovery action is taken.  So the
+administrator needs to examine this message and takes an appropriate
+action.  In either case, this notification is generally expected to
+succeed; so the fact it fails itself means there's something wrong in
+the BIND 10 system, and it would be advisable to check other log
+messages.
+</p></dd><dt><a name="LIBDDNS_DATASRC_ERROR"></a><span class="term">LIBDDNS_DATASRC_ERROR update client %1 failed due to data source error: %2</span></dt><dd><p>
+An update attempt failed due to some error in the corresponding data
+source.  This is generally an unexpected event, but can still happen
+for various reasons such as DB lock contention or a failure of the
+backend DB server.  The cause of the error is also logged.  It's
+advisable to check the message, and, if necessary, take an appropriate
+action (e.g., restarting the DB server if it dies).  If this message
+is logged the data source isn't modified due to the
+corresponding update request.  When used by the b10-ddns, the server
+will return a response with an RCODE of SERVFAIL.
+</p></dd><dt><a name="LIBDDNS_PREREQ_FORMERR"></a><span class="term">LIBDDNS_PREREQ_FORMERR update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL.</span></dt><dd><p>
+The prerequisite with the given name, class and type is not well-formed.
+The specific prerequisite is shown. In this case, it has a non-zero TTL value.
+A FORMERR error response is sent to the client.
+</p></dd><dt><a name="LIBDDNS_PREREQ_FORMERR_ANY"></a><span class="term">LIBDDNS_PREREQ_FORMERR_ANY update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL or rdata found.</span></dt><dd><p>
+The prerequisite with the given name, class and type is not well-formed.
+The specific prerequisite is shown. In this case, it either has a non-zero
+TTL value, or has rdata fields. A FORMERR error response is sent to the client.
+</p></dd><dt><a name="LIBDDNS_PREREQ_FORMERR_CLASS"></a><span class="term">LIBDDNS_PREREQ_FORMERR_CLASS update client %1 for zone %2: Format error in prerequisite (%3). Bad class.</span></dt><dd><p>
+The prerequisite with the given name, class and type is not well-formed.
+The specific prerequisite is shown. In this case, the class of the
+prerequisite should either match the class of the zone in the Zone Section,
+or it should be ANY or NONE, and it is not. A FORMERR error response is sent
+to the client.
+</p></dd><dt><a name="LIBDDNS_PREREQ_FORMERR_NONE"></a><span class="term">LIBDDNS_PREREQ_FORMERR_NONE update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL or rdata found.</span></dt><dd><p>
+The prerequisite with the given name, class and type is not well-formed.
+The specific prerequisite is shown. In this case, it either has a non-zero
+TTL value, or has rdata fields. A FORMERR error response is sent to the client.
+</p></dd><dt><a name="LIBDDNS_PREREQ_NAME_IN_USE_FAILED"></a><span class="term">LIBDDNS_PREREQ_NAME_IN_USE_FAILED update client %1 for zone %2: 'Name is in use' prerequisite not satisfied (%3), rcode: %4</span></dt><dd><p>
+A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
+was not satisfied is shown. The client is sent an error response with the
+given rcode.
+In this case, the specific prerequisite is 'Name is in use'. From RFC2136:
+Name is in use.  At least one RR with a specified NAME (in
+the zone and class specified by the Zone Section) must exist.
+Note that this prerequisite is NOT satisfied by empty
+nonterminals.
+</p></dd><dt><a name="LIBDDNS_PREREQ_NAME_NOT_IN_USE_FAILED"></a><span class="term">LIBDDNS_PREREQ_NAME_NOT_IN_USE_FAILED update client %1 for zone %2: 'Name is not in use' (%3) prerequisite not satisfied, rcode: %4</span></dt><dd><p>
+A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
+was not satisfied is shown. The client is sent an error response with the
+given rcode.
+In this case, the specific prerequisite is 'Name is not in use'.
+From RFC2136:
+Name is not in use.  No RR of any type is owned by a
+specified NAME.  Note that this prerequisite IS satisfied by
+empty nonterminals.
+</p></dd><dt><a name="LIBDDNS_PREREQ_NOTZONE"></a><span class="term">LIBDDNS_PREREQ_NOTZONE update client %1 for zone %2: prerequisite not in zone (%3)</span></dt><dd><p>
+A DDNS UPDATE prerequisite has a name that does not appear to be inside
+the zone specified in the Zone section of the UPDATE message.
+The specific prerequisite is shown. A NOTZONE error response is sent to
+the client.
+</p></dd><dt><a name="LIBDDNS_PREREQ_RRSET_DOES_NOT_EXIST_FAILED"></a><span class="term">LIBDDNS_PREREQ_RRSET_DOES_NOT_EXIST_FAILED update client %1 for zone %2: 'RRset does not exist' (%3) prerequisite not satisfied, rcode: %4</span></dt><dd><p>
+A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
+was not satisfied is shown. The client is sent an error response with the
+given rcode.
+In this case, the specific prerequisite is 'RRset does not exist'.
+From RFC2136:
+RRset does not exist.  No RRs with a specified NAME and TYPE
+(in the zone and class denoted by the Zone Section) can exist.
+</p></dd><dt><a name="LIBDDNS_PREREQ_RRSET_EXISTS_FAILED"></a><span class="term">LIBDDNS_PREREQ_RRSET_EXISTS_FAILED update client %1 for zone %2: 'RRset exists (value independent)' (%3) prerequisite not satisfied, rcode: %4</span></dt><dd><p>
+A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
+was not satisfied is shown. The client is sent an error response with the
+given rcode.
+In this case, the specific prerequisite is 'RRset exists (value independent)'.
+From RFC2136:
+RRset exists (value dependent).  A set of RRs with a
+specified NAME and TYPE exists and has the same members
+with the same RDATAs as the RRset specified here in this
+Section.
+</p></dd><dt><a name="LIBDDNS_PREREQ_RRSET_EXISTS_VAL_FAILED"></a><span class="term">LIBDDNS_PREREQ_RRSET_EXISTS_VAL_FAILED update client %1 for zone %2: 'RRset exists (value dependent)' (%3) prerequisite not satisfied, rcode: %4</span></dt><dd><p>
+A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
+was not satisfied is shown. The client is sent an error response with the
+given rcode.
+In this case, the specific prerequisite is 'RRset exists (value dependent)'.
+From RFC2136:
+RRset exists (value independent).  At least one RR with a
+specified NAME and TYPE (in the zone and class specified by
+the Zone Section) must exist.
+</p></dd><dt><a name="LIBDDNS_UPDATE_ADD_BAD_TYPE"></a><span class="term">LIBDDNS_UPDATE_ADD_BAD_TYPE update client %1 for zone %2: update addition RR bad type: %3</span></dt><dd><p>
+The Update section of a DDNS update message contains a statement
+that tries to add a record of an invalid type. Most likely the
+record has an RRType that is considered a 'meta' type, which
+cannot be zone content data. The specific record is shown.
+A FORMERR response is sent back to the client.
+</p></dd><dt><a name="LIBDDNS_UPDATE_APPROVED"></a><span class="term">LIBDDNS_UPDATE_APPROVED update client %1 for zone %2 approved</span></dt><dd><p>
+Debug message.  An update request was approved in terms of the zone's
+update ACL.
+</p></dd><dt><a name="LIBDDNS_UPDATE_BAD_CLASS"></a><span class="term">LIBDDNS_UPDATE_BAD_CLASS update client %1 for zone %2: bad class in update RR: %3</span></dt><dd><p>
+The Update section of a DDNS update message contains an RRset with
+a bad class. The class of the update RRset must be either the same
+as the class in the Zone Section, ANY, or NONE.
+A FORMERR response is sent back to the client.
+</p></dd><dt><a name="LIBDDNS_UPDATE_DATASRC_ERROR"></a><span class="term">LIBDDNS_UPDATE_DATASRC_ERROR error in datasource during DDNS update: %1</span></dt><dd><p>
+An error occured while committing the DDNS update changes to the
+datasource. The specific error is printed. A SERVFAIL response is sent
+back to the client.
+</p></dd><dt><a name="LIBDDNS_UPDATE_DELETE_BAD_TYPE"></a><span class="term">LIBDDNS_UPDATE_DELETE_BAD_TYPE update client %1 for zone %2: update deletion RR bad type: %3</span></dt><dd><p>
+The Update section of a DDNS update message contains a statement
+that tries to delete an rrset of an invalid type. Most likely the
+record has an RRType that is considered a 'meta' type, which
+cannot be zone content data. The specific record is shown.
+A FORMERR response is sent back to the client.
+</p></dd><dt><a name="LIBDDNS_UPDATE_DELETE_NONZERO_TTL"></a><span class="term">LIBDDNS_UPDATE_DELETE_NONZERO_TTL update client %1 for zone %2: update deletion RR has non-zero TTL: %3</span></dt><dd><p>
+The Update section of a DDNS update message contains a 'delete rrset'
+statement with a non-zero TTL. This is not allowed by the protocol.
+A FORMERR response is sent back to the client.
+</p></dd><dt><a name="LIBDDNS_UPDATE_DELETE_RRSET_NOT_EMPTY"></a><span class="term">LIBDDNS_UPDATE_DELETE_RRSET_NOT_EMPTY update client %1 for zone %2: update deletion RR contains data %3</span></dt><dd><p>
+The Update section of a DDNS update message contains a 'delete rrset'
+statement with a non-empty RRset. This is not allowed by the protocol.
+A FORMERR response is sent back to the client.
+</p></dd><dt><a name="LIBDDNS_UPDATE_DELETE_RR_BAD_TYPE"></a><span class="term">LIBDDNS_UPDATE_DELETE_RR_BAD_TYPE update client %1 for zone %2: update deletion RR bad type: %3</span></dt><dd><p>
+The Update section of a DDNS update message contains a statement
+that tries to delete one or more rrs of an invalid type. Most
+likely the records have an RRType that is considered a 'meta'
+type, which cannot be zone content data. The specific record is
+shown. A FORMERR response is sent back to the client.
+</p></dd><dt><a name="LIBDDNS_UPDATE_DELETE_RR_NONZERO_TTL"></a><span class="term">LIBDDNS_UPDATE_DELETE_RR_NONZERO_TTL update client %1 for zone %2: update deletion RR has non-zero TTL: %3</span></dt><dd><p>
+The Update section of a DDNS update message contains a 'delete rrs'
+statement with a non-zero TTL. This is not allowed by the protocol.
+A FORMERR response is sent back to the client.
+</p></dd><dt><a name="LIBDDNS_UPDATE_DENIED"></a><span class="term">LIBDDNS_UPDATE_DENIED update client %1 for zone %2 denied</span></dt><dd><p>
+Informational message.  An update request was denied because it was
+rejected by the zone's update ACL.  When this library is used by
+b10-ddns, the server will respond to the request with an RCODE of
+REFUSED as described in Section 3.3 of RFC2136.
+</p></dd><dt><a name="LIBDDNS_UPDATE_DROPPED"></a><span class="term">LIBDDNS_UPDATE_DROPPED update client %1 for zone %2 dropped</span></dt><dd><p>
+Informational message.  An update request was denied because it was
+rejected by the zone's update ACL.  When this library is used by
+b10-ddns, the server will then completely ignore the request; no
+response will be sent.
+</p></dd><dt><a name="LIBDDNS_UPDATE_ERROR"></a><span class="term">LIBDDNS_UPDATE_ERROR update client %1 for zone %2: %3</span></dt><dd><p>
+Debug message.  An error is found in processing a dynamic update
+request.  This log message is used for general errors that are not
+normally expected to happen.  So, in general, it would mean some
+problem in the client implementation or an interoperability issue
+with this implementation.  The client's address, the zone name and
+class, and description of the error are logged.
+</p></dd><dt><a name="LIBDDNS_UPDATE_FORWARD_FAIL"></a><span class="term">LIBDDNS_UPDATE_FORWARD_FAIL update client %1 for zone %2: update forwarding not supported</span></dt><dd><p>
+Debug message.  An update request is sent to a secondary server.  This
+is not necessarily invalid, but this implementation does not yet
+support update forwarding as specified in Section 6 of RFC2136 and it
+will simply return a response with an RCODE of NOTIMP to the client.
+The client's address and the zone name/class are logged.
+</p></dd><dt><a name="LIBDDNS_UPDATE_NOTAUTH"></a><span class="term">LIBDDNS_UPDATE_NOTAUTH update client %1 for zone %2: not authoritative for update zone</span></dt><dd><p>
+Debug message.  An update request was received for a zone for which
+the receiving server doesn't have authority.  In theory this is an
+unexpected event, but there are client implementations that could send
+update requests carelessly, so it may not necessarily be so uncommon
+in practice.  If possible, you may want to check the implementation or
+configuration of those clients to suppress the requests.  As specified
+in Section 3.1 of RFC2136, the receiving server will return a response
+with an RCODE of NOTAUTH.
+</p></dd><dt><a name="LIBDDNS_UPDATE_NOTZONE"></a><span class="term">LIBDDNS_UPDATE_NOTZONE update client %1 for zone %2: update RR out of zone %3</span></dt><dd><p>
+A DDNS UPDATE record has a name that does not appear to be inside
+the zone specified in the Zone section of the UPDATE message.
+The specific update record is shown. A NOTZONE error response is
+sent to the client.
+</p></dd><dt><a name="LIBDDNS_UPDATE_PREREQUISITE_FAILED"></a><span class="term">LIBDDNS_UPDATE_PREREQUISITE_FAILED prerequisite failed in update client %1 for zone %2: result code %3</span></dt><dd><p>
+The handling of the prerequisite section (RFC2136 Section 3.2) found
+that one of the prerequisites was not satisfied. The result code
+should give more information on what prerequisite type failed.
+If the result code is FORMERR, the prerequisite section was not well-formed.
+An error response with the given result code is sent back to the client.
+</p></dd><dt><a name="LIBDDNS_UPDATE_UNCAUGHT_EXCEPTION"></a><span class="term">LIBDDNS_UPDATE_UNCAUGHT_EXCEPTION update client %1 for zone %2: uncaught exception while processing update section: %3</span></dt><dd><p>
+An uncaught exception was encountered while processing the Update
+section of a DDNS message. The specific exception is shown in the log message.
+To make sure DDNS service is not interrupted, this problem is caught instead
+of reraised; The update is aborted, and a SERVFAIL is sent back to the client.
+This is most probably a bug in the DDNS code, but *could* be caused by
+the data source.
 </p></dd><dt><a name="LIBXFRIN_DIFFERENT_TTL"></a><span class="term">LIBXFRIN_DIFFERENT_TTL multiple data with different TTLs (%1, %2) on %3/%4/%5. Adjusting %2 -&gt; %1.</span></dt><dd><p>
 The xfrin module received an update containing multiple rdata changes for the
 same RRset. But the TTLs of these don't match each other. As we combine them
@@ -1634,6 +1966,8 @@ the reason given.
 An invalid message identification (ID) has been found during the read of
 a message file.  Message IDs should comprise only alphanumeric characters
 and the underscore, and should not start with a digit.
+</p></dd><dt><a name="LOG_LOCK_TEST_MESSAGE"></a><span class="term">LOG_LOCK_TEST_MESSAGE this is a test message.</span></dt><dd><p>
+This is a log message used in testing.
 </p></dd><dt><a name="LOG_NAMESPACE_EXTRA_ARGS"></a><span class="term">LOG_NAMESPACE_EXTRA_ARGS line %1: $NAMESPACE directive has too many arguments</span></dt><dd><p>
 The $NAMESPACE directive in a message file takes a single argument, a
 namespace in which all the generated symbol names are placed.  This error
@@ -1835,6 +2169,30 @@ an answer with a different given type and class.
 </p><p>
 This message indicates an internal error in the NSAS.  Please raise a
 bug report.
+</p></dd><dt><a name="PYSERVER_COMMON_AUTH_CONFIG_NAME_PARSER_ERROR"></a><span class="term">PYSERVER_COMMON_AUTH_CONFIG_NAME_PARSER_ERROR Invalid name when parsing Auth configuration: %1</span></dt><dd><p>
+There was an invalid name when parsing Auth configuration.
+</p></dd><dt><a name="PYSERVER_COMMON_AUTH_CONFIG_RRCLASS_ERROR"></a><span class="term">PYSERVER_COMMON_AUTH_CONFIG_RRCLASS_ERROR Invalid RRClass when parsing Auth configuration: %1</span></dt><dd><p>
+There was an invalid RR class when parsing Auth configuration.
+</p></dd><dt><a name="PYSERVER_COMMON_DNS_TCP_SEND_DONE"></a><span class="term">PYSERVER_COMMON_DNS_TCP_SEND_DONE completed sending TCP message to %1 (%2 bytes in total)</span></dt><dd><p>
+Debug message.  A complete DNS message has been successfully
+transmitted over a TCP connection, possibly after multiple send
+operations.  The destination address and the total size of the message
+(including the 2-byte length field) are shown in the log message.
+</p></dd><dt><a name="PYSERVER_COMMON_DNS_TCP_SEND_ERROR"></a><span class="term">PYSERVER_COMMON_DNS_TCP_SEND_ERROR failed to send TCP message to %1 (%2/%3 bytes sent): %4</span></dt><dd><p>
+A DNS message has been attempted to be sent out over a TCP connection,
+but it failed due to some network error.  Although it's not expected
+to happen too often, it can still happen for various reasons.  The
+administrator may want to examine the cause of the failure, which is
+included in the log message, to see if it requires some action to
+be taken at the server side.  When this message is logged, the
+corresponding  TCP connection was closed immediately after the error
+was detected.
+</p></dd><dt><a name="PYSERVER_COMMON_DNS_TCP_SEND_PENDING"></a><span class="term">PYSERVER_COMMON_DNS_TCP_SEND_PENDING sent part TCP message to %1 (up to %2/%3 bytes)</span></dt><dd><p>
+Debug message.  A part of DNS message has been transmitted over a TCP
+connection, and it's suspended because further attempt would block.
+The destination address and the total size of the message that has
+been transmitted so far (including the 2-byte length field) are shown
+in the log message.
 </p></dd><dt><a name="PYSERVER_COMMON_TSIG_KEYRING_DEINIT"></a><span class="term">PYSERVER_COMMON_TSIG_KEYRING_DEINIT Deinitializing global TSIG keyring</span></dt><dd><p>
 A debug message noting that the global TSIG keyring is being removed from
 memory. Most programs don't do that, they just exit, which is OK.
@@ -2402,10 +2760,6 @@ is unknown in the implementation. The most likely cause is an
 installation problem, where the specification file stats.spec is
 from a different version of BIND 10 than the stats module itself.
 Please check your installation.
-</p></dd><dt><a name="XFRIN_AUTH_CONFIG_NAME_PARSER_ERROR"></a><span class="term">XFRIN_AUTH_CONFIG_NAME_PARSER_ERROR Invalid name when parsing Auth configuration: %1</span></dt><dd><p>
-There was an invalid name when parsing Auth configuration.
-</p></dd><dt><a name="XFRIN_AUTH_CONFIG_RRCLASS_ERROR"></a><span class="term">XFRIN_AUTH_CONFIG_RRCLASS_ERROR Invalid RRClass when parsing Auth configuration: %1</span></dt><dd><p>
-There was an invalid RR class when parsing Auth configuration.
 </p></dd><dt><a name="XFRIN_AUTH_LOADZONE"></a><span class="term">XFRIN_AUTH_LOADZONE sending Auth loadzone for origin=%1, class=%2, datasrc=%3</span></dt><dd><p>
 There was a successful zone transfer, and the zone is served by b10-auth
 in the in-memory data source using sqlite3 as a backend. We send the
@@ -2689,11 +3043,15 @@ do not understand or support. The xfrout request will be ignored.
 In general, this should only occur for unexpected problems like
 memory allocation failures, as the query should already have been
 parsed by the b10-auth daemon, before it was passed here.
-</p></dd><dt><a name="XFROUT_PROCESS_REQUEST_ERROR"></a><span class="term">XFROUT_PROCESS_REQUEST_ERROR error processing transfer request: %2</span></dt><dd><p>
-There was an error processing a transfer request. The error is included
-in the log message, but at this point no specific information other
-than that could be given. This points to incomplete exception handling
-in the code.
+</p></dd><dt><a name="XFROUT_PROCESS_REQUEST_ERROR"></a><span class="term">XFROUT_PROCESS_REQUEST_ERROR error processing transfer request: %1</span></dt><dd><p>
+There was an error in receiving a transfer request from b10-auth.
+This is generally an unexpected event, but is possible when, for
+example, b10-auth terminates in the middle of forwarding the request.
+When this happens it's unlikely to be recoverable with the same
+communication session with b10-auth, so b10-xfrout drops it and
+waits for a new session.  In any case, this error indicates that
+there's something very wrong in the system, so it's advisable to check
+the over all status of the BIND 10 system.
 </p></dd><dt><a name="XFROUT_QUERY_DROPPED"></a><span class="term">XFROUT_QUERY_DROPPED %1 client %2: request to transfer %3 dropped</span></dt><dd><p>
 The xfrout process silently dropped a request to transfer zone to
 given host.  This is required by the ACLs.  The %2 represents the IP
@@ -2718,9 +3076,16 @@ The xfrout daemon received a shutdown command from the command channel
 and will now shut down.
 </p></dd><dt><a name="XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR"></a><span class="term">XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR error receiving the file descriptor for an XFR connection</span></dt><dd><p>
 There was an error receiving the file descriptor for the transfer
-request. Normally, the request is received by b10-auth, and passed on
-to the xfrout daemon, so it can answer directly. However, there was a
-problem receiving this file descriptor. The request will be ignored.
+request from b10-auth.  There can be several reasons for this, but
+the most likely cause is that b10-auth terminates for some reason
+(maybe it's a bug of b10-auth, maybe it's an intentional restart by
+the administrator), so depending on how this happens it may or may not
+be a serious error.  But in any case this is not expected to happen
+frequently, and it's advisable to figure out how this happened if
+this message is logged.  Even if this error happens xfrout will reset
+its internal state and will keep receiving further requests.  So
+if it's just a temporary restart of b10-auth the administrator does
+not have to do anything.
 </p></dd><dt><a name="XFROUT_REMOVE_OLD_UNIX_SOCKET_FILE_ERROR"></a><span class="term">XFROUT_REMOVE_OLD_UNIX_SOCKET_FILE_ERROR error removing unix socket file %1: %2</span></dt><dd><p>
 The unix socket file xfrout needs for contact with the auth daemon
 already exists, and needs to be removed first, but there is a problem

+ 639 - 39
doc/guide/bind10-messages.xml

@@ -303,6 +303,24 @@ discovered that the memory data source is enabled for the given class.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="AUTH_MESSAGE_FORWARD_ERROR">
+<term>AUTH_MESSAGE_FORWARD_ERROR failed to forward %1 request from %2: %3</term>
+<listitem><para>
+The authoritative server tried to forward some type DNS request
+message to a separate process (e.g., forwarding dynamic update
+requests to b10-ddns) to handle it, but it failed.  The authoritative
+server returns SERVFAIL to the client on behalf of the separate
+process.  The error could be configuration mismatch between b10-auth
+and the recipient component, or it may be because the requests are
+coming too fast and the receipient process cannot keep up with the
+rate, or some system level failure.  In either case this means the
+BIND 10 system is not working as expected, so the administrator should
+look into the cause and address the issue.  The log message includes
+the client's address (and port), and the error message sent from the
+lower layer that detects the failure.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="AUTH_NOTIFY_QUESTIONS">
 <term>AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY</term>
 <listitem><para>
@@ -882,6 +900,15 @@ The boss module is sending a SIGTERM signal to the given process.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="BIND10_SETGID">
+<term>BIND10_SETGID setting GID to %1</term>
+<listitem><para>
+The boss switches the process group ID to the given value.  This happens
+when BIND 10 starts with the -u option, and the group ID will be set to
+that of the specified user.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="BIND10_SETUID">
 <term>BIND10_SETUID setting UID to %1</term>
 <listitem><para>
@@ -1588,6 +1615,16 @@ are now applied, and no action from the administrator is necessary.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="CFGMGR_BACKED_UP_CONFIG_FILE">
+<term>CFGMGR_BACKED_UP_CONFIG_FILE Config file %1 was removed; a backup was made at %2</term>
+<listitem><para>
+BIND 10 has been started with the command to clear the configuration
+file.  The existing file has been backed up (moved) to the given file
+name. A new configuration file will be created in the original location
+when necessary.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE">
 <term>CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE Unable to parse response from module %1: %2</term>
 <listitem><para>
@@ -1647,15 +1684,6 @@ configuration is not stored.
 </para></listitem>
 </varlistentry>
 
-<varlistentry id="CFGMGR_RENAMED_CONFIG_FILE">
-<term>CFGMGR_RENAMED_CONFIG_FILE renamed configuration file %1 to %2, will create new %1</term>
-<listitem><para>
-BIND 10 has been started with the command to clear the configuration file.
-The existing file is backed up to the given file name, so that data is not
-immediately lost if this was done by accident.
-</para></listitem>
-</varlistentry>
-
 <varlistentry id="CFGMGR_STOPPED_BY_KEYBOARD">
 <term>CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down</term>
 <listitem><para>
@@ -2074,10 +2102,13 @@ matches or covers the given name is being started.
 </varlistentry>
 
 <varlistentry id="DATASRC_DATABASE_FINDNSEC3_COVER">
-<term>DATASRC_DATABASE_FINDNSEC3_COVER found a covering NSEC3 for %1: %2</term>
+<term>DATASRC_DATABASE_FINDNSEC3_COVER found a covering NSEC3 for %1 at label count %2: %3</term>
 <listitem><para>
 Debug information. An NSEC3 that covers the given name is found and
-being returned.  The found NSEC3 RRset is also displayed.
+being returned.  The found NSEC3 RRset is also displayed. When the shown label
+count is smaller than that of the given name, the matching NSEC3 is for a
+superdomain of the given name (see DATASRC_DATABSE_FINDNSEC3_TRYHASH).  The
+found NSEC3 RRset is also displayed.
 </para></listitem>
 </varlistentry>
 
@@ -2212,7 +2243,7 @@ has been requested and returned.
 </varlistentry>
 
 <varlistentry id="DATASRC_DATABASE_FOUND_RRSET">
-<term>DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %5</term>
+<term>DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %2</term>
 <listitem><para>
 The data returned by the database backend contained data for the given domain
 name, and it either matches the type or has a relevant type. The RRset that is
@@ -2411,7 +2442,7 @@ namespace but has no RRs assopciated with it). This will produce NXRRSET.
 </varlistentry>
 
 <varlistentry id="DATASRC_DATABASE_WILDCARD_MATCH">
-<term>DATASRC_DATABASE_WILDCARD_MATCH search in datasource %1 resulted in wildcard match at %5 with RRset %6</term>
+<term>DATASRC_DATABASE_WILDCARD_MATCH search in datasource %1 resulted in wildcard match at %2 with RRset %3</term>
 <listitem><para>
 The database doesn't contain directly matching name.  When searching
 for a wildcard match, a wildcard record matching the name and type of
@@ -3603,6 +3634,16 @@ is logged.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DDNS_AUTH_DBFILE_UPDATE">
+<term>DDNS_AUTH_DBFILE_UPDATE updated auth DB file to %1</term>
+<listitem><para>
+b10-ddns was notified of updates to the SQLite3 DB file that b10-auth
+uses for the underlying data source and on which b10-ddns needs to
+make updates.  b10-ddns then updated its internal setup so further
+updates would be made on the new DB.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DDNS_CC_SESSION_ERROR">
 <term>DDNS_CC_SESSION_ERROR error reading from cc channel: %1</term>
 <listitem><para>
@@ -3628,6 +3669,18 @@ startup time.  Details of the error are included in the log message.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DDNS_CONFIG_HANDLER_ERROR">
+<term>DDNS_CONFIG_HANDLER_ERROR failed to update ddns configuration: %1</term>
+<listitem><para>
+An update to b10-ddns configuration was delivered but an error was
+found while applying them.  None of the delivered updates were applied
+to the running b10-ddns system, and the server will keep running with
+the existing configuration.  If this happened in the initial
+configuration setup, the server will be running with the default
+configurations.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DDNS_DROP_CONN">
 <term>DDNS_DROP_CONN dropping connection on file descriptor %1 because of error %2</term>
 <listitem><para>
@@ -3639,6 +3692,33 @@ confused and sent bad data.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DDNS_GET_REMOTE_CONFIG_FAIL">
+<term>DDNS_GET_REMOTE_CONFIG_FAIL failed to get %1 module configuration %2 times: %3</term>
+<listitem><para>
+b10-ddns tried to get configuration of some remote modules for its
+operation, but it failed.  The most likely cause of this is that the
+remote module has not fully started up and b10-ddns couldn't get the
+configuration in a timely fashion.  b10-ddns attempts to retry it a
+few times, imposing a short delay, hoping it eventually succeeds if
+it's just a timing issue.  The number of total failed attempts is also
+logged.  If it reaches an internal threshold b10-ddns considers it a
+fatal error and terminates.  Even in that case, if b10-ddns is
+configured as a "dispensable" component (which is the default), the
+parent bind10 process will restart it, and there will be another
+chance of getting the remote configuration successfully.  These are
+not the optimal behavior, but it's believed to be sufficient in
+practice (there would normally be no failure in the first place).  If
+it really causes an operational trouble other than having a few of
+these log messages, please submit a bug report; there can be several
+ways to make it more sophisticated.  Another, less likely reason for
+having this error is because the remote modules are not actually
+configured to run.  If that's the case fixing the configuration should
+solve the problem - either by making sure the remote module will run
+or by not running b10-ddns (without these remote modules b10-ddns is
+not functional, so there's no point in running it in this case).
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DDNS_MODULECC_SESSION_ERROR">
 <term>DDNS_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1</term>
 <listitem><para>
@@ -3660,6 +3740,15 @@ coming from a b10-auth process.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DDNS_RECEIVED_AUTH_UPDATE">
+<term>DDNS_RECEIVED_AUTH_UPDATE received configuration updates from auth server</term>
+<listitem><para>
+b10-ddns is notified of updates to b10-auth configuration
+(including a report of the initial configuration) that b10-ddns might
+be interested in.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DDNS_RECEIVED_SHUTDOWN_COMMAND">
 <term>DDNS_RECEIVED_SHUTDOWN_COMMAND shutdown command received</term>
 <listitem><para>
@@ -3668,11 +3757,105 @@ and will now shut down.
 </para></listitem>
 </varlistentry>
 
-<varlistentry id="DDNS_RUNNING">
-<term>DDNS_RUNNING ddns server is running and listening for updates</term>
+<varlistentry id="DDNS_RECEIVED_ZONEMGR_UPDATE">
+<term>DDNS_RECEIVED_ZONEMGR_UPDATE received configuration updates from zonemgr</term>
 <listitem><para>
-The ddns process has successfully started and is now ready to receive commands
-and updates.
+b10-ddns is notified of updates to b10-zonemgr's configuration
+(including a report of the initial configuration).  It may possibly
+contain changes to the secondary zones, in which case b10-ddns will
+update its internal copy of that configuration.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DDNS_REQUEST_PARSE_FAIL">
+<term>DDNS_REQUEST_PARSE_FAIL failed to parse update request: %1</term>
+<listitem><para>
+b10-ddns received an update request via b10-auth, but the received
+data failed to pass minimum validation: it was either broken wire
+format data for a valid DNS message (e.g. it's shorter than the
+fixed-length header), or the opcode is not update, or TSIG is included
+in the request but it fails to validate.  Since b10-auth should have
+performed this level of checks, such an error shouldn't be detected at
+this stage and should rather be considered an internal bug.  This
+event is therefore logged at the error level, and the request is
+simply dropped.  Additional information of the error is also logged.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DDNS_REQUEST_TCP_QUOTA">
+<term>DDNS_REQUEST_TCP_QUOTA reject TCP update client %1 (%2 running)</term>
+<listitem><para>
+b10-ddns received a new update request from a client over TCP, but
+the number of TCP clients being handled by the server already reached
+the configured quota, so the latest client was rejected by closing
+the connection.  The administrator may want to check the status of
+b10-ddns, and if this happens even if the server is not very busy,
+the quota may have to be increased.  Or, if it's more likely to be
+malicious or simply bogus clients that somehow keep the TCP connection
+open for a long period, maybe they should be rejected with an
+appropriate ACL configuration or some lower layer filtering.  The
+number of existing TCP clients are shown in the log, which should be
+identical to the current quota.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DDNS_RESPONSE_SOCKET_ERROR">
+<term>DDNS_RESPONSE_SOCKET_ERROR failed to send update response to %1: %2</term>
+<listitem><para>
+Network I/O error happens in sending an update response.  The
+client's address that caused the error and error details are also
+logged.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DDNS_RESPONSE_TCP_SOCKET_ERROR">
+<term>DDNS_RESPONSE_TCP_SOCKET_ERROR failed to complete sending update response to %1 over TCP</term>
+<listitem><para>
+b10-ddns had tried to send an update response over TCP, and it hadn't
+been completed at that time, and a followup attempt to complete the
+send operation failed due to some network I/O error.  While a network
+error can happen any time, this event is quite unexpected for two
+reasons.  First, since the size of a response to an update request
+should be generally small, it's unlikely that the initial attempt
+didn't fail but wasn't completed.  Second, since the first attempt
+succeeded and the TCP connection had been established in the first
+place, it's more likely for the subsequent attempt to succeed.  In any
+case, there may not be able to do anything to fix it at the server
+side, but the administrator may want to check the general reachability
+with the client address.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DDNS_SECONDARY_ZONES_UPDATE">
+<term>DDNS_SECONDARY_ZONES_UPDATE updated secondary zone list (%1 zones are listed)</term>
+<listitem><para>
+b10-ddns has successfully updated the internal copy of secondary zones
+obtained from b10-zonemgr, based on a latest update to zonemgr's
+configuration.  The number of newly configured (unique) secondary
+zones is logged.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DDNS_SECONDARY_ZONES_UPDATE_FAIL">
+<term>DDNS_SECONDARY_ZONES_UPDATE_FAIL failed to update secondary zone list: %1</term>
+<listitem><para>
+An error message.  b10-ddns was notified of updates to a list of
+secondary zones from b10-zonemgr and tried to update its own internal
+copy of the list, but it failed.  This can happen if the configuration
+contains an error, and b10-zonemgr should also reject that update.
+Unfortunately, in the current implementation there is no way to ensure
+that both zonemgr and ddns have consistent information when an update
+contains an error; further, as of this writing zonemgr has a bug that
+it could partially update the list of secondary zones if part of the
+list has an error (see Trac ticket #2038).  b10-ddns still keeps
+running with the previous configuration, but it's strongly advisable
+to check log messages from zonemgr, and if it indicates there can be
+inconsistent state, it's better to restart the entire BIND 10 system
+(just restarting b10-ddns wouldn't be enough, because zonemgr can have
+partially updated configuration due to bug #2038).  The log message
+contains an error description, but it's intentionally kept simple as
+it's primarily a matter of zonemgr.  To know the details of the error,
+log messages of zonemgr should be consulted.
 </para></listitem>
 </varlistentry>
 
@@ -3694,6 +3877,14 @@ be completed, after which the process will exit.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DDNS_STARTED">
+<term>DDNS_STARTED ddns server is running and listening for updates</term>
+<listitem><para>
+The ddns process has successfully started and is now ready to receive commands
+and updates.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DDNS_STOPPED">
 <term>DDNS_STOPPED ddns server has stopped</term>
 <listitem><para>
@@ -3719,6 +3910,362 @@ normal circumstances. The exception type and message are printed.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DDNS_UPDATE_NOTIFY">
+<term>DDNS_UPDATE_NOTIFY notified %1 of updates to %2</term>
+<listitem><para>
+Debug message.  b10-ddns has made updates to a zone based on an update
+request and has successfully notified an external module of the updates.
+The notified module will use that information for updating its own
+state or any necessary protocol action such as zone reloading or sending
+notify messages to secondary servers.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DDNS_UPDATE_NOTIFY_FAIL">
+<term>DDNS_UPDATE_NOTIFY_FAIL failed to notify %1 of updates to %2: %3</term>
+<listitem><para>
+b10-ddns has made updates to a zone based on an update request and
+tried to notify an external component of the updates, but the
+notification fails.  One possible cause of this is that the external
+component is not really running and it times out in waiting for the
+response, although it will be less likely to happen in practice
+because these components will normally be configured to run when the
+server provides the authoritative DNS service; ddns is rather optional
+among them.  If this happens, however, it will suspend b10-ddns for a
+few seconds during which it cannot handle new requests (some may be
+delayed, some may be dropped, depending on the volume of the incoming
+requests).  This is obviously bad, and if this error happens due to
+this reason, the administrator should make sure the component in
+question should be configured to run.  For a longer term, b10-ddns
+should be more robust about this case such as by making this
+notification asynchronously and/or detecting the existence of the
+external components to avoid hopeless notification in the first place.
+Severity of this error for the receiving components depends on the
+type of the component.  If it's b10-xfrout, this means DNS notify
+messages won't be sent to secondary servers of the zone.  It's
+suboptimal, but not necessarily critical as the secondary servers will
+try to check the zone's status periodically.  If it's b10-auth and the
+notification was needed to have it reload the corresponding zone, it's
+more serious because b10-auth won't be able to serve the new version
+of the zone unless some explicit recovery action is taken.  So the
+administrator needs to examine this message and takes an appropriate
+action.  In either case, this notification is generally expected to
+succeed; so the fact it fails itself means there's something wrong in
+the BIND 10 system, and it would be advisable to check other log
+messages.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_DATASRC_ERROR">
+<term>LIBDDNS_DATASRC_ERROR update client %1 failed due to data source error: %2</term>
+<listitem><para>
+An update attempt failed due to some error in the corresponding data
+source.  This is generally an unexpected event, but can still happen
+for various reasons such as DB lock contention or a failure of the
+backend DB server.  The cause of the error is also logged.  It's
+advisable to check the message, and, if necessary, take an appropriate
+action (e.g., restarting the DB server if it dies).  If this message
+is logged the data source isn't modified due to the
+corresponding update request.  When used by the b10-ddns, the server
+will return a response with an RCODE of SERVFAIL.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_PREREQ_FORMERR">
+<term>LIBDDNS_PREREQ_FORMERR update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL.</term>
+<listitem><para>
+The prerequisite with the given name, class and type is not well-formed.
+The specific prerequisite is shown. In this case, it has a non-zero TTL value.
+A FORMERR error response is sent to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_PREREQ_FORMERR_ANY">
+<term>LIBDDNS_PREREQ_FORMERR_ANY update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL or rdata found.</term>
+<listitem><para>
+The prerequisite with the given name, class and type is not well-formed.
+The specific prerequisite is shown. In this case, it either has a non-zero
+TTL value, or has rdata fields. A FORMERR error response is sent to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_PREREQ_FORMERR_CLASS">
+<term>LIBDDNS_PREREQ_FORMERR_CLASS update client %1 for zone %2: Format error in prerequisite (%3). Bad class.</term>
+<listitem><para>
+The prerequisite with the given name, class and type is not well-formed.
+The specific prerequisite is shown. In this case, the class of the
+prerequisite should either match the class of the zone in the Zone Section,
+or it should be ANY or NONE, and it is not. A FORMERR error response is sent
+to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_PREREQ_FORMERR_NONE">
+<term>LIBDDNS_PREREQ_FORMERR_NONE update client %1 for zone %2: Format error in prerequisite (%3). Non-zero TTL or rdata found.</term>
+<listitem><para>
+The prerequisite with the given name, class and type is not well-formed.
+The specific prerequisite is shown. In this case, it either has a non-zero
+TTL value, or has rdata fields. A FORMERR error response is sent to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_PREREQ_NAME_IN_USE_FAILED">
+<term>LIBDDNS_PREREQ_NAME_IN_USE_FAILED update client %1 for zone %2: 'Name is in use' prerequisite not satisfied (%3), rcode: %4</term>
+<listitem><para>
+A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
+was not satisfied is shown. The client is sent an error response with the
+given rcode.
+In this case, the specific prerequisite is 'Name is in use'. From RFC2136:
+Name is in use.  At least one RR with a specified NAME (in
+the zone and class specified by the Zone Section) must exist.
+Note that this prerequisite is NOT satisfied by empty
+nonterminals.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_PREREQ_NAME_NOT_IN_USE_FAILED">
+<term>LIBDDNS_PREREQ_NAME_NOT_IN_USE_FAILED update client %1 for zone %2: 'Name is not in use' (%3) prerequisite not satisfied, rcode: %4</term>
+<listitem><para>
+A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
+was not satisfied is shown. The client is sent an error response with the
+given rcode.
+In this case, the specific prerequisite is 'Name is not in use'.
+From RFC2136:
+Name is not in use.  No RR of any type is owned by a
+specified NAME.  Note that this prerequisite IS satisfied by
+empty nonterminals.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_PREREQ_NOTZONE">
+<term>LIBDDNS_PREREQ_NOTZONE update client %1 for zone %2: prerequisite not in zone (%3)</term>
+<listitem><para>
+A DDNS UPDATE prerequisite has a name that does not appear to be inside
+the zone specified in the Zone section of the UPDATE message.
+The specific prerequisite is shown. A NOTZONE error response is sent to
+the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_PREREQ_RRSET_DOES_NOT_EXIST_FAILED">
+<term>LIBDDNS_PREREQ_RRSET_DOES_NOT_EXIST_FAILED update client %1 for zone %2: 'RRset does not exist' (%3) prerequisite not satisfied, rcode: %4</term>
+<listitem><para>
+A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
+was not satisfied is shown. The client is sent an error response with the
+given rcode.
+In this case, the specific prerequisite is 'RRset does not exist'.
+From RFC2136:
+RRset does not exist.  No RRs with a specified NAME and TYPE
+(in the zone and class denoted by the Zone Section) can exist.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_PREREQ_RRSET_EXISTS_FAILED">
+<term>LIBDDNS_PREREQ_RRSET_EXISTS_FAILED update client %1 for zone %2: 'RRset exists (value independent)' (%3) prerequisite not satisfied, rcode: %4</term>
+<listitem><para>
+A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
+was not satisfied is shown. The client is sent an error response with the
+given rcode.
+In this case, the specific prerequisite is 'RRset exists (value independent)'.
+From RFC2136:
+RRset exists (value dependent).  A set of RRs with a
+specified NAME and TYPE exists and has the same members
+with the same RDATAs as the RRset specified here in this
+Section.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_PREREQ_RRSET_EXISTS_VAL_FAILED">
+<term>LIBDDNS_PREREQ_RRSET_EXISTS_VAL_FAILED update client %1 for zone %2: 'RRset exists (value dependent)' (%3) prerequisite not satisfied, rcode: %4</term>
+<listitem><para>
+A DNS UPDATE prerequisite was not satisfied. The specific prerequisite that
+was not satisfied is shown. The client is sent an error response with the
+given rcode.
+In this case, the specific prerequisite is 'RRset exists (value dependent)'.
+From RFC2136:
+RRset exists (value independent).  At least one RR with a
+specified NAME and TYPE (in the zone and class specified by
+the Zone Section) must exist.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_ADD_BAD_TYPE">
+<term>LIBDDNS_UPDATE_ADD_BAD_TYPE update client %1 for zone %2: update addition RR bad type: %3</term>
+<listitem><para>
+The Update section of a DDNS update message contains a statement
+that tries to add a record of an invalid type. Most likely the
+record has an RRType that is considered a 'meta' type, which
+cannot be zone content data. The specific record is shown.
+A FORMERR response is sent back to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_APPROVED">
+<term>LIBDDNS_UPDATE_APPROVED update client %1 for zone %2 approved</term>
+<listitem><para>
+Debug message.  An update request was approved in terms of the zone's
+update ACL.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_BAD_CLASS">
+<term>LIBDDNS_UPDATE_BAD_CLASS update client %1 for zone %2: bad class in update RR: %3</term>
+<listitem><para>
+The Update section of a DDNS update message contains an RRset with
+a bad class. The class of the update RRset must be either the same
+as the class in the Zone Section, ANY, or NONE.
+A FORMERR response is sent back to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_DATASRC_ERROR">
+<term>LIBDDNS_UPDATE_DATASRC_ERROR error in datasource during DDNS update: %1</term>
+<listitem><para>
+An error occured while committing the DDNS update changes to the
+datasource. The specific error is printed. A SERVFAIL response is sent
+back to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_DELETE_BAD_TYPE">
+<term>LIBDDNS_UPDATE_DELETE_BAD_TYPE update client %1 for zone %2: update deletion RR bad type: %3</term>
+<listitem><para>
+The Update section of a DDNS update message contains a statement
+that tries to delete an rrset of an invalid type. Most likely the
+record has an RRType that is considered a 'meta' type, which
+cannot be zone content data. The specific record is shown.
+A FORMERR response is sent back to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_DELETE_NONZERO_TTL">
+<term>LIBDDNS_UPDATE_DELETE_NONZERO_TTL update client %1 for zone %2: update deletion RR has non-zero TTL: %3</term>
+<listitem><para>
+The Update section of a DDNS update message contains a 'delete rrset'
+statement with a non-zero TTL. This is not allowed by the protocol.
+A FORMERR response is sent back to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_DELETE_RRSET_NOT_EMPTY">
+<term>LIBDDNS_UPDATE_DELETE_RRSET_NOT_EMPTY update client %1 for zone %2: update deletion RR contains data %3</term>
+<listitem><para>
+The Update section of a DDNS update message contains a 'delete rrset'
+statement with a non-empty RRset. This is not allowed by the protocol.
+A FORMERR response is sent back to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_DELETE_RR_BAD_TYPE">
+<term>LIBDDNS_UPDATE_DELETE_RR_BAD_TYPE update client %1 for zone %2: update deletion RR bad type: %3</term>
+<listitem><para>
+The Update section of a DDNS update message contains a statement
+that tries to delete one or more rrs of an invalid type. Most
+likely the records have an RRType that is considered a 'meta'
+type, which cannot be zone content data. The specific record is
+shown. A FORMERR response is sent back to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_DELETE_RR_NONZERO_TTL">
+<term>LIBDDNS_UPDATE_DELETE_RR_NONZERO_TTL update client %1 for zone %2: update deletion RR has non-zero TTL: %3</term>
+<listitem><para>
+The Update section of a DDNS update message contains a 'delete rrs'
+statement with a non-zero TTL. This is not allowed by the protocol.
+A FORMERR response is sent back to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_DENIED">
+<term>LIBDDNS_UPDATE_DENIED update client %1 for zone %2 denied</term>
+<listitem><para>
+Informational message.  An update request was denied because it was
+rejected by the zone's update ACL.  When this library is used by
+b10-ddns, the server will respond to the request with an RCODE of
+REFUSED as described in Section 3.3 of RFC2136.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_DROPPED">
+<term>LIBDDNS_UPDATE_DROPPED update client %1 for zone %2 dropped</term>
+<listitem><para>
+Informational message.  An update request was denied because it was
+rejected by the zone's update ACL.  When this library is used by
+b10-ddns, the server will then completely ignore the request; no
+response will be sent.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_ERROR">
+<term>LIBDDNS_UPDATE_ERROR update client %1 for zone %2: %3</term>
+<listitem><para>
+Debug message.  An error is found in processing a dynamic update
+request.  This log message is used for general errors that are not
+normally expected to happen.  So, in general, it would mean some
+problem in the client implementation or an interoperability issue
+with this implementation.  The client's address, the zone name and
+class, and description of the error are logged.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_FORWARD_FAIL">
+<term>LIBDDNS_UPDATE_FORWARD_FAIL update client %1 for zone %2: update forwarding not supported</term>
+<listitem><para>
+Debug message.  An update request is sent to a secondary server.  This
+is not necessarily invalid, but this implementation does not yet
+support update forwarding as specified in Section 6 of RFC2136 and it
+will simply return a response with an RCODE of NOTIMP to the client.
+The client's address and the zone name/class are logged.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_NOTAUTH">
+<term>LIBDDNS_UPDATE_NOTAUTH update client %1 for zone %2: not authoritative for update zone</term>
+<listitem><para>
+Debug message.  An update request was received for a zone for which
+the receiving server doesn't have authority.  In theory this is an
+unexpected event, but there are client implementations that could send
+update requests carelessly, so it may not necessarily be so uncommon
+in practice.  If possible, you may want to check the implementation or
+configuration of those clients to suppress the requests.  As specified
+in Section 3.1 of RFC2136, the receiving server will return a response
+with an RCODE of NOTAUTH.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_NOTZONE">
+<term>LIBDDNS_UPDATE_NOTZONE update client %1 for zone %2: update RR out of zone %3</term>
+<listitem><para>
+A DDNS UPDATE record has a name that does not appear to be inside
+the zone specified in the Zone section of the UPDATE message.
+The specific update record is shown. A NOTZONE error response is
+sent to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_PREREQUISITE_FAILED">
+<term>LIBDDNS_UPDATE_PREREQUISITE_FAILED prerequisite failed in update client %1 for zone %2: result code %3</term>
+<listitem><para>
+The handling of the prerequisite section (RFC2136 Section 3.2) found
+that one of the prerequisites was not satisfied. The result code
+should give more information on what prerequisite type failed.
+If the result code is FORMERR, the prerequisite section was not well-formed.
+An error response with the given result code is sent back to the client.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="LIBDDNS_UPDATE_UNCAUGHT_EXCEPTION">
+<term>LIBDDNS_UPDATE_UNCAUGHT_EXCEPTION update client %1 for zone %2: uncaught exception while processing update section: %3</term>
+<listitem><para>
+An uncaught exception was encountered while processing the Update
+section of a DDNS message. The specific exception is shown in the log message.
+To make sure DDNS service is not interrupted, this problem is caught instead
+of reraised; The update is aborted, and a SERVFAIL is sent back to the client.
+This is most probably a bug in the DDNS code, but *could* be caused by
+the data source.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="LIBXFRIN_DIFFERENT_TTL">
 <term>LIBXFRIN_DIFFERENT_TTL multiple data with different TTLs (%1, %2) on %3/%4/%5. Adjusting %2 -&gt; %1.</term>
 <listitem><para>
@@ -3836,6 +4383,13 @@ and the underscore, and should not start with a digit.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="LOG_LOCK_TEST_MESSAGE">
+<term>LOG_LOCK_TEST_MESSAGE this is a test message.</term>
+<listitem><para>
+This is a log message used in testing.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="LOG_NAMESPACE_EXTRA_ARGS">
 <term>LOG_NAMESPACE_EXTRA_ARGS line %1: $NAMESPACE directive has too many arguments</term>
 <listitem><para>
@@ -4227,6 +4781,55 @@ bug report.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="PYSERVER_COMMON_AUTH_CONFIG_NAME_PARSER_ERROR">
+<term>PYSERVER_COMMON_AUTH_CONFIG_NAME_PARSER_ERROR Invalid name when parsing Auth configuration: %1</term>
+<listitem><para>
+There was an invalid name when parsing Auth configuration.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="PYSERVER_COMMON_AUTH_CONFIG_RRCLASS_ERROR">
+<term>PYSERVER_COMMON_AUTH_CONFIG_RRCLASS_ERROR Invalid RRClass when parsing Auth configuration: %1</term>
+<listitem><para>
+There was an invalid RR class when parsing Auth configuration.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="PYSERVER_COMMON_DNS_TCP_SEND_DONE">
+<term>PYSERVER_COMMON_DNS_TCP_SEND_DONE completed sending TCP message to %1 (%2 bytes in total)</term>
+<listitem><para>
+Debug message.  A complete DNS message has been successfully
+transmitted over a TCP connection, possibly after multiple send
+operations.  The destination address and the total size of the message
+(including the 2-byte length field) are shown in the log message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="PYSERVER_COMMON_DNS_TCP_SEND_ERROR">
+<term>PYSERVER_COMMON_DNS_TCP_SEND_ERROR failed to send TCP message to %1 (%2/%3 bytes sent): %4</term>
+<listitem><para>
+A DNS message has been attempted to be sent out over a TCP connection,
+but it failed due to some network error.  Although it's not expected
+to happen too often, it can still happen for various reasons.  The
+administrator may want to examine the cause of the failure, which is
+included in the log message, to see if it requires some action to
+be taken at the server side.  When this message is logged, the
+corresponding  TCP connection was closed immediately after the error
+was detected.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="PYSERVER_COMMON_DNS_TCP_SEND_PENDING">
+<term>PYSERVER_COMMON_DNS_TCP_SEND_PENDING sent part TCP message to %1 (up to %2/%3 bytes)</term>
+<listitem><para>
+Debug message.  A part of DNS message has been transmitted over a TCP
+connection, and it's suspended because further attempt would block.
+The destination address and the total size of the message that has
+been transmitted so far (including the 2-byte length field) are shown
+in the log message.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="PYSERVER_COMMON_TSIG_KEYRING_DEINIT">
 <term>PYSERVER_COMMON_TSIG_KEYRING_DEINIT Deinitializing global TSIG keyring</term>
 <listitem><para>
@@ -5474,20 +6077,6 @@ Please check your installation.
 </para></listitem>
 </varlistentry>
 
-<varlistentry id="XFRIN_AUTH_CONFIG_NAME_PARSER_ERROR">
-<term>XFRIN_AUTH_CONFIG_NAME_PARSER_ERROR Invalid name when parsing Auth configuration: %1</term>
-<listitem><para>
-There was an invalid name when parsing Auth configuration.
-</para></listitem>
-</varlistentry>
-
-<varlistentry id="XFRIN_AUTH_CONFIG_RRCLASS_ERROR">
-<term>XFRIN_AUTH_CONFIG_RRCLASS_ERROR Invalid RRClass when parsing Auth configuration: %1</term>
-<listitem><para>
-There was an invalid RR class when parsing Auth configuration.
-</para></listitem>
-</varlistentry>
-
 <varlistentry id="XFRIN_AUTH_LOADZONE">
 <term>XFRIN_AUTH_LOADZONE sending Auth loadzone for origin=%1, class=%2, datasrc=%3</term>
 <listitem><para>
@@ -6027,12 +6616,16 @@ parsed by the b10-auth daemon, before it was passed here.
 </varlistentry>
 
 <varlistentry id="XFROUT_PROCESS_REQUEST_ERROR">
-<term>XFROUT_PROCESS_REQUEST_ERROR error processing transfer request: %2</term>
+<term>XFROUT_PROCESS_REQUEST_ERROR error processing transfer request: %1</term>
 <listitem><para>
-There was an error processing a transfer request. The error is included
-in the log message, but at this point no specific information other
-than that could be given. This points to incomplete exception handling
-in the code.
+There was an error in receiving a transfer request from b10-auth.
+This is generally an unexpected event, but is possible when, for
+example, b10-auth terminates in the middle of forwarding the request.
+When this happens it's unlikely to be recoverable with the same
+communication session with b10-auth, so b10-xfrout drops it and
+waits for a new session.  In any case, this error indicates that
+there's something very wrong in the system, so it's advisable to check
+the over all status of the BIND 10 system.
 </para></listitem>
 </varlistentry>
 
@@ -6082,9 +6675,16 @@ and will now shut down.
 <term>XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR error receiving the file descriptor for an XFR connection</term>
 <listitem><para>
 There was an error receiving the file descriptor for the transfer
-request. Normally, the request is received by b10-auth, and passed on
-to the xfrout daemon, so it can answer directly. However, there was a
-problem receiving this file descriptor. The request will be ignored.
+request from b10-auth.  There can be several reasons for this, but
+the most likely cause is that b10-auth terminates for some reason
+(maybe it's a bug of b10-auth, maybe it's an intentional restart by
+the administrator), so depending on how this happens it may or may not
+be a serious error.  But in any case this is not expected to happen
+frequently, and it's advisable to figure out how this happened if
+this message is logged.  Even if this error happens xfrout will reset
+its internal state and will keep receiving further requests.  So
+if it's just a temporary restart of b10-auth the administrator does
+not have to do anything.
 </para></listitem>
 </varlistentry>
 

+ 14 - 14
src/bin/auth/auth_messages.mes

@@ -96,6 +96,20 @@ discovered that the memory data source is disabled for the given class.
 This is a debug message reporting that the authoritative server has
 discovered that the memory data source is enabled for the given class.
 
+% AUTH_MESSAGE_FORWARD_ERROR failed to forward %1 request from %2: %3
+The authoritative server tried to forward some type DNS request
+message to a separate process (e.g., forwarding dynamic update
+requests to b10-ddns) to handle it, but it failed.  The authoritative
+server returns SERVFAIL to the client on behalf of the separate
+process.  The error could be configuration mismatch between b10-auth
+and the recipient component, or it may be because the requests are
+coming too fast and the receipient process cannot keep up with the
+rate, or some system level failure.  In either case this means the
+BIND 10 system is not working as expected, so the administrator should
+look into the cause and address the issue.  The log message includes
+the client's address (and port), and the error message sent from the
+lower layer that detects the failure.
+
 % AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY
 This debug message is logged by the authoritative server when it receives
 a NOTIFY packet that contains zero or more than one question. (A valid
@@ -255,20 +269,6 @@ processed by the authoritative server has been found to contain an
 unsupported opcode. (The opcode is included in the message.) The server
 will return an error code of NOTIMPL to the sender.
 
-% AUTH_MESSAGE_FORWARD_ERROR failed to forward %1 request from %2: %3
-The authoritative server tried to forward some type DNS request
-message to a separate process (e.g., forwarding dynamic update
-requests to b10-ddns) to handle it, but it failed.  The authoritative
-server returns SERVFAIL to the client on behalf of the separate
-process.  The error could be configuration mismatch between b10-auth
-and the recipient component, or it may be because the requests are
-coming too fast and the receipient process cannot keep up with the
-rate, or some system level failure.  In either case this means the
-BIND 10 system is not working as expected, so the administrator should
-look into the cause and address the issue.  The log message includes
-the client's address (and port), and the error message sent from the
-lower layer that detects the failure.
-
 % AUTH_XFRIN_CHANNEL_CREATED XFRIN session channel created
 This is a debug message indicating that the authoritative server has
 created a channel to the XFRIN (Transfer-in) process.  It is issued

+ 1 - 49
src/bin/auth/auth_srv.cc

@@ -43,7 +43,6 @@
 
 #include <datasrc/query.h>
 #include <datasrc/data_source.h>
-#include <datasrc/memory_datasrc.h>
 #include <datasrc/static_datasrc.h>
 #include <datasrc/sqlite3_datasrc.h>
 #include <datasrc/client_list.h>
@@ -249,9 +248,6 @@ public:
     AbstractSession* xfrin_session_;
 
     /// In-memory data source.  Currently class IN only for simplicity.
-    const RRClass memory_client_class_;
-    isc::datasrc::DataSourceClientContainerPtr memory_client_container_;
-
     /// Hot spot cache
     isc::datasrc::HotCache cache_;
 
@@ -322,7 +318,6 @@ AuthSrvImpl::AuthSrvImpl(const bool use_cache,
                          BaseSocketSessionForwarder& ddns_forwarder) :
     config_session_(NULL),
     xfrin_session_(NULL),
-    memory_client_class_(RRClass::IN()),
     statistics_timer_(io_service_),
     counters_(),
     keyring_(NULL),
@@ -505,48 +500,6 @@ AuthSrv::getConfigSession() const {
     return (impl_->config_session_);
 }
 
-isc::datasrc::DataSourceClientContainerPtr
-AuthSrv::getInMemoryClientContainer(const RRClass& rrclass) {
-    if (rrclass != impl_->memory_client_class_) {
-        isc_throw(InvalidParameter,
-                  "Memory data source is not supported for RR class "
-                  << rrclass);
-    }
-    return (impl_->memory_client_container_);
-}
-
-isc::datasrc::DataSourceClient*
-AuthSrv::getInMemoryClient(const RRClass& rrclass) {
-    if (hasInMemoryClient()) {
-        return (&getInMemoryClientContainer(rrclass)->getInstance());
-    } else {
-        return (NULL);
-    }
-}
-
-bool
-AuthSrv::hasInMemoryClient() const {
-    return (impl_->memory_client_container_);
-}
-
-void
-AuthSrv::setInMemoryClient(const isc::dns::RRClass& rrclass,
-                           DataSourceClientContainerPtr memory_client)
-{
-    if (rrclass != impl_->memory_client_class_) {
-        isc_throw(InvalidParameter,
-                  "Memory data source is not supported for RR class "
-                  << rrclass);
-    } else if (!impl_->memory_client_container_ && memory_client) {
-        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_ENABLED)
-                  .arg(rrclass);
-    } else if (impl_->memory_client_container_ && !memory_client) {
-        LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_MEM_DATASRC_DISABLED)
-                  .arg(rrclass);
-    }
-    impl_->memory_client_container_ = memory_client;
-}
-
 uint32_t
 AuthSrv::getStatisticsTimerInterval() const {
     return (impl_->statistics_timer_.getInterval() / 1000);
@@ -713,8 +666,6 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
     }
 
     try {
-        // If a memory data source is configured call the separate
-        // Query::process()
         const ConstQuestionPtr question = *message.beginQuestion();
         const boost::shared_ptr<datasrc::ClientList>
             list(getClientList(question->getClass()));
@@ -724,6 +675,7 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
             query_.process(*list, qname, qtype, message, dnssec_ok);
         } else {
             makeErrorMessage(renderer_, message, buffer, Rcode::REFUSED());
+            return (true);
         }
     } catch (const Exception& ex) {
         LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());

+ 1 - 80
src/bin/auth/auth_srv.h

@@ -43,7 +43,6 @@ class BaseSocketSessionForwarder;
 }
 }
 namespace datasrc {
-class InMemoryClient;
 class ConfigurableClientList;
 }
 namespace xfr {
@@ -130,20 +129,7 @@ public:
                         isc::util::OutputBuffer& buffer,
                         isc::asiodns::DNSServer* server);
 
-    /// \brief Updates the data source for the \c AuthSrv object.
-    ///
-    /// This method installs or replaces the data source that the \c AuthSrv
-    /// object refers to for query processing.
-    /// Although the method name is generic, the only thing it does is to
-    /// update the data source information.
-    /// If there is a data source installed, it will be replaced with the
-    /// new one.
-    ///
-    /// In the current implementation, the SQLite data source and InMemoryClient
-    /// are assumed.
-    /// We can enable memory data source and get the path of SQLite database by
-    /// the \c config parameter.  If we disabled memory data source, the SQLite
-    /// data source will be used.
+    /// \brief Updates the configuration for the \c AuthSrv object.
     ///
     /// On success this method returns a data \c Element (in the form of a
     /// pointer like object) indicating the successful result,
@@ -239,71 +225,6 @@ public:
     ///
     void setXfrinSession(isc::cc::AbstractSession* xfrin_session);
 
-    /// Returns the in-memory data source configured for the \c AuthSrv,
-    /// if any, as a pointer.
-    ///
-    /// This is mostly a convenience function around
-    /// \c getInMemoryClientContainer, which saves the caller the step
-    /// of having to call getInstance().
-    /// The pointer is of course only valid as long as the container
-    /// exists.
-    ///
-    /// The in-memory data source is configured per RR class.  However,
-    /// the data source may not be available for all RR classes.
-    /// If it is not available for the specified RR class, an exception of
-    /// class \c InvalidParameter will be thrown.
-    /// This method never throws an exception otherwise.
-    ///
-    /// Even for supported RR classes, the in-memory data source is not
-    /// configured by default.  In that case a NULL (shared) pointer will
-    /// be returned.
-    ///
-    /// \param rrclass The RR class of the requested in-memory data source.
-    /// \return A pointer to the in-memory data source, if configured;
-    /// otherwise NULL.
-    isc::datasrc::DataSourceClient* getInMemoryClient(
-        const isc::dns::RRClass& rrclass);
-
-    /// Returns the DataSourceClientContainer of the in-memory datasource
-    ///
-    /// \exception InvalidParameter if the given class does not match
-    ///            the one in the memory data source, or if the memory
-    ///            datasource has not been set (callers can check with
-    ///            \c hasMemoryDataSource())
-    ///
-    /// \param rrclass The RR class of the requested in-memory data source.
-    /// \return A shared pointer to the in-memory data source, if configured;
-    /// otherwise an empty shared pointer.
-    isc::datasrc::DataSourceClientContainerPtr getInMemoryClientContainer(
-        const isc::dns::RRClass& rrclass);
-
-    /// Checks if the in-memory data source has been set.
-    ///
-    /// Right now, only one datasource at a time is effectively supported.
-    /// This is a helper method to check whether it is the in-memory one.
-    /// This is mostly useful for current testing, and is expected to be
-    /// removed (or changed in behaviour) soon, when the general
-    /// multi-data-source framework is completed.
-    ///
-    /// \return True if the in-memory datasource has been set.
-    bool hasInMemoryClient() const;
-
-    /// Sets or replaces the in-memory data source of the specified RR class.
-    ///
-    /// Some RR classes may not be supported, in which case an exception
-    /// of class \c InvalidParameter will be thrown.
-    /// This method never throws an exception otherwise.
-    ///
-    /// If there is already an in memory data source configured, it will be
-    /// replaced with the newly specified one.
-    /// \c memory_client can be an empty shared pointer, in which case it
-    /// will (re)disable the in-memory data source.
-    ///
-    /// \param rrclass The RR class of the in-memory data source to be set.
-    /// \param memory_client A (shared) pointer to \c InMemoryClient to be set.
-    void setInMemoryClient(const isc::dns::RRClass& rrclass,
-        isc::datasrc::DataSourceClientContainerPtr memory_client);
-
     /// \brief Set the communication session with Statistics.
     ///
     /// This function never throws an exception as far as

+ 3 - 3
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: May 16, 2012
+.\"      Date: June 20, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-AUTH" "8" "May 16, 2012" "BIND10" "BIND10"
+.TH "B10\-AUTH" "8" "June 20, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -49,7 +49,7 @@ Do not cache answers in memory\&. The default is to use the cache for faster res
 .PP
 \fB\-v\fR
 .RS 4
-Enabled verbose mode\&. This enables diagnostic messages to STDERR\&.
+Enable verbose logging mode\&. This enables logging of diagnostic messages at the maximum debug level\&.
 .RE
 .SH "CONFIGURATION AND COMMANDS"
 .PP

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

@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>May 16, 2012</date>
+    <date>June 20, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -94,8 +94,8 @@
       <varlistentry>
         <term><option>-v</option></term>
         <listitem><para>
-          Enabled verbose mode. This enables diagnostic messages to
-          STDERR.
+	  Enable verbose logging mode. This enables logging of
+	  diagnostic messages at the maximum debug level.
         </para></listitem>
       </varlistentry>
 

+ 4 - 1
src/bin/auth/command.cc

@@ -185,7 +185,9 @@ private:
     // On success, it sets 'old_zone_finder_' to the zone to be updated.
     // It returns true if everything is okay; and false if the command is
     // valid but there's no need for further process.
-    bool validate(AuthSrv& server, isc::data::ConstElementPtr args) {
+    bool validate(AuthSrv& , isc::data::ConstElementPtr ) {
+#if 0
+        TODO: Rewrite
         if (args == NULL) {
             isc_throw(AuthCommandError, "Null argument");
         }
@@ -235,6 +237,7 @@ private:
         old_zone_finder_ = boost::static_pointer_cast<InMemoryZoneFinder>(
             result.zone_finder);
 
+#endif
         return (true);
     }
 

+ 4 - 2
src/bin/auth/datasrc_configurator.h

@@ -48,7 +48,9 @@ private:
                                     isc::data::ConstElementPtr config,
                                     const isc::config::ConfigData&)
     {
-        reconfigure(config);
+        if (config->contains("classes")) {
+            reconfigure(config->get("classes"));
+        }
     }
     static Server* server_;
     static isc::config::ModuleCCSession* session_;
@@ -142,7 +144,7 @@ public:
                         push_back(RollbackConfiguration(rrclass,
                             list->getConfiguration()));
                 } else {
-                    list.reset(new List);
+                    list.reset(new List(rrclass));
                     need_set = true;
                     rollback_sets.push_back(RollbackPair(rrclass, ListPtr()));
                 }

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

@@ -88,7 +88,7 @@ usage() {
     cerr << "Usage:  b10-auth [-u user] [-nv]"
          << endl;
     cerr << "\t-n: do not cache answers in memory" << endl;
-    cerr << "\t-v: verbose output" << endl;
+    cerr << "\t-v: verbose logging (debug-level)" << endl;
     exit(1);
 }
 
@@ -207,6 +207,11 @@ main(int argc, char* argv[]) {
 
         // Start the data source configuration
         DataSourceConfigurator::init(config_session, auth_server);
+        // HACK: The default is not passed to the handler. This one will
+        // get the default (or, current value). Further updates will work
+        // the usual way.
+        DataSourceConfigurator::reconfigure(
+            config_session->getRemoteConfigValue("data_sources", "classes"));
 
         // Now start asynchronous read.
         config_session->start();

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

@@ -7,6 +7,7 @@ AM_CPPFLAGS += -DAUTH_OBJ_DIR=\"$(abs_top_builddir)/src/bin/auth\"
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
 AM_CPPFLAGS += -DTEST_OWN_DATA_DIR=\"$(abs_top_srcdir)/src/bin/auth/tests/testdata\"
 AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
+AM_CPPFLAGS += -DDSRC_DIR=\"$(abs_top_builddir)/src/lib/datasrc\"
 AM_CPPFLAGS += -DPLUGIN_DATA_PATH=\"$(abs_top_srcdir)/src/bin/cfgmgr/plugins\"
 AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
 
@@ -22,7 +23,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-        libtool --mode=execute $(VALGRIND_COMMAND)
+        $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 # Do not define global tests, use check-local so
 # environment can be set (needed for dynamic loading)

+ 228 - 217
src/bin/auth/tests/auth_srv_unittest.cc

@@ -49,6 +49,7 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
+#include <boost/foreach.hpp>
 
 #include <vector>
 
@@ -80,12 +81,11 @@ const char* const CONFIG_TESTDB =
 const char* const BADCONFIG_TESTDB =
     "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}";
 
+const char* const STATIC_DSRC_FILE = DSRC_DIR "/static.zone";
+
 // This is a configuration that uses the in-memory data source containing
 // a signed example zone.
-const char* const CONFIG_INMEMORY_EXAMPLE =
-    "{\"datasources\": [{\"type\": \"memory\","
-    "\"zones\": [{\"origin\": \"example\","
-    "\"file\": \"" TEST_DATA_DIR "/rfc5155-example.zone.signed\"}]}]}";
+const char* const CONFIG_INMEMORY_EXAMPLE = TEST_DATA_DIR "/rfc5155-example.zone.signed";
 
 class AuthSrvTest : public SrvTestBase {
 protected:
@@ -193,7 +193,8 @@ protected:
 // by default.  The resulting wire-format data will be stored in 'data'.
 void
 createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
-    const Name version_name("version.bind");
+    const Name version_name("VERSION.BIND.");
+    const Name apex_name("BIND.");
     Message message(Message::RENDER);
 
     UnitTestUtil::createRequestMessage(message, Opcode::QUERY(),
@@ -206,9 +207,9 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
     rrset_version->addRdata(generic::TXT(PACKAGE_STRING));
     message.addRRset(Message::SECTION_ANSWER, rrset_version);
 
-    RRsetPtr rrset_version_ns = RRsetPtr(new RRset(version_name, RRClass::CH(),
+    RRsetPtr rrset_version_ns = RRsetPtr(new RRset(apex_name, RRClass::CH(),
                                                    RRType::NS(), RRTTL(0)));
-    rrset_version_ns->addRdata(generic::NS(version_name));
+    rrset_version_ns->addRdata(generic::NS(apex_name));
     message.addRRset(Message::SECTION_AUTHORITY, rrset_version_ns);
 
     MessageRenderer renderer;
@@ -220,26 +221,6 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
                 renderer.getLength());
 }
 
-// In the following tests we confirm the response data is rendered in
-// wire format in the expected way.
-
-// The most primitive check: checking the result of the processMessage()
-// method
-TEST_F(AuthSrvTest, DISABLED_builtInQuery) { // Needs builtin
-    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
-                                       default_qid, Name("version.bind"),
-                                       RRClass::CH(), RRType::TXT());
-    createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, *parse_message, *response_obuffer,
-                          &dnsserv);
-    createBuiltinVersionResponse(default_qid, response_data);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        response_obuffer->getData(),
-                        response_obuffer->getLength(),
-                        &response_data[0], response_data.size());
-    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
-}
-
 // We did not configure any client lists. Therefore it should be REFUSED
 TEST_F(AuthSrvTest, noClientList) {
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
@@ -254,51 +235,6 @@ TEST_F(AuthSrvTest, noClientList) {
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
 }
 
-// Same test emulating the UDPServer class behavior (defined in libasiolink).
-// This is not a good test in that it assumes internal implementation details
-// of UDPServer, but we've encountered a regression due to the introduction
-// of that class, so we add a test for that case to prevent such a regression
-// in future.
-// Besides, the generalization of UDPServer is probably too much for the
-// authoritative only server in terms of performance, and it's quite likely
-// we need to drop it for the authoritative server implementation.
-// At that point we can drop this test, too.
-TEST_F(AuthSrvTest, DISABLED_builtInQueryViaDNSServer) { //Needs builtin
-    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
-                                       default_qid, Name("version.bind"),
-                                       RRClass::CH(), RRType::TXT());
-    createRequestPacket(request_message, IPPROTO_UDP);
-
-    (*server.getDNSLookupProvider())(*io_message, parse_message,
-                                     response_message,
-                                     response_obuffer, &dnsserv);
-    (*server.getDNSAnswerProvider())(*io_message, parse_message,
-                                     response_message, response_obuffer);
-
-    createBuiltinVersionResponse(default_qid, response_data);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        response_obuffer->getData(),
-                        response_obuffer->getLength(),
-                        &response_data[0], response_data.size());
-}
-
-// Same type of test as builtInQueryViaDNSServer but for an error response.
-TEST_F(AuthSrvTest, DISABLED_iqueryViaDNSServer) { // Needs builtin
-    createDataFromFile("iquery_fromWire.wire");
-    (*server.getDNSLookupProvider())(*io_message, parse_message,
-                                     response_message,
-                                     response_obuffer, &dnsserv);
-    (*server.getDNSAnswerProvider())(*io_message, parse_message,
-                                     response_message, response_obuffer);
-
-    UnitTestUtil::readWireData("iquery_response_fromWire.wire",
-                               response_data);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        response_obuffer->getData(),
-                        response_obuffer->getLength(),
-                        &response_data[0], response_data.size());
-}
-
 // Unsupported requests.  Should result in NOTIMP.
 TEST_F(AuthSrvTest, unsupportedRequest) {
     unsupportedRequest();
@@ -363,43 +299,6 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
     checkAllRcodeCountersZero();
 }
 
-// Try giving the server a TSIG signed request and see it can anwer signed as
-// well
-TEST_F(AuthSrvTest, DISABLED_TSIGSigned) { // Needs builtin
-    // Prepare key, the client message, etc
-    const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
-    TSIGContext context(key);
-    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
-                                       Name("version.bind"), RRClass::CH(),
-                                       RRType::TXT());
-    createRequestPacket(request_message, IPPROTO_UDP, &context);
-
-    // Run the message through the server
-    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
-    keyring->add(key);
-    server.setTSIGKeyRing(&keyring);
-    server.processMessage(*io_message, *parse_message, *response_obuffer,
-                          &dnsserv);
-
-    // What did we get?
-    EXPECT_TRUE(dnsserv.hasAnswer());
-    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
-                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
-    // We need to parse the message ourself, or getTSIGRecord won't work
-    InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
-    Message m(Message::PARSE);
-    m.fromWire(ib);
-
-    const TSIGRecord* tsig = m.getTSIGRecord();
-    ASSERT_TRUE(tsig != NULL) << "Missing TSIG signature";
-    TSIGError error(context.verify(tsig, response_obuffer->getData(),
-                                   response_obuffer->getLength()));
-    EXPECT_EQ(TSIGError::NOERROR(), error) <<
-        "The server signed the response, but it doesn't seem to be valid";
-
-    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
-}
-
 // Give the server a signed request, but don't give it the key. It will
 // not be able to verify it, returning BADKEY
 TEST_F(AuthSrvTest, TSIGSignedBadKey) {
@@ -850,6 +749,155 @@ updateDatabase(AuthSrv* server, const char* params) {
     DataSourceConfigurator::testReconfigure(server, config);
 }
 
+void
+updateInMemory(AuthSrv* server, const char* origin, const char* filename) {
+    const ConstElementPtr config(Element::fromJSON("{"
+        "\"IN\": [{"
+        "   \"type\": \"MasterFiles\","
+        "   \"params\": {"
+        "       \"" + string(origin) + "\": \"" + string(filename) + "\""
+        "   },"
+        "   \"cache-enable\": true"
+        "}],"
+        "\"CH\": [{"
+        "   \"type\": \"static\","
+        "   \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
+        "}]}"));
+    DataSourceConfigurator::testReconfigure(server, config);
+}
+
+void
+updateBuiltin(AuthSrv* server) {
+    const ConstElementPtr config(Element::fromJSON("{"
+        "\"CH\": [{"
+        "   \"type\": \"static\","
+        "   \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
+        "}]}"));
+    DataSourceConfigurator::testReconfigure(server, config);
+}
+
+// Try giving the server a TSIG signed request and see it can anwer signed as
+// well
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_TSIGSigned) { // Needs builtin
+#else
+TEST_F(AuthSrvTest, TSIGSigned) {
+#endif
+    // Prepare key, the client message, etc
+    updateBuiltin(&server);
+    const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
+    TSIGContext context(key);
+    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+                                       Name("VERSION.BIND."), RRClass::CH(),
+                                       RRType::TXT());
+    createRequestPacket(request_message, IPPROTO_UDP, &context);
+
+    // Run the message through the server
+    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+    keyring->add(key);
+    server.setTSIGKeyRing(&keyring);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
+
+    // What did we get?
+    EXPECT_TRUE(dnsserv.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
+    // We need to parse the message ourself, or getTSIGRecord won't work
+    InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
+    Message m(Message::PARSE);
+    m.fromWire(ib);
+
+    const TSIGRecord* tsig = m.getTSIGRecord();
+    ASSERT_TRUE(tsig != NULL) << "Missing TSIG signature";
+    TSIGError error(context.verify(tsig, response_obuffer->getData(),
+                                   response_obuffer->getLength()));
+    EXPECT_EQ(TSIGError::NOERROR(), error) <<
+        "The server signed the response, but it doesn't seem to be valid";
+
+    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
+}
+
+// Same test emulating the UDPServer class behavior (defined in libasiolink).
+// This is not a good test in that it assumes internal implementation details
+// of UDPServer, but we've encountered a regression due to the introduction
+// of that class, so we add a test for that case to prevent such a regression
+// in future.
+// Besides, the generalization of UDPServer is probably too much for the
+// authoritative only server in terms of performance, and it's quite likely
+// we need to drop it for the authoritative server implementation.
+// At that point we can drop this test, too.
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_builtInQueryViaDNSServer) {
+#else
+TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
+#endif
+    updateBuiltin(&server);
+    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+                                       default_qid, Name("VERSION.BIND."),
+                                       RRClass::CH(), RRType::TXT());
+    createRequestPacket(request_message, IPPROTO_UDP);
+
+    (*server.getDNSLookupProvider())(*io_message, parse_message,
+                                     response_message,
+                                     response_obuffer, &dnsserv);
+    (*server.getDNSAnswerProvider())(*io_message, parse_message,
+                                     response_message, response_obuffer);
+
+    createBuiltinVersionResponse(default_qid, response_data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        response_obuffer->getData(),
+                        response_obuffer->getLength(),
+                        &response_data[0], response_data.size());
+}
+
+// In the following tests we confirm the response data is rendered in
+// wire format in the expected way.
+
+// The most primitive check: checking the result of the processMessage()
+// method
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_builtInQuery) {
+#else
+TEST_F(AuthSrvTest, builtInQuery) {
+#endif
+    updateBuiltin(&server);
+    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+                                       default_qid, Name("VERSION.BIND."),
+                                       RRClass::CH(), RRType::TXT());
+    createRequestPacket(request_message, IPPROTO_UDP);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
+    createBuiltinVersionResponse(default_qid, response_data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        response_obuffer->getData(),
+                        response_obuffer->getLength(),
+                        &response_data[0], response_data.size());
+    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
+}
+
+// Same type of test as builtInQueryViaDNSServer but for an error response.
+#ifdef USE_STATIC_LINK
+TEST_F(AuthSrvTest, DISABLED_iqueryViaDNSServer) { // Needs builtin
+#else
+TEST_F(AuthSrvTest, iqueryViaDNSServer) { // Needs builtin
+#endif
+    updateBuiltin(&server);
+    createDataFromFile("iquery_fromWire.wire");
+    (*server.getDNSLookupProvider())(*io_message, parse_message,
+                                     response_message,
+                                     response_obuffer, &dnsserv);
+    (*server.getDNSAnswerProvider())(*io_message, parse_message,
+                                     response_message, response_obuffer);
+
+    UnitTestUtil::readWireData("iquery_response_fromWire.wire",
+                               response_data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        response_obuffer->getData(),
+                        response_obuffer->getLength(),
+                        &response_data[0], response_data.size());
+}
+
 // Install a Sqlite3 data source with testing data.
 #ifdef USE_STATIC_LINK
 TEST_F(AuthSrvTest, DISABLED_updateConfig) {
@@ -870,9 +918,9 @@ TEST_F(AuthSrvTest, updateConfig) {
 }
 
 #ifdef USE_STATIC_LINK
-TEST_F(AuthSrvTest, datasourceFail) {
-#else
 TEST_F(AuthSrvTest, DISABLED_datasourceFail) {
+#else
+TEST_F(AuthSrvTest, datasourceFail) {
 #endif
     updateDatabase(&server, CONFIG_TESTDB);
 
@@ -889,9 +937,9 @@ TEST_F(AuthSrvTest, DISABLED_datasourceFail) {
 }
 
 #ifdef USE_STATIC_LINK
-TEST_F(AuthSrvTest, updateConfigFail) {
-#else
 TEST_F(AuthSrvTest, DISABLED_updateConfigFail) {
+#else
+TEST_F(AuthSrvTest, updateConfigFail) {
 #endif
     // First, load a valid data source.
     updateDatabase(&server, CONFIG_TESTDB);
@@ -905,28 +953,23 @@ TEST_F(AuthSrvTest, DISABLED_updateConfigFail) {
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
-    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
-                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
 
-TEST_F(AuthSrvTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_updateWithInMemoryClient
-#else
-       DISABLED_updateWithInMemoryClient // Needs #2046
-#endif
-    )
-{
+TEST_F(AuthSrvTest, updateWithInMemoryClient) {
     // Test configuring memory data source.  Detailed test cases are covered
     // in the configuration tests.  We only check the AuthSrv interface here.
 
-    // By default memory data source isn't enabled
-    EXPECT_FALSE(server.hasInMemoryClient());
-    updateConfig(&server,
-                 "{\"datasources\": [{\"type\": \"memory\"}]}", true);
+    // Create an empty in-memory
+    const ConstElementPtr config(Element::fromJSON("{"
+        "\"IN\": [{"
+        "   \"type\": \"MasterFiles\","
+        "   \"params\": {},"
+        "   \"cache-enable\": true"
+        "}]}"));
+    DataSourceConfigurator::testReconfigure(&server, config);
     // after successful configuration, we should have one (with empty zoneset).
-    EXPECT_TRUE(server.hasInMemoryClient());
-    EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
 
     // The memory data source is empty, should return REFUSED rcode.
     createDataFromFile("examplequery_fromWire.wire");
@@ -937,21 +980,12 @@ TEST_F(AuthSrvTest,
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
 }
 
-TEST_F(AuthSrvTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_queryWithInMemoryClientNoDNSSEC
-#else
-       DISABLED_queryWithInMemoryClientNoDNSSEC // Needs #2046
-#endif
-    )
-{
+TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) {
     // In this example, we do simple check that query is handled from the
     // query handler class, and confirm it returns no error and a non empty
     // answer section.  Detailed examination on the response content
     // for various types of queries are tested in the query tests.
-    updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
-    EXPECT_TRUE(server.hasInMemoryClient());
-    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
+    updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
 
     createDataFromFile("nsec3query_nodnssec_fromWire.wire");
     server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -962,20 +996,11 @@ TEST_F(AuthSrvTest,
                 opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
 }
 
-TEST_F(AuthSrvTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_queryWithInMemoryClientDNSSEC
-#else
-       DISABLED_queryWithInMemoryClientDNSSEC // Needs #2046
-#endif
-    )
-{
+TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) {
     // Similar to the previous test, but the query has the DO bit on.
     // The response should contain RRSIGs, and should have more RRs than
     // the previous case.
-    updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
-    EXPECT_TRUE(server.hasInMemoryClient());
-    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
+    updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
 
     createDataFromFile("nsec3query_fromWire.wire");
     server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -990,17 +1015,16 @@ TEST_F(AuthSrvTest,
 #ifdef USE_STATIC_LINK
        DISABLED_chQueryWithInMemoryClient
 #else
-       DISABLED_chQueryWithInMemoryClient // FIXME: Needs the built-in
+       chQueryWithInMemoryClient
 #endif
     )
 {
-    // Configure memory data source for class IN
-    updateConfig(&server, "{\"datasources\": "
-                 "[{\"class\": \"IN\", \"type\": \"memory\"}]}", true);
+    // Set up the in-memory
+    updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
 
     // This shouldn't affect the result of class CH query
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
-                                       default_qid, Name("version.bind"),
+                                       default_qid, Name("VERSION.BIND."),
                                        RRClass::CH(), RRType::TXT());
     createRequestPacket(request_message, IPPROTO_UDP);
     server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -1303,11 +1327,6 @@ public:
         return (real_zone_finder_->findNSEC3(name, recursive));
     }
 
-    virtual isc::dns::Name
-    findPreviousName(const isc::dns::Name& query) const {
-        return (real_zone_finder_->findPreviousName(query));
-    }
-
 private:
     isc::datasrc::ZoneFinderPtr real_zone_finder_;
     ThrowWhen throw_when_;
@@ -1331,7 +1350,7 @@ public:
     ///                      throw std::exception
     /// \param fake_rrset If non NULL, it will be used as an answer to
     /// find() for that name and type.
-    FakeClient(isc::datasrc::DataSourceClientContainerPtr real_client,
+    FakeClient(const DataSourceClient* real_client,
                ThrowWhen throw_when, bool isc_exception,
                ConstRRsetPtr fake_rrset = ConstRRsetPtr()) :
         real_client_ptr_(real_client),
@@ -1350,7 +1369,7 @@ public:
     findZone(const isc::dns::Name& name) const {
         checkThrow(THROW_AT_FIND_ZONE, throw_when_, isc_exception_);
         const FindResult result =
-            real_client_ptr_->getInstance().findZone(name);
+            real_client_ptr_->findZone(name);
         return (FindResult(result.code, isc::datasrc::ZoneFinderPtr(
                                         new FakeZoneFinder(result.zone_finder,
                                                            throw_when_,
@@ -1370,38 +1389,39 @@ public:
                   "fake data source");
     }
 private:
-    const isc::datasrc::DataSourceClientContainerPtr real_client_ptr_;
+    const DataSourceClient* real_client_ptr_;
     ThrowWhen throw_when_;
     bool isc_exception_;
     ConstRRsetPtr fake_rrset_;
 };
 
-class FakeContainer : public isc::datasrc::DataSourceClientContainer {
+class FakeList : public isc::datasrc::ConfigurableClientList {
 public:
-    /// \brief Creates a fake container for the given in-memory client
+    /// \brief Creates a fake list for the given in-memory client
     ///
-    /// The initializer creates a fresh instance of a memory datasource,
-    /// which is ignored for the rest (but we do not allow 'null' containers
-    /// atm, and this is only needed in these tests, this may be changed
-    /// if we generalize the container class a bit more)
-    ///
-    /// It will also create a FakeClient, with the given arguments, which
-    /// is actually used when the instance is requested.
-    FakeContainer(isc::datasrc::DataSourceClientContainerPtr real_client,
-                  ThrowWhen throw_when, bool isc_exception,
-                  ConstRRsetPtr fake_rrset = ConstRRsetPtr()) :
-        DataSourceClientContainer("memory",
-                                  Element::fromJSON("{\"type\": \"memory\"}")),
-        client_(new FakeClient(real_client, throw_when, isc_exception,
-                               fake_rrset))
-    {}
-
-    isc::datasrc::DataSourceClient& getInstance() {
-        return (*client_);
+    /// It will create a FakeClient for each client in the original list,
+    /// with the given arguments, which is used when searching for the
+    /// corresponding data source.
+    FakeList(const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+             real_list, ThrowWhen throw_when, bool isc_exception,
+             ConstRRsetPtr fake_rrset = ConstRRsetPtr()) :
+        ConfigurableClientList(RRClass::IN()),
+        real_(real_list)
+    {
+        BOOST_FOREACH(const DataSourceInfo& info, real_->getDataSources()) {
+             const isc::datasrc::DataSourceClientPtr
+                 client(new FakeClient(info.data_src_client_ != NULL ?
+                                       info.data_src_client_ :
+                                       info.cache_.get(),
+                                       throw_when, isc_exception, fake_rrset));
+             clients_.push_back(client);
+             data_sources_.push_back(DataSourceInfo(client.get(),
+                 isc::datasrc::DataSourceClientContainerPtr(), false));
+        }
     }
-
 private:
-    const boost::scoped_ptr<isc::datasrc::DataSourceClient> client_;
+    const boost::shared_ptr<isc::datasrc::ConfigurableClientList> real_;
+    vector<isc::datasrc::DataSourceClientPtr> clients_;
 };
 
 } // end anonymous namespace for throwing proxy classes
@@ -1414,18 +1434,16 @@ TEST_F(AuthSrvTest,
 #ifdef USE_STATIC_LINK
        DISABLED_queryWithInMemoryClientProxy
 #else
-       DISABLED_queryWithInMemoryClientProxy // Needs #2046
+       queryWithInMemoryClientProxy
 #endif
     )
 {
     // Set real inmem client to proxy
-    updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
-    EXPECT_TRUE(server.hasInMemoryClient());
-
-    isc::datasrc::DataSourceClientContainerPtr fake_client_container(
-        new FakeContainer(server.getInMemoryClientContainer(rrclass),
-                          THROW_NEVER, false));
-    server.setInMemoryClient(rrclass, fake_client_container);
+    updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
+    boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+        list(new FakeList(server.getClientList(RRClass::IN()), THROW_NEVER,
+                          false));
+    server.setClientList(RRClass::IN(), list);
 
     createDataFromFile("nsec3query_nodnssec_fromWire.wire");
     server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -1443,28 +1461,22 @@ TEST_F(AuthSrvTest,
 // If non null rrset is given, it will be passed to the proxy so it can
 // return some faked response.
 void
-setupThrow(AuthSrv* server, const char *config, ThrowWhen throw_when,
-           bool isc_exception, ConstRRsetPtr rrset = ConstRRsetPtr())
+setupThrow(AuthSrv* server, ThrowWhen throw_when, bool isc_exception,
+           ConstRRsetPtr rrset = ConstRRsetPtr())
 {
-    // Set real inmem client to proxy
-    updateConfig(server, config, true);
-
-    // Set it to throw on findZone(), this should result in
-    // SERVFAIL on any exception
-    isc::datasrc::DataSourceClientContainerPtr fake_client_container(
-        new FakeContainer(
-            server->getInMemoryClientContainer(isc::dns::RRClass::IN()),
-            throw_when, isc_exception, rrset));
+    updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
 
-    ASSERT_TRUE(server->hasInMemoryClient());
-    server->setInMemoryClient(isc::dns::RRClass::IN(), fake_client_container);
+    boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+        list(new FakeList(server->getClientList(RRClass::IN()), throw_when,
+                          isc_exception, rrset));
+    server->setClientList(RRClass::IN(), list);
 }
 
 TEST_F(AuthSrvTest,
 #ifdef USE_STATIC_LINK
        DISABLED_queryWithThrowingProxyServfails
 #else
-       DISABLED_queryWithThrowingProxyServfails // Needs #2046
+       queryWithThrowingProxyServfails
 #endif
     )
 {
@@ -1480,11 +1492,11 @@ TEST_F(AuthSrvTest,
                                              RRClass::IN(), RRType::TXT());
     for (ThrowWhen* when(throws); *when != THROW_NEVER; ++when) {
         createRequestPacket(request_message, IPPROTO_UDP);
-        setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, true);
+        setupThrow(&server, *when, true);
         processAndCheckSERVFAIL();
         // To be sure, check same for non-isc-exceptions
         createRequestPacket(request_message, IPPROTO_UDP);
-        setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, false);
+        setupThrow(&server, *when, false);
         processAndCheckSERVFAIL();
     }
 }
@@ -1495,12 +1507,12 @@ TEST_F(AuthSrvTest,
 #ifdef USE_STATIC_LINK
        DISABLED_queryWithInMemoryClientProxyGetClass
 #else
-       DISABLED_queryWithInMemoryClientProxyGetClass // Needs #2046
+       queryWithInMemoryClientProxyGetClass
 #endif
     )
 {
     createDataFromFile("nsec3query_nodnssec_fromWire.wire");
-    setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, THROW_AT_GET_CLASS, true);
+    setupThrow(&server, THROW_AT_GET_CLASS, true);
 
     // getClass is not called so it should just answer
     server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -1515,7 +1527,7 @@ TEST_F(AuthSrvTest,
 #ifdef USE_STATIC_LINK
        DISABLED_queryWithThrowingInToWire
 #else
-       DISABLED_queryWithThrowingInToWire // Needs #2046
+       queryWithThrowingInToWire
 #endif
     )
 {
@@ -1524,8 +1536,7 @@ TEST_F(AuthSrvTest,
     ConstRRsetPtr empty_rrset(new RRset(Name("foo.example"),
                                         RRClass::IN(), RRType::TXT(),
                                         RRTTL(0)));
-    setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, THROW_NEVER, true,
-               empty_rrset);
+    setupThrow(&server, THROW_NEVER, true, empty_rrset);
 
     // Repeat the query processing two times.  Due to the faked RRset,
     // toWire() should throw, and it should result in SERVFAIL.
@@ -1692,9 +1703,9 @@ TEST_F(AuthSrvTest, clientList) {
               server.getClientList(RRClass::IN()));
     // Put something in.
     const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
-        list(new isc::datasrc::ConfigurableClientList());
+        list(new isc::datasrc::ConfigurableClientList(RRClass::IN()));
     const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
-        list2(new isc::datasrc::ConfigurableClientList());
+        list2(new isc::datasrc::ConfigurableClientList(RRClass::CH()));
     server.setClientList(RRClass::IN(), list);
     server.setClientList(RRClass::CH(), list2);
     // There are two things in the list and they are IN and CH

+ 31 - 4
src/bin/auth/tests/command_unittest.cc

@@ -177,6 +177,9 @@ TEST_F(AuthCommandTest, shutdownIncorrectPID) {
     EXPECT_EQ(0, rcode_);
 }
 
+#if 0
+TODO: This needs to be rewritten
+Also, reenable everywhere
 // A helper function commonly used for the "loadzone" command tests.
 // It configures the server with a memory data source containing two
 // zones, and checks the zones are correctly loaded.
@@ -196,6 +199,7 @@ zoneChecks(AuthSrv& server) {
               findZone(Name("ns.test2.example")).zone_finder->
               find(Name("ns.test2.example"), RRType::AAAA())->code);
 }
+#endif
 
 void
 configureZones(AuthSrv& server) {
@@ -214,9 +218,11 @@ configureZones(AuthSrv& server) {
                             "  \"file\": \""
                                TEST_DATA_BUILDDIR "/test2.zone.copied\"}"
                             "]}]}"));
-    zoneChecks(server);
+    //TODO: zoneChecks(server);
 }
 
+#if 0
+TODO: This needs to be rewritten and re-enabled
 void
 newZoneChecks(AuthSrv& server) {
     EXPECT_TRUE(server.getInMemoryClient(RRClass::IN()));
@@ -236,6 +242,7 @@ newZoneChecks(AuthSrv& server) {
               findZone(Name("ns.test2.example")).zone_finder->
               find(Name("ns.test2.example"), RRType::AAAA())->code);
 }
+#endif
 
 TEST_F(AuthCommandTest,
 #ifdef USE_STATIC_LINK
@@ -258,7 +265,7 @@ TEST_F(AuthCommandTest,
                                     Element::fromJSON(
                                         "{\"origin\": \"test1.example\"}"));
     checkAnswer(0);
-    newZoneChecks(server_);
+    //TODO: newZoneChecks(server_);
 }
 
 TEST_F(AuthCommandTest,
@@ -306,11 +313,14 @@ TEST_F(AuthCommandTest,
 
     server_.updateConfig(map);
 
+#if 0
+    FIXME: This needs to be done slightly differently
     // Check that the A record at www.example.org does not exist
     ASSERT_TRUE(server_.hasInMemoryClient());
     EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getInMemoryClient(RRClass::IN())->
               findZone(Name("example.org")).zone_finder->
               find(Name("www.example.org"), RRType::A())->code);
+#endif
 
     // Add the record to the underlying sqlite database, by loading
     // it as a separate datasource, and updating it
@@ -328,11 +338,14 @@ TEST_F(AuthCommandTest,
     sql_updater->addRRset(*rrset);
     sql_updater->commit();
 
+#if 0
+    TODO:
     // This new record is in the database now, but should not be in the
     // memory-datasource yet, so check again
     EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getInMemoryClient(RRClass::IN())->
               findZone(Name("example.org")).zone_finder->
               find(Name("www.example.org"), RRType::A())->code);
+#endif
 
     // Now send the command to reload it
     result_ = execAuthServerCommand(server_, "loadzone",
@@ -340,19 +353,24 @@ TEST_F(AuthCommandTest,
                                         "{\"origin\": \"example.org\"}"));
     checkAnswer(0);
 
+#if 0
     // And now it should be present too.
     EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
               findZone(Name("example.org")).zone_finder->
               find(Name("www.example.org"), RRType::A())->code);
+#endif
 
     // Some error cases. First, the zone has no configuration. (note .com here)
     result_ = execAuthServerCommand(server_, "loadzone",
         Element::fromJSON("{\"origin\": \"example.com\"}"));
     checkAnswer(1);
+#if 0
+    FIXME
     // The previous zone is not hurt in any way
     EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
               findZone(Name("example.org")).zone_finder->
               find(Name("example.org"), RRType::SOA())->code);
+#endif
 
     module_session.setLocalConfig(Element::fromJSON("{\"datasources\": []}"));
     result_ = execAuthServerCommand(server_, "loadzone",
@@ -360,10 +378,13 @@ TEST_F(AuthCommandTest,
                                         "{\"origin\": \"example.org\"}"));
     checkAnswer(1);
 
+#if 0
+    FIXME
     // The previous zone is not hurt in any way
     EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
               findZone(Name("example.org")).zone_finder->
               find(Name("example.org"), RRType::SOA())->code);
+#endif
     // Configure an unreadable zone. Should fail, but leave the original zone
     // data there
     const ElementPtr
@@ -383,10 +404,13 @@ TEST_F(AuthCommandTest,
     result_ = execAuthServerCommand(server_, "loadzone",
         Element::fromJSON("{\"origin\": \"example.com\"}"));
     checkAnswer(1);
+#if 0
+    FIXME
     // The previous zone is not hurt in any way
     EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
               findZone(Name("example.org")).zone_finder->
               find(Name("example.org"), RRType::SOA())->code);
+#endif
 
     // Broken configuration (not valid against the spec)
     const ElementPtr
@@ -398,10 +422,13 @@ TEST_F(AuthCommandTest,
                                  "]}"));
     module_session.setLocalConfig(broken);
     checkAnswer(1);
+#if 0
+    FIXME
     // The previous zone is not hurt in any way
     EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
               findZone(Name("example.org")).zone_finder->
               find(Name("example.org"), RRType::SOA())->code);
+#endif
 }
 
 TEST_F(AuthCommandTest,
@@ -421,7 +448,7 @@ TEST_F(AuthCommandTest,
                                     Element::fromJSON(
                                         "{\"origin\": \"test1.example\"}"));
     checkAnswer(1);
-    zoneChecks(server_);     // zone shouldn't be replaced
+    //zoneChecks(server_);     // zone shouldn't be replaced
 }
 
 TEST_F(AuthCommandTest,
@@ -442,7 +469,7 @@ TEST_F(AuthCommandTest,
                                     Element::fromJSON(
                                         "{\"origin\": \"test1.example\"}"));
     checkAnswer(1);
-    zoneChecks(server_);     // zone shouldn't be replaced
+    //zoneChecks(server_);     // zone shouldn't be replaced
 }
 
 TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) {

+ 3 - 2
src/bin/auth/tests/config_unittest.cc

@@ -80,7 +80,8 @@ TEST_F(AuthConfigTest, versionConfig) {
 }
 
 TEST_F(AuthConfigTest, exceptionGuarantee) {
-    EXPECT_FALSE(server.hasInMemoryClient());
+    server.setStatisticsTimerInterval(1234);
+    EXPECT_EQ(1234, server.getStatisticsTimerInterval());
     // This configuration contains an invalid item, which will trigger
     // an exception.
     EXPECT_THROW(configureAuthServer(
@@ -89,7 +90,7 @@ TEST_F(AuthConfigTest, exceptionGuarantee) {
                          "{ \"no_such_config_var\": 1}")),
                  AuthConfigError);
     // The server state shouldn't change
-    EXPECT_FALSE(server.hasInMemoryClient());
+    EXPECT_EQ(1234, server.getStatisticsTimerInterval());
 }
 
 TEST_F(AuthConfigTest, badConfig) {

+ 15 - 9
src/bin/auth/tests/datasrc_configurator_unittest.cc

@@ -35,7 +35,7 @@ class DatasrcConfiguratorTest;
 
 class FakeList {
 public:
-    FakeList() :
+    FakeList(const RRClass&) :
         configuration_(new ListElement)
     {}
     void configure(const ConstElementPtr& configuration, bool allow_cache) {
@@ -115,9 +115,15 @@ protected:
     void SetUp() {
         init();
     }
+    ElementPtr buildConfig(const string& config) const {
+        const ElementPtr internal(Element::fromJSON(config));
+        const ElementPtr external(Element::fromJSON("{\"version\": 1}"));
+        external->set("classes", internal);
+        return (external);
+    }
     void initializeINList() {
         const ElementPtr
-            config(Element::fromJSON("{\"IN\": [{\"type\": \"xxx\"}]}"));
+            config(buildConfig("{\"IN\": [{\"type\": \"xxx\"}]}"));
         session.addMessage(createCommand("config_update", config), "data_sources",
                            "*");
         mccs->checkCommand();
@@ -164,7 +170,7 @@ TEST_F(DatasrcConfiguratorTest, modifyList) {
     initializeINList();
     // And now change the configuration of the list
     const ElementPtr
-        config(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}]}"));
+        config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}]}"));
     session.addMessage(createCommand("config_update", config), "data_sources",
                        "*");
     log_ = "";
@@ -179,7 +185,7 @@ TEST_F(DatasrcConfiguratorTest, modifyList) {
 // Check we can have multiple lists at once
 TEST_F(DatasrcConfiguratorTest, multiple) {
     const ElementPtr
-        config(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], "
+        config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}], "
                                  "\"CH\": [{\"type\": \"xxx\"}]}"));
     session.addMessage(createCommand("config_update", config), "data_sources",
                        "*");
@@ -200,8 +206,8 @@ TEST_F(DatasrcConfiguratorTest, multiple) {
 TEST_F(DatasrcConfiguratorTest, updateAdd) {
     initializeINList();
     const ElementPtr
-        config(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], "
-                                 "\"CH\": [{\"type\": \"xxx\"}]}"));
+        config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}], "
+                           "\"CH\": [{\"type\": \"xxx\"}]}"));
     session.addMessage(createCommand("config_update", config), "data_sources",
                        "*");
     log_ = "";
@@ -218,7 +224,7 @@ TEST_F(DatasrcConfiguratorTest, updateAdd) {
 TEST_F(DatasrcConfiguratorTest, updateDelete) {
     initializeINList();
     const ElementPtr
-        config(Element::fromJSON("{}"));
+        config(buildConfig("{}"));
     session.addMessage(createCommand("config_update", config), "data_sources",
                        "*");
     log_ = "";
@@ -236,8 +242,8 @@ TEST_F(DatasrcConfiguratorTest, rollbackAddition) {
     initializeINList();
     // The configuration is wrong. However, the CH one will get done first.
     const ElementPtr
-        config(Element::fromJSON("{\"IN\": [{\"type\": 13}], "
-                                 "\"CH\": [{\"type\": \"xxx\"}]}"));
+        config(buildConfig("{\"IN\": [{\"type\": 13}], "
+                           "\"CH\": [{\"type\": \"xxx\"}]}"));
     session.addMessage(createCommand("config_update", config), "data_sources",
                        "*");
     log_ = "";

+ 0 - 4
src/bin/auth/tests/query_unittest.cc

@@ -452,10 +452,6 @@ public:
     // answers when DNSSEC is required.
     void setNSEC3Flag(bool on) { use_nsec3_ = on; }
 
-    virtual Name findPreviousName(const Name&) const {
-        isc_throw(isc::NotImplemented, "Mock doesn't support previous name");
-    }
-
     // This method allows tests to insert new record in the middle of the test.
     //
     // \param record_txt textual RR representation of RR (such as soa_txt, etc)

+ 5 - 0
src/bin/bind10/bind10_messages.mes

@@ -160,6 +160,11 @@ The boss module is sending a SIGKILL signal to the given process.
 % BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2)
 The boss module is sending a SIGTERM signal to the given process.
 
+% BIND10_SETGID setting GID to %1
+The boss switches the process group ID to the given value.  This happens
+when BIND 10 starts with the -u option, and the group ID will be set to
+that of the specified user.
+
 % BIND10_SETUID setting UID to %1
 The boss switches the user it runs as to the given UID.
 

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

@@ -169,8 +169,8 @@ class BoB:
     
     def __init__(self, msgq_socket_file=None, data_path=None,
                  config_filename=None, clear_config=False, nocache=False,
-                 verbose=False, nokill=False, setuid=None, username=None,
-                 cmdctl_port=None, wait_time=10):
+                 verbose=False, nokill=False, setuid=None, setgid=None,
+                 username=None, cmdctl_port=None, wait_time=10):
         """
             Initialize the Boss of BIND. This is a singleton (only one can run).
         
@@ -208,6 +208,7 @@ class BoB:
         self.components_to_restart = []
         self.runnable = False
         self.uid = setuid
+        self.gid = setgid
         self.username = username
         self.verbose = verbose
         self.nokill = nokill
@@ -1156,12 +1157,14 @@ def main():
 
     # Check user ID.
     setuid = None
+    setgid = None
     username = None
     if options.user:
         # Try getting information about the user, assuming UID passed.
         try:
             pw_ent = pwd.getpwuid(int(options.user))
             setuid = pw_ent.pw_uid
+            setgid = pw_ent.pw_gid
             username = pw_ent.pw_name
         except ValueError:
             pass
@@ -1175,6 +1178,7 @@ def main():
         try:
             pw_ent = pwd.getpwnam(options.user)
             setuid = pw_ent.pw_uid
+            setgid = pw_ent.pw_gid
             username = pw_ent.pw_name
         except KeyError:
             pass
@@ -1205,7 +1209,7 @@ def main():
         boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
                            options.config_file, options.clear_config,
                            options.nocache, options.verbose, options.nokill,
-                           setuid, username, options.cmdctl_port,
+                           setuid, setgid, username, options.cmdctl_port,
                            options.wait_time)
         startup_result = boss_of_bind.startup()
         if startup_result:

+ 7 - 8
src/bin/bindctl/bindctl.1

@@ -2,12 +2,12 @@
 .\"     Title: bindctl
 .\"    Author: [see the "AUTHORS" section]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: December 23, 2010
+.\"      Date: June 20, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "BINDCTL" "1" "December 23, 2010" "BIND10" "BIND10"
+.TH "BINDCTL" "1" "June 20, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -35,7 +35,7 @@ via its interactive command interpreter\&.
 communicates over a HTTPS REST\-ful interface provided by
 \fBb10-cmdctl\fR(8)\&. The
 \fBb10-cfgmgr\fR(8)
-daemon stores the configurations and defines the commands\&.
+daemon stores the configurations\&.
 .SH "ARGUMENTS"
 .PP
 The arguments are as follows:
@@ -91,9 +91,9 @@ Display the version number and exit\&.
 .SH "AUTHENTICATION"
 .PP
 The tool will authenticate using a username and password\&. On the first successful login, it will save the details to a comma\-separated\-value (CSV) file which will be used for later uses of
-\fBbindctl\fR\&. The file name is
-default_user\&.csv
-located under the directory specified by the \-\-csv\-file\-dir option\&.
+\fBbindctl\fR\&. The file name is "default_user\&.csv" located under the directory specified by the
+\fB\-\-csv\-file\-dir\fR
+option\&.
 .SH "USAGE"
 .PP
 The
@@ -115,8 +115,7 @@ keyword to receive usage assistance for a module or a module\'s command\&.
 The
 \fBquit\fR
 command is used to exit
-\fBbindctl\fR
-(and doesn\'t stop the BIND 10 services)\&.
+\fBbindctl\fR\&. (It doesn\'t stop the BIND 10 services\&.)
 .PP
 The following module is available by default:
 \fBconfig\fR

+ 10 - 9
src/bin/bindctl/bindctl.xml

@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "&#8212;">]>
 <!--
- - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-2012  Internet Systems Consortium, Inc. ("ISC")
  -
  - Permission to use, copy, modify, and/or distribute this software for any
  - purpose with or without fee is hereby granted, provided that the above
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>December 23, 2010</date>
+    <date>June 20, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -74,7 +74,7 @@
       <citerefentry><refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
       The
 <citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-      daemon stores the configurations and defines the commands.
+      daemon stores the configurations.
     </para>
 
   </refsect1>
@@ -120,8 +120,8 @@
 	    The directory name in which the user/password CSV file
             is stored (see AUTHENTICATION).
 	    By default this option doesn't have any value,
-	    in which case the ".bind10" directory under the user's
-            home directory will be used.
+	    in which case the "<filename>.bind10</filename>" directory
+            under the user's home directory will be used.
           </para>
          </listitem>
       </varlistentry>
@@ -167,8 +167,9 @@
       On the first successful login, it will save the details to
       a comma-separated-value (CSV) file
       which will be used for later uses of <command>bindctl</command>.
-      The file name is <filename>default_user.csv</filename>
-      located under the directory specified by the --csv-file-dir option.
+      The file name is "<filename>default_user.csv</filename>"
+      located under the directory specified by the
+      <option>--csv-file-dir</option> option.
     </para>
 
 <!-- TODO: mention HTTPS? -->
@@ -208,8 +209,8 @@
      <para>
        The <command>quit</command>
        command is used to exit
-       <command>bindctl</command>
-       (and doesn't stop the BIND 10 services).
+       <command>bindctl</command>.
+       (It doesn't stop the BIND 10 services.)
      </para>
 
      <para>

+ 3 - 9
src/bin/cfgmgr/b10-cfgmgr.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-cfgmgr
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: April 12, 2010
+.\"      Date: June 20, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-CFGMGR" "8" "April 12, 2010" "BIND10" "BIND10"
+.TH "B10\-CFGMGR" "8" "June 20, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -42,10 +42,6 @@ C\-Channel connection\&. If this connection is not established,
 will exit\&.
 .PP
 The daemon may be cleanly stopped by sending the SIGTERM signal to the process\&. This shutdown does not notify the subscribers\&.
-.PP
-When it exits, it saves its current configuration to
-/usr/local/var/bind10\-devel/b10\-config\&.db\&.
-
 .SH "ARGUMENTS"
 .PP
 The arguments are as follows:
@@ -59,9 +55,7 @@ will use the default configurations\&. The name of the backup file can be found
 .PP
 \fB\-c\fR \fIconfig\-filename\fR, \fB\-\-config\-filename\fR \fIconfig\-filename\fR
 .RS 4
-The configuration database filename to use\&. Can be either absolute or relative to data path\&.
-.sp
-Defaults to b10\-config\&.db
+The configuration database filename to use\&. Can be either absolute or relative to data path\&. It defaults to "b10\-config\&.db"\&.
 .RE
 .PP
 \fB\-p\fR \fIdata\-path\fR, \fB\-\-data\-path\fR \fIdata\-path\fR

+ 3 - 10
src/bin/cfgmgr/b10-cfgmgr.xml

@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>April 12, 2010</date>
+    <date>June 20, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -80,13 +80,6 @@
       subscribers.
     </para>
 
-    <para>
-      When it exits, it saves its current configuration to
-      <filename>/usr/local/var/bind10-devel/b10-config.db</filename>.
-<!-- TODO: fix path -->
-<!-- TODO: does it periodically save configuration? -->
-    </para>
-
 <!-- TODO: add a verbose or quiet switch so it is not so noisy -->
   </refsect1>
 
@@ -121,8 +114,8 @@
         </term>
         <listitem>
           <para>The configuration database filename to use. Can be either
-          absolute or relative to data path.</para>
-          <para>Defaults to b10-config.db</para>
+          absolute or relative to data path.
+          It defaults to "<filename>b10-config.db</filename>".</para>
         </listitem>
       </varlistentry>
 

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

@@ -1,6 +1,9 @@
 SUBDIRS = tests
 
-EXTRA_DIST = README logging.spec tsig_keys.spec datasrc.spec
+EXTRA_DIST = README logging.spec tsig_keys.spec
+
+datasrc.spec: datasrc.spec.pre
+	$(SED) -e "s|@@PKGDATADIR@@|$(pkgdatadir)|" datasrc.spec.pre >$@
 
 config_plugindir = @prefix@/share/@PACKAGE@/config_plugins
 config_plugin_DATA = logging.spec tsig_keys.spec datasrc.spec

+ 26 - 1
src/bin/cfgmgr/plugins/datasrc.spec

@@ -7,7 +7,15 @@
                 "item_name": "classes",
                 "item_type": "named_set",
                 "item_optional": false,
-                "item_default": {},
+                "item_default": {
+                    "CH": [
+                        {
+                            "type": "static",
+                            "cache-enable": false,
+                            "params": "@@PKGDATADIR@@/static.zone"
+                        }
+                    ]
+                },
                 "named_set_item_spec": {
                     "item_name": "class",
                     "item_type": "list",
@@ -30,6 +38,23 @@
                                 "item_type": "any",
                                 "item_optional": false,
                                 "item_default": null
+                            },
+                            {
+                                "item_name": "cache-enable",
+                                "item_type": "boolean",
+                                "item_optional": false,
+                                "item_default": false
+                            },
+                            {
+                                "item_name": "cache-zones",
+                                "item_type": "list",
+                                "item_optional": true,
+                                "list_item_spec": {
+                                    "item_name": "zone",
+                                    "item_type": "string",
+                                    "item_optional": false,
+                                    "item_default": ""
+                                }
                             }
                         ]
                     }

File diff suppressed because it is too large
+ 17 - 20
src/bin/dbutil/b10-dbutil.8


+ 25 - 21
src/bin/dbutil/b10-dbutil.xml

@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>March 20, 2012</date>
+    <date>June 20, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -60,14 +60,15 @@
   <refsect1>
     <title>DESCRIPTION</title>
     <para>
-      The <command>b10-dbutil</command> utility is a general administration
-      utility for SQL databases. (Currently only SQLite is supported by
-      BIND 10.)  It can report the current verion of the schema, and upgrade
-      an existing database to the latest version of the schema.
+      The <command>b10-dbutil</command> utility is a general
+      administration utility for SQL databases for BIND 10. (Currently
+      only SQLite is supported by BIND 10.)  It can report the
+      current verion of the schema, and upgrade an existing database
+      to the latest version of the schema.
     </para>
 
     <para>
-      <command>b10-dbutil</command> operates in one of two modes, check mode
+      <command>b10-dbutil</command> operates in one of two modesr: check mode
       or upgrade mode.
     </para>
 
@@ -76,9 +77,10 @@
       utility reads the version of the database schema from the database
       and prints it.  It will tell you whether the schema is at the latest
       version supported by BIND 10. Exit status is 0 if the schema is at
-      the correct version, 1 if the schema is at an older version, 2 if
+      the correct version, 1 if the schema is at an older version, or 2 if
       the schema is at a version not yet supported by this version of
-      b10-dbutil. Any higher value indicates an error during command-line
+      <command>b10-dbutil</command>.
+      Any higher value indicates an error during command-line
       parsing or execution.
     </para>
 
@@ -115,8 +117,8 @@
         </term>
         <listitem>
           <para>Selects the version check function, which reports the
-          current version of the database.  This is incompatible
-          with the --upgrade option.
+          current version of the database.  This is mutually exclusive
+          with the <option>--upgrade</option> option.
           </para>
         </listitem>
       </varlistentry>
@@ -126,11 +128,12 @@
          <option>--noconfirm</option>
         </term>
         <listitem>
-          <para>Only valid with --upgrade, this disables the prompt.
+          <para>Only valid with <option>--upgrade</option>, this disables
+          the prompt.
           Normally the utility will print a warning that an upgrade is
           about to take place and request that you type "Yes" to continue.
           If this switch is given on the command line, no prompt will
-          be issued: the utility will just perform the upgrade.
+          be issued and the utility will just perform the upgrade.
           </para>
         </listitem>
       </varlistentry>
@@ -141,15 +144,16 @@
         </term>
         <listitem>
           <para>Selects the upgrade function, which upgrades the database
-          to the latest version of the schema.  This is incompatible
-          with the --upgrade option.
+          to the latest version of the schema.  This is mutually exclusive
+          with the <option>--check</option> option.
           </para>
           <para>
-          The upgrade function will upgrade a BIND 10 database - no matter how
-          old the schema - preserving all data.  A backup file is created
-          before the upgrade (with the same name as the database, but with
-          ".backup" suffixed to it).  If the upgrade fails, this file can
-          be copied back to restore the original database.
+          The upgrade function will upgrade a BIND 10 database &mdash;
+          no matter how old the schema &mdash; preserving all data.
+	  A backup file is created before the upgrade (with the
+	  same name as the database, but with ".backup" suffixed
+	  to it).  If the upgrade fails, this file can be copied
+	  back to restore the original database.
           </para>
         </listitem>
       </varlistentry>
@@ -160,7 +164,7 @@
         </term>
         <listitem>
           <para>Enable verbose mode.  Each SQL command issued by the
-          utility will be printed to stderr before it is executed.</para>
+          utility will be printed to STDERR before it is executed.</para>
         </listitem>
       </varlistentry>
 
@@ -181,7 +185,7 @@
         </term>
         <listitem>
           <para>
-          Name of the database file to check of upgrade.
+          Name of the database file to check or upgrade.
           </para>
         </listitem>
       </varlistentry>

+ 36 - 25
src/bin/ddns/b10-ddns.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-ddns
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: February 28, 2012
+.\"      Date: June 18, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-DDNS" "8" "February 28, 2012" "BIND10" "BIND10"
+.TH "B10\-DDNS" "8" "June 18, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -29,33 +29,29 @@ The
 \fBb10\-ddns\fR
 daemon provides the BIND 10 Dynamic Update (DDNS) service, as specified in RFC 2136\&. Normally it is started by the
 \fBbind10\fR(8)
-boss process\&. When the
+boss process\&.
+.PP
+When the
 \fBb10\-auth\fR
-DNS server receives a DDNS update,
+authoritative DNS server receives an UPDATE request, it internally forwards the request to
+\fBb10\-ddns\fR, which handles the rest of the request processing\&. When the processing is completed
 \fBb10\-ddns\fR
-updates the zone in the BIND 10 zone data store\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-.PP
-Currently installed is a dummy component\&. It does not provide any functionality\&. It is a skeleton implementation that will be expanded later\&.
-.sp .5v
-.RE
+will send a response to the client with the RCODE set to the value as specified in RFC 2136\&. If the zone has been changed as a result, it will internally notify
+\fBb10\-auth\fR
+and
+\fBb10\-xfrout\fR
+so the new version of the zone will be served, and other secondary servers will be notified via the DNS notify protocol\&.
 .PP
 This daemon communicates with BIND 10 over a
 \fBb10-msgq\fR(8)
 C\-Channel connection\&. If this connection is not established,
 \fBb10\-ddns\fR
-will exit\&.
+will exit\&. The
+\fBb10\-ddns\fR
+daemon also depends on some other BIND 10 components (either directly or indirectly):
+\fBb10-auth\fR(8),
+\fBb10-xfrout\fR(8), and
+\fBb10-zonemgr\fR(8)\&.
 .PP
 
 \fBb10\-ddns\fR
@@ -65,9 +61,16 @@ receives its configurations from
 .PP
 The arguments are as follows:
 .PP
+\fB\-h\fR, \fB\-\-help\fR
+.RS 4
+Print the command line arguments and exit\&.
+.RE
+.PP
 \fB\-v\fR, \fB\-\-verbose\fR
 .RS 4
-This value is ignored at this moment, but is provided for compatibility with the bind10 Boss process
+This value is ignored at this moment, but is provided for compatibility with the
+\fBbind10\fR
+Boss process\&.
 .RE
 .SH "CONFIGURATION AND COMMANDS"
 .PP
@@ -75,7 +78,13 @@ The configurable settings are:
 .PP
 
 \fIzones\fR
-The zones option is a named set of zones that can be updated with DDNS\&. Each entry has one element called update_acl, which is a list of access control rules that define update permissions\&. By default this is empty; DDNS must be explicitely enabled per zone\&.
+The zones option is a list of configuration items for specific zones that can be updated with DDNS\&. Each entry is a map that can contain the following items:
+\fIorigin\fR
+is a textual domain name of the zone;
+\fIclass\fR
+(text) is the RR class of the zone; and
+\fIupdate_acl\fR
+is an ACL that controls permission for updates\&. See the BIND 10 Guide for configuration details\&. Note that not listing a zone in this list does not directly mean update requests for the zone are rejected, but the end result is the same because the default ACL for updates is to deny all requests\&.
 .PP
 The module commands are:
 .PP
@@ -91,13 +100,15 @@ argument to select the process ID to stop\&. (Note that the BIND 10 boss process
 \fBb10-auth\fR(8),
 \fBb10-cfgmgr\fR(8),
 \fBb10-msgq\fR(8),
+\fBb10-xfrout\fR(8),
+\fBb10-zonemgr\fR(8),
 \fBbind10\fR(8),
 BIND 10 Guide\&.
 .SH "HISTORY"
 .PP
 The
 \fBb10\-ddns\fR
-daemon was first implemented in December 2011 for the ISC BIND 10 project\&.
+daemon was first implemented in December 2011 for the ISC BIND 10 project\&. The first functional version was released in June 2012\&.
 .SH "COPYRIGHT"
 .br
 Copyright \(co 2011-2012 Internet Systems Consortium, Inc. ("ISC")

+ 51 - 15
src/bin/ddns/b10-ddns.xml

@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>February 28, 2012</date>
+    <date>June 18, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -58,23 +58,33 @@
       Normally it is started by the
       <citerefentry><refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       boss process.
-      When the <command>b10-auth</command> DNS server receives
-      a DDNS update, <command>b10-ddns</command> updates the zone
-      in the BIND 10 zone data store.
     </para>
 
-    <note><para>
-      Currently installed is a dummy component. It does not provide
-      any functionality. It is a skeleton implementation that
-      will be expanded later.
-<!-- TODO: #1458 -->
-    </para></note>
+    <para>
+      When the <command>b10-auth</command> authoritative DNS server
+      receives an UPDATE request, it internally forwards the request
+      to <command>b10-ddns</command>, which handles the rest of the
+      request processing.
+      When the processing is completed <command>b10-ddns</command>
+      will send a response to the client with the RCODE set to the
+      value as specified in RFC 2136.
+      If the zone has been changed as a result, it will internally
+      notify <command>b10-auth</command> and
+      <command>b10-xfrout</command> so the new version of the zone will
+      be served, and other secondary servers will be notified via the
+      DNS notify protocol.
+    </para>
 
     <para>
       This daemon communicates with BIND 10 over a
       <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       C-Channel connection.  If this connection is not established,
       <command>b10-ddns</command> will exit.
+      The <command>b10-ddns</command> daemon also depends on some other
+      BIND 10 components (either directly or indirectly):
+      <citerefentry><refentrytitle>b10-auth</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>b10-xfrout</refentrytitle><manvolnum>8</manvolnum></citerefentry>, and
+      <citerefentry><refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
     </para>
 
     <para>
@@ -92,13 +102,24 @@
 
       <varlistentry>
         <term>
+          <option>-h</option>,
+          <option>--help</option>
+        </term>
+        <listitem>
+          <para>
+            Print the command line arguments and exit.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
           <option>-v</option>,
           <option>--verbose</option>
         </term>
         <listitem>
           <para>
             This value is ignored at this moment, but is provided for
-            compatibility with the bind10 Boss process
+            compatibility with the <command>bind10</command> Boss process.
           </para>
         </listitem>
       </varlistentry>
@@ -112,10 +133,18 @@
     </para>
     <para>
       <varname>zones</varname>
-      The zones option is a named set of zones that can be updated with
-      DDNS. Each entry has one element called update_acl, which is
-      a list of access control rules that define update permissions.
-      By default this is empty; DDNS must be explicitely enabled per zone.
+      The zones option is a list of configuration items for specific
+      zones that can be updated with DDNS. Each entry is a map that
+      can contain the following items:
+      <varname>origin</varname> is a textual domain name of the zone;
+      <varname>class</varname> (text) is the RR class of the zone; and
+      <varname>update_acl</varname> is an ACL that controls
+      permission for updates.
+      See the BIND 10 Guide for configuration details.
+      Note that not listing a zone in this list does not directly
+      mean update requests for the zone are rejected, but the end
+      result is the same because the default ACL for updates is to
+      deny all requests.
     </para>
 
     <para>
@@ -145,6 +174,12 @@
         <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
+        <refentrytitle>b10-xfrout</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
         <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citetitle>BIND 10 Guide</citetitle>.
@@ -156,6 +191,7 @@
     <para>
       The <command>b10-ddns</command> daemon was first implemented
       in December 2011 for the ISC BIND 10 project.
+      The first functional version was released in June 2012.
     </para>
   </refsect1>
 </refentry><!--

+ 2 - 3
src/bin/ddns/ddns.py.in

@@ -29,7 +29,6 @@ from isc.config.module_spec import ModuleSpecError
 from isc.cc import SessionError, SessionTimeout, ProtocolError
 import isc.util.process
 import isc.util.cio.socketsession
-from isc.notify.notify_out import ZONE_NEW_DATA_READY_CMD
 import isc.server_common.tsig_keyring
 from isc.server_common.dns_tcp import DNSTCPContext
 from isc.datasrc import DataSourceClient
@@ -543,7 +542,7 @@ class DDNSServer:
     def __notify_xfrout(self, zname, zclass):
         '''Notify xfrout of the update.'''
         param = {'zone_name': zname.to_text(), 'zone_class': zclass.to_text()}
-        msg = create_command(ZONE_NEW_DATA_READY_CMD, param)
+        msg = create_command('notify', param)
         self.__notify_update(XFROUT_MODULE_NAME, msg, zname, zclass)
 
     def __notify_update(self, modname, msg, zname, zclass):
@@ -615,7 +614,7 @@ class DDNSServer:
         Get and process all commands sent from cfgmgr or other modules.
         This loops waiting for events until self.shutdown() has been called.
         '''
-        logger.info(DDNS_RUNNING)
+        logger.info(DDNS_STARTED)
         cc_fileno = self._cc.get_socket().fileno()
         listen_fileno = self._listen_socket.fileno()
         while not self._shutdown:

+ 32 - 17
src/bin/ddns/ddns_messages.mes

@@ -153,10 +153,6 @@ case, there may not be able to do anything to fix it at the server
 side, but the administrator may want to check the general reachability
 with the client address.
 
-% DDNS_RUNNING ddns server is running and listening for updates
-The ddns process has successfully started and is now ready to receive commands
-and updates.
-
 % DDNS_SECONDARY_ZONES_UPDATE updated secondary zone list (%1 zones are listed)
 b10-ddns has successfully updated the internal copy of secondary zones
 obtained from b10-zonemgr, based on a latest update to zonemgr's
@@ -192,6 +188,10 @@ The ddns process is shutting down. It will no longer listen for new commands
 or updates. Any command or update that is being addressed at this moment will
 be completed, after which the process will exit.
 
+% DDNS_STARTED ddns server is running and listening for updates
+The ddns process has successfully started and is now ready to receive commands
+and updates.
+
 % DDNS_STOPPED ddns server has stopped
 The ddns process has successfully stopped and is no longer listening for or
 handling commands or updates, and will now exit.
@@ -214,16 +214,31 @@ notify messages to secondary servers.
 
 % DDNS_UPDATE_NOTIFY_FAIL failed to notify %1 of updates to %2: %3
 b10-ddns has made updates to a zone based on an update request and
-tried to notify an external module of the updates, but the
-notification fails.  Severity of this effect depends on the type of
-the module.  If it's b10-xfrout, this means DNS notify messages won't
-be sent to secondary servers of the zone.  It's suboptimal, but not
-necessarily critical as the secondary servers will try to check the
-zone's status periodically.  If it's b10-auth and the notification was
-needed to have it reload the corresponding zone, it's more serious
-because b10-auth won't be able to serve the new version of the zone
-unless some explicit recovery action is taken.  So the administrator
-needs to examine this message and takes an appropriate action.  In
-either case, this notification is generally expected to succeed; so
-the fact it fails itself means there's something wrong in the BIND 10
-system, and it would be advisable to check other log messages.
+tried to notify an external component of the updates, but the
+notification fails.  One possible cause of this is that the external
+component is not really running and it times out in waiting for the
+response, although it will be less likely to happen in practice
+because these components will normally be configured to run when the
+server provides the authoritative DNS service; ddns is rather optional
+among them.  If this happens, however, it will suspend b10-ddns for a
+few seconds during which it cannot handle new requests (some may be
+delayed, some may be dropped, depending on the volume of the incoming
+requests).  This is obviously bad, and if this error happens due to
+this reason, the administrator should make sure the component in
+question should be configured to run.  For a longer term, b10-ddns
+should be more robust about this case such as by making this
+notification asynchronously and/or detecting the existence of the
+external components to avoid hopeless notification in the first place.
+Severity of this error for the receiving components depends on the
+type of the component.  If it's b10-xfrout, this means DNS notify
+messages won't be sent to secondary servers of the zone.  It's
+suboptimal, but not necessarily critical as the secondary servers will
+try to check the zone's status periodically.  If it's b10-auth and the
+notification was needed to have it reload the corresponding zone, it's
+more serious because b10-auth won't be able to serve the new version
+of the zone unless some explicit recovery action is taken.  So the
+administrator needs to examine this message and takes an appropriate
+action.  In either case, this notification is generally expected to
+succeed; so the fact it fails itself means there's something wrong in
+the BIND 10 system, and it would be advisable to check other log
+messages.

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

@@ -31,6 +31,7 @@ BUILT_SOURCES = spec_config.h
 pkglibexec_PROGRAMS = b10-dhcp4
 
 b10_dhcp4_SOURCES = main.cc dhcp4_srv.cc dhcp4_srv.h
+b10_dhcp4_SOURCES += ctrl_dhcp4_srv.cc ctrl_dhcp4_srv.h
 
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
@@ -42,6 +43,9 @@ b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/liblog.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+
 
 b10_dhcp4dir = $(pkgdatadir)
 b10_dhcp4_DATA = dhcp4.spec

+ 159 - 0
src/bin/dhcp4/ctrl_dhcp4_srv.cc

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

+ 123 - 0
src/bin/dhcp4/ctrl_dhcp4_srv.h

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

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

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

+ 10 - 2
src/bin/dhcp4/dhcp4_srv.cc

@@ -52,15 +52,23 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
 }
 
 Dhcpv4Srv::~Dhcpv4Srv() {
-    cout << "DHCPv4 server shutdown." << endl;
+    cout << "b10-dhcp4: DHCPv4 server terminating." << endl;
     IfaceMgr::instance().closeSockets();
 }
 
+void Dhcpv4Srv::shutdown() {
+    cout << "b10-dhcp4: DHCPv4 server shutdown." << endl;
+    shutdown_ = true;
+}
+
 bool
 Dhcpv4Srv::run() {
     while (!shutdown_) {
+        /// @todo: calculate actual timeout once we have lease database
+        int timeout = 1000;
+
         // client's message and server's response
-        Pkt4Ptr query = IfaceMgr::instance().receive4();
+        Pkt4Ptr query = IfaceMgr::instance().receive4(timeout);
         Pkt4Ptr rsp;
 
         if (query) {

+ 10 - 0
src/bin/dhcp4/dhcp4_srv.h

@@ -32,6 +32,13 @@ namespace dhcp {
 /// that is going to be used as server-identifier, receives incoming
 /// packets, processes them, manages leases assignment and generates
 /// appropriate responses.
+///
+/// This class does not support any controlling mechanisms directly.
+/// See derived \ref ControlledDhcv4Srv class for support for
+/// command and configuration updates over msgq.
+///
+/// For detailed explanation or relations between main(), ControlledDhcpv4Srv,
+/// Dhcpv4Srv and other classes, see \ref dhcpv4Session.
 class Dhcpv4Srv : public boost::noncopyable {
 
     public:
@@ -60,6 +67,9 @@ class Dhcpv4Srv : public boost::noncopyable {
     ///         critical error.
     bool run();
 
+    /// @brief Instructs the server to shut down.
+    void shutdown();
+
 protected:
     /// @brief Processes incoming DISCOVER and returns response.
     ///

+ 27 - 44
src/bin/dhcp4/main.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2009-2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -13,41 +13,31 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <config.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include <cassert>
 #include <iostream>
-
 #include <exceptions/exceptions.h>
-#if 0
-// TODO cc is not used yet. It should be eventually
-#include <cc/session.h>
-#include <config/ccsession.h>
-#endif
-
-#include <util/buffer.h>
 #include <log/dummylog.h>
-
-#include <dhcp4/spec_config.h>
+#include <log/logger_support.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4_srv.h>
 #include <dhcp/dhcp4.h>
 
 using namespace std;
-using namespace isc::util;
-
-using namespace isc;
 using namespace isc::dhcp;
 
+
+
+/// This file contains entry point (main() function) for standard DHCPv4 server
+/// component for BIND10 framework. It parses command-line arguments and
+/// instantiates ControlledDhcpv4Srv class that is responsible for establishing
+/// connection with msgq (receiving commands and configuration) and also
+/// creating Dhcpv4 server object as well.
+///
+/// For detailed explanation or relations between main(), ControlledDhcpv4Srv,
+/// Dhcpv4Srv and other classes, see \ref dhcpv4Session.
+
 namespace {
 
-bool verbose_mode = false;
+const char* const DHCP4_NAME = "b10-dhcp4";
 
 void
 usage() {
@@ -62,6 +52,7 @@ usage() {
 int
 main(int argc, char* argv[]) {
     int ch;
+    bool verbose_mode = false; // should server be verbose?
     int port_number = DHCP4_SERVER_PORT; // The default. any other values are
                                          // useful for testing only.
 
@@ -85,8 +76,13 @@ main(int argc, char* argv[]) {
         }
     }
 
-    cout << "My pid=" << getpid() << ", binding to port " << port_number
-         << ", verbose " << (verbose_mode?"yes":"no") << endl;
+    // Initialize logging.  If verbose, we'll use maximum verbosity.
+    isc::log::initLogger(DHCP4_NAME,
+                         (verbose_mode ? isc::log::DEBUG : isc::log::INFO),
+                         isc::log::MAX_DEBUG_LEVEL, NULL);
+
+    cout << "b10-dhcp4: My pid=" << getpid() << ", binding to port " 
+         << port_number << ", verbose " << (verbose_mode?"yes":"no") << endl;
 
     if (argc - optind > 0) {
         usage();
@@ -94,27 +90,14 @@ main(int argc, char* argv[]) {
 
     int ret = EXIT_SUCCESS;
 
-    // TODO remainder of auth to dhcp4 code copy. We need to enable this in
-    //      dhcp4 eventually
-#if 0
-    Session* cc_session = NULL;
-    Session* statistics_session = NULL;
-    ModuleCCSession* config_session = NULL;
-#endif
     try {
-        string specfile;
-        if (getenv("B10_FROM_BUILD")) {
-            specfile = string(getenv("B10_FROM_BUILD")) +
-                "/src/bin/auth/dhcp4.spec";
-        } else {
-            specfile = string(DHCP4_SPECFILE_LOCATION);
-        }
 
         cout << "[b10-dhcp4] Initiating DHCPv4 server operation." << endl;
 
-        Dhcpv4Srv* srv = new Dhcpv4Srv(port_number);
-
-        srv->run();
+        ControlledDhcpv4Srv* server = new ControlledDhcpv4Srv(port_number);
+        server->run();
+        delete server;
+        server = NULL;
 
     } catch (const std::exception& ex) {
         cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl;

+ 5 - 2
src/bin/dhcp4/tests/Makefile.am

@@ -39,16 +39,17 @@ AM_LDFLAGS = -static
 endif
 
 TESTS_ENVIRONMENT = \
-        libtool --mode=execute $(VALGRIND_COMMAND)
+        $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST
 
 TESTS += dhcp4_unittests
 
-dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc
+dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc ../ctrl_dhcp4_srv.cc
 dhcp4_unittests_SOURCES += dhcp4_unittests.cc
 dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
+dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc
 
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
@@ -64,6 +65,8 @@ dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 endif
 
 noinst_PROGRAMS = $(TESTS)

+ 85 - 0
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc

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

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

@@ -35,7 +35,7 @@ AM_LDFLAGS = -static
 endif
 
 TESTS_ENVIRONMENT = \
-        libtool --mode=execute $(VALGRIND_COMMAND)
+        $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

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

@@ -15,7 +15,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-        libtool --mode=execute $(VALGRIND_COMMAND)
+        $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

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

@@ -9,7 +9,7 @@ AM_LDFLAGS = -static
 endif
 
 TESTS_ENVIRONMENT = \
-        libtool --mode=execute $(VALGRIND_COMMAND)
+        $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

File diff suppressed because it is too large
+ 3 - 2
src/bin/stats/b10-stats-httpd.8


+ 8 - 6
src/bin/stats/b10-stats-httpd.xml

@@ -55,16 +55,16 @@
       process runs as a process separated from the process of the BIND 10 Stats
       daemon (<command>b10-stats</command>). The server is initially executed
       by the BIND 10 boss process (<command>bind10</command>) and eventually
-      exited by it.  The server is intended to be server requests by HTTP
+      exited by it.  The server is intended to serve requests by HTTP
       clients like web browsers and third-party modules. When the server is
       asked, it requests BIND 10 statistics data or its schema from
-      <command>b10-stats</command>, and it sends the data back in Python
-      dictionary format and the server converts it into XML format. The server
-      sends it to the HTTP client. The server can send three types of document,
+      <command>b10-stats</command> which sends the data back in Python
+      dictionary format, and the server converts it into XML format. The server
+      sends it to the HTTP client. The server can send three types of documents,
       which are XML (Extensible Markup Language), XSD (XML Schema definition)
       and XSL (Extensible Stylesheet Language). The XML document is the
-      statistics data of BIND 10, The XSD document is the data schema of it,
-      and The XSL document is the style sheet to be showed for the web
+      statistics data of BIND 10, the XSD document is the data schema of it,
+      and the XSL document is the style sheet to be showed for the web
       browsers. There is different URL for each document. But please note that
       you would be redirected to the URL of XML document if you request the URL
       of the root document. For example, you would be redirected to
@@ -82,6 +82,8 @@
     </para>
   </refsect1>
 
+<!-- TODO: this is too verbose; move some of this into the guide instead -->
+
   <refsect1>
     <title>OPTIONS</title>
     <para>The argument is as follow:</para>

+ 11 - 10
src/bin/stats/b10-stats.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-stats
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: March 1, 2012
+.\"      Date: June 20, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-STATS" "8" "March 1, 2012" "BIND10" "BIND10"
+.TH "B10\-STATS" "8" "June 20, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -27,18 +27,21 @@ b10-stats \- BIND 10 statistics module
 .PP
 The
 \fBb10\-stats\fR
-is a daemon forked by
-\fBbind10\fR\&. Stats module collects statistics data from each module and reports statistics information via
-\fBbindctl\fR\&. It communicates by using the Command Channel by
+daemon collects statistics data from each BIND 10 module\&. Its statistics information may be reported via
+\fBbindctl\fR
+or
+\fBb10\-stats\-httpd\fR\&. It is started by
+\fBbind10\fR
+and communicates by using the Command Channel by
 \fBb10\-msgq\fR
 with other modules like
 \fBbind10\fR,
 \fBb10\-auth\fR
-and so on\&. It waits for coming data from other modules, then other modules send data to stats module periodically\&. Other modules send stats data to stats module independently from implementation of stats module, so the frequency of sending data may not be constant\&. Stats module collects data and aggregates it\&.
+and so on\&. It waits for coming data from other modules, then other modules send data to stats module periodically\&. Other modules send stats data to stats module independently from implementation of stats module, so the frequency of sending data may not be constant\&. The stats module collects data and aggregates it\&.
 \fBb10\-stats\fR
 invokes an internal command for
 \fBbind10\fR
-after its initial starting because it\'s sure to collect statistics data from
+after its initial starting to make sure it collects statistics data from
 \fBbind10\fR\&.
 .SH "OPTIONS"
 .PP
@@ -46,9 +49,7 @@ The arguments are as follows:
 .PP
 \fB\-v\fR, \fB\-\-verbose\fR
 .RS 4
-This
-\fBb10\-stats\fR
-switches to verbose mode\&. It sends verbose messages to STDOUT\&.
+This enables maximum debug logging\&.
 .RE
 .SH "CONFIGURATION AND COMMANDS"
 .PP

+ 17 - 18
src/bin/stats/b10-stats.xml

@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>March 1, 2012</date>
+    <date>June 20, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -52,22 +52,22 @@
   <refsect1>
     <title>DESCRIPTION</title>
     <para>
-      The <command>b10-stats</command> is a daemon forked by
-      <command>bind10</command>. Stats module collects statistics data
-      from each module and reports statistics information
-      via <command>bindctl</command>.  It communicates by using the
+      The <command>b10-stats</command> daemon collects statistics data
+      from each BIND 10 module. Its statistics information may be
+      reported via <command>bindctl</command> or
+      <command>b10-stats-httpd</command>.  It is started by
+      <command>bind10</command> and communicates by using the
       Command Channel by <command>b10-msgq</command> with other
-      modules
-      like <command>bind10</command>, <command>b10-auth</command> and
-      so on. It waits for coming data from other modules, then other
-      modules send data to stats module periodically. Other modules
-      send stats data to stats module independently from
-      implementation of stats module, so the frequency of sending data
-      may not be constant. Stats module collects data and aggregates
-      it. <command>b10-stats</command> invokes an internal command
-      for <command>bind10</command> after its initial starting because it's
-      sure to collect statistics data from <command>bind10</command>.
-<!-- TODO: reword that last sentence? -->
+      modules like <command>bind10</command>, <command>b10-auth</command>
+      and so on. It waits for coming data from other modules, then
+      other modules send data to stats module periodically. Other
+      modules send stats data to stats module independently from
+      implementation of stats module, so the frequency of sending
+      data may not be constant. The stats module collects data and
+      aggregates it. <command>b10-stats</command> invokes an internal
+      command for <command>bind10</command> after its initial
+      starting to make sure it collects statistics data from
+      <command>bind10</command>.
     </para>
   </refsect1>
 
@@ -79,8 +79,7 @@
         <term><option>-v</option>, <option>--verbose</option></term>
         <listitem>
 	  <para>
-          This <command>b10-stats</command> switches to verbose
-          mode. It sends verbose messages to STDOUT.
+          This enables maximum debug logging.
 	  </para>
         </listitem>
       </varlistentry>

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

@@ -495,7 +495,7 @@ if __name__ == "__main__":
         parser = OptionParser()
         parser.add_option(
             "-v", "--verbose", dest="verbose", action="store_true",
-            help="display more about what is going on")
+            help="enable maximum debug logging")
         (options, args) = parser.parse_args()
         if options.verbose:
             isc.log.init("b10-stats", "DEBUG", 99)

+ 1 - 1
src/bin/stats/stats_httpd.py.in

@@ -826,7 +826,7 @@ if __name__ == "__main__":
         parser = OptionParser()
         parser.add_option(
             "-v", "--verbose", dest="verbose", action="store_true",
-            help="display more about what is going on")
+            help="enable maximum debug logging")
         (options, args) = parser.parse_args()
         if options.verbose:
             isc.log.init("b10-stats-httpd", "DEBUG", 99)

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

@@ -71,7 +71,6 @@ AUTH_MODULE_NAME = 'Auth'
 XFROUT_MODULE_NAME = 'Xfrout'
 ZONE_MANAGER_MODULE_NAME = 'Zonemgr'
 REFRESH_FROM_ZONEMGR = 'refresh_from_zonemgr'
-ZONE_XFRIN_FAILED = 'zone_xfrin_failed'
 
 # Constants for debug levels.
 DBG_XFRIN_TRACE = logger.DBGLVL_TRACE_BASIC
@@ -1580,7 +1579,7 @@ class Xfrin:
                 logger.error(XFRIN_MSGQ_SEND_ERROR, XFROUT_MODULE_NAME, ZONE_MANAGER_MODULE_NAME)
 
         else:
-            msg = create_command(ZONE_XFRIN_FAILED, param)
+            msg = create_command(notify_out.ZONE_XFRIN_FAILED, param)
             # catch the exception, in case msgq has been killed.
             try:
                 seq = self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)

+ 66 - 17
src/bin/xfrout/tests/xfrout_test.py.in

@@ -60,6 +60,9 @@ class MySocket():
         self.sendqueue.extend(data);
         return len(data)
 
+    def fileno(self):
+        return 42               # simply return a constant dummy value
+
     def readsent(self):
         if len(self.sendqueue) >= 2:
             size = 2 + struct.unpack("!H", self.sendqueue[:2])[0]
@@ -1155,6 +1158,15 @@ class TestUnixSockServer(unittest.TestCase):
     def setUp(self):
         self.write_sock, self.read_sock = socket.socketpair()
         self.unix = MyUnixSockServer()
+        # Some test below modify these module-wide attributes.  We'll need
+        # to restore them at the end of each test, so we remember them here.
+        self.__select_bak = xfrout.select.select
+        self.__recv_fd_back = xfrout.recv_fd
+
+    def tearDown(self):
+        # Restore possibly faked module-wide attributes.
+        xfrout.select.select = self.__select_bak
+        xfrout.recv_fd = self.__recv_fd_back
 
     def test_tsig_keyring(self):
         """
@@ -1196,28 +1208,29 @@ class TestUnixSockServer(unittest.TestCase):
         # We test with UDP, as it can be "connected" without other
         # endpoint.  Note that in the current implementation _guess_remote()
         # unconditionally returns SOCK_STREAM.
-        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
-            sock.connect(('127.0.0.1', 12345))
-            self.assertEqual((socket.AF_INET, socket.SOCK_STREAM,
-                              ('127.0.0.1', 12345)),
-                             self.unix._guess_remote(sock.fileno()))
-
+        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        sock.connect(('127.0.0.1', 12345))
+        self.assertEqual((socket.AF_INET, socket.SOCK_STREAM,
+                          ('127.0.0.1', 12345)),
+                         self.unix._guess_remote(sock.fileno()))
+        sock.close()
         if socket.has_ipv6:
             # Don't check IPv6 address on hosts not supporting them
-            with socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) as sock:
-                sock.connect(('::1', 12345))
-                self.assertEqual((socket.AF_INET6, socket.SOCK_STREAM,
-                                  ('::1', 12345, 0, 0)),
-                                 self.unix._guess_remote(sock.fileno()))
-
+            sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+            sock.connect(('::1', 12345))
+            self.assertEqual((socket.AF_INET6, socket.SOCK_STREAM,
+                              ('::1', 12345, 0, 0)),
+                             self.unix._guess_remote(sock.fileno()))
+            sock.close()
             # Try when pretending there's no IPv6 support
             # (No need to pretend when there's really no IPv6)
             xfrout.socket.has_ipv6 = False
-            with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
-                sock.connect(('127.0.0.1', 12345))
-                self.assertEqual((socket.AF_INET, socket.SOCK_STREAM,
-                                  ('127.0.0.1', 12345)),
-                                 self.unix._guess_remote(sock.fileno()))
+            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+            sock.connect(('127.0.0.1', 12345))
+            self.assertEqual((socket.AF_INET, socket.SOCK_STREAM,
+                              ('127.0.0.1', 12345)),
+                             self.unix._guess_remote(sock.fileno()))
+            sock.close()
             # Return it back
             xfrout.socket.has_ipv6 = True
 
@@ -1409,6 +1422,42 @@ class TestUnixSockServer(unittest.TestCase):
         sys.stdout = old_stdout
         os.rmdir(dir_name)
 
+    def __fake_select(self, r, w, e):
+        '''select emulator used in select_loop_fail test.'''
+        # This simplified faked function assumes to be called at most once,
+        # and in that case just return a pre-configured "readable" sockets.
+        if self.__select_count > 0:
+            raise RuntimeError('select called unexpected number of times')
+        self.__select_count += 1
+        return (self.__select_return_redable, [], [])
+
+    def test_select_loop_fail(self):
+        '''Check failure events in the main loop.'''
+        # setup faked select() environments
+        self.unix._read_sock = MySocket(socket.AF_INET6, socket.SOCK_STREAM)
+        xfrout.select.select = self.__fake_select
+        self.__select_return_redable = [MySocket(socket.AF_INET6,
+                                                 socket.SOCK_STREAM)]
+
+        # Check that loop terminates if recv_fd() fails.
+        for ret_code in [-1, FD_SYSTEM_ERROR]:
+            # fake recv_fd so it returns the faked failure code.
+            xfrout.recv_fd = lambda fileno: ret_code
+
+            # reset the counter, go to the loop.
+            self.__select_count = 0
+            self.unix._select_loop(self.__select_return_redable[0])
+            # select should have been called exactly once.
+            self.assertEqual(1, self.__select_count)
+
+        # Next, we test the case where recf_fd succeeds but receiving the
+        # request fails.
+        self.__select_count = 0
+        xfrout.recv_fd = lambda fileno: 1
+        self.unix._receive_query_message = lambda fd: None
+        self.unix._select_loop(self.__select_return_redable[0])
+        self.assertEqual(1, self.__select_count)
+
 class TestInitialization(unittest.TestCase):
     def setEnv(self, name, value):
         if value is None:

+ 42 - 22
src/bin/xfrout/xfrout.py.in

@@ -678,30 +678,40 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
         except socket.error:
             logger.error(XFROUT_FETCH_REQUEST_ERROR)
             return
+        self._select_loop(request)
+
+    def _select_loop(self, request_sock):
+        '''Main loop for a single session between xfrout and auth.
+
+        This is a dedicated subroutine of handle_request(), but is defined
+        as a separate "protected" method for the convenience of tests.
+        '''
 
         # Check self._shutdown_event to ensure the real shutdown comes.
         # Linux could trigger a spurious readable event on the _read_sock
         # due to a bug, so we need perform a double check.
         while not self._shutdown_event.is_set(): # Check if xfrout is shutdown
             try:
-                (rlist, wlist, xlist) = select.select([self._read_sock, request], [], [])
+                (rlist, wlist, xlist) = select.select([self._read_sock,
+                                                       request_sock], [], [])
             except select.error as e:
                 if e.args[0] == errno.EINTR:
                     (rlist, wlist, xlist) = ([], [], [])
                     continue
                 else:
-                    logger.error(XFROUT_SOCKET_SELECT_ERROR, str(e))
+                    logger.error(XFROUT_SOCKET_SELECT_ERROR, e)
                     break
 
-            # self.server._shutdown_event will be set by now, if it is not a false
-            # alarm
+            # self.server._shutdown_event will be set by now, if it is not a
+            # false alarm
             if self._read_sock in rlist:
                 continue
 
             try:
-                self.process_request(request)
+                if not self.process_request(request_sock):
+                    break
             except Exception as pre:
-                logger.error(XFROUT_PROCESS_REQUEST_ERROR, str(pre))
+                logger.error(XFROUT_PROCESS_REQUEST_ERROR, pre)
                 break
 
     def _handle_request_noblock(self):
@@ -713,26 +723,33 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
 
     def process_request(self, request):
         """Receive socket fd and query message from auth, then
-        start a new thread to process the request."""
+        start a new thread to process the request.
+
+        Return: True if everything is okay; otherwise False, in which case
+        the calling thread will terminate.
+
+        """
         sock_fd = recv_fd(request.fileno())
         if sock_fd < 0:
-            # This may happen when one xfrout process try to connect to
-            # xfrout unix socket server, to check whether there is another
-            # xfrout running.
-            if sock_fd == FD_SYSTEM_ERROR:
-                logger.error(XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR)
-            return
+            logger.warn(XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR)
+            return False
 
-        # receive request msg
+        # receive request msg.  If it fails we simply terminate the thread;
+        # it might be possible to recover from this state, but it's more likely
+        # that auth and xfrout are in inconsistent states.  So it will make
+        # more sense to restart in a new session.
         request_data = self._receive_query_message(request)
-        if not request_data:
-            return
+        if request_data is None:
+            # The specific exception type doesn't matter so we use session
+            # error.
+            raise XfroutSessionError('Failed to get complete xfr request')
 
         t = threading.Thread(target=self.finish_request,
-                             args = (sock_fd, request_data))
+                             args=(sock_fd, request_data))
         if self.daemon_threads:
             t.daemon = True
         t.start()
+        return True
 
     def _guess_remote(self, sock_fd):
         """Guess remote address and port of the socket.
@@ -753,8 +770,9 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
             # (Any idea how to simulate this in test?)
             sock_domain = socket.AF_INET
 
-        with socket.fromfd(sock_fd, sock_domain, socket.SOCK_STREAM) as sock:
-            peer = sock.getpeername()
+        sock = socket.fromfd(sock_fd, sock_domain, socket.SOCK_STREAM)
+        peer = sock.getpeername()
+        sock.close()
 
         # Identify the correct socket family.  Due to the above "trick",
         # we cannot simply use sock.family.
@@ -805,11 +823,13 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
         is being used by one running xfrout process. If it is,
         return True, or else return False. '''
         try:
-            with socket.socket(socket.AF_UNIX) as sock:
-                sock.connect(sock_file)
+            sock = socket.socket(socket.AF_UNIX)
+            sock.connect(sock_file)
         except socket.error as err:
+            sock.close()
             return False
         else:
+            sock.close()
             return True
 
     def shutdown(self):
@@ -988,7 +1008,7 @@ class XfroutServer:
             self.shutdown()
             answer = create_answer(0)
 
-        elif cmd == notify_out.ZONE_NEW_DATA_READY_CMD:
+        elif cmd == "notify":
             zone_name = args.get('zone_name')
             zone_class = args.get('zone_class')
             if not zone_class:

+ 19 - 8
src/bin/xfrout/xfrout_messages.mes

@@ -115,11 +115,15 @@ In general, this should only occur for unexpected problems like
 memory allocation failures, as the query should already have been
 parsed by the b10-auth daemon, before it was passed here.
 
-% XFROUT_PROCESS_REQUEST_ERROR error processing transfer request: %2
-There was an error processing a transfer request. The error is included
-in the log message, but at this point no specific information other
-than that could be given. This points to incomplete exception handling
-in the code.
+% XFROUT_PROCESS_REQUEST_ERROR error processing transfer request: %1
+There was an error in receiving a transfer request from b10-auth.
+This is generally an unexpected event, but is possible when, for
+example, b10-auth terminates in the middle of forwarding the request.
+When this happens it's unlikely to be recoverable with the same
+communication session with b10-auth, so b10-xfrout drops it and
+waits for a new session.  In any case, this error indicates that
+there's something very wrong in the system, so it's advisable to check
+the over all status of the BIND 10 system.
 
 % XFROUT_QUERY_DROPPED %1 client %2: request to transfer %3 dropped
 The xfrout process silently dropped a request to transfer zone to
@@ -149,9 +153,16 @@ and will now shut down.
 
 % XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR error receiving the file descriptor for an XFR connection
 There was an error receiving the file descriptor for the transfer
-request. Normally, the request is received by b10-auth, and passed on
-to the xfrout daemon, so it can answer directly. However, there was a
-problem receiving this file descriptor. The request will be ignored.
+request from b10-auth.  There can be several reasons for this, but
+the most likely cause is that b10-auth terminates for some reason
+(maybe it's a bug of b10-auth, maybe it's an intentional restart by
+the administrator), so depending on how this happens it may or may not
+be a serious error.  But in any case this is not expected to happen
+frequently, and it's advisable to figure out how this happened if
+this message is logged.  Even if this error happens xfrout will reset
+its internal state and will keep receiving further requests.  So
+if it's just a temporary restart of b10-auth the administrator does
+not have to do anything.
 
 % XFROUT_REMOVE_OLD_UNIX_SOCKET_FILE_ERROR error removing unix socket file %1: %2
 The unix socket file xfrout needs for contact with the auth daemon

+ 2 - 1
src/bin/zonemgr/tests/zonemgr_test.py

@@ -21,6 +21,7 @@ import os
 import tempfile
 from zonemgr import *
 from isc.testutils.ccsession_mock import MockModuleCCSession
+from isc.notify import notify_out
 
 ZONE_NAME_CLASS1_IN = ("example.net.", "IN")
 ZONE_NAME_CLASS1_CH = ("example.net.", "CH")
@@ -684,7 +685,7 @@ class TestZonemgr(unittest.TestCase):
         self.assertEqual(answer1, self.zonemgr._parse_cmd_params(params1, ZONE_NOTIFY_COMMAND))
         params2 = {"zone_name" : "example.com.", "zone_class" : "IN"}
         answer2 = ZONE_NAME_CLASS3_IN
-        self.assertEqual(answer2, self.zonemgr._parse_cmd_params(params2, ZONE_XFRIN_SUCCESS_COMMAND))
+        self.assertEqual(answer2, self.zonemgr._parse_cmd_params(params2, notify_out.ZONE_NEW_DATA_READY_CMD))
         self.assertRaises(ZonemgrException, self.zonemgr._parse_cmd_params, params2, ZONE_NOTIFY_COMMAND)
         params1 = {"zone_class" : "CH"}
         self.assertRaises(ZonemgrException, self.zonemgr._parse_cmd_params, params2, ZONE_NOTIFY_COMMAND)

+ 4 - 5
src/bin/zonemgr/zonemgr.py.in

@@ -39,6 +39,7 @@ from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
 import isc.util.process
 from isc.log_messages.zonemgr_messages import *
+from isc.notify import notify_out
 
 # Initialize logging for called modules.
 isc.log.init("b10-zonemgr")
@@ -78,8 +79,6 @@ XFRIN_MODULE_NAME = 'Xfrin'
 AUTH_MODULE_NAME = 'Auth'
 
 # define command name
-ZONE_XFRIN_FAILED_COMMAND = 'zone_xfrin_failed'
-ZONE_XFRIN_SUCCESS_COMMAND = 'zone_new_data_ready'
 ZONE_REFRESH_COMMAND = 'refresh_from_zonemgr'
 ZONE_NOTIFY_COMMAND = 'notify'
 
@@ -623,7 +622,7 @@ class Zonemgr:
     def command_handler(self, command, args):
         """Handle command receivd from command channel.
         ZONE_NOTIFY_COMMAND is issued by Auth process;
-        ZONE_XFRIN_SUCCESS_COMMAND and ZONE_XFRIN_FAILED_COMMAND are issued by
+        ZONE_NEW_DATA_READY_CMD and ZONE_XFRIN_FAILED are issued by
         Xfrin process;
         shutdown is issued by a user or Boss process. """
         answer = create_answer(0)
@@ -637,7 +636,7 @@ class Zonemgr:
             # Send notification to zonemgr timer thread
             self._master_socket.send(b" ")# make self._slave_socket readble
 
-        elif command == ZONE_XFRIN_SUCCESS_COMMAND:
+        elif command == notify_out.ZONE_NEW_DATA_READY_CMD:
             """ Handle xfrin success command"""
             zone_name_class = self._parse_cmd_params(args, command)
             logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_XFRIN_SUCCESS, zone_name_class[0], zone_name_class[1])
@@ -645,7 +644,7 @@ class Zonemgr:
                 self._zone_refresh.zone_refresh_success(zone_name_class)
             self._master_socket.send(b" ")# make self._slave_socket readble
 
-        elif command == ZONE_XFRIN_FAILED_COMMAND:
+        elif command == notify_out.ZONE_XFRIN_FAILED:
             """ Handle xfrin fail command"""
             zone_name_class = self._parse_cmd_params(args, command)
             logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_XFRIN_FAILED, zone_name_class[0], zone_name_class[1])

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

@@ -9,7 +9,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

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

@@ -13,7 +13,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

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

@@ -19,7 +19,7 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

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

@@ -6,7 +6,7 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

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

@@ -29,7 +29,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

+ 16 - 0
src/lib/cc/session.cc

@@ -88,6 +88,7 @@ public:
     void startRead(boost::function<void()> user_handler);
     void setTimeout(size_t seconds) { timeout_ = seconds; };
     size_t getTimeout() const { return timeout_; };
+    int getSocketDesc();
 
     long int sequence_; // the next sequence number to use
     std::string lname_;
@@ -254,6 +255,16 @@ SessionImpl::internalRead(const asio::error_code& error,
     }
 }
 
+int
+SessionImpl::getSocketDesc() {
+    /// @todo boost 1.42 uses native() method, but it is deprecated
+    /// in 1.49 and native_handle() is recommended instead
+    if (!socket_.is_open()) {
+        isc_throw(InvalidOperation, "Can't return socket desciptor: no socket opened.");
+    }
+    return socket_.native();
+}
+
 Session::Session(asio::io_service& io_service) :
     impl_(new SessionImpl(io_service))
 {}
@@ -273,6 +284,11 @@ Session::startRead(boost::function<void()> read_callback) {
     impl_->startRead(read_callback);
 }
 
+int
+Session::getSocketDesc() const {
+    return impl_->getSocketDesc();
+}
+
 namespace {                     // maybe unnecessary.
 // This is a helper class to make the establish() method (below) exception-safe
 // with the RAII approach.

+ 5 - 0
src/lib/cc/session.h

@@ -141,6 +141,11 @@ namespace isc {
             virtual bool hasQueuedMsgs() const;
             virtual void setTimeout(size_t milliseconds);
             virtual size_t getTimeout() const;
+
+            /// @brief returns socket descriptor from underlying socket connection
+            ///
+            /// @param returns socket descriptor used for session connection
+            virtual int getSocketDesc() const;
     private:
             void sendmsg(isc::data::ConstElementPtr msg);
             void sendmsg(isc::data::ConstElementPtr env,

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

@@ -17,7 +17,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

+ 13 - 0
src/lib/cc/tests/session_unittests.cc

@@ -34,6 +34,9 @@ TEST(AsioSession, establish) {
     asio::io_service io_service_;
     Session sess(io_service_);
 
+    // can't return socket desciptor before session is established
+    EXPECT_THROW(sess.getSocketDesc(), isc::InvalidOperation);
+
     EXPECT_THROW(
         sess.establish("/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
                        "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
@@ -235,4 +238,14 @@ TEST_F(SessionTest, run_with_handler_timeout) {
     ASSERT_THROW(my_io_service.run(), SessionTimeout);
 }
 
+TEST_F(SessionTest, get_socket_descr) {
+    tds->setSendLname();
+    sess.establish(BIND10_TEST_SOCKET_FILE);
 
+    int socket = 0;
+    // session is established, so getSocketDesc() should work
+    EXPECT_NO_THROW(socket = sess.getSocketDesc());
+
+    // expect actual socket handle to be returned, not 0
+    EXPECT_LT(0, socket);
+}

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

@@ -15,7 +15,7 @@ noinst_LTLIBRARIES = libfake_session.la
 libfake_session_la_SOURCES = fake_session.h fake_session.cc
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

+ 1 - 1
src/lib/cryptolink/tests/Makefile.am

@@ -11,7 +11,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

+ 1 - 0
src/lib/datasrc/.gitignore

@@ -2,3 +2,4 @@
 /datasrc_messages.h
 /datasrc_config.h
 /datasrc_config.h.pre
+/static.zone

+ 3 - 4
src/lib/datasrc/Makefile.am

@@ -36,6 +36,7 @@ libdatasrc_la_SOURCES += client.h iterator.h
 libdatasrc_la_SOURCES += database.h database.cc
 libdatasrc_la_SOURCES += factory.h factory.cc
 libdatasrc_la_SOURCES += client_list.h client_list.cc
+libdatasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc
 nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
 libdatasrc_la_LDFLAGS = -no-undefined -version-info 1:0:1
 
@@ -49,14 +50,12 @@ sqlite3_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 sqlite3_ds_la_LIBADD += libdatasrc.la
 sqlite3_ds_la_LIBADD += $(SQLITE_LIBS)
 
-memory_ds_la_SOURCES = memory_datasrc.h memory_datasrc.cc
-memory_ds_la_SOURCES += memory_datasrc_link.cc
+memory_ds_la_SOURCES = memory_datasrc_link.cc
 memory_ds_la_LDFLAGS = -module -avoid-version
 memory_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 memory_ds_la_LIBADD += libdatasrc.la
 
-static_ds_la_SOURCES = memory_datasrc.h memory_datasrc.cc
-static_ds_la_SOURCES += static_datasrc_link.cc
+static_ds_la_SOURCES = static_datasrc_link.cc
 static_ds_la_LDFLAGS = -module -avoid-version
 static_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 static_ds_la_LIBADD += libdatasrc.la

+ 112 - 18
src/lib/datasrc/client_list.cc

@@ -15,22 +15,45 @@
 #include "client_list.h"
 #include "client.h"
 #include "factory.h"
+#include "memory_datasrc.h"
 
 #include <memory>
 #include <boost/foreach.hpp>
 
 using namespace isc::data;
+using namespace isc::dns;
 using namespace std;
+using namespace boost;
 
 namespace isc {
 namespace datasrc {
 
+ConfigurableClientList::DataSourceInfo::DataSourceInfo(
+    DataSourceClient* data_src_client,
+    const DataSourceClientContainerPtr& container, bool has_cache) :
+    data_src_client_(data_src_client),
+    container_(container)
+{
+    if (has_cache) {
+        cache_.reset(new InMemoryClient);
+    }
+}
+
+ConfigurableClientList::DataSourceInfo::DataSourceInfo(bool has_cache) :
+    data_src_client_(NULL)
+{
+    if (has_cache) {
+        cache_.reset(new InMemoryClient);
+    }
+}
+
 void
-ConfigurableClientList::configure(const ConstElementPtr& config, bool) {
+ConfigurableClientList::configure(const ConstElementPtr& config,
+                                  bool allow_cache)
+{
     if (!config) {
         isc_throw(isc::BadValue, "NULL configuration passed");
     }
-    // TODO: Implement the cache
     // TODO: Implement recycling from the old configuration.
     size_t i(0); // Outside of the try to be able to access it in the catch
     try {
@@ -48,12 +71,86 @@ ConfigurableClientList::configure(const ConstElementPtr& config, bool) {
             if (paramConf == ConstElementPtr()) {
                 paramConf.reset(new NullElement());
             }
-            // TODO: Special-case the master files type.
-            // Ask the factory to create the data source for us
-            const DataSourcePair ds(this->getDataSourceClient(type,
-                                                              paramConf));
-            // And put it into the vector
-            new_data_sources.push_back(DataSourceInfo(ds.first, ds.second));
+            const bool want_cache(allow_cache &&
+                                  dconf->contains("cache-enable") &&
+                                  dconf->get("cache-enable")->boolValue());
+
+            if (type == "MasterFiles") {
+                // In case the cache is not allowed, we just skip the master
+                // files (at least for now)
+                if (!allow_cache) {
+                    continue;
+                }
+                if (!want_cache) {
+                    isc_throw(ConfigurationError, "The cache must be enabled "
+                              "for the MasterFiles type");
+                }
+                new_data_sources.push_back(DataSourceInfo(true));
+            } else {
+                // Ask the factory to create the data source for us
+                const DataSourcePair ds(this->getDataSourceClient(type,
+                                                                  paramConf));
+                // And put it into the vector
+                new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
+                                                          want_cache));
+            }
+
+            if (want_cache) {
+                if (!dconf->contains("cache-zones") && type != "MasterFiles") {
+                    isc_throw(isc::NotImplemented, "Auto-detection of zones "
+                              "to cache is not yet implemented, supply "
+                              "cache-zones parameter");
+                    // TODO: Auto-detect list of all zones in the
+                    // data source.
+                }
+
+                // List the zones we are loading
+                vector<string> zones_origins;
+                if (type == "MasterFiles") {
+                    const map<string, ConstElementPtr>
+                        zones_files(paramConf->mapValue());
+                    for (map<string, ConstElementPtr>::const_iterator
+                         it(zones_files.begin()); it != zones_files.end();
+                         ++it) {
+                        zones_origins.push_back(it->first);
+                    }
+                } else {
+                    const ConstElementPtr zones(dconf->get("cache-zones"));
+                    for (size_t i(0); i < zones->size(); ++i) {
+                        zones_origins.push_back(zones->get(i)->stringValue());
+                    }
+                }
+
+                const shared_ptr<InMemoryClient>
+                    cache(new_data_sources.back().cache_);
+                const DataSourceClient* const
+                    client(new_data_sources.back().data_src_client_);
+                for (vector<string>::const_iterator it(zones_origins.begin());
+                     it != zones_origins.end(); ++it) {
+                    const Name origin(*it);
+                    shared_ptr<InMemoryZoneFinder>
+                        finder(new
+                            InMemoryZoneFinder(rrclass_, origin));
+                    if (type == "MasterFiles") {
+                        finder->load(paramConf->get(*it)->stringValue());
+                    } else {
+                        ZoneIteratorPtr iterator;
+                        try {
+                            iterator = client->getIterator(origin);
+                        }
+                        catch (const DataSourceError&) {
+                            isc_throw(ConfigurationError, "Unable to cache "
+                                      "non-existent zone " << origin);
+                        }
+                        if (!iterator) {
+                            isc_throw(isc::Unexpected, "Got NULL iterator for "
+                                      "zone " << origin);
+                        }
+                        finder->load(*iterator);
+                    }
+                    cache->addZone(finder);
+                }
+            }
         }
         // If everything is OK up until now, we have the new configuration
         // ready. So just put it there and let the old one die when we exit
@@ -92,14 +189,11 @@ ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
     } candidate;
 
     BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
-        // TODO: Once we have support for the caches, consider them too here
-        // somehow. This would probably get replaced by a function, that
-        // checks if there's a cache available, if it is, checks the loaded
-        // zones and zones expected to be in the real data source. If it is
-        // the cached one, provide the cached one. If it is in the external
-        // data source, use the datasource and don't provide the finder yet.
-        const DataSourceClient::FindResult result(
-            info.data_src_client_->findZone(name));
+        DataSourceClient* client(info.cache_ ? info.cache_.get() :
+                                 info.data_src_client_);
+        const DataSourceClient::FindResult result(client->findZone(name));
+        // TODO: Once we mark the zones that are not loaded, but are present
+        // in the data source somehow, check them too.
         switch (result.code) {
             case result::SUCCESS:
                 // If we found an exact match, we have no hope to getting
@@ -107,7 +201,7 @@ ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
 
                 // TODO: In case we have only the datasource and not the finder
                 // and the need_updater parameter is true, get the zone there.
-                return (FindResult(info.data_src_client_, result.zone_finder,
+                return (FindResult(client, result.zone_finder,
                                    true));
             case result::PARTIALMATCH:
                 if (!want_exact_match) {
@@ -128,7 +222,7 @@ ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
                     if (labels > candidate.matched_labels ||
                         !candidate.matched) {
                         // This one is strictly better. Replace it.
-                        candidate.datasrc_client = info.data_src_client_;
+                        candidate.datasrc_client = client;
                         candidate.finder = result.zone_finder;
                         candidate.matched_labels = labels;
                         candidate.matched = true;

+ 21 - 15
src/lib/datasrc/client_list.h

@@ -16,6 +16,7 @@
 #define DATASRC_CONTAINER_H
 
 #include <dns/name.h>
+#include <dns/rrclass.h>
 #include <cc/data.h>
 #include <exceptions/exceptions.h>
 
@@ -33,6 +34,7 @@ typedef boost::shared_ptr<DataSourceClient> DataSourceClientPtr;
 class DataSourceClientContainer;
 typedef boost::shared_ptr<DataSourceClientContainer>
     DataSourceClientContainerPtr;
+class InMemoryClient;
 
 /// \brief The list of data source clients.
 ///
@@ -185,7 +187,11 @@ typedef boost::shared_ptr<const ClientList> ConstClientListPtr;
 /// inherited except for tests.
 class ConfigurableClientList : public ClientList {
 public:
-    ConfigurableClientList() :
+    /// \brief Constructor
+    ///
+    /// \param rrclass For which class the list should work.
+    ConfigurableClientList(const isc::dns::RRClass &rrclass) :
+        rrclass_(rrclass),
         configuration_(new isc::data::ListElement)
     {}
     /// \brief Exception thrown when there's an error in configuration.
@@ -216,6 +222,11 @@ public:
     /// \throw ConfigurationError if the configuration is invalid in some
     ///     sense.
     /// \throw BadValue if configuration is NULL
+    /// \throw Unexpected if something misbehaves (like the data source
+    ///     returning NULL iterator).
+    /// \throw NotImplemented if the auto-detection of list of zones is
+    ///     needed.
+    /// \throw Whatever is propagated from within the data source.
     void configure(const isc::data::ConstElementPtr& configuration,
                    bool allow_cache);
 
@@ -236,20 +247,14 @@ public:
     ///
     /// \todo The content yet to be defined.
     struct DataSourceInfo {
-        /// \brief Default constructor.
-        ///
-        /// Don't use directly. It is here so the structure can live in
-        /// a vector.
-        DataSourceInfo() :
-            data_src_client_(NULL)
-        {}
+        // Plays a role of default constructor too (for vector)
+        DataSourceInfo(bool has_cache = false);
         DataSourceInfo(DataSourceClient* data_src_client,
-                       const DataSourceClientContainerPtr& container) :
-            data_src_client_(data_src_client),
-            container_(container)
-        {}
+                       const DataSourceClientContainerPtr& container,
+                       bool has_cache);
         DataSourceClient* data_src_client_;
         DataSourceClientContainerPtr container_;
+        boost::shared_ptr<InMemoryClient> cache_;
     };
 
     /// \brief The collection of data sources.
@@ -286,9 +291,6 @@ protected:
     virtual DataSourcePair getDataSourceClient(const std::string& type,
                                                const data::ConstElementPtr&
                                                configuration);
-private:
-    /// \brief Currently active configuration.
-    isc::data::ConstElementPtr configuration_;
 public:
     /// \brief Access to the data source clients.
     ///
@@ -297,6 +299,10 @@ public:
     /// it might be, so it is just made public (there's no real reason to
     /// hide it).
     const DataSources& getDataSources() const { return (data_sources_); }
+private:
+    const isc::dns::RRClass rrclass_;
+    /// \brief Currently active configuration.
+    isc::data::ConstElementPtr configuration_;
 };
 
 } // namespace datasrc

+ 3 - 3
src/lib/datasrc/database.cc

@@ -1126,9 +1126,9 @@ DatabaseClient::Finder::findPreviousName(const Name& name) const {
                                                  name.reverse().toText()));
     try {
         return (Name(str));
-    }
-    catch (const isc::dns::NameParserException&) {
-        isc_throw(DataSourceError, "Bad name " + str + " from findPreviousName");
+    } catch (const isc::dns::NameParserException&) {
+        isc_throw(DataSourceError, "Bad name " + str +
+                  " from findPreviousName");
     }
 }
 

+ 7 - 6
src/lib/datasrc/database.h

@@ -743,8 +743,9 @@ public:
 
     /// \brief It returns the previous name in DNSSEC order.
     ///
-    /// This is used in DatabaseClient::findPreviousName and does more
-    /// or less the real work, except for working on strings.
+    /// Gets the previous name in the DNSSEC order. This can be used
+    /// to find the correct NSEC records for proving nonexistence
+    /// of domains.
     ///
     /// \param rname The name to ask for previous of, in reversed form.
     ///     We use the reversed form (see isc::dns::Name::reverse),
@@ -904,10 +905,6 @@ public:
             std::vector<isc::dns::ConstRRsetPtr>& target,
             const FindOptions options = FIND_DEFAULT);
 
-        /// \brief Implementation of ZoneFinder::findPreviousName method.
-        virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
-            const;
-
         /// Look for NSEC3 for proving (non)existence of given name.
         ///
         /// See documentation in \c Zone.
@@ -1108,6 +1105,10 @@ public:
             bool probed_;
         };
 
+        /// \brief A simple wrapper for identifying the previous name
+        /// of the given name in the underlying database.
+        isc::dns::Name findPreviousName(const isc::dns::Name& name) const;
+
         /// \brief Search result of \c findDelegationPoint().
         ///
         /// This is a tuple combining the result of the search - a status code

+ 0 - 6
src/lib/datasrc/memory_datasrc.cc

@@ -1774,12 +1774,6 @@ InMemoryZoneFinder::getFileName() const {
     return (impl_->file_name_);
 }
 
-isc::dns::Name
-InMemoryZoneFinder::findPreviousName(const isc::dns::Name&) const {
-    isc_throw(NotImplemented, "InMemory data source doesn't support DNSSEC "
-              "yet, can't find previous name");
-}
-
 /// Implementation details for \c InMemoryClient hidden from the public
 /// interface.
 ///

+ 0 - 6
src/lib/datasrc/memory_datasrc.h

@@ -87,12 +87,6 @@ public:
     virtual FindNSEC3Result
     findNSEC3(const isc::dns::Name& name, bool recursive);
 
-    /// \brief Imelementation of the ZoneFinder::findPreviousName method
-    ///
-    /// This one throws NotImplemented exception, as InMemory doesn't
-    /// support DNSSEC currently.
-    virtual isc::dns::Name findPreviousName(const isc::dns::Name& query) const;
-
     /// \brief Inserts an rrset into the zone.
     ///
     /// It puts another RRset into the zone.

+ 2 - 0
src/lib/datasrc/static.zone.pre

@@ -6,7 +6,9 @@
 ;;
 ;; in the bindctl.
 
+;; This is here mostly for technical reasons.
 BIND.           0   CH  SOA bind. authors.bind. 0 28800 7200 604800 86400
+BIND.           0   CH  NS  BIND.
 
 VERSION.BIND.   0   CH  TXT "@@VERSION_STRING@@"
 ;; HOSTNAME.BIND    0   CH  TXT "localhost"

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

@@ -18,7 +18,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 noinst_PROGRAMS =
@@ -67,7 +67,6 @@ run_unittests_SOURCES += client_list_unittest.cc
 # We need the actual module implementation in the tests (they are not part
 # of libdatasrc)
 run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc
-run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)

+ 300 - 26
src/lib/datasrc/tests/client_list_unittest.cc

@@ -14,9 +14,13 @@
 
 #include <datasrc/client_list.h>
 #include <datasrc/client.h>
+#include <datasrc/iterator.h>
 #include <datasrc/data_source.h>
+#include <datasrc/memory_datasrc.h>
 
 #include <dns/rrclass.h>
+#include <dns/rrttl.h>
+#include <dns/rdataclass.h>
 
 #include <gtest/gtest.h>
 
@@ -57,12 +61,38 @@ public:
         FindNSEC3Result findNSEC3(const Name&, bool) {
             isc_throw(isc::NotImplemented, "Not implemented");
         }
-        Name findPreviousName(const Name&) const {
-            isc_throw(isc::NotImplemented, "Not implemented");
-        }
     private:
         Name origin_;
     };
+    class Iterator : public ZoneIterator {
+    public:
+        Iterator(const Name& origin) :
+            origin_(origin),
+            finished_(false),
+            soa_(new RRset(origin_, RRClass::IN(), RRType::SOA(), RRTTL(3600)))
+        {
+            // The RData here is bogus, but it is not used to anything. There
+            // just needs to be some.
+            soa_->addRdata(rdata::generic::SOA(Name::ROOT_NAME(),
+                                               Name::ROOT_NAME(),
+                                               0, 0, 0, 0, 0));
+        }
+        virtual isc::dns::ConstRRsetPtr getNextRRset() {
+            if (finished_) {
+                return (ConstRRsetPtr());
+            } else {
+                finished_ = true;
+                return (soa_);
+            }
+        }
+        virtual isc::dns::ConstRRsetPtr getSOA() const {
+            return (soa_);
+        }
+    private:
+        const Name origin_;
+        bool finished_;
+        const isc::dns::RRsetPtr soa_;
+    };
     // Constructor from a list of zones.
     MockDataSourceClient(const char* zone_names[]) {
         for (const char** zone(zone_names); *zone; ++zone) {
@@ -75,7 +105,15 @@ public:
                          const ConstElementPtr& configuration) :
         type_(type),
         configuration_(configuration)
-    {}
+    {
+        EXPECT_NE("MasterFiles", type) << "MasterFiles is a special case "
+            "and it never should be created as a data source client";
+        if (configuration_->getType() == Element::list) {
+            for (size_t i(0); i < configuration_->size(); ++i) {
+                zones.insert(Name(configuration_->get(i)->stringValue()));
+            }
+        }
+    }
     virtual FindResult findZone(const Name& name) const {
         if (zones.empty()) {
             return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
@@ -106,6 +144,20 @@ public:
     {
         isc_throw(isc::NotImplemented, "Not implemented");
     }
+    virtual ZoneIteratorPtr getIterator(const Name& name, bool) const {
+        if (name == Name("noiter.org")) {
+            isc_throw(isc::NotImplemented, "Asked not to be implemented");
+        } else if (name == Name("null.org")) {
+            return (ZoneIteratorPtr());
+        } else {
+            FindResult result(findZone(name));
+            if (result.code == isc::datasrc::result::SUCCESS) {
+                return (ZoneIteratorPtr(new Iterator(name)));
+            } else {
+                isc_throw(DataSourceError, "No such zone");
+            }
+        }
+    }
     const string type_;
     const ConstElementPtr configuration_;
 private:
@@ -117,6 +169,9 @@ private:
 // some methods to dig directly in the internals, for the tests.
 class TestedList : public ConfigurableClientList {
 public:
+    TestedList(const RRClass& rrclass) :
+        ConfigurableClientList(rrclass)
+    {}
     DataSources& getDataSources() { return (data_sources_); }
     // Overwrite the list's method to get a data source with given type
     // and configuration. We mock the data source and don't create the
@@ -165,11 +220,10 @@ class ListTest : public ::testing::Test {
 public:
     ListTest() :
         // The empty list corresponds to a list with no elements inside
-        list_(new TestedList()),
+        list_(new TestedList(RRClass::IN())),
         config_elem_(Element::fromJSON("["
             "{"
             "   \"type\": \"test_type\","
-            "   \"cache\": \"off\","
             "   \"params\": {}"
             "}]"))
     {
@@ -178,20 +232,28 @@ public:
                 ds(new MockDataSourceClient(ds_zones[i]));
             ds_.push_back(ds);
             ds_info_.push_back(ConfigurableClientList::DataSourceInfo(ds.get(),
-                DataSourceClientContainerPtr()));
+                DataSourceClientContainerPtr(), false));
         }
     }
     // Check the positive result is as we expect it.
     void positiveResult(const ClientList::FindResult& result,
                         const shared_ptr<MockDataSourceClient>& dsrc,
                         const Name& name, bool exact,
-                        const char* test)
+                        const char* test, bool from_cache = false)
     {
         SCOPED_TRACE(test);
-        EXPECT_EQ(dsrc.get(), result.dsrc_client_);
         ASSERT_NE(ZoneFinderPtr(), result.finder_);
         EXPECT_EQ(name, result.finder_->getOrigin());
         EXPECT_EQ(exact, result.exact_match_);
+        if (from_cache) {
+            EXPECT_NE(shared_ptr<InMemoryZoneFinder>(),
+                      dynamic_pointer_cast<InMemoryZoneFinder>(
+                          result.finder_)) << "Finder is not from cache";
+            EXPECT_TRUE(NULL !=
+                        dynamic_cast<InMemoryClient*>(result.dsrc_client_));
+        } else {
+            EXPECT_EQ(dsrc.get(), result.dsrc_client_);
+        }
     }
     // Configure the list with multiple data sources, according to
     // some configuration. It uses the index as parameter, to be able to
@@ -223,7 +285,8 @@ public:
                 FAIL() << "Unknown configuration index " << index;
         }
     }
-    void checkDS(size_t index, const string& type, const string& params) const
+    void checkDS(size_t index, const string& type, const string& params,
+                 bool cache) const
     {
         ASSERT_GT(list_->getDataSources().size(), index);
         MockDataSourceClient* ds(dynamic_cast<MockDataSourceClient*>(
@@ -233,6 +296,8 @@ public:
         ASSERT_NE(ds, static_cast<const MockDataSourceClient*>(NULL));
         EXPECT_EQ(type, ds->type_);
         EXPECT_TRUE(Element::fromJSON(params)->equals(*ds->configuration_));
+        EXPECT_EQ(cache, list_->getDataSources()[index].cache_ !=
+                  shared_ptr<InMemoryClient>());
     }
     shared_ptr<TestedList> list_;
     const ClientList::FindResult negativeResult_;
@@ -352,7 +417,7 @@ TEST_F(ListTest, multiBestMatch) {
 
 // Check the configuration is empty when the list is empty
 TEST_F(ListTest, configureEmpty) {
-    ConstElementPtr elem(new ListElement);
+    const ConstElementPtr elem(new ListElement);
     list_->configure(elem, true);
     EXPECT_TRUE(list_->getDataSources().empty());
     // Check the exact configuration is preserved
@@ -361,7 +426,7 @@ TEST_F(ListTest, configureEmpty) {
 
 // Check we can get multiple data sources and they are in the right order.
 TEST_F(ListTest, configureMulti) {
-    ConstElementPtr elem(Element::fromJSON("["
+    const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"type1\","
         "   \"cache\": \"off\","
@@ -375,8 +440,8 @@ TEST_F(ListTest, configureMulti) {
     ));
     list_->configure(elem, true);
     EXPECT_EQ(2, list_->getDataSources().size());
-    checkDS(0, "type1", "{}");
-    checkDS(1, "type2", "{}");
+    checkDS(0, "type1", "{}", false);
+    checkDS(1, "type2", "{}", false);
     // Check the exact configuration is preserved
     EXPECT_EQ(elem, list_->getConfiguration());
 }
@@ -403,7 +468,7 @@ TEST_F(ListTest, configureParams) {
             "}]"));
         list_->configure(elem, true);
         EXPECT_EQ(1, list_->getDataSources().size());
-        checkDS(0, "t", *param);
+        checkDS(0, "t", *param, false);
     }
 }
 
@@ -425,55 +490,264 @@ TEST_F(ListTest, wrongConfig) {
         "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": null}]",
         "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": []}]",
         "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": {}}]",
-        // TODO: Once cache is supported, add some invalid cache values
+        // Bad type of cache-enable
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": 13, \"cache-zones\": []}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": \"xx\", \"cache-zones\": []}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": [], \"cache-zones\": []}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": {}, \"cache-zones\": []}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": null, \"cache-zones\": []}]",
+        // Bad type of cache-zones
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": \"x\"}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": true}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": null}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": 13}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": {}}]",
+        // Some bad inputs for MasterFiles special case
+
+        // It must have the cache enabled
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {}}]",
+        // No cache-zones allowed here
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": true,"
+         "\"param\": {}, \"cache-zones\": []}]",
+        // Some bad types of params
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": []}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": 13}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": true}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": null}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": \"x\"}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {\".\": 13}}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {\".\": true}}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {\".\": null}}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {\".\": []}}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {\".\": {}}}]",
         NULL
     };
     // Put something inside to see it survives the exception
     list_->configure(config_elem_, true);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
     for (const char** config(configs); *config; ++config) {
         SCOPED_TRACE(*config);
         ConstElementPtr elem(Element::fromJSON(*config));
         EXPECT_THROW(list_->configure(elem, true),
                      ConfigurableClientList::ConfigurationError);
         // Still untouched
-        checkDS(0, "test_type", "{}");
+        checkDS(0, "test_type", "{}", false);
         EXPECT_EQ(1, list_->getDataSources().size());
     }
 }
 
 // The param thing defaults to null. Cache is not used yet.
 TEST_F(ListTest, defaults) {
-    ConstElementPtr elem(Element::fromJSON("["
+    const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"type1\""
         "}]"));
     list_->configure(elem, true);
     EXPECT_EQ(1, list_->getDataSources().size());
-    checkDS(0, "type1", "null");
+    checkDS(0, "type1", "null", false);
 }
 
 // Check we can call the configure multiple times, to change the configuration
 TEST_F(ListTest, reconfigure) {
-    ConstElementPtr empty(new ListElement);
+    const ConstElementPtr empty(new ListElement);
     list_->configure(config_elem_, true);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
     list_->configure(empty, true);
     EXPECT_TRUE(list_->getDataSources().empty());
     list_->configure(config_elem_, true);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
 }
 
 // Make sure the data source error exception from the factory is propagated
 TEST_F(ListTest, dataSrcError) {
-    ConstElementPtr elem(Element::fromJSON("["
+    const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"error\""
         "}]"));
     list_->configure(config_elem_, true);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
     EXPECT_THROW(list_->configure(elem, true), DataSourceError);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
+}
+
+// Check we can get the cache
+TEST_F(ListTest, configureCacheEmpty) {
+    const ConstElementPtr elem(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [],"
+        "   \"params\": {}"
+        "},"
+        "{"
+        "   \"type\": \"type2\","
+        "   \"cache-enable\": false,"
+        "   \"cache-zones\": [],"
+        "   \"params\": {}"
+        "}]"
+    ));
+    list_->configure(elem, true);
+    EXPECT_EQ(2, list_->getDataSources().size());
+    checkDS(0, "type1", "{}", true);
+    checkDS(1, "type2", "{}", false);
+}
+
+// But no cache if we disallow it globally
+TEST_F(ListTest, configureCacheDisabled) {
+    const ConstElementPtr elem(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [],"
+        "   \"params\": {}"
+        "},"
+        "{"
+        "   \"type\": \"type2\","
+        "   \"cache-enable\": false,"
+        "   \"cache-zones\": [],"
+        "   \"params\": {}"
+        "}]"
+    ));
+    list_->configure(elem, false);
+    EXPECT_EQ(2, list_->getDataSources().size());
+    checkDS(0, "type1", "{}", false);
+    checkDS(1, "type2", "{}", false);
+}
+
+// Put some zones into the cache
+TEST_F(ListTest, cacheZones) {
+    const ConstElementPtr elem(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [\"example.org\", \"example.com\"],"
+        "   \"params\": [\"example.org\", \"example.com\", \"exmaple.cz\"]"
+        "}]"));
+    list_->configure(elem, true);
+    checkDS(0, "type1", "[\"example.org\", \"example.com\", \"exmaple.cz\"]",
+            true);
+
+    const shared_ptr<InMemoryClient> cache(list_->getDataSources()[0].cache_);
+    EXPECT_EQ(2, cache->getZoneCount());
+
+    EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.org")).code);
+    EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.com")).code);
+    EXPECT_EQ(result::NOTFOUND, cache->findZone(Name("example.cz")).code);
+    EXPECT_EQ(RRClass::IN(),
+              cache->findZone(Name("example.org")).zone_finder->getClass());
+
+    // These are cached and answered from the cache
+    positiveResult(list_->find(Name("example.com.")), ds_[0],
+                   Name("example.com."), true, "com", true);
+    positiveResult(list_->find(Name("example.org.")), ds_[0],
+                   Name("example.org."), true, "org", true);
+    positiveResult(list_->find(Name("sub.example.com.")), ds_[0],
+                   Name("example.com."), false, "Subdomain of com", true);
+    // For now, the ones not cached are ignored.
+    EXPECT_TRUE(negativeResult_ == list_->find(Name("example.cz.")));
+}
+
+// Check the caching handles misbehaviour from the data source and
+// misconfiguration gracefully
+TEST_F(ListTest, badCache) {
+    list_->configure(config_elem_, true);
+    checkDS(0, "test_type", "{}", false);
+    // First, the zone is not in the data source
+    const ConstElementPtr elem1(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [\"example.org\"],"
+        "   \"params\": []"
+        "}]"));
+    EXPECT_THROW(list_->configure(elem1, true),
+                 ConfigurableClientList::ConfigurationError);
+    checkDS(0, "test_type", "{}", false);
+    // Now, the zone doesn't give an iterator
+    const ConstElementPtr elem2(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [\"noiter.org\"],"
+        "   \"params\": [\"noiter.org\"]"
+        "}]"));
+    EXPECT_THROW(list_->configure(elem2, true), isc::NotImplemented);
+    checkDS(0, "test_type", "{}", false);
+    // Now, the zone returns NULL iterator
+    const ConstElementPtr elem3(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [\"null.org\"],"
+        "   \"params\": [\"null.org\"]"
+        "}]"));
+    EXPECT_THROW(list_->configure(elem3, true), isc::Unexpected);
+    checkDS(0, "test_type", "{}", false);
+    // The autodetection of zones is not enabled
+    const ConstElementPtr elem4(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": true,"
+        "   \"params\": [\"example.org\"]"
+        "}]"));
+    EXPECT_THROW(list_->configure(elem4, true), isc::NotImplemented);
+    checkDS(0, "test_type", "{}", false);
+}
+
+TEST_F(ListTest, masterFiles) {
+    const ConstElementPtr elem(Element::fromJSON("["
+        "{"
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {"
+        "       \".\": \"" TEST_DATA_DIR "/root.zone\""
+        "   }"
+        "}]"));
+    list_->configure(elem, true);
+
+    // It has only the cache
+    EXPECT_EQ(NULL, list_->getDataSources()[0].data_src_client_);
+
+    // And it can search
+    positiveResult(list_->find(Name(".")), ds_[0], Name("."), true, "com",
+                   true);
+
+    // If cache is not enabled, nothing is loaded
+    list_->configure(elem, false);
+    EXPECT_EQ(0, list_->getDataSources().size());
 }
 
 }

+ 0 - 34
src/lib/datasrc/tests/database_unittest.cc

@@ -3622,33 +3622,6 @@ TYPED_TEST(DatabaseClientTest, compoundUpdate) {
                this->empty_rdatas_);
 }
 
-TYPED_TEST(DatabaseClientTest, previous) {
-    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
-
-    EXPECT_EQ(Name("www.example.org."),
-              finder->findPreviousName(Name("www2.example.org.")));
-    // Check a name that doesn't exist there
-    EXPECT_EQ(Name("www.example.org."),
-              finder->findPreviousName(Name("www1.example.org.")));
-    if (this->is_mock_) { // We can't really force the DB to throw
-        // Check it doesn't crash or anything if the underlying DB throws
-        DataSourceClient::FindResult
-            zone(this->client_->findZone(Name("bad.example.org")));
-        finder =
-            dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder);
-
-        EXPECT_THROW(finder->findPreviousName(Name("bad.example.org")),
-                     isc::NotImplemented);
-    } else {
-        // No need to test this on mock one, because we test only that
-        // the exception gets through
-
-        // A name before the origin
-        EXPECT_THROW(finder->findPreviousName(Name("example.com")),
-                     isc::NotImplemented);
-    }
-}
-
 TYPED_TEST(DatabaseClientTest, invalidRdata) {
     boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
@@ -3676,13 +3649,6 @@ TEST_F(MockDatabaseClientTest, missingNSEC) {
                this->expected_rdatas_, this->expected_sig_rdatas_);
 }
 
-TEST_F(MockDatabaseClientTest, badName) {
-    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
-
-    EXPECT_THROW(finder->findPreviousName(Name("brokenname.example.org.")),
-                 DataSourceError);
-}
-
 /*
  * Test correct use of the updater with a journal.
  */

+ 0 - 8
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -683,14 +683,6 @@ public:
 };
 
 /**
- * \brief Check that findPreviousName throws as it should now.
- */
-TEST_F(InMemoryZoneFinderTest, findPreviousName) {
-    EXPECT_THROW(zone_finder_.findPreviousName(Name("www.example.org")),
-                 isc::NotImplemented);
-}
-
-/**
  * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
  *
  * Takes the created zone finder and checks its properties they are the same

+ 0 - 25
src/lib/datasrc/zone.h

@@ -617,31 +617,6 @@ public:
     /// a matching NSEC3, in the form of \c FindNSEC3Result object.
     virtual FindNSEC3Result
     findNSEC3(const isc::dns::Name& name, bool recursive) = 0;
-
-    /// \brief Get previous name in the zone
-    ///
-    /// Gets the previous name in the DNSSEC order. This can be used
-    /// to find the correct NSEC records for proving nonexistence
-    /// of domains.
-    ///
-    /// The concrete implementation might throw anything it thinks appropriate,
-    /// however it is recommended to stick to the ones listed here. The user
-    /// of this method should be able to handle any exceptions.
-    ///
-    /// This method does not include under-zone-cut data (glue data).
-    ///
-    /// \param query The name for which one we look for a previous one. The
-    ///     queried name doesn't have to exist in the zone.
-    /// \return The preceding name
-    ///
-    /// \throw NotImplemented in case the data source backend doesn't support
-    ///     DNSSEC or there is no previous in the zone (NSEC records might be
-    ///     missing in the DB, the queried name is less or equal to the apex).
-    /// \throw DataSourceError for low-level or internal datasource errors
-    ///     (like broken connection to database, wrong data living there).
-    /// \throw std::bad_alloc For allocation errors.
-    virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
-        const = 0;
     //@}
 };
 

+ 1 - 1
src/lib/dhcp/Makefile.am

@@ -36,7 +36,7 @@ libdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
 libdhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 libdhcp___la_LIBADD   = $(top_builddir)/src/lib/asiolink/libasiolink.la
 libdhcp___la_LIBADD  += $(top_builddir)/src/lib/util/libutil.la
-libdhcp___la_LDFLAGS  = -no-undefined -version-info 1:0:0
+libdhcp___la_LDFLAGS  = -no-undefined -version-info 2:0:0
 
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the

+ 82 - 12
src/lib/dhcp/iface_mgr.cc

@@ -18,6 +18,7 @@
 #include <string.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <sys/select.h>
 
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
@@ -121,7 +122,8 @@ bool IfaceMgr::Iface::delSocket(uint16_t sockfd) {
 
 IfaceMgr::IfaceMgr()
     :control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
-     control_buf_(new char[control_buf_len_])
+     control_buf_(new char[control_buf_len_]),
+     session_socket_(INVALID_SOCKET), session_callback_(NULL)
 {
 
     cout << "IfaceMgr initialization." << endl;
@@ -230,11 +232,13 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
          iface!=ifaces_.end();
          ++iface) {
 
-        cout << "Trying interface " << iface->getFullName() << endl;
+        cout << "Trying opening socket on interface " << iface->getFullName() << endl;
 
         if (iface->flag_loopback_ ||
             !iface->flag_up_ ||
             !iface->flag_running_) {
+            cout << "Interface " << iface->getFullName()
+                 << " not suitable: is loopback, is down or not running" << endl;
             continue;
         }
 
@@ -685,35 +689,101 @@ IfaceMgr::send(const Pkt4Ptr& pkt)
 
 
 boost::shared_ptr<Pkt4>
-IfaceMgr::receive4() {
+IfaceMgr::receive4(uint32_t timeout) {
 
     const SocketInfo* candidate = 0;
     IfaceCollection::const_iterator iface;
+
+    fd_set sockets;
+    FD_ZERO(&sockets);
+    int maxfd = 0;
+
+    stringstream names;
+
+    /// @todo: marginal performance optimization. We could create the set once
+    /// and then use its copy for select(). Please note that select() modifies
+    /// provided set to indicated which sockets have something to read.
     for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
 
-        /// @todo: rewrite this as part of #1555
         for (SocketCollection::const_iterator s = iface->sockets_.begin();
              s != iface->sockets_.end(); ++s) {
 
-            // We don't want IPv6 addresses here.
-            if (s->addr_.getFamily() != AF_INET) {
-                continue;
+            // Only deal with IPv4 addresses.
+            if (s->addr_.getFamily() == AF_INET) {
+                names << s->sockfd_ << "(" << iface->getName() << ") ";
+
+                // Add this socket to listening set
+                FD_SET(s->sockfd_, &sockets);
+                if (maxfd < s->sockfd_) {
+                    maxfd = s->sockfd_;
+                }
             }
+        }
+    }
 
-            // This address looks good.
-            if (!candidate) {
+    // if there is session socket registered...
+    if (session_socket_ != INVALID_SOCKET) {
+        // at it to the set as well
+        FD_SET(session_socket_, &sockets);
+        if (maxfd < session_socket_)
+            maxfd = session_socket_;
+        names << session_socket_ << "(session)";
+    }
+
+    /// @todo: implement sub-second precision one day
+    struct timeval select_timeout;
+    select_timeout.tv_sec = timeout;
+    select_timeout.tv_usec = 0;
+
+    cout << "Trying to receive data on sockets: " << names.str()
+         << ". Timeout is " << timeout << " seconds." << endl;
+    int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
+    cout << "select returned " << result << endl;
+
+    if (result == 0) {
+        // nothing received and timeout has been reached
+        return (Pkt4Ptr()); // NULL
+    } else if (result < 0) {
+        cout << "Socket read error: " << strerror(errno) << endl;
+
+        /// @todo: perhaps throw here?
+        return (Pkt4Ptr()); // NULL
+    }
+
+    // Let's find out which socket has the data
+    if ((session_socket_ != INVALID_SOCKET) && (FD_ISSET(session_socket_, &sockets))) {
+        // something received over session socket
+        cout << "BIND10 command or config available over session socket." << endl;
+
+        if (session_callback_) {
+            // in theory we could call io_service.run_one() here, instead of
+            // implementing callback mechanism, but that would introduce
+            // asiolink dependency to libdhcp++ and that is something we want
+            // to avoid (see CPE market and out long term plans for minimalistic
+            // implementations.
+            session_callback_();
+        }
+
+        return (Pkt4Ptr()); // NULL
+    }
+
+    // Let's find out which interface/socket has the data
+    for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
+        for (SocketCollection::const_iterator s = iface->sockets_.begin();
+             s != iface->sockets_.end(); ++s) {
+            if (FD_ISSET(s->sockfd_, &sockets)) {
                 candidate = &(*s);
                 break;
             }
         }
-
         if (candidate) {
             break;
         }
     }
 
     if (!candidate) {
-        isc_throw(Unexpected, "Failed to find any suitable sockets on all interfaces.");
+        cout << "Received data over unknown socket." << endl;
+        return (Pkt4Ptr()); // NULL
     }
 
     cout << "Trying to receive over UDP4 socket " << candidate->sockfd_ << " bound to "
@@ -750,7 +820,7 @@ IfaceMgr::receive4() {
     m.msg_control = &control_buf_[0];
     m.msg_controllen = control_buf_len_;
 
-    int result = recvmsg(candidate->sockfd_, &m, 0);
+    result = recvmsg(candidate->sockfd_, &m, 0);
     if (result < 0) {
         cout << "Failed to receive UDP4 data." << endl;
         return (Pkt4Ptr()); // NULL

+ 25 - 5
src/lib/dhcp/iface_mgr.h

@@ -39,6 +39,9 @@ public:
     /// type that defines list of addresses
     typedef std::vector<isc::asiolink::IOAddress> AddressCollection;
 
+    /// defines callback used when commands are received over control session
+    typedef void (*SessionCallback) (void);
+
     /// maximum MAC address length (Infiniband uses 20 bytes)
     static const unsigned int MAX_MAC_LEN = 20;
 
@@ -351,12 +354,10 @@ public:
     /// If reception is successful and all information about its sender
     /// are obtained, Pkt4 object is created and returned.
     ///
-    /// TODO Start using select() and add timeout to be able
-    /// to not wait infinitely, but rather do something useful
-    /// (e.g. remove expired leases)
+    /// @param timeout specifies timeout (in seconds)
     ///
     /// @return Pkt4 object representing received packet (or NULL)
-    Pkt4Ptr receive4();
+    Pkt4Ptr receive4(uint32_t timeout);
 
     /// Opens UDP/IP socket and binds it to address, interface and port.
     ///
@@ -401,6 +402,21 @@ public:
     /// @return number of detected interfaces
     uint16_t countIfaces() { return ifaces_.size(); }
 
+    /// @brief Sets session socket and a callback
+    ///
+    /// Specifies session socket and a callback that will be called
+    /// when data will be received over that socket.
+    ///
+    /// @param socketfd socket descriptor
+    /// @param callback callback function
+    void set_session_socket(int socketfd, SessionCallback callback) {
+        session_socket_ = socketfd;
+        session_callback_ = callback;
+    }
+
+    /// A value of socket descriptor representing "not specified" state.
+    static const int INVALID_SOCKET = -1;
+
     // don't use private, we need derived classes in tests
 protected:
 
@@ -487,7 +503,6 @@ protected:
     /// control-buffer, used in transmission and reception
     boost::scoped_array<char> control_buf_;
 
-
     /// @brief A wrapper for OS-specific operations before sending IPv4 packet
     ///
     /// @param m message header (will be later used for sendmsg() call)
@@ -505,6 +520,11 @@ protected:
     /// @return true if successful, false otherwise
     bool os_receive4(struct msghdr& m, Pkt4Ptr& pkt);
 
+    /// socket descriptor of the session socket
+    int session_socket_;
+
+    /// a callback that will be called when data arrives over session_socket_
+    SessionCallback session_callback_;
 private:
 
     /// @brief Creates a single instance of this class (a singleton implementation)

+ 1 - 1
src/lib/dhcp/tests/Makefile.am

@@ -20,7 +20,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

+ 51 - 2
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -7,7 +7,7 @@
 // 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
+// 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.
@@ -17,6 +17,7 @@
 #include <fstream>
 #include <sstream>
 
+#include <unistd.h>
 #include <arpa/inet.h>
 #include <gtest/gtest.h>
 
@@ -381,7 +382,7 @@ TEST_F(IfaceMgrTest, sendReceive4) {
 
     EXPECT_EQ(true, ifacemgr->send(sendPkt));
 
-    rcvPkt = ifacemgr->receive4();
+    rcvPkt = ifacemgr->receive4(10);
 
     ASSERT_TRUE(rcvPkt); // received our own packet
 
@@ -899,4 +900,52 @@ TEST_F(IfaceMgrTest, DISABLED_detectIfaces_linux) {
 }
 #endif
 
+volatile bool callback_ok;
+
+void my_callback(void) {
+    cout << "Callback triggered." << endl;
+    callback_ok = true;
+}
+
+TEST_F(IfaceMgrTest, controlSession) {
+    // tests if extra control socket and its callback can be passed and
+    // it is supported properly by receive4() method.
+
+    callback_ok = false;
+
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+    // create pipe and register it as extra socket
+    int pipefd[2];
+    EXPECT_TRUE(pipe(pipefd) == 0);
+    EXPECT_NO_THROW(ifacemgr->set_session_socket(pipefd[0], my_callback));
+
+    Pkt4Ptr pkt4;
+    pkt4 = ifacemgr->receive4(1);
+
+    // Our callback should not be called this time (there was no data)
+    EXPECT_FALSE(callback_ok);
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt4);
+
+    // Now, send some data over pipe (38 bytes)
+    EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+    // ... and repeat
+    pkt4 = ifacemgr->receive4(1);
+
+    // IfaceMgr should not process control socket data as incoming packets
+    EXPECT_FALSE(pkt4);
+
+    // There was some data, so this time callback should be called
+    EXPECT_TRUE(callback_ok);
+
+    delete ifacemgr;
+
+    // close both pipe ends
+    close(pipefd[1]);
+    close(pipefd[0]);
+}
+
 }

+ 1 - 1
src/lib/dns/tests/Makefile.am

@@ -15,7 +15,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

+ 1 - 1
src/lib/exceptions/tests/Makefile.am

@@ -10,7 +10,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

+ 1 - 1
src/lib/log/Makefile.am

@@ -47,5 +47,5 @@ if USE_CLANGPP
 liblog_la_CXXFLAGS += -Wno-error
 endif
 liblog_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
-liblog_la_LIBADD   = $(LOG4CPLUS_LIBS) $(top_builddir)/src/lib/util/libutil.la
+liblog_la_LIBADD   = $(top_builddir)/src/lib/util/libutil.la $(LOG4CPLUS_LIBS)
 liblog_la_LDFLAGS = -no-undefined -version-info 1:0:0

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

@@ -48,7 +48,7 @@ logger_lock_test_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 logger_lock_test_LDADD += $(AM_LDADD) $(LOG4CPLUS_LIBS)
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 if HAVE_GTEST
 TESTS =
@@ -61,9 +61,9 @@ endif
 AM_CPPFLAGS += $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
 AM_LDFLAGS  += $(GTEST_LDFLAGS)
 
+AM_LDADD += $(top_builddir)/src/lib/util/libutil.la
 AM_LDADD += $(top_builddir)/src/lib/log/liblog.la
 AM_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-AM_LDADD += $(top_builddir)/src/lib/util/libutil.la
 AM_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 AM_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 AM_LDADD += $(GTEST_LDADD)

+ 1 - 1
src/lib/nsas/tests/Makefile.am

@@ -26,7 +26,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 TESTS =
 if HAVE_GTEST

+ 4 - 0
src/lib/python/isc/bind10/special_component.py

@@ -37,6 +37,7 @@ class SockCreator(BaseComponent):
         BaseComponent.__init__(self, boss, kind)
         self.__creator = None
         self.__uid = boss.uid
+        self.__gid = boss.gid
 
     def _start_internal(self):
         self._boss.curproc = 'b10-sockcreator'
@@ -45,6 +46,9 @@ class SockCreator(BaseComponent):
         self._boss.register_process(self.pid(), self)
         self._boss.set_creator(self.__creator)
         self._boss.log_started(self.pid())
+        if self.__gid is not None:
+            logger.info(BIND10_SETGID, self.__gid)
+            posix.setgid(self.__gid)
         if self.__uid is not None:
             logger.info(BIND10_SETUID, self.__uid)
             posix.setuid(self.__uid)

+ 12 - 1
src/lib/python/isc/bind10/tests/component_test.py

@@ -104,6 +104,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
         self.__stop_process_params = None
         self.__start_simple_params = None
         # Pretending to be boss
+        self.gid = None
+        self.__gid_set = None
         self.uid = None
         self.__uid_set = None
 
@@ -609,6 +611,9 @@ class ComponentTests(BossUtils, unittest.TestCase):
         self.assertTrue(process.killed)
         self.assertFalse(process.terminated)
 
+    def setgid(self, gid):
+        self.__gid_set = gid
+
     def setuid(self, uid):
         self.__uid_set = uid
 
@@ -637,7 +642,9 @@ class ComponentTests(BossUtils, unittest.TestCase):
         """
         component = isc.bind10.special_component.SockCreator(None, self,
                                                              'needed', None)
+        orig_setgid = isc.bind10.special_component.posix.setgid
         orig_setuid = isc.bind10.special_component.posix.setuid
+        isc.bind10.special_component.posix.setgid = self.setgid
         isc.bind10.special_component.posix.setuid = self.setuid
         orig_creator = \
             isc.bind10.special_component.isc.bind10.sockcreator.Creator
@@ -645,18 +652,22 @@ class ComponentTests(BossUtils, unittest.TestCase):
         isc.bind10.special_component.isc.bind10.sockcreator.Creator = \
             lambda path: self.FakeCreator()
         component.start()
-        # No uid set in boss, nothing called.
+        # No gid/uid set in boss, nothing called.
+        self.assertIsNone(self.__gid_set)
         self.assertIsNone(self.__uid_set)
         # Doesn't do anything, but doesn't crash
         component.stop()
         component.kill()
         component.kill(True)
+        self.gid = 4200
         self.uid = 42
         component = isc.bind10.special_component.SockCreator(None, self,
                                                              'needed', None)
         component.start()
         # This time, it get's called
+        self.assertEqual(4200, self.__gid_set)
         self.assertEqual(42, self.__uid_set)
+        isc.bind10.special_component.posix.setgid = orig_setgid
         isc.bind10.special_component.posix.setuid = orig_setuid
         isc.bind10.special_component.isc.bind10.sockcreator.Creator = \
             orig_creator

+ 0 - 0
src/lib/python/isc/bind10/tests/sockcreator_test.py


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