Browse Source

Merge #2044

It is the cache for ClientList.

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

+ 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
 446.	[bug]		muks
 	A number of warnings reported by Python about unclosed file and
 	A number of warnings reported by Python about unclosed file and
 	socket objects were fixed. Some related code was also made safer.
 	socket objects were fixed. Some related code was also made safer.
@@ -16,7 +44,7 @@
 	authoritative data even on a delegation point.
 	authoritative data even on a delegation point.
 	(Trac #1912, git 7130da883f823ce837c10cbf6e216a15e1996e5d)
 	(Trac #1912, git 7130da883f823ce837c10cbf6e216a15e1996e5d)
 
 
-443.    [func]*		muks
+443.	[func]*		muks
 	The logger now uses a lockfile named `logger_lockfile' that is
 	The logger now uses a lockfile named `logger_lockfile' that is
 	created in the local state directory to mutually separate
 	created in the local state directory to mutually separate
 	individual logging operations from various processes. This is
 	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.
 This is the source for the development version of BIND 10.
 
 
 BIND is the popular implementation of a DNS server, developer
 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
 BIND10-devel is new development leading up to the production
 BIND 10 release. It contains prototype code and experimental
 BIND 10 release. It contains prototype code and experimental
 interfaces. Nevertheless it is ready to use now for testing the
 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
 This release includes the bind10 master process, b10-msgq message
 bus, b10-auth authoritative DNS server (with SQLite3 and in-memory
 bus, b10-auth authoritative DNS server (with SQLite3 and in-memory

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

@@ -15,11 +15,47 @@
  * only), as support for transmission to hosts without IPv4 address
  * only), as support for transmission to hosts without IPv4 address
  * assigned is not implemented in IfaceMgr yet.
  * 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 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
  * @page dhcpv6 DHCPv6 Server Component
  *
  *
@@ -42,9 +78,9 @@
  *
  *
  * DHCPv6 server component is not integrated with boss yet.
  * 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
  * libdhcp++ is an all-purpose DHCP-manipulation library, written in
  * C++. It offers packet parsing and assembly, DHCPv4 and DHCPv6
  * 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
  * isc::dhcp::Option::delOption(), isc::dhcp::Option::getOption() can be used
  * for that purpose.
  * for that purpose.
  *
  *
- * @section lidhcpIfaceMgr Interface Manager
+ * @section libdhcpIfaceMgr Interface Manager
  *
  *
  * Interface Manager (or IfaceMgr) is an abstraction layer about low-level
  * Interface Manager (or IfaceMgr) is an abstraction layer about low-level
  * network operations. In particlar, it provides information about existing
  * network operations. In particlar, it provides information about existing

+ 3 - 0
doc/devel/mainpage.dox

@@ -19,8 +19,11 @@
  *
  *
  * @section DHCP
  * @section DHCP
  * - @subpage dhcpv4
  * - @subpage dhcpv4
+ *   - @subpage dhcpv4Session
  * - @subpage dhcpv6
  * - @subpage dhcpv6
  * - @subpage libdhcp
  * - @subpage libdhcp
+ *   - @subpage libdhcpIntro
+ *   - @subpage libdhcpIfaceMgr
  *
  *
  * @section misc Miscellaneous topics
  * @section misc Miscellaneous topics
  * - @subpage LoggingApi
  * - @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>
     <abstract>
       <para>BIND 10 is a framework that features Domain Name System
       <para>BIND 10 is a framework that features Domain Name System
       (DNS) suite and Dynamic Host Configuration Protocol (DHCP)
       (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
       authoritative and recursive DNS servers, and experimental DHCPv4
       and DHCPv6 servers.
       and DHCPv6 servers.
       </para>
       </para>
@@ -59,6 +59,8 @@
     <section id="acknowledgements">
     <section id="acknowledgements">
       <title>Acknowledgements</title>
       <title>Acknowledgements</title>
 
 
+<!-- TODO: acknowledge all sponsors and CNNIC and CZNIC too -->
+
       <para>ISC would like to acknowledge generous support for
       <para>ISC would like to acknowledge generous support for
       BIND 10 development of DHCPv4 and DHCPv6 components provided
       BIND 10 development of DHCPv4 and DHCPv6 components provided
       by <ulink url="http://www.comcast.com/">Comcast</ulink>.</para>
       by <ulink url="http://www.comcast.com/">Comcast</ulink>.</para>
@@ -72,11 +74,13 @@
     <para>
     <para>
       BIND is the popular implementation of a DNS server, developer
       BIND is the popular implementation of a DNS server, developer
       interfaces, and DNS tools.
       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
       BIND 10 provides a EDNS0- and DNSSEC-capable authoritative
       DNS server and a caching recursive name server which also
       DNS server and a caching recursive name server which also
       provides forwarding.
       provides forwarding.
+      It also provides experimental DHCPv4 and DHCPv6 servers.
     </para>
     </para>
 
 
     <para>
     <para>
@@ -87,9 +91,10 @@
     <section>
     <section>
       <title>Supported Platforms</title>
       <title>Supported Platforms</title>
       <para>
       <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
   It has been tested on Sparc, i386, and amd64 hardware
   platforms.
   platforms.
@@ -104,7 +109,7 @@
       <para>
       <para>
         BIND 10 requires at least Python 3.1
         BIND 10 requires at least Python 3.1
         (<ulink url="http://www.python.org/"/>).
         (<ulink url="http://www.python.org/"/>).
-        It has also been tested with Python 3.2.
+        It also works with Python 3.2.
       </para>
       </para>
 
 
       <para>
       <para>
@@ -117,6 +122,7 @@
         BIND 10 uses the log4cplus C++ logging library
         BIND 10 uses the log4cplus C++ logging library
         (<ulink url="http://log4cplus.sourceforge.net/"/>).
         (<ulink url="http://log4cplus.sourceforge.net/"/>).
         It requires at least log4cplus version 1.0.3.
         It requires at least log4cplus version 1.0.3.
+<!-- TODO: It is recommended to use at least version .... -->
       </para>
       </para>
 
 
       <para>
       <para>
@@ -127,13 +133,11 @@
       </para>
       </para>
 
 
       <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>
       </para>
 <!-- TODO: this will change ... -->
 <!-- TODO: this will change ... -->
 
 
@@ -198,6 +202,16 @@
 
 
           <listitem>
           <listitem>
             <simpara>
             <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;
               <command>b10-msgq</command> &mdash;
               Message bus daemon.
               Message bus daemon.
               This process coordinates communication between all of the other
               This process coordinates communication between all of the other
@@ -209,8 +223,8 @@
             <simpara>
             <simpara>
               <command>b10-resolver</command> &mdash;
               <command>b10-resolver</command> &mdash;
               Recursive name server.
               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>
             </simpara>
           </listitem>
           </listitem>
 
 
@@ -253,15 +267,14 @@
               <command>b10-xfrout</command> &mdash;
               <command>b10-xfrout</command> &mdash;
               Outgoing zone transfer service.
               Outgoing zone transfer service.
               This process is used to handle transfer requests to
               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>
             </simpara>
           </listitem>
           </listitem>
 
 
           <listitem>
           <listitem>
             <simpara>
             <simpara>
               <command>b10-zonemgr</command> &mdash;
               <command>b10-zonemgr</command> &mdash;
-              Secondary manager.
+              Secondary zone manager.
               This process keeps track of timers and other
               This process keeps track of timers and other
               necessary information for BIND 10 to act as a slave server.
               necessary information for BIND 10 to act as a slave server.
             </simpara>
             </simpara>
@@ -271,8 +284,8 @@
       </para>
       </para>
 
 
       <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>
       </para>
 
 
     </section>
     </section>
@@ -287,7 +300,7 @@
           <listitem>
           <listitem>
             <simpara>
             <simpara>
               <command>bindctl</command> &mdash;
               <command>bindctl</command> &mdash;
-              interactive administration interface.
+              Interactive administration interface.
               This is a low-level command-line tool which allows
               This is a low-level command-line tool which allows
               a developer or an experienced administrator to control
               a developer or an experienced administrator to control
               BIND 10.
               BIND 10.
@@ -296,7 +309,7 @@
           <listitem>
           <listitem>
             <simpara>
             <simpara>
               <command>b10-loadzone</command> &mdash;
               <command>b10-loadzone</command> &mdash;
-              zone file loader.
+              Zone file loader.
               This tool will load standard masterfile-format zone files into
               This tool will load standard masterfile-format zone files into
               BIND 10.
               BIND 10.
             </simpara>
             </simpara>
@@ -304,7 +317,7 @@
           <listitem>
           <listitem>
             <simpara>
             <simpara>
               <command>b10-cmdctl-usermgr</command> &mdash;
               <command>b10-cmdctl-usermgr</command> &mdash;
-              user access control.
+              User access control.
               This tool allows an administrator to authorize additional users
               This tool allows an administrator to authorize additional users
               to manage BIND 10.
               to manage BIND 10.
             </simpara>
             </simpara>
@@ -347,6 +360,7 @@ var/
       for C++ and Python for the message bus, configuration backend,
       for C++ and Python for the message bus, configuration backend,
       and, of course, DNS. These include detailed developer
       and, of course, DNS. These include detailed developer
       documentation and code examples.
       documentation and code examples.
+<!-- TODO: DHCP also but no Python yet. -->
 <!-- TODO point to this -->
 <!-- TODO point to this -->
     </para>
     </para>
 
 
@@ -355,12 +369,100 @@ var/
   <chapter id="installation">
   <chapter id="installation">
     <title>Installation</title>
     <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">
     <section id="build-requirements">
       <title>Building Requirements</title>
       <title>Building Requirements</title>
 
 
         <para>
         <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>
         </para>
 
 
         <note>
         <note>
@@ -404,7 +506,7 @@ as a dependency earlier -->
         </para>
         </para>
 
 
         <para>
         <para>
-          Visit the wiki at <ulink
+          Visit the user-contributed wiki at <ulink
           url="http://bind10.isc.org/wiki/SystemSpecificNotes" />
           url="http://bind10.isc.org/wiki/SystemSpecificNotes" />
           for system-specific installation tips.
           for system-specific installation tips.
         </para>
         </para>
@@ -473,7 +575,7 @@ as a dependency earlier -->
         </listitem>
         </listitem>
 
 
         <listitem>
         <listitem>
-
+<!-- TODO: this is wrong; b10-auth is not started by default any more -->
          <para>Test it; for example:
          <para>Test it; for example:
             <screen>$ <userinput>dig @127.0.0.1 -c CH -t TXT authors.bind</userinput></screen>
             <screen>$ <userinput>dig @127.0.0.1 -c CH -t TXT authors.bind</userinput></screen>
          </para>
          </para>
@@ -499,10 +601,10 @@ as a dependency earlier -->
       <title>Installation from source</title>
       <title>Installation from source</title>
       <para>
       <para>
         BIND 10 is open source software written in C++ and Python.
         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>
       </para>
 
 
       <section>
       <section>
@@ -530,7 +632,7 @@ as a dependency earlier -->
 
 
         <note>
         <note>
           <para>
           <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),
             software will be required:  automake (v1.11 or newer),
             libtoolize, and autoconf (2.59 or newer).
             libtoolize, and autoconf (2.59 or newer).
             These may need to be installed.
             These may need to be installed.
@@ -538,11 +640,12 @@ as a dependency earlier -->
         </note>
         </note>
 
 
         <para>
         <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
           control system. This is powered by Git and all the BIND 10
           development is public.
           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>
         <para>
         <para>
           The code can be checked out from
           The code can be checked out from
@@ -555,8 +658,8 @@ as a dependency earlier -->
         <para>
         <para>
           When checking out the code from
           When checking out the code from
           the code version control system, it doesn't include the
           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>
           They can be created by running <command>autoreconf</command>
           with the <option>--install</option> switch.
           with the <option>--install</option> switch.
           This will run <command>autoconf</command>,
           This will run <command>autoconf</command>,
@@ -580,7 +683,7 @@ as a dependency earlier -->
         </para>
         </para>
         <para>
         <para>
           Run <command>./configure</command> with the <option>--help</option>
           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>
           <variablelist>
 
 
@@ -668,65 +771,6 @@ as a dependency earlier -->
 
 
   <!-- TODO: tests -->
   <!-- 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>
     </section>
 
 
   <!--
   <!--
@@ -832,6 +876,7 @@ as a dependency earlier -->
         in usual way. This is the list of components that need to be started
         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:
         in a special way, with the value of special used for them:
         <table>
         <table>
+          <title>Special startup components</title>
           <tgroup cols='3' align='left'>
           <tgroup cols='3' align='left'>
           <colspec colname='component'/>
           <colspec colname='component'/>
           <colspec colname='special'/>
           <colspec colname='special'/>
@@ -878,7 +923,7 @@ as a dependency earlier -->
         message bus. The special components already know their
         message bus. The special components already know their
         address, but the usual ones don't. The address is by
         address, but the usual ones don't. The address is by
         convention the thing after <emphasis>b10-</emphasis>, with
         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).
         would have <quote>Stats</quote> as its address).
 <!-- TODO: this should be simplified so we don't even have to document it -->
 <!-- TODO: this should be simplified so we don't even have to document it -->
       </para>
       </para>
@@ -1833,7 +1878,6 @@ what if a NOTIFY is sent?
 
 
   <chapter id="xfrout">
   <chapter id="xfrout">
     <title>Outbound Zone Transfers</title>
     <title>Outbound Zone Transfers</title>
-
     <para>
     <para>
       The <command>b10-xfrout</command> process is started by
       The <command>b10-xfrout</command> process is started by
       <command>bind10</command>.
       <command>bind10</command>.
@@ -1909,6 +1953,325 @@ what is XfroutClient xfr_client??
 
 
   </chapter>
   </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">
   <chapter id="resolverserver">
     <title>Recursive Name Server</title>
     <title>Recursive Name Server</title>
 
 
@@ -2110,21 +2473,22 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
       </para>
       </para>
 
 
       <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
         At start, the server will detect available network interfaces
         and will attempt to open UDP sockets on all interfaces that
         and will attempt to open UDP sockets on all interfaces that
         are up, running, are not loopback, and have IPv4 address
         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>
           <listitem>
             <simpara>Upon start, the server will open sockets on all
             <simpara>Upon start, the server will open sockets on all
             interfaces that are not loopback, are up and running and
             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>
           <listitem>
           <listitem>
             <simpara>PRL (Parameter Request List, a list of options
             <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>
       currently only supported on Linux systems.</para>
 
 
       <para>For non-Linux systems, there is currently stub
       <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>
 
 
     <section id="packet-handling">
     <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>
 </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
 This is a debug message reporting that the authoritative server has
 discovered that the memory data source is enabled for the given class.
 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>
 </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
 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
 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.
 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>
 </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.
 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>
 </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.
 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>
 </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
 An older version of the configuration database has been found, from which
 there was an automatic upgrade path to the current version. These changes
 there was an automatic upgrade path to the current version. These changes
 are now applied, and no action from the administrator is necessary.
 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>
 </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 configuration manager sent a configuration update to a module, but
 the module responded with an answer that could not be parsed. The answer
 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
 error is given. The most likely cause is that the system does not have
 write access to the configuration database file. The updated
 write access to the configuration database file. The updated
 configuration is not stored.
 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>
 </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
 There was a keyboard interrupt signal to stop the cfgmgr daemon. The
 daemon will now shut down.
 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>
 </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
 Debug information. A search in an database data source for NSEC3 that
 matches or covers the given name is being started.
 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
 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>
 </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)
 Debug information. An NSEC3 that matches (a possibly superdomain of)
 the given name is found and being returned.  When the shown label
 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
 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
 located RRs that match the name and class but not the type.  DNSSEC information
 has been requested and returned.
 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
 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
 name, and it either matches the type or has a relevant type. The RRset that is
 returned is printed.
 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
 nonterminal (e.g. there's nothing at *.example.com but something like
 subdomain.*.example.org, do exist: so *.example.org exists in the
 subdomain.*.example.org, do exist: so *.example.org exists in the
 namespace but has no RRs assopciated with it). This will produce NXRRSET.
 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
 The database doesn't contain directly matching name.  When searching
 for a wildcard match, a wildcard record matching the name and type of
 for a wildcard match, a wildcard record matching the name and type of
 the query was found. The data at this point is returned.
 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
 (probably coming from b10-auth). We continue serving on whatever other
 connections we already have, but this connection is dropped. The reason
 connections we already have, but this connection is dropped. The reason
 is logged.
 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>
 </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
 There was a problem reading from the command and control channel. The
 most likely cause is that the msgq process is not running.
 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>
 </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
 The ddns process encountered an error when installing the configuration at
 startup time.  Details of the error are included in the log message.
 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>
 </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
 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
 connects to the ddns daemon). This might be OK, for example when the
 authoritative server shuts down, the connection would get closed. It also
 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
 can mean the system is busy and can't keep up or that the other side got
 confused and sent bad data.
 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>
 </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
 There was a problem in the lower level module handling configuration and
 control commands.  This could happen for various reasons, but the most likely
 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
 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
 comes from is logged. The connection is over a unix domain socket and is likely
 coming from a b10-auth process.
 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>
 </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
 The ddns process received a shutdown command from the command channel
 and will now shut down.
 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>
 </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.
 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
 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
 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
 or updates. Any command or update that is being addressed at this moment will
 be completed, after which the process will exit.
 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>
 </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
 The ddns process has successfully stopped and is no longer listening for or
 handling commands or updates, and will now exit.
 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
 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
 down. This is indicative of a programming error and should not happen under
 normal circumstances. The exception type and message are printed.
 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>
 </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
 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
 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
 An invalid message identification (ID) has been found during the read of
 a message file.  Message IDs should comprise only alphanumeric characters
 a message file.  Message IDs should comprise only alphanumeric characters
 and the underscore, and should not start with a digit.
 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>
 </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
 The $NAMESPACE directive in a message file takes a single argument, a
 namespace in which all the generated symbol names are placed.  This error
 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>
 </p><p>
 This message indicates an internal error in the NSAS.  Please raise a
 This message indicates an internal error in the NSAS.  Please raise a
 bug report.
 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>
 </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
 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.
 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
 installation problem, where the specification file stats.spec is
 from a different version of BIND 10 than the stats module itself.
 from a different version of BIND 10 than the stats module itself.
 Please check your installation.
 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>
 </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
 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
 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
 In general, this should only occur for unexpected problems like
 memory allocation failures, as the query should already have been
 memory allocation failures, as the query should already have been
 parsed by the b10-auth daemon, before it was passed here.
 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>
 </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
 The xfrout process silently dropped a request to transfer zone to
 given host.  This is required by the ACLs.  The %2 represents the IP
 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.
 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>
 </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
 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>
 </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
 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
 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>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="AUTH_NOTIFY_QUESTIONS">
 <term>AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY</term>
 <term>AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY</term>
 <listitem><para>
 <listitem><para>
@@ -882,6 +900,15 @@ The boss module is sending a SIGTERM signal to the given process.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="BIND10_SETUID">
 <term>BIND10_SETUID setting UID to %1</term>
 <term>BIND10_SETUID setting UID to %1</term>
 <listitem><para>
 <listitem><para>
@@ -1588,6 +1615,16 @@ are now applied, and no action from the administrator is necessary.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE">
 <term>CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE Unable to parse response from module %1: %2</term>
 <term>CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE Unable to parse response from module %1: %2</term>
 <listitem><para>
 <listitem><para>
@@ -1647,15 +1684,6 @@ configuration is not stored.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="CFGMGR_STOPPED_BY_KEYBOARD">
 <term>CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down</term>
 <term>CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down</term>
 <listitem><para>
 <listitem><para>
@@ -2074,10 +2102,13 @@ matches or covers the given name is being started.
 </varlistentry>
 </varlistentry>
 
 
 <varlistentry id="DATASRC_DATABASE_FINDNSEC3_COVER">
 <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>
 <listitem><para>
 Debug information. An NSEC3 that covers the given name is found and
 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>
 </para></listitem>
 </varlistentry>
 </varlistentry>
 
 
@@ -2212,7 +2243,7 @@ has been requested and returned.
 </varlistentry>
 </varlistentry>
 
 
 <varlistentry id="DATASRC_DATABASE_FOUND_RRSET">
 <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>
 <listitem><para>
 The data returned by the database backend contained data for the given domain
 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
 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>
 
 
 <varlistentry id="DATASRC_DATABASE_WILDCARD_MATCH">
 <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>
 <listitem><para>
 The database doesn't contain directly matching name.  When searching
 The database doesn't contain directly matching name.  When searching
 for a wildcard match, a wildcard record matching the name and type of
 for a wildcard match, a wildcard record matching the name and type of
@@ -3603,6 +3634,16 @@ is logged.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="DDNS_CC_SESSION_ERROR">
 <term>DDNS_CC_SESSION_ERROR error reading from cc channel: %1</term>
 <term>DDNS_CC_SESSION_ERROR error reading from cc channel: %1</term>
 <listitem><para>
 <listitem><para>
@@ -3628,6 +3669,18 @@ startup time.  Details of the error are included in the log message.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="DDNS_DROP_CONN">
 <term>DDNS_DROP_CONN dropping connection on file descriptor %1 because of error %2</term>
 <term>DDNS_DROP_CONN dropping connection on file descriptor %1 because of error %2</term>
 <listitem><para>
 <listitem><para>
@@ -3639,6 +3692,33 @@ confused and sent bad data.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="DDNS_MODULECC_SESSION_ERROR">
 <term>DDNS_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1</term>
 <term>DDNS_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1</term>
 <listitem><para>
 <listitem><para>
@@ -3660,6 +3740,15 @@ coming from a b10-auth process.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="DDNS_RECEIVED_SHUTDOWN_COMMAND">
 <term>DDNS_RECEIVED_SHUTDOWN_COMMAND shutdown command received</term>
 <term>DDNS_RECEIVED_SHUTDOWN_COMMAND shutdown command received</term>
 <listitem><para>
 <listitem><para>
@@ -3668,11 +3757,105 @@ and will now shut down.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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>
 <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>
 </para></listitem>
 </varlistentry>
 </varlistentry>
 
 
@@ -3694,6 +3877,14 @@ be completed, after which the process will exit.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="DDNS_STOPPED">
 <term>DDNS_STOPPED ddns server has stopped</term>
 <term>DDNS_STOPPED ddns server has stopped</term>
 <listitem><para>
 <listitem><para>
@@ -3719,6 +3910,362 @@ normal circumstances. The exception type and message are printed.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <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>
 <term>LIBXFRIN_DIFFERENT_TTL multiple data with different TTLs (%1, %2) on %3/%4/%5. Adjusting %2 -&gt; %1.</term>
 <listitem><para>
 <listitem><para>
@@ -3836,6 +4383,13 @@ and the underscore, and should not start with a digit.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="LOG_NAMESPACE_EXTRA_ARGS">
 <term>LOG_NAMESPACE_EXTRA_ARGS line %1: $NAMESPACE directive has too many arguments</term>
 <term>LOG_NAMESPACE_EXTRA_ARGS line %1: $NAMESPACE directive has too many arguments</term>
 <listitem><para>
 <listitem><para>
@@ -4227,6 +4781,55 @@ bug report.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="PYSERVER_COMMON_TSIG_KEYRING_DEINIT">
 <term>PYSERVER_COMMON_TSIG_KEYRING_DEINIT Deinitializing global TSIG keyring</term>
 <term>PYSERVER_COMMON_TSIG_KEYRING_DEINIT Deinitializing global TSIG keyring</term>
 <listitem><para>
 <listitem><para>
@@ -5474,20 +6077,6 @@ Please check your installation.
 </para></listitem>
 </para></listitem>
 </varlistentry>
 </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">
 <varlistentry id="XFRIN_AUTH_LOADZONE">
 <term>XFRIN_AUTH_LOADZONE sending Auth loadzone for origin=%1, class=%2, datasrc=%3</term>
 <term>XFRIN_AUTH_LOADZONE sending Auth loadzone for origin=%1, class=%2, datasrc=%3</term>
 <listitem><para>
 <listitem><para>
@@ -6027,12 +6616,16 @@ parsed by the b10-auth daemon, before it was passed here.
 </varlistentry>
 </varlistentry>
 
 
 <varlistentry id="XFROUT_PROCESS_REQUEST_ERROR">
 <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>
 <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>
 </para></listitem>
 </varlistentry>
 </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>
 <term>XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR error receiving the file descriptor for an XFR connection</term>
 <listitem><para>
 <listitem><para>
 There was an error receiving the file descriptor for the transfer
 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>
 </para></listitem>
 </varlistentry>
 </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
 This is a debug message reporting that the authoritative server has
 discovered that the memory data source is enabled for the given class.
 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
 % AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY
 This debug message is logged by the authoritative server when it receives
 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
 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
 unsupported opcode. (The opcode is included in the message.) The server
 will return an error code of NOTIMPL to the sender.
 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
 % AUTH_XFRIN_CHANNEL_CREATED XFRIN session channel created
 This is a debug message indicating that the authoritative server has
 This is a debug message indicating that the authoritative server has
 created a channel to the XFRIN (Transfer-in) process.  It is issued
 created a channel to the XFRIN (Transfer-in) process.  It is issued

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

@@ -2,12 +2,12 @@
 .\"     Title: b10-auth
 .\"     Title: b10-auth
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: May 16, 2012
+.\"      Date: June 20, 2012
 .\"    Manual: BIND10
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"  Language: English
 .\"
 .\"
-.TH "B10\-AUTH" "8" "May 16, 2012" "BIND10" "BIND10"
+.TH "B10\-AUTH" "8" "June 20, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
@@ -49,7 +49,7 @@ Do not cache answers in memory\&. The default is to use the cache for faster res
 .PP
 .PP
 \fB\-v\fR
 \fB\-v\fR
 .RS 4
 .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
 .RE
 .SH "CONFIGURATION AND COMMANDS"
 .SH "CONFIGURATION AND COMMANDS"
 .PP
 .PP

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

@@ -20,7 +20,7 @@
 <refentry>
 <refentry>
 
 
   <refentryinfo>
   <refentryinfo>
-    <date>May 16, 2012</date>
+    <date>June 20, 2012</date>
   </refentryinfo>
   </refentryinfo>
 
 
   <refmeta>
   <refmeta>
@@ -94,8 +94,8 @@
       <varlistentry>
       <varlistentry>
         <term><option>-v</option></term>
         <term><option>-v</option></term>
         <listitem><para>
         <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>
         </para></listitem>
       </varlistentry>
       </varlistentry>
 
 

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

@@ -88,7 +88,7 @@ usage() {
     cerr << "Usage:  b10-auth [-u user] [-nv]"
     cerr << "Usage:  b10-auth [-u user] [-nv]"
          << endl;
          << endl;
     cerr << "\t-n: do not cache answers in memory" << 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);
     exit(1);
 }
 }
 
 

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

@@ -22,7 +22,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 CLEANFILES = *.gcno *.gcda
 
 
 TESTS_ENVIRONMENT = \
 TESTS_ENVIRONMENT = \
-        libtool --mode=execute $(VALGRIND_COMMAND)
+        $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 
 # Do not define global tests, use check-local so
 # Do not define global tests, use check-local so
 # environment can be set (needed for dynamic loading)
 # environment can be set (needed for dynamic loading)

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

@@ -1303,11 +1303,6 @@ public:
         return (real_zone_finder_->findNSEC3(name, recursive));
         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:
 private:
     isc::datasrc::ZoneFinderPtr real_zone_finder_;
     isc::datasrc::ZoneFinderPtr real_zone_finder_;
     ThrowWhen throw_when_;
     ThrowWhen throw_when_;

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

@@ -452,10 +452,6 @@ public:
     // answers when DNSSEC is required.
     // answers when DNSSEC is required.
     void setNSEC3Flag(bool on) { use_nsec3_ = on; }
     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.
     // 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)
     // \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)
 % BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2)
 The boss module is sending a SIGTERM signal to the given process.
 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
 % BIND10_SETUID setting UID to %1
 The boss switches the user it runs as to the given UID.
 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,
     def __init__(self, msgq_socket_file=None, data_path=None,
                  config_filename=None, clear_config=False, nocache=False,
                  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).
             Initialize the Boss of BIND. This is a singleton (only one can run).
         
         
@@ -208,6 +208,7 @@ class BoB:
         self.components_to_restart = []
         self.components_to_restart = []
         self.runnable = False
         self.runnable = False
         self.uid = setuid
         self.uid = setuid
+        self.gid = setgid
         self.username = username
         self.username = username
         self.verbose = verbose
         self.verbose = verbose
         self.nokill = nokill
         self.nokill = nokill
@@ -1156,12 +1157,14 @@ def main():
 
 
     # Check user ID.
     # Check user ID.
     setuid = None
     setuid = None
+    setgid = None
     username = None
     username = None
     if options.user:
     if options.user:
         # Try getting information about the user, assuming UID passed.
         # Try getting information about the user, assuming UID passed.
         try:
         try:
             pw_ent = pwd.getpwuid(int(options.user))
             pw_ent = pwd.getpwuid(int(options.user))
             setuid = pw_ent.pw_uid
             setuid = pw_ent.pw_uid
+            setgid = pw_ent.pw_gid
             username = pw_ent.pw_name
             username = pw_ent.pw_name
         except ValueError:
         except ValueError:
             pass
             pass
@@ -1175,6 +1178,7 @@ def main():
         try:
         try:
             pw_ent = pwd.getpwnam(options.user)
             pw_ent = pwd.getpwnam(options.user)
             setuid = pw_ent.pw_uid
             setuid = pw_ent.pw_uid
+            setgid = pw_ent.pw_gid
             username = pw_ent.pw_name
             username = pw_ent.pw_name
         except KeyError:
         except KeyError:
             pass
             pass
@@ -1205,7 +1209,7 @@ def main():
         boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
         boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
                            options.config_file, options.clear_config,
                            options.config_file, options.clear_config,
                            options.nocache, options.verbose, options.nokill,
                            options.nocache, options.verbose, options.nokill,
-                           setuid, username, options.cmdctl_port,
+                           setuid, setgid, username, options.cmdctl_port,
                            options.wait_time)
                            options.wait_time)
         startup_result = boss_of_bind.startup()
         startup_result = boss_of_bind.startup()
         if startup_result:
         if startup_result:

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

@@ -2,12 +2,12 @@
 .\"     Title: bindctl
 .\"     Title: bindctl
 .\"    Author: [see the "AUTHORS" section]
 .\"    Author: [see the "AUTHORS" section]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: December 23, 2010
+.\"      Date: June 20, 2012
 .\"    Manual: BIND10
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"  Language: English
 .\"
 .\"
-.TH "BINDCTL" "1" "December 23, 2010" "BIND10" "BIND10"
+.TH "BINDCTL" "1" "June 20, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
@@ -35,7 +35,7 @@ via its interactive command interpreter\&.
 communicates over a HTTPS REST\-ful interface provided by
 communicates over a HTTPS REST\-ful interface provided by
 \fBb10-cmdctl\fR(8)\&. The
 \fBb10-cmdctl\fR(8)\&. The
 \fBb10-cfgmgr\fR(8)
 \fBb10-cfgmgr\fR(8)
-daemon stores the configurations and defines the commands\&.
+daemon stores the configurations\&.
 .SH "ARGUMENTS"
 .SH "ARGUMENTS"
 .PP
 .PP
 The arguments are as follows:
 The arguments are as follows:
@@ -91,9 +91,9 @@ Display the version number and exit\&.
 .SH "AUTHENTICATION"
 .SH "AUTHENTICATION"
 .PP
 .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
 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"
 .SH "USAGE"
 .PP
 .PP
 The
 The
@@ -115,8 +115,7 @@ keyword to receive usage assistance for a module or a module\'s command\&.
 The
 The
 \fBquit\fR
 \fBquit\fR
 command is used to exit
 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
 .PP
 The following module is available by default:
 The following module is available by default:
 \fBconfig\fR
 \fBconfig\fR

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

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

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

@@ -2,12 +2,12 @@
 .\"     Title: b10-cfgmgr
 .\"     Title: b10-cfgmgr
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: April 12, 2010
+.\"      Date: June 20, 2012
 .\"    Manual: BIND10
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"  Language: English
 .\"
 .\"
-.TH "B10\-CFGMGR" "8" "April 12, 2010" "BIND10" "BIND10"
+.TH "B10\-CFGMGR" "8" "June 20, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
@@ -42,10 +42,6 @@ C\-Channel connection\&. If this connection is not established,
 will exit\&.
 will exit\&.
 .PP
 .PP
 The daemon may be cleanly stopped by sending the SIGTERM signal to the process\&. This shutdown does not notify the subscribers\&.
 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"
 .SH "ARGUMENTS"
 .PP
 .PP
 The arguments are as follows:
 The arguments are as follows:
@@ -59,9 +55,7 @@ will use the default configurations\&. The name of the backup file can be found
 .PP
 .PP
 \fB\-c\fR \fIconfig\-filename\fR, \fB\-\-config\-filename\fR \fIconfig\-filename\fR
 \fB\-c\fR \fIconfig\-filename\fR, \fB\-\-config\-filename\fR \fIconfig\-filename\fR
 .RS 4
 .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
 .RE
 .PP
 .PP
 \fB\-p\fR \fIdata\-path\fR, \fB\-\-data\-path\fR \fIdata\-path\fR
 \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>
 <refentry>
 
 
   <refentryinfo>
   <refentryinfo>
-    <date>April 12, 2010</date>
+    <date>June 20, 2012</date>
   </refentryinfo>
   </refentryinfo>
 
 
   <refmeta>
   <refmeta>
@@ -80,13 +80,6 @@
       subscribers.
       subscribers.
     </para>
     </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 -->
 <!-- TODO: add a verbose or quiet switch so it is not so noisy -->
   </refsect1>
   </refsect1>
 
 
@@ -121,8 +114,8 @@
         </term>
         </term>
         <listitem>
         <listitem>
           <para>The configuration database filename to use. Can be either
           <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>
         </listitem>
       </varlistentry>
       </varlistentry>
 
 

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>
 <refentry>
 
 
   <refentryinfo>
   <refentryinfo>
-    <date>March 20, 2012</date>
+    <date>June 20, 2012</date>
   </refentryinfo>
   </refentryinfo>
 
 
   <refmeta>
   <refmeta>
@@ -60,14 +60,15 @@
   <refsect1>
   <refsect1>
     <title>DESCRIPTION</title>
     <title>DESCRIPTION</title>
     <para>
     <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>
 
 
     <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.
       or upgrade mode.
     </para>
     </para>
 
 
@@ -76,9 +77,10 @@
       utility reads the version of the database schema from the database
       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
       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
       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
       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.
       parsing or execution.
     </para>
     </para>
 
 
@@ -115,8 +117,8 @@
         </term>
         </term>
         <listitem>
         <listitem>
           <para>Selects the version check function, which reports the
           <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>
           </para>
         </listitem>
         </listitem>
       </varlistentry>
       </varlistentry>
@@ -126,11 +128,12 @@
          <option>--noconfirm</option>
          <option>--noconfirm</option>
         </term>
         </term>
         <listitem>
         <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
           Normally the utility will print a warning that an upgrade is
           about to take place and request that you type "Yes" to continue.
           about to take place and request that you type "Yes" to continue.
           If this switch is given on the command line, no prompt will
           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>
           </para>
         </listitem>
         </listitem>
       </varlistentry>
       </varlistentry>
@@ -141,15 +144,16 @@
         </term>
         </term>
         <listitem>
         <listitem>
           <para>Selects the upgrade function, which upgrades the database
           <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>
           <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>
           </para>
         </listitem>
         </listitem>
       </varlistentry>
       </varlistentry>
@@ -160,7 +164,7 @@
         </term>
         </term>
         <listitem>
         <listitem>
           <para>Enable verbose mode.  Each SQL command issued by the
           <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>
         </listitem>
       </varlistentry>
       </varlistentry>
 
 
@@ -181,7 +185,7 @@
         </term>
         </term>
         <listitem>
         <listitem>
           <para>
           <para>
-          Name of the database file to check of upgrade.
+          Name of the database file to check or upgrade.
           </para>
           </para>
         </listitem>
         </listitem>
       </varlistentry>
       </varlistentry>

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

@@ -2,12 +2,12 @@
 .\"     Title: b10-ddns
 .\"     Title: b10-ddns
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: February 28, 2012
+.\"      Date: June 18, 2012
 .\"    Manual: BIND10
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"  Language: English
 .\"
 .\"
-.TH "B10\-DDNS" "8" "February 28, 2012" "BIND10" "BIND10"
+.TH "B10\-DDNS" "8" "June 18, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
@@ -29,33 +29,29 @@ The
 \fBb10\-ddns\fR
 \fBb10\-ddns\fR
 daemon provides the BIND 10 Dynamic Update (DDNS) service, as specified in RFC 2136\&. Normally it is started by the
 daemon provides the BIND 10 Dynamic Update (DDNS) service, as specified in RFC 2136\&. Normally it is started by the
 \fBbind10\fR(8)
 \fBbind10\fR(8)
-boss process\&. When the
+boss process\&.
+.PP
+When the
 \fBb10\-auth\fR
 \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
 \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
 .PP
 This daemon communicates with BIND 10 over a
 This daemon communicates with BIND 10 over a
 \fBb10-msgq\fR(8)
 \fBb10-msgq\fR(8)
 C\-Channel connection\&. If this connection is not established,
 C\-Channel connection\&. If this connection is not established,
 \fBb10\-ddns\fR
 \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
 .PP
 
 
 \fBb10\-ddns\fR
 \fBb10\-ddns\fR
@@ -65,9 +61,16 @@ receives its configurations from
 .PP
 .PP
 The arguments are as follows:
 The arguments are as follows:
 .PP
 .PP
+\fB\-h\fR, \fB\-\-help\fR
+.RS 4
+Print the command line arguments and exit\&.
+.RE
+.PP
 \fB\-v\fR, \fB\-\-verbose\fR
 \fB\-v\fR, \fB\-\-verbose\fR
 .RS 4
 .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
 .RE
 .SH "CONFIGURATION AND COMMANDS"
 .SH "CONFIGURATION AND COMMANDS"
 .PP
 .PP
@@ -75,7 +78,13 @@ The configurable settings are:
 .PP
 .PP
 
 
 \fIzones\fR
 \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
 .PP
 The module commands are:
 The module commands are:
 .PP
 .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-auth\fR(8),
 \fBb10-cfgmgr\fR(8),
 \fBb10-cfgmgr\fR(8),
 \fBb10-msgq\fR(8),
 \fBb10-msgq\fR(8),
+\fBb10-xfrout\fR(8),
+\fBb10-zonemgr\fR(8),
 \fBbind10\fR(8),
 \fBbind10\fR(8),
 BIND 10 Guide\&.
 BIND 10 Guide\&.
 .SH "HISTORY"
 .SH "HISTORY"
 .PP
 .PP
 The
 The
 \fBb10\-ddns\fR
 \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"
 .SH "COPYRIGHT"
 .br
 .br
 Copyright \(co 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 Copyright \(co 2011-2012 Internet Systems Consortium, Inc. ("ISC")

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

@@ -20,7 +20,7 @@
 <refentry>
 <refentry>
 
 
   <refentryinfo>
   <refentryinfo>
-    <date>February 28, 2012</date>
+    <date>June 18, 2012</date>
   </refentryinfo>
   </refentryinfo>
 
 
   <refmeta>
   <refmeta>
@@ -58,23 +58,33 @@
       Normally it is started by the
       Normally it is started by the
       <citerefentry><refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       <citerefentry><refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       boss process.
       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>
     </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>
     <para>
       This daemon communicates with BIND 10 over a
       This daemon communicates with BIND 10 over a
       <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       C-Channel connection.  If this connection is not established,
       C-Channel connection.  If this connection is not established,
       <command>b10-ddns</command> will exit.
       <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>
 
 
     <para>
     <para>
@@ -92,13 +102,24 @@
 
 
       <varlistentry>
       <varlistentry>
         <term>
         <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>-v</option>,
           <option>--verbose</option>
           <option>--verbose</option>
         </term>
         </term>
         <listitem>
         <listitem>
           <para>
           <para>
             This value is ignored at this moment, but is provided for
             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>
           </para>
         </listitem>
         </listitem>
       </varlistentry>
       </varlistentry>
@@ -112,10 +133,18 @@
     </para>
     </para>
     <para>
     <para>
       <varname>zones</varname>
       <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>
 
 
     <para>
     <para>
@@ -145,6 +174,12 @@
         <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
         <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       </citerefentry>,
       <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>
         <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       </citerefentry>,
       <citetitle>BIND 10 Guide</citetitle>.
       <citetitle>BIND 10 Guide</citetitle>.
@@ -156,6 +191,7 @@
     <para>
     <para>
       The <command>b10-ddns</command> daemon was first implemented
       The <command>b10-ddns</command> daemon was first implemented
       in December 2011 for the ISC BIND 10 project.
       in December 2011 for the ISC BIND 10 project.
+      The first functional version was released in June 2012.
     </para>
     </para>
   </refsect1>
   </refsect1>
 </refentry><!--
 </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
 from isc.cc import SessionError, SessionTimeout, ProtocolError
 import isc.util.process
 import isc.util.process
 import isc.util.cio.socketsession
 import isc.util.cio.socketsession
-from isc.notify.notify_out import ZONE_NEW_DATA_READY_CMD
 import isc.server_common.tsig_keyring
 import isc.server_common.tsig_keyring
 from isc.server_common.dns_tcp import DNSTCPContext
 from isc.server_common.dns_tcp import DNSTCPContext
 from isc.datasrc import DataSourceClient
 from isc.datasrc import DataSourceClient
@@ -543,7 +542,7 @@ class DDNSServer:
     def __notify_xfrout(self, zname, zclass):
     def __notify_xfrout(self, zname, zclass):
         '''Notify xfrout of the update.'''
         '''Notify xfrout of the update.'''
         param = {'zone_name': zname.to_text(), 'zone_class': zclass.to_text()}
         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)
         self.__notify_update(XFROUT_MODULE_NAME, msg, zname, zclass)
 
 
     def __notify_update(self, modname, 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.
         Get and process all commands sent from cfgmgr or other modules.
         This loops waiting for events until self.shutdown() has been called.
         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()
         cc_fileno = self._cc.get_socket().fileno()
         listen_fileno = self._listen_socket.fileno()
         listen_fileno = self._listen_socket.fileno()
         while not self._shutdown:
         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
 side, but the administrator may want to check the general reachability
 with the client address.
 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)
 % DDNS_SECONDARY_ZONES_UPDATE updated secondary zone list (%1 zones are listed)
 b10-ddns has successfully updated the internal copy of secondary zones
 b10-ddns has successfully updated the internal copy of secondary zones
 obtained from b10-zonemgr, based on a latest update to zonemgr's
 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
 or updates. Any command or update that is being addressed at this moment will
 be completed, after which the process will exit.
 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
 % DDNS_STOPPED ddns server has stopped
 The ddns process has successfully stopped and is no longer listening for or
 The ddns process has successfully stopped and is no longer listening for or
 handling commands or updates, and will now exit.
 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
 % 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
 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
 pkglibexec_PROGRAMS = b10-dhcp4
 
 
 b10_dhcp4_SOURCES = main.cc dhcp4_srv.cc dhcp4_srv.h
 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
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
 # 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/exceptions/libexceptions.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.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/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_dhcp4dir = $(pkgdatadir)
 b10_dhcp4_DATA = dhcp4.spec
 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_spec": {
-    "module_name": "dhcp4",
+    "module_name": "Dhcp4",
     "module_description": "DHCPv4 server daemon",
     "module_description": "DHCPv4 server daemon",
     "config_data": [
     "config_data": [
       { "item_name": "interface",
       { "item_name": "interface",
@@ -9,6 +9,18 @@
         "item_default": "eth0"
         "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() {
 Dhcpv4Srv::~Dhcpv4Srv() {
-    cout << "DHCPv4 server shutdown." << endl;
+    cout << "b10-dhcp4: DHCPv4 server terminating." << endl;
     IfaceMgr::instance().closeSockets();
     IfaceMgr::instance().closeSockets();
 }
 }
 
 
+void Dhcpv4Srv::shutdown() {
+    cout << "b10-dhcp4: DHCPv4 server shutdown." << endl;
+    shutdown_ = true;
+}
+
 bool
 bool
 Dhcpv4Srv::run() {
 Dhcpv4Srv::run() {
     while (!shutdown_) {
     while (!shutdown_) {
+        /// @todo: calculate actual timeout once we have lease database
+        int timeout = 1000;
+
         // client's message and server's response
         // client's message and server's response
-        Pkt4Ptr query = IfaceMgr::instance().receive4();
+        Pkt4Ptr query = IfaceMgr::instance().receive4(timeout);
         Pkt4Ptr rsp;
         Pkt4Ptr rsp;
 
 
         if (query) {
         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
 /// that is going to be used as server-identifier, receives incoming
 /// packets, processes them, manages leases assignment and generates
 /// packets, processes them, manages leases assignment and generates
 /// appropriate responses.
 /// 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 {
 class Dhcpv4Srv : public boost::noncopyable {
 
 
     public:
     public:
@@ -60,6 +67,9 @@ class Dhcpv4Srv : public boost::noncopyable {
     ///         critical error.
     ///         critical error.
     bool run();
     bool run();
 
 
+    /// @brief Instructs the server to shut down.
+    void shutdown();
+
 protected:
 protected:
     /// @brief Processes incoming DISCOVER and returns response.
     /// @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
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -13,41 +13,31 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <config.h>
 #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 <iostream>
-
 #include <exceptions/exceptions.h>
 #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 <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 <dhcp4/dhcp4_srv.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp4.h>
 
 
 using namespace std;
 using namespace std;
-using namespace isc::util;
-
-using namespace isc;
 using namespace isc::dhcp;
 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 {
 namespace {
 
 
-bool verbose_mode = false;
+const char* const DHCP4_NAME = "b10-dhcp4";
 
 
 void
 void
 usage() {
 usage() {
@@ -62,6 +52,7 @@ usage() {
 int
 int
 main(int argc, char* argv[]) {
 main(int argc, char* argv[]) {
     int ch;
     int ch;
+    bool verbose_mode = false; // should server be verbose?
     int port_number = DHCP4_SERVER_PORT; // The default. any other values are
     int port_number = DHCP4_SERVER_PORT; // The default. any other values are
                                          // useful for testing only.
                                          // 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) {
     if (argc - optind > 0) {
         usage();
         usage();
@@ -94,27 +90,14 @@ main(int argc, char* argv[]) {
 
 
     int ret = EXIT_SUCCESS;
     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 {
     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;
         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) {
     } catch (const std::exception& ex) {
         cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl;
         cerr << "[b10-dhcp4] Server failed: " << ex.what() << endl;

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

@@ -39,16 +39,17 @@ AM_LDFLAGS = -static
 endif
 endif
 
 
 TESTS_ENVIRONMENT = \
 TESTS_ENVIRONMENT = \
-        libtool --mode=execute $(VALGRIND_COMMAND)
+        $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 
 TESTS =
 TESTS =
 if HAVE_GTEST
 if HAVE_GTEST
 
 
 TESTS += dhcp4_unittests
 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_unittests.cc
 dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
 dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
+dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc
 
 
 if USE_CLANGPP
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
 # 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/exceptions/libexceptions.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.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/asiolink/libasiolink.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 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
 endif
 
 
 TESTS_ENVIRONMENT = \
 TESTS_ENVIRONMENT = \
-        libtool --mode=execute $(VALGRIND_COMMAND)
+        $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 
 TESTS =
 TESTS =
 if HAVE_GTEST
 if HAVE_GTEST

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

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

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

@@ -9,7 +9,7 @@ AM_LDFLAGS = -static
 endif
 endif
 
 
 TESTS_ENVIRONMENT = \
 TESTS_ENVIRONMENT = \
-        libtool --mode=execute $(VALGRIND_COMMAND)
+        $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 
 TESTS =
 TESTS =
 if HAVE_GTEST
 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
       process runs as a process separated from the process of the BIND 10 Stats
       daemon (<command>b10-stats</command>). The server is initially executed
       daemon (<command>b10-stats</command>). The server is initially executed
       by the BIND 10 boss process (<command>bind10</command>) and eventually
       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
       clients like web browsers and third-party modules. When the server is
       asked, it requests BIND 10 statistics data or its schema from
       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)
       which are XML (Extensible Markup Language), XSD (XML Schema definition)
       and XSL (Extensible Stylesheet Language). The XML document is the
       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
       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
       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
       of the root document. For example, you would be redirected to
@@ -82,6 +82,8 @@
     </para>
     </para>
   </refsect1>
   </refsect1>
 
 
+<!-- TODO: this is too verbose; move some of this into the guide instead -->
+
   <refsect1>
   <refsect1>
     <title>OPTIONS</title>
     <title>OPTIONS</title>
     <para>The argument is as follow:</para>
     <para>The argument is as follow:</para>

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

@@ -2,12 +2,12 @@
 .\"     Title: b10-stats
 .\"     Title: b10-stats
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: March 1, 2012
+.\"      Date: June 20, 2012
 .\"    Manual: BIND10
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"  Language: English
 .\"
 .\"
-.TH "B10\-STATS" "8" "March 1, 2012" "BIND10" "BIND10"
+.TH "B10\-STATS" "8" "June 20, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
@@ -27,18 +27,21 @@ b10-stats \- BIND 10 statistics module
 .PP
 .PP
 The
 The
 \fBb10\-stats\fR
 \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
 \fBb10\-msgq\fR
 with other modules like
 with other modules like
 \fBbind10\fR,
 \fBbind10\fR,
 \fBb10\-auth\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
 \fBb10\-stats\fR
 invokes an internal command for
 invokes an internal command for
 \fBbind10\fR
 \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\&.
 \fBbind10\fR\&.
 .SH "OPTIONS"
 .SH "OPTIONS"
 .PP
 .PP
@@ -46,9 +49,7 @@ The arguments are as follows:
 .PP
 .PP
 \fB\-v\fR, \fB\-\-verbose\fR
 \fB\-v\fR, \fB\-\-verbose\fR
 .RS 4
 .RS 4
-This
-\fBb10\-stats\fR
-switches to verbose mode\&. It sends verbose messages to STDOUT\&.
+This enables maximum debug logging\&.
 .RE
 .RE
 .SH "CONFIGURATION AND COMMANDS"
 .SH "CONFIGURATION AND COMMANDS"
 .PP
 .PP

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

@@ -20,7 +20,7 @@
 <refentry>
 <refentry>
 
 
   <refentryinfo>
   <refentryinfo>
-    <date>March 1, 2012</date>
+    <date>June 20, 2012</date>
   </refentryinfo>
   </refentryinfo>
 
 
   <refmeta>
   <refmeta>
@@ -52,22 +52,22 @@
   <refsect1>
   <refsect1>
     <title>DESCRIPTION</title>
     <title>DESCRIPTION</title>
     <para>
     <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
       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>
     </para>
   </refsect1>
   </refsect1>
 
 
@@ -79,8 +79,7 @@
         <term><option>-v</option>, <option>--verbose</option></term>
         <term><option>-v</option>, <option>--verbose</option></term>
         <listitem>
         <listitem>
 	  <para>
 	  <para>
-          This <command>b10-stats</command> switches to verbose
-          mode. It sends verbose messages to STDOUT.
+          This enables maximum debug logging.
 	  </para>
 	  </para>
         </listitem>
         </listitem>
       </varlistentry>
       </varlistentry>

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

@@ -495,7 +495,7 @@ if __name__ == "__main__":
         parser = OptionParser()
         parser = OptionParser()
         parser.add_option(
         parser.add_option(
             "-v", "--verbose", dest="verbose", action="store_true",
             "-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()
         (options, args) = parser.parse_args()
         if options.verbose:
         if options.verbose:
             isc.log.init("b10-stats", "DEBUG", 99)
             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 = OptionParser()
         parser.add_option(
         parser.add_option(
             "-v", "--verbose", dest="verbose", action="store_true",
             "-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()
         (options, args) = parser.parse_args()
         if options.verbose:
         if options.verbose:
             isc.log.init("b10-stats-httpd", "DEBUG", 99)
             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'
 XFROUT_MODULE_NAME = 'Xfrout'
 ZONE_MANAGER_MODULE_NAME = 'Zonemgr'
 ZONE_MANAGER_MODULE_NAME = 'Zonemgr'
 REFRESH_FROM_ZONEMGR = 'refresh_from_zonemgr'
 REFRESH_FROM_ZONEMGR = 'refresh_from_zonemgr'
-ZONE_XFRIN_FAILED = 'zone_xfrin_failed'
 
 
 # Constants for debug levels.
 # Constants for debug levels.
 DBG_XFRIN_TRACE = logger.DBGLVL_TRACE_BASIC
 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)
                 logger.error(XFRIN_MSGQ_SEND_ERROR, XFROUT_MODULE_NAME, ZONE_MANAGER_MODULE_NAME)
 
 
         else:
         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.
             # catch the exception, in case msgq has been killed.
             try:
             try:
                 seq = self._send_cc_session.group_sendmsg(msg, ZONE_MANAGER_MODULE_NAME)
                 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);
         self.sendqueue.extend(data);
         return len(data)
         return len(data)
 
 
+    def fileno(self):
+        return 42               # simply return a constant dummy value
+
     def readsent(self):
     def readsent(self):
         if len(self.sendqueue) >= 2:
         if len(self.sendqueue) >= 2:
             size = 2 + struct.unpack("!H", self.sendqueue[:2])[0]
             size = 2 + struct.unpack("!H", self.sendqueue[:2])[0]
@@ -1155,6 +1158,15 @@ class TestUnixSockServer(unittest.TestCase):
     def setUp(self):
     def setUp(self):
         self.write_sock, self.read_sock = socket.socketpair()
         self.write_sock, self.read_sock = socket.socketpair()
         self.unix = MyUnixSockServer()
         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):
     def test_tsig_keyring(self):
         """
         """
@@ -1196,28 +1208,29 @@ class TestUnixSockServer(unittest.TestCase):
         # We test with UDP, as it can be "connected" without other
         # We test with UDP, as it can be "connected" without other
         # endpoint.  Note that in the current implementation _guess_remote()
         # endpoint.  Note that in the current implementation _guess_remote()
         # unconditionally returns SOCK_STREAM.
         # 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:
         if socket.has_ipv6:
             # Don't check IPv6 address on hosts not supporting them
             # 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
             # Try when pretending there's no IPv6 support
             # (No need to pretend when there's really no IPv6)
             # (No need to pretend when there's really no IPv6)
             xfrout.socket.has_ipv6 = False
             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
             # Return it back
             xfrout.socket.has_ipv6 = True
             xfrout.socket.has_ipv6 = True
 
 
@@ -1409,6 +1422,42 @@ class TestUnixSockServer(unittest.TestCase):
         sys.stdout = old_stdout
         sys.stdout = old_stdout
         os.rmdir(dir_name)
         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):
 class TestInitialization(unittest.TestCase):
     def setEnv(self, name, value):
     def setEnv(self, name, value):
         if value is None:
         if value is None:

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

@@ -678,30 +678,40 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
         except socket.error:
         except socket.error:
             logger.error(XFROUT_FETCH_REQUEST_ERROR)
             logger.error(XFROUT_FETCH_REQUEST_ERROR)
             return
             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.
         # Check self._shutdown_event to ensure the real shutdown comes.
         # Linux could trigger a spurious readable event on the _read_sock
         # Linux could trigger a spurious readable event on the _read_sock
         # due to a bug, so we need perform a double check.
         # due to a bug, so we need perform a double check.
         while not self._shutdown_event.is_set(): # Check if xfrout is shutdown
         while not self._shutdown_event.is_set(): # Check if xfrout is shutdown
             try:
             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:
             except select.error as e:
                 if e.args[0] == errno.EINTR:
                 if e.args[0] == errno.EINTR:
                     (rlist, wlist, xlist) = ([], [], [])
                     (rlist, wlist, xlist) = ([], [], [])
                     continue
                     continue
                 else:
                 else:
-                    logger.error(XFROUT_SOCKET_SELECT_ERROR, str(e))
+                    logger.error(XFROUT_SOCKET_SELECT_ERROR, e)
                     break
                     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:
             if self._read_sock in rlist:
                 continue
                 continue
 
 
             try:
             try:
-                self.process_request(request)
+                if not self.process_request(request_sock):
+                    break
             except Exception as pre:
             except Exception as pre:
-                logger.error(XFROUT_PROCESS_REQUEST_ERROR, str(pre))
+                logger.error(XFROUT_PROCESS_REQUEST_ERROR, pre)
                 break
                 break
 
 
     def _handle_request_noblock(self):
     def _handle_request_noblock(self):
@@ -713,26 +723,33 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
 
 
     def process_request(self, request):
     def process_request(self, request):
         """Receive socket fd and query message from auth, then
         """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())
         sock_fd = recv_fd(request.fileno())
         if sock_fd < 0:
         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)
         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,
         t = threading.Thread(target=self.finish_request,
-                             args = (sock_fd, request_data))
+                             args=(sock_fd, request_data))
         if self.daemon_threads:
         if self.daemon_threads:
             t.daemon = True
             t.daemon = True
         t.start()
         t.start()
+        return True
 
 
     def _guess_remote(self, sock_fd):
     def _guess_remote(self, sock_fd):
         """Guess remote address and port of the socket.
         """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?)
             # (Any idea how to simulate this in test?)
             sock_domain = socket.AF_INET
             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",
         # Identify the correct socket family.  Due to the above "trick",
         # we cannot simply use sock.family.
         # 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,
         is being used by one running xfrout process. If it is,
         return True, or else return False. '''
         return True, or else return False. '''
         try:
         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:
         except socket.error as err:
+            sock.close()
             return False
             return False
         else:
         else:
+            sock.close()
             return True
             return True
 
 
     def shutdown(self):
     def shutdown(self):
@@ -988,7 +1008,7 @@ class XfroutServer:
             self.shutdown()
             self.shutdown()
             answer = create_answer(0)
             answer = create_answer(0)
 
 
-        elif cmd == notify_out.ZONE_NEW_DATA_READY_CMD:
+        elif cmd == "notify":
             zone_name = args.get('zone_name')
             zone_name = args.get('zone_name')
             zone_class = args.get('zone_class')
             zone_class = args.get('zone_class')
             if not 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
 memory allocation failures, as the query should already have been
 parsed by the b10-auth daemon, before it was passed here.
 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
 % XFROUT_QUERY_DROPPED %1 client %2: request to transfer %3 dropped
 The xfrout process silently dropped a request to transfer zone to
 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
 % 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
 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
 % 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
 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
 import tempfile
 from zonemgr import *
 from zonemgr import *
 from isc.testutils.ccsession_mock import MockModuleCCSession
 from isc.testutils.ccsession_mock import MockModuleCCSession
+from isc.notify import notify_out
 
 
 ZONE_NAME_CLASS1_IN = ("example.net.", "IN")
 ZONE_NAME_CLASS1_IN = ("example.net.", "IN")
 ZONE_NAME_CLASS1_CH = ("example.net.", "CH")
 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))
         self.assertEqual(answer1, self.zonemgr._parse_cmd_params(params1, ZONE_NOTIFY_COMMAND))
         params2 = {"zone_name" : "example.com.", "zone_class" : "IN"}
         params2 = {"zone_name" : "example.com.", "zone_class" : "IN"}
         answer2 = ZONE_NAME_CLASS3_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)
         self.assertRaises(ZonemgrException, self.zonemgr._parse_cmd_params, params2, ZONE_NOTIFY_COMMAND)
         params1 = {"zone_class" : "CH"}
         params1 = {"zone_class" : "CH"}
         self.assertRaises(ZonemgrException, self.zonemgr._parse_cmd_params, params2, ZONE_NOTIFY_COMMAND)
         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 *
 from isc.config.ccsession import *
 import isc.util.process
 import isc.util.process
 from isc.log_messages.zonemgr_messages import *
 from isc.log_messages.zonemgr_messages import *
+from isc.notify import notify_out
 
 
 # Initialize logging for called modules.
 # Initialize logging for called modules.
 isc.log.init("b10-zonemgr")
 isc.log.init("b10-zonemgr")
@@ -78,8 +79,6 @@ XFRIN_MODULE_NAME = 'Xfrin'
 AUTH_MODULE_NAME = 'Auth'
 AUTH_MODULE_NAME = 'Auth'
 
 
 # define command name
 # 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_REFRESH_COMMAND = 'refresh_from_zonemgr'
 ZONE_NOTIFY_COMMAND = 'notify'
 ZONE_NOTIFY_COMMAND = 'notify'
 
 
@@ -623,7 +622,7 @@ class Zonemgr:
     def command_handler(self, command, args):
     def command_handler(self, command, args):
         """Handle command receivd from command channel.
         """Handle command receivd from command channel.
         ZONE_NOTIFY_COMMAND is issued by Auth process;
         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;
         Xfrin process;
         shutdown is issued by a user or Boss process. """
         shutdown is issued by a user or Boss process. """
         answer = create_answer(0)
         answer = create_answer(0)
@@ -637,7 +636,7 @@ class Zonemgr:
             # Send notification to zonemgr timer thread
             # Send notification to zonemgr timer thread
             self._master_socket.send(b" ")# make self._slave_socket readble
             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"""
             """ Handle xfrin success command"""
             zone_name_class = self._parse_cmd_params(args, 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])
             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._zone_refresh.zone_refresh_success(zone_name_class)
             self._master_socket.send(b" ")# make self._slave_socket readble
             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"""
             """ Handle xfrin fail command"""
             zone_name_class = self._parse_cmd_params(args, 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])
             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
 CLEANFILES = *.gcno *.gcda
 
 
 TESTS_ENVIRONMENT = \
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 
 TESTS =
 TESTS =
 if HAVE_GTEST
 if HAVE_GTEST

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

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

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

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

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

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

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

@@ -88,6 +88,7 @@ public:
     void startRead(boost::function<void()> user_handler);
     void startRead(boost::function<void()> user_handler);
     void setTimeout(size_t seconds) { timeout_ = seconds; };
     void setTimeout(size_t seconds) { timeout_ = seconds; };
     size_t getTimeout() const { return timeout_; };
     size_t getTimeout() const { return timeout_; };
+    int getSocketDesc();
 
 
     long int sequence_; // the next sequence number to use
     long int sequence_; // the next sequence number to use
     std::string lname_;
     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) :
 Session::Session(asio::io_service& io_service) :
     impl_(new SessionImpl(io_service))
     impl_(new SessionImpl(io_service))
 {}
 {}
@@ -273,6 +284,11 @@ Session::startRead(boost::function<void()> read_callback) {
     impl_->startRead(read_callback);
     impl_->startRead(read_callback);
 }
 }
 
 
+int
+Session::getSocketDesc() const {
+    return impl_->getSocketDesc();
+}
+
 namespace {                     // maybe unnecessary.
 namespace {                     // maybe unnecessary.
 // This is a helper class to make the establish() method (below) exception-safe
 // This is a helper class to make the establish() method (below) exception-safe
 // with the RAII approach.
 // with the RAII approach.

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

@@ -141,6 +141,11 @@ namespace isc {
             virtual bool hasQueuedMsgs() const;
             virtual bool hasQueuedMsgs() const;
             virtual void setTimeout(size_t milliseconds);
             virtual void setTimeout(size_t milliseconds);
             virtual size_t getTimeout() const;
             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:
     private:
             void sendmsg(isc::data::ConstElementPtr msg);
             void sendmsg(isc::data::ConstElementPtr msg);
             void sendmsg(isc::data::ConstElementPtr env,
             void sendmsg(isc::data::ConstElementPtr env,

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

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

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

@@ -34,6 +34,9 @@ TEST(AsioSession, establish) {
     asio::io_service io_service_;
     asio::io_service io_service_;
     Session sess(io_service_);
     Session sess(io_service_);
 
 
+    // can't return socket desciptor before session is established
+    EXPECT_THROW(sess.getSocketDesc(), isc::InvalidOperation);
+
     EXPECT_THROW(
     EXPECT_THROW(
         sess.establish("/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
         sess.establish("/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
                        "/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);
     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
 libfake_session_la_SOURCES = fake_session.h fake_session.cc
 
 
 TESTS_ENVIRONMENT = \
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 
 TESTS =
 TESTS =
 if HAVE_GTEST
 if HAVE_GTEST

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

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

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

@@ -2,3 +2,4 @@
 /datasrc_messages.h
 /datasrc_messages.h
 /datasrc_config.h
 /datasrc_config.h
 /datasrc_config.h.pre
 /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 += database.h database.cc
 libdatasrc_la_SOURCES += factory.h factory.cc
 libdatasrc_la_SOURCES += factory.h factory.cc
 libdatasrc_la_SOURCES += client_list.h client_list.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
 nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
 libdatasrc_la_LDFLAGS = -no-undefined -version-info 1:0:1
 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 += libdatasrc.la
 sqlite3_ds_la_LIBADD += $(SQLITE_LIBS)
 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_LDFLAGS = -module -avoid-version
 memory_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 memory_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 memory_ds_la_LIBADD += libdatasrc.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_LDFLAGS = -module -avoid-version
 static_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 static_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 static_ds_la_LIBADD += libdatasrc.la
 static_ds_la_LIBADD += libdatasrc.la

+ 65 - 13
src/lib/datasrc/client_list.cc

@@ -15,22 +15,37 @@
 #include "client_list.h"
 #include "client_list.h"
 #include "client.h"
 #include "client.h"
 #include "factory.h"
 #include "factory.h"
+#include "memory_datasrc.h"
 
 
 #include <memory>
 #include <memory>
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 
 
 using namespace isc::data;
 using namespace isc::data;
+using namespace isc::dns;
 using namespace std;
 using namespace std;
+using namespace boost;
 
 
 namespace isc {
 namespace isc {
 namespace datasrc {
 namespace datasrc {
 
 
+ConfigurableClientList::DataSourceInfo::DataSourceInfo(
+    DataSourceClient* data_src_client,
+    const DataSourceClientContainerPtr& container, bool hasCache) :
+    data_src_client_(data_src_client),
+    container_(container)
+{
+    if (hasCache) {
+        cache_.reset(new InMemoryClient);
+    }
+}
+
 void
 void
-ConfigurableClientList::configure(const ConstElementPtr& config, bool) {
+ConfigurableClientList::configure(const ConstElementPtr& config,
+                                  bool allow_cache)
+{
     if (!config) {
     if (!config) {
         isc_throw(isc::BadValue, "NULL configuration passed");
         isc_throw(isc::BadValue, "NULL configuration passed");
     }
     }
-    // TODO: Implement the cache
     // TODO: Implement recycling from the old configuration.
     // TODO: Implement recycling from the old configuration.
     size_t i(0); // Outside of the try to be able to access it in the catch
     size_t i(0); // Outside of the try to be able to access it in the catch
     try {
     try {
@@ -52,8 +67,48 @@ ConfigurableClientList::configure(const ConstElementPtr& config, bool) {
             // Ask the factory to create the data source for us
             // Ask the factory to create the data source for us
             const DataSourcePair ds(this->getDataSourceClient(type,
             const DataSourcePair ds(this->getDataSourceClient(type,
                                                               paramConf));
                                                               paramConf));
+            const bool want_cache(allow_cache &&
+                                  dconf->contains("cache-enable") &&
+                                  dconf->get("cache-enable")->boolValue());
             // And put it into the vector
             // And put it into the vector
-            new_data_sources.push_back(DataSourceInfo(ds.first, ds.second));
+            new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
+                                                      want_cache));
+            if (want_cache) {
+                if (!dconf->contains("cache-zones")) {
+                    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.
+                }
+                const ConstElementPtr zones(dconf->get("cache-zones"));
+                const shared_ptr<InMemoryClient>
+                    cache(new_data_sources.back().cache_);
+                const DataSourceClient* const
+                    client(new_data_sources.back().data_src_client_);
+                for (size_t i(0); i < zones->size(); ++i) {
+                    const Name origin(zones->get(i)->stringValue());
+                    const DataSourceClient::FindResult
+                        zone(client->findZone(origin));
+                    if (zone.code != result::SUCCESS) {
+                        // The data source does not contain the zone, it can't
+                        // be cached.
+                        isc_throw(ConfigurationError, "Unable to cache "
+                                  "non-existent zone " << origin);
+                    }
+                    shared_ptr<InMemoryZoneFinder>
+                        finder(new
+                            InMemoryZoneFinder(zone.zone_finder->getClass(),
+                                               origin));
+                    ZoneIteratorPtr iterator(client->getIterator(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
         // 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
         // ready. So just put it there and let the old one die when we exit
@@ -92,14 +147,11 @@ ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
     } candidate;
     } candidate;
 
 
     BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
     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) {
         switch (result.code) {
             case result::SUCCESS:
             case result::SUCCESS:
                 // If we found an exact match, we have no hope to getting
                 // If we found an exact match, we have no hope to getting
@@ -107,7 +159,7 @@ ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
 
 
                 // TODO: In case we have only the datasource and not the finder
                 // TODO: In case we have only the datasource and not the finder
                 // and the need_updater parameter is true, get the zone there.
                 // 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));
                                    true));
             case result::PARTIALMATCH:
             case result::PARTIALMATCH:
                 if (!want_exact_match) {
                 if (!want_exact_match) {
@@ -128,7 +180,7 @@ ConfigurableClientList::find(const dns::Name& name, bool want_exact_match,
                     if (labels > candidate.matched_labels ||
                     if (labels > candidate.matched_labels ||
                         !candidate.matched) {
                         !candidate.matched) {
                         // This one is strictly better. Replace it.
                         // This one is strictly better. Replace it.
-                        candidate.datasrc_client = info.data_src_client_;
+                        candidate.datasrc_client = client;
                         candidate.finder = result.zone_finder;
                         candidate.finder = result.zone_finder;
                         candidate.matched_labels = labels;
                         candidate.matched_labels = labels;
                         candidate.matched = true;
                         candidate.matched = true;

+ 9 - 4
src/lib/datasrc/client_list.h

@@ -33,6 +33,7 @@ typedef boost::shared_ptr<DataSourceClient> DataSourceClientPtr;
 class DataSourceClientContainer;
 class DataSourceClientContainer;
 typedef boost::shared_ptr<DataSourceClientContainer>
 typedef boost::shared_ptr<DataSourceClientContainer>
     DataSourceClientContainerPtr;
     DataSourceClientContainerPtr;
+class InMemoryClient;
 
 
 /// \brief The list of data source clients.
 /// \brief The list of data source clients.
 ///
 ///
@@ -216,6 +217,11 @@ public:
     /// \throw ConfigurationError if the configuration is invalid in some
     /// \throw ConfigurationError if the configuration is invalid in some
     ///     sense.
     ///     sense.
     /// \throw BadValue if configuration is NULL
     /// \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,
     void configure(const isc::data::ConstElementPtr& configuration,
                    bool allow_cache);
                    bool allow_cache);
 
 
@@ -244,12 +250,11 @@ public:
             data_src_client_(NULL)
             data_src_client_(NULL)
         {}
         {}
         DataSourceInfo(DataSourceClient* data_src_client,
         DataSourceInfo(DataSourceClient* data_src_client,
-                       const DataSourceClientContainerPtr& container) :
-            data_src_client_(data_src_client),
-            container_(container)
-        {}
+                       const DataSourceClientContainerPtr& container,
+                       bool hasCache);
         DataSourceClient* data_src_client_;
         DataSourceClient* data_src_client_;
         DataSourceClientContainerPtr container_;
         DataSourceClientContainerPtr container_;
+        boost::shared_ptr<InMemoryClient> cache_;
     };
     };
 
 
     /// \brief The collection of data sources.
     /// \brief The collection of data sources.

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

@@ -1126,9 +1126,9 @@ DatabaseClient::Finder::findPreviousName(const Name& name) const {
                                                  name.reverse().toText()));
                                                  name.reverse().toText()));
     try {
     try {
         return (Name(str));
         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.
     /// \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.
     /// \param rname The name to ask for previous of, in reversed form.
     ///     We use the reversed form (see isc::dns::Name::reverse),
     ///     We use the reversed form (see isc::dns::Name::reverse),
@@ -904,10 +905,6 @@ public:
             std::vector<isc::dns::ConstRRsetPtr>& target,
             std::vector<isc::dns::ConstRRsetPtr>& target,
             const FindOptions options = FIND_DEFAULT);
             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.
         /// Look for NSEC3 for proving (non)existence of given name.
         ///
         ///
         /// See documentation in \c Zone.
         /// See documentation in \c Zone.
@@ -1108,6 +1105,10 @@ public:
             bool probed_;
             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().
         /// \brief Search result of \c findDelegationPoint().
         ///
         ///
         /// This is a tuple combining the result of the search - a status code
         /// 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_);
     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
 /// Implementation details for \c InMemoryClient hidden from the public
 /// interface.
 /// interface.
 ///
 ///

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

@@ -87,12 +87,6 @@ public:
     virtual FindNSEC3Result
     virtual FindNSEC3Result
     findNSEC3(const isc::dns::Name& name, bool recursive);
     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.
     /// \brief Inserts an rrset into the zone.
     ///
     ///
     /// It puts another RRset into the zone.
     /// It puts another RRset into the zone.

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

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

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

@@ -14,9 +14,13 @@
 
 
 #include <datasrc/client_list.h>
 #include <datasrc/client_list.h>
 #include <datasrc/client.h>
 #include <datasrc/client.h>
+#include <datasrc/iterator.h>
 #include <datasrc/data_source.h>
 #include <datasrc/data_source.h>
+#include <datasrc/memory_datasrc.h>
 
 
 #include <dns/rrclass.h>
 #include <dns/rrclass.h>
+#include <dns/rrttl.h>
+#include <dns/rdataclass.h>
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
@@ -41,7 +45,7 @@ public:
         Name getOrigin() const { return (origin_); }
         Name getOrigin() const { return (origin_); }
         // The rest is not to be called, so just have them
         // The rest is not to be called, so just have them
         RRClass getClass() const {
         RRClass getClass() const {
-            isc_throw(isc::NotImplemented, "Not implemented");
+            return (RRClass::IN());
         }
         }
         shared_ptr<Context> find(const Name&, const RRType&,
         shared_ptr<Context> find(const Name&, const RRType&,
                                  const FindOptions)
                                  const FindOptions)
@@ -57,12 +61,38 @@ public:
         FindNSEC3Result findNSEC3(const Name&, bool) {
         FindNSEC3Result findNSEC3(const Name&, bool) {
             isc_throw(isc::NotImplemented, "Not implemented");
             isc_throw(isc::NotImplemented, "Not implemented");
         }
         }
-        Name findPreviousName(const Name&) const {
-            isc_throw(isc::NotImplemented, "Not implemented");
-        }
     private:
     private:
         Name origin_;
         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.
     // Constructor from a list of zones.
     MockDataSourceClient(const char* zone_names[]) {
     MockDataSourceClient(const char* zone_names[]) {
         for (const char** zone(zone_names); *zone; ++zone) {
         for (const char** zone(zone_names); *zone; ++zone) {
@@ -75,7 +105,13 @@ public:
                          const ConstElementPtr& configuration) :
                          const ConstElementPtr& configuration) :
         type_(type),
         type_(type),
         configuration_(configuration)
         configuration_(configuration)
-    {}
+    {
+        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 {
     virtual FindResult findZone(const Name& name) const {
         if (zones.empty()) {
         if (zones.empty()) {
             return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
             return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
@@ -106,6 +142,15 @@ public:
     {
     {
         isc_throw(isc::NotImplemented, "Not implemented");
         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 {
+            return (ZoneIteratorPtr(new Iterator(name)));
+        }
+    }
     const string type_;
     const string type_;
     const ConstElementPtr configuration_;
     const ConstElementPtr configuration_;
 private:
 private:
@@ -169,7 +214,6 @@ public:
         config_elem_(Element::fromJSON("["
         config_elem_(Element::fromJSON("["
             "{"
             "{"
             "   \"type\": \"test_type\","
             "   \"type\": \"test_type\","
-            "   \"cache\": \"off\","
             "   \"params\": {}"
             "   \"params\": {}"
             "}]"))
             "}]"))
     {
     {
@@ -178,20 +222,28 @@ public:
                 ds(new MockDataSourceClient(ds_zones[i]));
                 ds(new MockDataSourceClient(ds_zones[i]));
             ds_.push_back(ds);
             ds_.push_back(ds);
             ds_info_.push_back(ConfigurableClientList::DataSourceInfo(ds.get(),
             ds_info_.push_back(ConfigurableClientList::DataSourceInfo(ds.get(),
-                DataSourceClientContainerPtr()));
+                DataSourceClientContainerPtr(), false));
         }
         }
     }
     }
     // Check the positive result is as we expect it.
     // Check the positive result is as we expect it.
     void positiveResult(const ClientList::FindResult& result,
     void positiveResult(const ClientList::FindResult& result,
                         const shared_ptr<MockDataSourceClient>& dsrc,
                         const shared_ptr<MockDataSourceClient>& dsrc,
                         const Name& name, bool exact,
                         const Name& name, bool exact,
-                        const char* test)
+                        const char* test, bool from_cache = false)
     {
     {
         SCOPED_TRACE(test);
         SCOPED_TRACE(test);
-        EXPECT_EQ(dsrc.get(), result.dsrc_client_);
         ASSERT_NE(ZoneFinderPtr(), result.finder_);
         ASSERT_NE(ZoneFinderPtr(), result.finder_);
         EXPECT_EQ(name, result.finder_->getOrigin());
         EXPECT_EQ(name, result.finder_->getOrigin());
         EXPECT_EQ(exact, result.exact_match_);
         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
     // Configure the list with multiple data sources, according to
     // some configuration. It uses the index as parameter, to be able to
     // some configuration. It uses the index as parameter, to be able to
@@ -223,7 +275,8 @@ public:
                 FAIL() << "Unknown configuration index " << index;
                 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);
         ASSERT_GT(list_->getDataSources().size(), index);
         MockDataSourceClient* ds(dynamic_cast<MockDataSourceClient*>(
         MockDataSourceClient* ds(dynamic_cast<MockDataSourceClient*>(
@@ -233,6 +286,8 @@ public:
         ASSERT_NE(ds, static_cast<const MockDataSourceClient*>(NULL));
         ASSERT_NE(ds, static_cast<const MockDataSourceClient*>(NULL));
         EXPECT_EQ(type, ds->type_);
         EXPECT_EQ(type, ds->type_);
         EXPECT_TRUE(Element::fromJSON(params)->equals(*ds->configuration_));
         EXPECT_TRUE(Element::fromJSON(params)->equals(*ds->configuration_));
+        EXPECT_EQ(cache, list_->getDataSources()[index].cache_ !=
+                  shared_ptr<InMemoryClient>());
     }
     }
     shared_ptr<TestedList> list_;
     shared_ptr<TestedList> list_;
     const ClientList::FindResult negativeResult_;
     const ClientList::FindResult negativeResult_;
@@ -352,7 +407,7 @@ TEST_F(ListTest, multiBestMatch) {
 
 
 // Check the configuration is empty when the list is empty
 // Check the configuration is empty when the list is empty
 TEST_F(ListTest, configureEmpty) {
 TEST_F(ListTest, configureEmpty) {
-    ConstElementPtr elem(new ListElement);
+    const ConstElementPtr elem(new ListElement);
     list_->configure(elem, true);
     list_->configure(elem, true);
     EXPECT_TRUE(list_->getDataSources().empty());
     EXPECT_TRUE(list_->getDataSources().empty());
     // Check the exact configuration is preserved
     // Check the exact configuration is preserved
@@ -361,7 +416,7 @@ TEST_F(ListTest, configureEmpty) {
 
 
 // Check we can get multiple data sources and they are in the right order.
 // Check we can get multiple data sources and they are in the right order.
 TEST_F(ListTest, configureMulti) {
 TEST_F(ListTest, configureMulti) {
-    ConstElementPtr elem(Element::fromJSON("["
+    const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "{"
         "   \"type\": \"type1\","
         "   \"type\": \"type1\","
         "   \"cache\": \"off\","
         "   \"cache\": \"off\","
@@ -375,8 +430,8 @@ TEST_F(ListTest, configureMulti) {
     ));
     ));
     list_->configure(elem, true);
     list_->configure(elem, true);
     EXPECT_EQ(2, list_->getDataSources().size());
     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
     // Check the exact configuration is preserved
     EXPECT_EQ(elem, list_->getConfiguration());
     EXPECT_EQ(elem, list_->getConfiguration());
 }
 }
@@ -403,7 +458,7 @@ TEST_F(ListTest, configureParams) {
             "}]"));
             "}]"));
         list_->configure(elem, true);
         list_->configure(elem, true);
         EXPECT_EQ(1, list_->getDataSources().size());
         EXPECT_EQ(1, list_->getDataSources().size());
-        checkDS(0, "t", *param);
+        checkDS(0, "t", *param, false);
     }
     }
 }
 }
 
 
@@ -425,55 +480,198 @@ TEST_F(ListTest, wrongConfig) {
         "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": null}]",
         "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": null}]",
         "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": []}]",
         "[{\"type\": \"test_type\", \"params\": 13}, {\"type\": []}]",
         "[{\"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\": {}}]",
         NULL
         NULL
     };
     };
     // Put something inside to see it survives the exception
     // Put something inside to see it survives the exception
     list_->configure(config_elem_, true);
     list_->configure(config_elem_, true);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
     for (const char** config(configs); *config; ++config) {
     for (const char** config(configs); *config; ++config) {
         SCOPED_TRACE(*config);
         SCOPED_TRACE(*config);
         ConstElementPtr elem(Element::fromJSON(*config));
         ConstElementPtr elem(Element::fromJSON(*config));
         EXPECT_THROW(list_->configure(elem, true),
         EXPECT_THROW(list_->configure(elem, true),
                      ConfigurableClientList::ConfigurationError);
                      ConfigurableClientList::ConfigurationError);
         // Still untouched
         // Still untouched
-        checkDS(0, "test_type", "{}");
+        checkDS(0, "test_type", "{}", false);
         EXPECT_EQ(1, list_->getDataSources().size());
         EXPECT_EQ(1, list_->getDataSources().size());
     }
     }
 }
 }
 
 
 // The param thing defaults to null. Cache is not used yet.
 // The param thing defaults to null. Cache is not used yet.
 TEST_F(ListTest, defaults) {
 TEST_F(ListTest, defaults) {
-    ConstElementPtr elem(Element::fromJSON("["
+    const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "{"
         "   \"type\": \"type1\""
         "   \"type\": \"type1\""
         "}]"));
         "}]"));
     list_->configure(elem, true);
     list_->configure(elem, true);
     EXPECT_EQ(1, list_->getDataSources().size());
     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
 // Check we can call the configure multiple times, to change the configuration
 TEST_F(ListTest, reconfigure) {
 TEST_F(ListTest, reconfigure) {
-    ConstElementPtr empty(new ListElement);
+    const ConstElementPtr empty(new ListElement);
     list_->configure(config_elem_, true);
     list_->configure(config_elem_, true);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
     list_->configure(empty, true);
     list_->configure(empty, true);
     EXPECT_TRUE(list_->getDataSources().empty());
     EXPECT_TRUE(list_->getDataSources().empty());
     list_->configure(config_elem_, true);
     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
 // Make sure the data source error exception from the factory is propagated
 TEST_F(ListTest, dataSrcError) {
 TEST_F(ListTest, dataSrcError) {
-    ConstElementPtr elem(Element::fromJSON("["
+    const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "{"
         "   \"type\": \"error\""
         "   \"type\": \"error\""
         "}]"));
         "}]"));
     list_->configure(config_elem_, true);
     list_->configure(config_elem_, true);
-    checkDS(0, "test_type", "{}");
+    checkDS(0, "test_type", "{}", false);
     EXPECT_THROW(list_->configure(elem, true), DataSourceError);
     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);
+
+    // 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);
 }
 }
 
 
 }
 }

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

@@ -3622,33 +3622,6 @@ TYPED_TEST(DatabaseClientTest, compoundUpdate) {
                this->empty_rdatas_);
                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) {
 TYPED_TEST(DatabaseClientTest, invalidRdata) {
     boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
     boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
 
@@ -3676,13 +3649,6 @@ TEST_F(MockDatabaseClientTest, missingNSEC) {
                this->expected_rdatas_, this->expected_sig_rdatas_);
                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.
  * 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.
  * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
  *
  *
  * Takes the created zone finder and checks its properties they are the same
  * 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.
     /// a matching NSEC3, in the form of \c FindNSEC3Result object.
     virtual FindNSEC3Result
     virtual FindNSEC3Result
     findNSEC3(const isc::dns::Name& name, bool recursive) = 0;
     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_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 libdhcp___la_LIBADD   = $(top_builddir)/src/lib/asiolink/libasiolink.la
 libdhcp___la_LIBADD   = $(top_builddir)/src/lib/asiolink/libasiolink.la
 libdhcp___la_LIBADD  += $(top_builddir)/src/lib/util/libutil.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
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
 # 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 <string.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <arpa/inet.h>
+#include <sys/select.h>
 
 
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
@@ -121,7 +122,8 @@ bool IfaceMgr::Iface::delSocket(uint16_t sockfd) {
 
 
 IfaceMgr::IfaceMgr()
 IfaceMgr::IfaceMgr()
     :control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
     :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;
     cout << "IfaceMgr initialization." << endl;
@@ -230,11 +232,13 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
          iface!=ifaces_.end();
          iface!=ifaces_.end();
          ++iface) {
          ++iface) {
 
 
-        cout << "Trying interface " << iface->getFullName() << endl;
+        cout << "Trying opening socket on interface " << iface->getFullName() << endl;
 
 
         if (iface->flag_loopback_ ||
         if (iface->flag_loopback_ ||
             !iface->flag_up_ ||
             !iface->flag_up_ ||
             !iface->flag_running_) {
             !iface->flag_running_) {
+            cout << "Interface " << iface->getFullName()
+                 << " not suitable: is loopback, is down or not running" << endl;
             continue;
             continue;
         }
         }
 
 
@@ -685,35 +689,101 @@ IfaceMgr::send(const Pkt4Ptr& pkt)
 
 
 
 
 boost::shared_ptr<Pkt4>
 boost::shared_ptr<Pkt4>
-IfaceMgr::receive4() {
+IfaceMgr::receive4(uint32_t timeout) {
 
 
     const SocketInfo* candidate = 0;
     const SocketInfo* candidate = 0;
     IfaceCollection::const_iterator iface;
     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) {
     for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
 
 
-        /// @todo: rewrite this as part of #1555
         for (SocketCollection::const_iterator s = iface->sockets_.begin();
         for (SocketCollection::const_iterator s = iface->sockets_.begin();
              s != iface->sockets_.end(); ++s) {
              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);
                 candidate = &(*s);
                 break;
                 break;
             }
             }
         }
         }
-
         if (candidate) {
         if (candidate) {
             break;
             break;
         }
         }
     }
     }
 
 
     if (!candidate) {
     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 "
     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_control = &control_buf_[0];
     m.msg_controllen = control_buf_len_;
     m.msg_controllen = control_buf_len_;
 
 
-    int result = recvmsg(candidate->sockfd_, &m, 0);
+    result = recvmsg(candidate->sockfd_, &m, 0);
     if (result < 0) {
     if (result < 0) {
         cout << "Failed to receive UDP4 data." << endl;
         cout << "Failed to receive UDP4 data." << endl;
         return (Pkt4Ptr()); // NULL
         return (Pkt4Ptr()); // NULL

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

@@ -39,6 +39,9 @@ public:
     /// type that defines list of addresses
     /// type that defines list of addresses
     typedef std::vector<isc::asiolink::IOAddress> AddressCollection;
     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)
     /// maximum MAC address length (Infiniband uses 20 bytes)
     static const unsigned int MAX_MAC_LEN = 20;
     static const unsigned int MAX_MAC_LEN = 20;
 
 
@@ -351,12 +354,10 @@ public:
     /// If reception is successful and all information about its sender
     /// If reception is successful and all information about its sender
     /// are obtained, Pkt4 object is created and returned.
     /// 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)
     /// @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.
     /// Opens UDP/IP socket and binds it to address, interface and port.
     ///
     ///
@@ -401,6 +402,21 @@ public:
     /// @return number of detected interfaces
     /// @return number of detected interfaces
     uint16_t countIfaces() { return ifaces_.size(); }
     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
     // don't use private, we need derived classes in tests
 protected:
 protected:
 
 
@@ -487,7 +503,6 @@ protected:
     /// control-buffer, used in transmission and reception
     /// control-buffer, used in transmission and reception
     boost::scoped_array<char> control_buf_;
     boost::scoped_array<char> control_buf_;
 
 
-
     /// @brief A wrapper for OS-specific operations before sending IPv4 packet
     /// @brief A wrapper for OS-specific operations before sending IPv4 packet
     ///
     ///
     /// @param m message header (will be later used for sendmsg() call)
     /// @param m message header (will be later used for sendmsg() call)
@@ -505,6 +520,11 @@ protected:
     /// @return true if successful, false otherwise
     /// @return true if successful, false otherwise
     bool os_receive4(struct msghdr& m, Pkt4Ptr& pkt);
     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:
 private:
 
 
     /// @brief Creates a single instance of this class (a singleton implementation)
     /// @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
 CLEANFILES = *.gcno *.gcda
 
 
 TESTS_ENVIRONMENT = \
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 
 TESTS =
 TESTS =
 if HAVE_GTEST
 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
 // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 // 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
 // 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
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
@@ -17,6 +17,7 @@
 #include <fstream>
 #include <fstream>
 #include <sstream>
 #include <sstream>
 
 
+#include <unistd.h>
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
@@ -381,7 +382,7 @@ TEST_F(IfaceMgrTest, sendReceive4) {
 
 
     EXPECT_EQ(true, ifacemgr->send(sendPkt));
     EXPECT_EQ(true, ifacemgr->send(sendPkt));
 
 
-    rcvPkt = ifacemgr->receive4();
+    rcvPkt = ifacemgr->receive4(10);
 
 
     ASSERT_TRUE(rcvPkt); // received our own packet
     ASSERT_TRUE(rcvPkt); // received our own packet
 
 
@@ -899,4 +900,52 @@ TEST_F(IfaceMgrTest, DISABLED_detectIfaces_linux) {
 }
 }
 #endif
 #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
 CLEANFILES = *.gcno *.gcda
 
 
 TESTS_ENVIRONMENT = \
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 
 TESTS =
 TESTS =
 if HAVE_GTEST
 if HAVE_GTEST

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

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

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

@@ -47,5 +47,5 @@ if USE_CLANGPP
 liblog_la_CXXFLAGS += -Wno-error
 liblog_la_CXXFLAGS += -Wno-error
 endif
 endif
 liblog_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 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
 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)
 logger_lock_test_LDADD += $(AM_LDADD) $(LOG4CPLUS_LIBS)
 
 
 TESTS_ENVIRONMENT = \
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 
 if HAVE_GTEST
 if HAVE_GTEST
 TESTS =
 TESTS =
@@ -61,9 +61,9 @@ endif
 AM_CPPFLAGS += $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
 AM_CPPFLAGS += $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
 AM_LDFLAGS  += $(GTEST_LDFLAGS)
 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/log/liblog.la
 AM_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.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/exceptions/libexceptions.la
 AM_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 AM_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 AM_LDADD += $(GTEST_LDADD)
 AM_LDADD += $(GTEST_LDADD)

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

@@ -26,7 +26,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 CLEANFILES = *.gcno *.gcda
 
 
 TESTS_ENVIRONMENT = \
 TESTS_ENVIRONMENT = \
-	libtool --mode=execute $(VALGRIND_COMMAND)
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 
 TESTS =
 TESTS =
 if HAVE_GTEST
 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)
         BaseComponent.__init__(self, boss, kind)
         self.__creator = None
         self.__creator = None
         self.__uid = boss.uid
         self.__uid = boss.uid
+        self.__gid = boss.gid
 
 
     def _start_internal(self):
     def _start_internal(self):
         self._boss.curproc = 'b10-sockcreator'
         self._boss.curproc = 'b10-sockcreator'
@@ -45,6 +46,9 @@ class SockCreator(BaseComponent):
         self._boss.register_process(self.pid(), self)
         self._boss.register_process(self.pid(), self)
         self._boss.set_creator(self.__creator)
         self._boss.set_creator(self.__creator)
         self._boss.log_started(self.pid())
         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:
         if self.__uid is not None:
             logger.info(BIND10_SETUID, self.__uid)
             logger.info(BIND10_SETUID, self.__uid)
             posix.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.__stop_process_params = None
         self.__start_simple_params = None
         self.__start_simple_params = None
         # Pretending to be boss
         # Pretending to be boss
+        self.gid = None
+        self.__gid_set = None
         self.uid = None
         self.uid = None
         self.__uid_set = None
         self.__uid_set = None
 
 
@@ -609,6 +611,9 @@ class ComponentTests(BossUtils, unittest.TestCase):
         self.assertTrue(process.killed)
         self.assertTrue(process.killed)
         self.assertFalse(process.terminated)
         self.assertFalse(process.terminated)
 
 
+    def setgid(self, gid):
+        self.__gid_set = gid
+
     def setuid(self, uid):
     def setuid(self, uid):
         self.__uid_set = uid
         self.__uid_set = uid
 
 
@@ -637,7 +642,9 @@ class ComponentTests(BossUtils, unittest.TestCase):
         """
         """
         component = isc.bind10.special_component.SockCreator(None, self,
         component = isc.bind10.special_component.SockCreator(None, self,
                                                              'needed', None)
                                                              'needed', None)
+        orig_setgid = isc.bind10.special_component.posix.setgid
         orig_setuid = isc.bind10.special_component.posix.setuid
         orig_setuid = isc.bind10.special_component.posix.setuid
+        isc.bind10.special_component.posix.setgid = self.setgid
         isc.bind10.special_component.posix.setuid = self.setuid
         isc.bind10.special_component.posix.setuid = self.setuid
         orig_creator = \
         orig_creator = \
             isc.bind10.special_component.isc.bind10.sockcreator.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 = \
         isc.bind10.special_component.isc.bind10.sockcreator.Creator = \
             lambda path: self.FakeCreator()
             lambda path: self.FakeCreator()
         component.start()
         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)
         self.assertIsNone(self.__uid_set)
         # Doesn't do anything, but doesn't crash
         # Doesn't do anything, but doesn't crash
         component.stop()
         component.stop()
         component.kill()
         component.kill()
         component.kill(True)
         component.kill(True)
+        self.gid = 4200
         self.uid = 42
         self.uid = 42
         component = isc.bind10.special_component.SockCreator(None, self,
         component = isc.bind10.special_component.SockCreator(None, self,
                                                              'needed', None)
                                                              'needed', None)
         component.start()
         component.start()
         # This time, it get's called
         # This time, it get's called
+        self.assertEqual(4200, self.__gid_set)
         self.assertEqual(42, self.__uid_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.posix.setuid = orig_setuid
         isc.bind10.special_component.isc.bind10.sockcreator.Creator = \
         isc.bind10.special_component.isc.bind10.sockcreator.Creator = \
             orig_creator
             orig_creator

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

@@ -304,15 +304,12 @@ class WrapTests(unittest.TestCase):
         # Transfer the descriptor
         # Transfer the descriptor
         send_fd(t1.fileno(), p1.fileno())
         send_fd(t1.fileno(), p1.fileno())
         p1.close()
         p1.close()
+        p1 = socket.fromfd(t2.read_fd(), socket.AF_UNIX, socket.SOCK_STREAM)
 
 
-        with socket.fromfd(t2.read_fd(), socket.AF_UNIX,
-                           socket.SOCK_STREAM) as p1:
-            # Now, pass some data trough the socket
-            p1.send(b'A')
-            data = p2.recv(1)
-            self.assertEqual(b'A', data)
-
-        p2.close()
+        # Now, pass some data trough the socket
+        p1.send(b'A')
+        data = p2.recv(1)
+        self.assertEqual(b'A', data)
 
 
         # Test the wrapping didn't hurt the socket's usual methods
         # Test the wrapping didn't hurt the socket's usual methods
         t1.send(b'B')
         t1.send(b'B')
@@ -322,6 +319,11 @@ class WrapTests(unittest.TestCase):
         data = t1.recv(1)
         data = t1.recv(1)
         self.assertEqual(b'C', data)
         self.assertEqual(b'C', data)
 
 
+        # Explicitly close temporary socket pair as the Python
+        # interpreter expects it.  It may not be 100% exception safe,
+        # but since this is only for tests we prefer brevity.
+        p1.close()
+        p2.close()
         t1.close()
         t1.close()
         t2.close()
         t2.close()
 
 

+ 6 - 6
src/lib/python/isc/config/cfgmgr_messages.mes

@@ -20,6 +20,12 @@ An older version of the configuration database has been found, from which
 there was an automatic upgrade path to the current version. These changes
 there was an automatic upgrade path to the current version. These changes
 are now applied, and no action from the administrator is necessary.
 are now applied, and no action from the administrator is necessary.
 
 
+% CFGMGR_BACKED_UP_CONFIG_FILE Config file %1 was removed; a backup was made at %2
+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.
+
 % CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE Unable to parse response from module %1: %2
 % CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE Unable to parse response from module %1: %2
 The configuration manager sent a configuration update to a module, but
 The configuration manager sent a configuration update to a module, but
 the module responded with an answer that could not be parsed. The answer
 the module responded with an answer that could not be parsed. The answer
@@ -55,12 +61,6 @@ error is given. The most likely cause is that the system does not have
 write access to the configuration database file. The updated
 write access to the configuration database file. The updated
 configuration is not stored.
 configuration is not stored.
 
 
-% CFGMGR_BACKED_UP_CONFIG_FILE Config file %1 was removed; a backup was made at %2
-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.
-
 % CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
 % CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
 There was a keyboard interrupt signal to stop the cfgmgr daemon. The
 There was a keyboard interrupt signal to stop the cfgmgr daemon. The
 daemon will now shut down.
 daemon will now shut down.

+ 0 - 18
src/lib/python/isc/datasrc/finder_inc.cc

@@ -225,22 +225,4 @@ In the second element a single RRset is returned for cases where the\n\
 result is some kind of delegation, CNAME or similar; in other cases\n\
 result is some kind of delegation, CNAME or similar; in other cases\n\
 a list of RRsets is returned, containing all the results.\n\
 a list of RRsets is returned, containing all the results.\n\
 ";
 ";
-
-const char* const ZoneFinder_find_previous_name_doc = "\
-find_previous_name(isc.dns.Name) -> isc.dns.Name\n\
-\n\
-Gets the previous name in the DNSSEC order. This can be used\n\
-to find the correct NSEC records for proving nonexistence\n\
-of domains.\n\
-\n\
-This method does not include under-zone-cut data (glue data).\n\
-\n\
-Raises isc.datasrc.NotImplemented in case the data source backend\n\
-doesn't support DNSSEC or there is no previous in the zone (NSEC\n\
-records might be missing in the DB, the queried name is less or\n\
-equal to the apex).\n\
-\n\
-Raises isc.datasrc.Error for low-level or internal datasource errors\n\
-(like broken connection to database, wrong data living there).\n\
-";
 } // unnamed namespace
 } // unnamed namespace

+ 0 - 27
src/lib/python/isc/datasrc/finder_python.cc

@@ -254,31 +254,6 @@ ZoneFinder_find_all(PyObject* po_self, PyObject* args) {
                                                         args));
                                                         args));
 }
 }
 
 
-PyObject*
-ZoneFinder_findPreviousName(PyObject* po_self, PyObject* args) {
-    s_ZoneFinder* const self = static_cast<s_ZoneFinder*>(po_self);
-    PyObject* name_obj;
-    if (PyArg_ParseTuple(args, "O!", &name_type, &name_obj)) {
-        try {
-            return (createNameObject(
-                self->cppobj->findPreviousName(PyName_ToName(name_obj))));
-        } catch (const isc::NotImplemented& nie) {
-            PyErr_SetString(getDataSourceException("NotImplemented"),
-                            nie.what());
-            return (NULL);
-        } catch (const std::exception& exc) {
-            PyErr_SetString(getDataSourceException("Error"), exc.what());
-            return (NULL);
-        } catch (...) {
-            PyErr_SetString(getDataSourceException("Error"),
-                            "Unexpected exception");
-            return (NULL);
-        }
-    } else {
-        return (NULL);
-    }
-}
-
 // This list contains the actual set of functions we have in
 // This list contains the actual set of functions we have in
 // python. Each entry has
 // python. Each entry has
 // 1. Python method name
 // 1. Python method name
@@ -291,8 +266,6 @@ PyMethodDef ZoneFinder_methods[] = {
     { "get_class", ZoneFinder_getClass, METH_NOARGS, ZoneFinder_getClass_doc },
     { "get_class", ZoneFinder_getClass, METH_NOARGS, ZoneFinder_getClass_doc },
     { "find", ZoneFinder_find, METH_VARARGS, ZoneFinder_find_doc },
     { "find", ZoneFinder_find, METH_VARARGS, ZoneFinder_find_doc },
     { "find_all", ZoneFinder_find_all, METH_VARARGS, ZoneFinder_findAll_doc },
     { "find_all", ZoneFinder_find_all, METH_VARARGS, ZoneFinder_findAll_doc },
-    { "find_previous_name", ZoneFinder_findPreviousName, METH_VARARGS,
-      ZoneFinder_find_previous_name_doc },
     { NULL, NULL, 0, NULL }
     { NULL, NULL, 0, NULL }
 };
 };
 
 

+ 0 - 19
src/lib/python/isc/datasrc/tests/datasrc_test.py

@@ -426,25 +426,6 @@ class DataSrcClient(unittest.TestCase):
                           isc.dns.RRType.A(),
                           isc.dns.RRType.A(),
                           "foo")
                           "foo")
 
 
-    def test_find_previous(self):
-        dsc = isc.datasrc.DataSourceClient("sqlite3", READ_ZONE_DB_CONFIG)
-
-        result, finder = dsc.find_zone(isc.dns.Name("example.com"))
-        self.assertEqual(finder.SUCCESS, result)
-
-        prev = finder.find_previous_name(isc.dns.Name("bbb.example.com"))
-        self.assertEqual("example.com.", prev.to_text())
-
-        prev = finder.find_previous_name(isc.dns.Name("zzz.example.com"))
-        self.assertEqual("www.example.com.", prev.to_text())
-
-        prev = finder.find_previous_name(prev)
-        self.assertEqual("*.wild.example.com.", prev.to_text())
-
-        self.assertRaises(isc.datasrc.NotImplemented,
-                          finder.find_previous_name,
-                          isc.dns.Name("com"))
-
 class DataSrcUpdater(unittest.TestCase):
 class DataSrcUpdater(unittest.TestCase):
 
 
     def setUp(self):
     def setUp(self):

+ 3 - 1
src/lib/python/isc/notify/notify_out.py

@@ -34,7 +34,9 @@ logger = isc.log.Logger("notify_out")
 # initialized yet. see trac ticket #1103
 # initialized yet. see trac ticket #1103
 from isc.dns import *
 from isc.dns import *
 
 
-ZONE_NEW_DATA_READY_CMD = 'notify'
+ZONE_NEW_DATA_READY_CMD = 'zone_new_data_ready'
+ZONE_XFRIN_FAILED = 'zone_xfrin_failed'
+
 _MAX_NOTIFY_NUM = 30
 _MAX_NOTIFY_NUM = 30
 _MAX_NOTIFY_TRY_NUM = 5
 _MAX_NOTIFY_TRY_NUM = 5
 _EVENT_NONE = 0
 _EVENT_NONE = 0

+ 63 - 61
src/lib/python/isc/util/cio/tests/socketsession_test.py

@@ -128,63 +128,64 @@ class TestForwarder(unittest.TestCase):
 
 
     def check_push_and_pop(self, family, type, protocol, local, remote,
     def check_push_and_pop(self, family, type, protocol, local, remote,
                            data, new_connection):
                            data, new_connection):
-        with self.create_socket(family, type, protocol, local, True) as sock:
-            fwd_fd = sock.fileno()
-            if protocol == IPPROTO_TCP:
-                client_addr = ('::1', 0, 0, 0) if family == AF_INET6 \
-                    else ('127.0.0.1', 0)
-                client_sock = self.create_socket(family, type, protocol,
-                                                 client_addr, False)
-                client_sock.setblocking(False)
-                try:
-                    client_sock.connect(local)
-                except socket.error:
-                    pass
-                server_sock, _ = sock.accept()
-                fwd_fd = server_sock.fileno()
-
-            # If a new connection is required, start the "server", have the
-            # internal forwarder connect to it, and then internally accept it.
-            if new_connection:
-                self.start_listen()
-                self.forwarder.connect_to_receiver()
-                self.accept_sock = self.accept_forwarder()
-
-            # Then push one socket session via the forwarder.
-            self.forwarder.push(fwd_fd, family, type, protocol, local, remote,
-                                data)
-
-            # Pop the socket session we just pushed from a local receiver, and
-            # check the content.
-            receiver = SocketSessionReceiver(self.accept_sock)
-            signal.alarm(1)
-            sock_session = receiver.pop()
-            signal.alarm(0)
-            passed_sock = sock_session[0]
-            self.assertNotEqual(fwd_fd, passed_sock.fileno())
-            self.assertEqual(family, passed_sock.family)
-            self.assertEqual(type, passed_sock.type)
-            self.assertEqual(protocol, passed_sock.proto)
-            self.assertEqual(local, sock_session[1])
-            self.assertEqual(remote, sock_session[2])
-            self.assertEqual(data, sock_session[3])
-
-            # Check if the passed FD is usable by sending some data from it.
-            passed_sock.setblocking(True)
-            if protocol == IPPROTO_UDP:
-                self.assertEqual(len(TEST_DATA), passed_sock.sendto(TEST_DATA,
-                                                                    local))
-                sock.settimeout(10)
-                self.assertEqual(TEST_DATA, sock.recvfrom(len(TEST_DATA))[0])
-            else:
-                self.assertEqual(len(TEST_DATA), passed_sock.send(TEST_DATA))
-                client_sock.setblocking(True)
-                client_sock.settimeout(10)
-                self.assertEqual(TEST_DATA, client_sock.recv(len(TEST_DATA)))
-                server_sock.close()
-                client_sock.close()
-
-            passed_sock.close()
+        sock = self.create_socket(family, type, protocol, local, True)
+        fwd_fd = sock.fileno()
+        if protocol == IPPROTO_TCP:
+            client_addr = ('::1', 0, 0, 0) if family == AF_INET6 \
+                else ('127.0.0.1', 0)
+            client_sock = self.create_socket(family, type, protocol,
+                                             client_addr, False)
+            client_sock.setblocking(False)
+            try:
+                client_sock.connect(local)
+            except socket.error:
+                pass
+            server_sock, _ = sock.accept()
+            fwd_fd = server_sock.fileno()
+
+        # If a new connection is required, start the "server", have the
+        # internal forwarder connect to it, and then internally accept it.
+        if new_connection:
+            self.start_listen()
+            self.forwarder.connect_to_receiver()
+            self.accept_sock = self.accept_forwarder()
+
+        # Then push one socket session via the forwarder.
+        self.forwarder.push(fwd_fd, family, type, protocol, local, remote,
+                            data)
+
+        # Pop the socket session we just pushed from a local receiver, and
+        # check the content.
+        receiver = SocketSessionReceiver(self.accept_sock)
+        signal.alarm(1)
+        sock_session = receiver.pop()
+        signal.alarm(0)
+        passed_sock = sock_session[0]
+        self.assertNotEqual(fwd_fd, passed_sock.fileno())
+        self.assertEqual(family, passed_sock.family)
+        self.assertEqual(type, passed_sock.type)
+        self.assertEqual(protocol, passed_sock.proto)
+        self.assertEqual(local, sock_session[1])
+        self.assertEqual(remote, sock_session[2])
+        self.assertEqual(data, sock_session[3])
+
+        # Check if the passed FD is usable by sending some data from it.
+        passed_sock.setblocking(True)
+        if protocol == IPPROTO_UDP:
+            self.assertEqual(len(TEST_DATA), passed_sock.sendto(TEST_DATA,
+                                                                local))
+            sock.settimeout(10)
+            self.assertEqual(TEST_DATA, sock.recvfrom(len(TEST_DATA))[0])
+        else:
+            self.assertEqual(len(TEST_DATA), passed_sock.send(TEST_DATA))
+            client_sock.setblocking(True)
+            client_sock.settimeout(10)
+            self.assertEqual(TEST_DATA, client_sock.recv(len(TEST_DATA)))
+            server_sock.close()
+            client_sock.close()
+
+        passed_sock.close()
+        sock.close()
 
 
     def test_push_and_pop(self):
     def test_push_and_pop(self):
         # This is a straightforward port of C++ pushAndPop test.  See the
         # This is a straightforward port of C++ pushAndPop test.  See the
@@ -243,10 +244,11 @@ class TestForwarder(unittest.TestCase):
         s = socket.socket(socket.AF_UNIX, SOCK_STREAM, 0)
         s = socket.socket(socket.AF_UNIX, SOCK_STREAM, 0)
         s.setblocking(False)
         s.setblocking(False)
         s.connect(TEST_UNIX_FILE)
         s.connect(TEST_UNIX_FILE)
-        with self.accept_forwarder() as accept_sock:
-            receiver = SocketSessionReceiver(accept_sock)
-            s.close()
-            self.assertRaises(SocketSessionError, receiver.pop)
+        accept_sock = self.accept_forwarder()
+        receiver = SocketSessionReceiver(accept_sock)
+        s.close()
+        self.assertRaises(SocketSessionError, receiver.pop)
+        accept_sock.close()
 
 
 class TestReceiver(unittest.TestCase):
 class TestReceiver(unittest.TestCase):
     # We only check a couple of failure cases on construction.  Valid cases
     # We only check a couple of failure cases on construction.  Valid cases

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

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

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

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

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

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

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

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

+ 0 - 0
src/lib/xfr/tests/Makefile.am


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