Browse Source

merged trunk@3240 changes into this branch

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac292@3248 e5f2f494-b856-4b98-b285-d166d9295462
Kazunori Fujiwara 14 years ago
parent
commit
44ea119ae4
100 changed files with 1655 additions and 2247 deletions
  1. 94 4
      ChangeLog
  2. 2 1
      README
  3. 74 11
      configure.ac
  4. 46 25
      doc/guide/bind10-guide.html
  5. 46 10
      doc/guide/bind10-guide.xml
  6. 4 1
      src/bin/Makefile.am
  7. 1 1
      src/bin/auth/Makefile.am
  8. 96 10
      src/bin/auth/asio_link.cc
  9. 25 6
      src/bin/auth/auth_srv.cc
  10. 114 2
      src/bin/auth/auth_srv.h
  11. 1 0
      src/bin/auth/b10-auth.8
  12. 3 0
      src/bin/auth/b10-auth.xml
  13. 3 22
      src/bin/auth/tests/Makefile.am
  14. 34 13
      src/bin/auth/tests/auth_srv_unittest.cc
  15. 2 2
      src/bin/auth/tests/run_unittests.cc
  16. 1 1
      src/bin/bind10/Makefile.am
  17. 22 2
      src/bin/bind10/bind10.8
  18. 83 51
      src/bin/bind10/bind10.py.in
  19. 18 0
      src/bin/bind10/bind10.xml
  20. 1 1
      src/bin/bind10/run_bind10.sh.in
  21. 23 0
      src/bin/bind10/tests/args_test.py
  22. 3 24
      src/bin/bind10/tests/bind10_test.py
  23. 1 1
      src/bin/bindctl/Makefile.am
  24. 3 0
      src/bin/bindctl/bindctl-source.py.in
  25. 1 1
      src/bin/cfgmgr/Makefile.am
  26. 3 0
      src/bin/cfgmgr/b10-cfgmgr.py.in
  27. 2 2
      src/bin/cmdctl/Makefile.am
  28. 23 17
      src/bin/cmdctl/cmdctl.py.in
  29. 2 0
      src/bin/host/host.cc
  30. 1 2
      src/bin/loadzone/Makefile.am
  31. 4 0
      src/bin/loadzone/b10-loadzone.py.in
  32. 1 1
      src/bin/msgq/Makefile.am
  33. 7 0
      src/bin/msgq/msgq.py.in
  34. 3 0
      src/bin/usermgr/b10-cmdctl-usermgr.py.in
  35. 3 5
      src/bin/xfrin/Makefile.am
  36. 1 0
      src/bin/xfrin/TODO
  37. 45 10
      src/bin/xfrin/b10-xfrin.8
  38. 62 17
      src/bin/xfrin/b10-xfrin.xml
  39. 5 5
      src/bin/xfrin/tests/xfrin_test.py
  40. 28 30
      src/bin/xfrin/xfrin.py.in
  41. 1 1
      src/bin/xfrout/Makefile.am
  42. 2 1
      src/bin/xfrout/TODO
  43. 18 9
      src/bin/xfrout/b10-xfrout.8
  44. 22 4
      src/bin/xfrout/b10-xfrout.xml
  45. 52 6
      src/bin/xfrout/tests/xfrout_test.py
  46. 45 43
      src/bin/xfrout/xfrout.py.in
  47. 11 1
      src/bin/zonemgr/Makefile.am
  48. 1 0
      src/bin/zonemgr/TODO
  49. 49 17
      src/bin/zonemgr/tests/zonemgr_test.py
  50. 55 39
      src/bin/zonemgr/zonemgr.py.in
  51. 24 0
      src/bin/zonemgr/zonemgr.spec.pre.in
  52. 2 0
      src/lib/bench/benchmark_util.cc
  53. 2 1
      src/lib/bench/tests/loadquery_unittest.cc
  54. 12 8
      src/lib/cc/data.cc
  55. 1 2
      src/lib/cc/tests/Makefile.am
  56. 1 2
      src/lib/cc/tests/run_unittests.cc
  57. 1 43
      src/lib/config/Makefile.am
  58. 2 4
      src/lib/config/ccsession.cc
  59. 3 4
      src/lib/config/tests/Makefile.am
  60. 1 2
      src/lib/config/tests/ccsession_unittests.cc
  61. 1 1
      src/lib/config/tests/data_def_unittests_config.h.in
  62. 2 4
      src/lib/config/tests/module_spec_unittests.cc
  63. 1 2
      src/lib/config/tests/run_unittests.cc
  64. 1 0
      src/lib/datasrc/data_source.cc
  65. 2 4
      src/lib/datasrc/static_datasrc.cc
  66. 2 0
      src/lib/datasrc/tests/datasrc_unittest.cc
  67. 1 0
      src/lib/datasrc/tests/query_unittest.cc
  68. 1 2
      src/lib/datasrc/tests/run_unittests.cc
  69. 5 2
      src/lib/dns/Makefile.am
  70. 1 1
      src/lib/dns/exceptions.cc
  71. 6 8
      src/lib/dns/gen-rdatacode.py.in
  72. 141 292
      src/lib/dns/message.cc
  73. 52 368
      src/lib/dns/message.h
  74. 2 2
      src/lib/dns/name.h
  75. 4 1
      src/lib/dns/python/Makefile.am
  76. 74 678
      src/lib/dns/python/message_python.cc
  77. 10 1
      src/lib/dns/python/messagerenderer_python.cc
  78. 4 4
      src/lib/dns/python/name_python.cc
  79. 25 1
      src/lib/dns/python/pydnspp.cc
  80. 1 2
      src/lib/dns/python/question_python.cc
  81. 11 2
      src/lib/dns/python/rdata_python.cc
  82. 1 1
      src/lib/dns/python/rrclass_python.cc
  83. 1 1
      src/lib/dns/python/rrset_python.cc
  84. 1 1
      src/lib/dns/python/rrttl_python.cc
  85. 1 1
      src/lib/dns/python/rrtype_python.cc
  86. 7 3
      src/lib/dns/python/tests/Makefile.am
  87. 42 273
      src/lib/dns/python/tests/message_python_test.py
  88. 1 0
      src/lib/dns/python/tests/messagerenderer_python_test.py
  89. 1 14
      src/lib/dns/python/tests/question_python_test.py
  90. 4 8
      src/lib/dns/question.cc
  91. 11 22
      src/lib/dns/rdata.cc
  92. 6 12
      src/lib/dns/rdata/ch_3/a_1.cc
  93. 5 10
      src/lib/dns/rdata/generic/cname_5.cc
  94. 5 10
      src/lib/dns/rdata/generic/dname_39.cc
  95. 7 14
      src/lib/dns/rdata/generic/dnskey_48.cc
  96. 7 14
      src/lib/dns/rdata/generic/ds_43.cc
  97. 6 12
      src/lib/dns/rdata/generic/mx_15.cc
  98. 1 1
      src/lib/dns/rdata/generic/mx_15.h
  99. 5 10
      src/lib/dns/rdata/generic/ns_2.cc
  100. 0 0
      src/lib/dns/rdata/generic/nsec3_50.cc

+ 94 - 4
ChangeLog

@@ -1,4 +1,94 @@
-  91.	[bug]		jinmei
+  110.  [func]      Michal Vaner
+	Added isc.net.check module to check ip addresses and ports for correctness
+	and isc.net.addr to hold IP address. The bind10, xfrin and cmdctl programs
+	are modified to use it.
+	(Trac #353, svn r3240)
+
+  109.  [func]		naokikambe
+	Added the initial version of the stats module for the statistics
+	feature of BIND 10, which supports the restricted features and
+	items and reports via bindctl command (Trac #191, r3218)
+	Added the document of the stats module, which is about how stats
+	module collects the data (Trac #170, [wiki:StatsModule])
+
+  108.	[func]		jerry		
+	src/bin/zonemgr: Provide customizable configurations for
+	lowerbound_refresh, lowerbound_retry, max_transfer_timeout and
+	jitter_scope. (Trac #340, r3205)	
+
+  107.  [func]       zhang likun
+	Remove the parameter 'db_file' for command 'retransfer' of
+	xfrin module. xfrin.spec will not be generated by script.
+	(Trac #329, r3171)
+
+  106.  [bug]       zhang likun
+	When xfrin can't connect with one zone's master, it should tell
+	the bad news to zonemgr, so that zonemgr can reset the timer for
+	that zone. (Trac #329, r3170)
+
+  105.  [bug]       Michal Vaner
+	Python processes: they no longer take 100% CPU while idle
+	due to a busy loop in reading command session in a nonblocking way.
+	(Trac #349, svn r3153)
+
+  104.	[bug]		jerry
+	bin/zonemgr: zonemgr should be attempting to refresh expired zones.
+	(Trac #336, r3139)
+				   
+  103.	[bug]		jerry
+	lib/python/isc/log: Fixed an issue with python logging,
+	python log shouldn't die with OSError.(Trac #267, r3137)
+				   
+  102.	[build]		jinmei
+	Disable threads in ASIO to minimize build time dependency.
+	(Trac #345, r3100)
+
+  101.	[func]		jinmei
+	src/lib/dns: Completed Opcode and Rcode implementation with more
+	tests and documentation.  API is mostly the same but the
+	validation was a bit tightened. (Trac #351, svn r3056)
+
+  100.  [func]      Michal Vaner
+	Python processes: support naming of python processes so
+	they're not all called python3.
+	(Trac #322, svn r3052)
+
+  99.	[func]*		jinmei
+	Introduced a separate EDNS class to encapsulate EDNS related
+	information more cleanly.  The related APIs are changed a bit,
+	although it won't affect most of higher level applications.
+	(Trac #311, svn r3020)
+
+  98.	[build]		jinmei
+	The ./configure script now tries to search some common include
+	paths for boost header files to minimize the need for explicit
+	configuration with --with-boost-include. (Trac #323, svn r3006)
+
+  97.	[func]		jinmei
+	Added a micro benchmark test for query processing of b10-auth.
+	(Trac #308, svn r2982)
+
+  96.	[bug]		jinmei
+	Fixed two small issues with configure: Do not set CXXFLAGS so that
+	it can be customized; Make sure --disable-static works.
+	(Trac #325, r2976)
+
+bind10-devel-20100917 released on September 17, 2010 
+
+  95.	[doc]		jreed
+	Add b10-zonemgr manual page. Update other docs to introduce
+	this secondary manager. (Trac #341, svn r2951)
+
+  95.	[bug]		jreed
+	bin/xfrout and bin/zonemgr: Fixed some stderr output.
+	(Trac #342, svn r2949)
+
+  94.	[bug]		jelte
+  	bin/xfrout:  Fixed a problem in xfrout where only 2 or 3 RRs
+	were used per DNS message in the xfrout stream.
+	(Trac #334, r2931)
+
+  93.	[bug]		jinmei
 	lib/datasrc: A DS query could crash the library (and therefore,
 	e.g. the authoritative server) if some RR of the same apex name
 	is stored in the hot spot cache.  (Trac #307, svn r2923)
@@ -11,7 +101,7 @@
 
   91.	[func]*		jinmei
 	lib/cc: Use const pointers and const member functions for the API
-	as much as possible for safer operations.  Basically this does
+	as much as possible for safer operations.  Basically this does not
 	change the observable behavior, but some of the API were changed
 	in a backward incompatible manner.  This change also involves more
 	copies, but at this moment the overhead is deemed acceptable.
@@ -40,8 +130,8 @@
 	zone axfr/ixfr finishing, the server will notify its slaves.
 	(Trac #289, svn r2737)
 
-  86.   [func]		jerry
-    	bin/zonemgr: Added zone manager module. The zone manager is one 
+  86.	[func]		jerry
+	bin/zonemgr: Added zone manager module. The zone manager is one 
 	of the co-operating processes of BIND10, which keeps track of 
 	timers and other information necessary for BIND10 to act as a 
 	slave. (Trac #215, svn r2737)

+ 2 - 1
README

@@ -17,7 +17,8 @@ This release includes the bind10 master process, b10-msgq message
 bus, b10-auth authoritative DNS server (with SQLite3 backend),
 b10-cmdctl remote control daemon, b10-cfgmgr configuration manager,
 b10-xfrin AXFR inbound service, b10-xfrout outgoing AXFR service,
-and a new libdns++ library for C++ with a python wrapper.
+b10-zonemgr secondary manager, and a new libdns++ library for C++
+with a python wrapper.
 
 Documentation is included and also available via the BIND 10
 website at http://bind10.isc.org/

+ 74 - 11
configure.ac

@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20100701, bind10-dev@isc.org)
+AC_INIT(bind10-devel, 20101013, bind10-dev@isc.org)
 AC_CONFIG_SRCDIR(README)
 AM_INIT_AUTOMAKE
 AC_CONFIG_HEADERS([config.h])
@@ -40,6 +40,20 @@ AC_HELP_STRING([--enable-static-link],
   [enable_static_link=yes], [enable_static_link=no])
 AM_CONDITIONAL(USE_STATIC_LINK, test $enable_static_link = yes)
 
+# Check validity about some libtool options
+if test $enable_static_link = yes -a $enable_static = no; then
+	AC_MSG_ERROR([--enable-static-link requires --enable-static])
+fi
+if test $enable_shared = no; then
+	AC_MSG_ERROR([BIND 10 requires shared libraries to be built])
+fi
+
+# allow configuring without setproctitle.
+AC_ARG_ENABLE(setproctitle-check,
+AC_HELP_STRING([--disable-setproctitle-check],
+  [do not check for python setproctitle module (used to give nice names to python processes)]),
+  setproctitle_check=$enableval, setproctitle_check=yes)
+
 # OS dependent configuration
 SET_ENV_LIBRARY_PATH=no
 ENV_LIBRARY_PATH=LD_LIBRARY_PATH
@@ -154,6 +168,18 @@ fi
 AC_SUBST(PYTHON_LIB)
 LDFLAGS=$LDFLAGS_SAVED
 
+# Check for the setproctitle module
+if test "$setproctitle_check" = "yes" ; then
+    AC_MSG_CHECKING(for setproctitle module)
+    if "$PYTHON" -c 'import setproctitle' 2>/dev/null ; then
+        AC_MSG_RESULT(ok)
+    else
+        AC_MSG_RESULT(missing)
+        AC_MSG_ERROR([Missing setproctitle module. Either install it or provide --disable-setproctitle-check.
+In that case we will continue, but naming of python processes will not work.])
+    fi
+fi
+
 # TODO: check for _sqlite3.py module
 
 # Compiler dependent settings: define some mandatory CXXFLAGS here.
@@ -167,7 +193,6 @@ LDFLAGS=$LDFLAGS_SAVED
 # specify the default warning flags in CXXFLAGS and let specific modules
 # "override" the default.
 
-CXXFLAGS=-g
 werror_ok=0
 
 # SunStudio compiler requires special compiler options for boost
@@ -236,7 +261,6 @@ AC_ARG_WITH(gtest,
 [  --with-gtest=PATH       specify a path to gtest header files (PATH/include) and library (PATH/lib)],
     gtest_path="$withval", gtest_path="no")
 
-
 USE_LCOV="no"
 if test "$lcov" != "no"; then
 	# force gtest if not set
@@ -270,14 +294,31 @@ if test "$lcov" != "no"; then
 fi
 AC_SUBST(USE_LCOV)
 
+#
+# Configure Boost header path
+#
+# If explicitly specified, use it.
 AC_ARG_WITH([boost-include],
   AC_HELP_STRING([--with-boost-include=PATH],
     [specify exact directory for Boost headers]),
     [boost_include_path="$withval"])
+# If not specified, try some common paths.
+if test -z "$with_boost_include"; then
+	boostdirs="/usr/local /usr/pkg /opt /opt/local"
+	for d in $boostdirs
+	do
+		if test -f $d/include/boost/shared_ptr.hpp; then
+			boost_include_path=$d/include
+			break
+		fi
+	done
+fi
 if test "${boost_include_path}" ; then
 	BOOST_INCLUDES="-I${boost_include_path}"
 	CPPFLAGS="$CPPFLAGS $BOOST_INCLUDES"
 fi
+AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp],,
+  AC_MSG_ERROR([Missing required header files.]))
 AC_SUBST(BOOST_INCLUDES)
 
 #
@@ -352,6 +393,9 @@ AC_SUBST(PTHREAD_LDFLAGS)
 #
 CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/ext/asio"
 #
+# Disable threads: Currently we don't use them.
+CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_THREADS=1"
+#
 # kqueue portability: ASIO uses kqueue by default if it's available (it's
 # generally available in BSD variants).  Unfortunately, some public
 # implementation of kqueue forces a conversion from a pointer to an integer,
@@ -389,12 +433,8 @@ if test "X$ac_cv_have_devpoll" = "Xyes" -a "X$GXX" = "Xyes"; then
 	CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_DEV_POLL=1"
 fi
 
-# Check for headers from required devel kits.
-AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp],,
-  AC_MSG_ERROR([Missing required header files.]))
-
 AC_ARG_ENABLE(man, [AC_HELP_STRING([--enable-man],
-  [regenerate man pages [default=no]])] ,enable_man=yes, enable_man=no)
+  [regenerate man pages [default=no]])], enable_man=yes, enable_man=no)
 
 AM_CONDITIONAL(ENABLE_MAN, test x$enable_man != xno)
 
@@ -423,13 +463,23 @@ AC_CONFIG_FILES([Makefile
                  src/bin/msgq/tests/Makefile
                  src/bin/auth/Makefile
                  src/bin/auth/tests/Makefile
+                 src/bin/auth/tests/testdata/Makefile
+                 src/bin/auth/benchmarks/Makefile
                  src/bin/xfrin/Makefile
                  src/bin/xfrin/tests/Makefile
                  src/bin/xfrout/Makefile
                  src/bin/xfrout/tests/Makefile
                  src/bin/zonemgr/Makefile
                  src/bin/zonemgr/tests/Makefile
+                 src/bin/stats/Makefile
+                 src/bin/stats/tests/Makefile
+                 src/bin/stats/tests/isc/Makefile
+                 src/bin/stats/tests/isc/cc/Makefile
+                 src/bin/stats/tests/isc/config/Makefile
+                 src/bin/stats/tests/isc/utils/Makefile
+                 src/bin/stats/tests/testdata/Makefile
                  src/bin/usermgr/Makefile
+                 src/bin/tests/Makefile
                  src/lib/Makefile
                  src/lib/bench/Makefile
                  src/lib/bench/example/Makefile
@@ -438,6 +488,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/cc/tests/Makefile
                  src/lib/python/Makefile
                  src/lib/python/isc/Makefile
+                 src/lib/python/isc/utils/Makefile
+                 src/lib/python/isc/utils/tests/Makefile
                  src/lib/python/isc/datasrc/Makefile
                  src/lib/python/isc/cc/Makefile
                  src/lib/python/isc/cc/tests/Makefile
@@ -445,13 +497,16 @@ AC_CONFIG_FILES([Makefile
                  src/lib/python/isc/config/tests/Makefile
                  src/lib/python/isc/log/Makefile
                  src/lib/python/isc/log/tests/Makefile
+                 src/lib/python/isc/net/Makefile
+                 src/lib/python/isc/net/tests/Makefile
                  src/lib/python/isc/notify/Makefile
                  src/lib/python/isc/notify/tests/Makefile
                  src/lib/config/Makefile
                  src/lib/config/tests/Makefile
-                 src/lib/config/testdata/Makefile
+                 src/lib/config/tests/testdata/Makefile
                  src/lib/dns/Makefile
                  src/lib/dns/tests/Makefile
+                 src/lib/dns/tests/testdata/Makefile
                  src/lib/dns/python/Makefile
                  src/lib/dns/python/tests/Makefile
                  src/lib/exceptions/Makefile
@@ -468,7 +523,6 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/cmdctl/cmdctl.spec.pre
            src/bin/xfrin/tests/xfrin_test
            src/bin/xfrin/xfrin.py
-           src/bin/xfrin/xfrin.spec.pre
            src/bin/xfrin/run_b10-xfrin.sh
            src/bin/xfrout/xfrout.py
            src/bin/xfrout/xfrout.spec.pre
@@ -478,6 +532,12 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/zonemgr/zonemgr.spec.pre
            src/bin/zonemgr/tests/zonemgr_test
            src/bin/zonemgr/run_b10-zonemgr.sh
+           src/bin/stats/stats.py
+           src/bin/stats/stats_stub.py
+           src/bin/stats/stats.spec.pre
+           src/bin/stats/run_b10-stats.sh
+           src/bin/stats/run_b10-stats_stub.sh
+           src/bin/stats/tests/stats_test
            src/bin/bind10/bind10.py
            src/bin/bind10/tests/bind10_test
            src/bin/bind10/run_bind10.sh
@@ -495,6 +555,7 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/msgq/run_msgq.sh
            src/bin/auth/auth.spec.pre
            src/bin/auth/spec_config.h.pre
+           src/bin/tests/process_rename_test.py
            src/lib/config/tests/data_def_unittests_config.h
            src/lib/python/isc/config/tests/config_test
            src/lib/python/isc/cc/tests/cc_test
@@ -510,6 +571,9 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            chmod +x src/bin/xfrin/run_b10-xfrin.sh
            chmod +x src/bin/xfrout/run_b10-xfrout.sh
            chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
+           chmod +x src/bin/stats/tests/stats_test
+           chmod +x src/bin/stats/run_b10-stats.sh
+           chmod +x src/bin/stats/run_b10-stats_stub.sh
            chmod +x src/bin/bind10/run_bind10.sh
            chmod +x src/bin/cmdctl/tests/cmdctl_test
            chmod +x src/bin/xfrin/tests/xfrin_test
@@ -543,7 +607,6 @@ Package:
 Flags:
   DEFS:          $DEFS
   CPPFLAGS:      $CPPFLAGS
-  CFLAGS:        $CFLAGS
   CXXFLAGS:      $CXXFLAGS
   B10_CXXFLAGS:  $B10_CXXFLAGS
 dnl includes too

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


+ 46 - 10
doc/guide/bind10-guide.xml

@@ -42,7 +42,7 @@
 
     <note>
       <para>
-        BIND 10, at this time, does not provide an recursive
+        BIND 10, at this time, does not provide a recursive
         DNS server. It does provide a EDNS0- and DNSSEC-capable
         authoritative DNS server.
       </para>
@@ -74,9 +74,9 @@
 	For this development prototype release, the only supported
 	data source backend is SQLite3. The authoritative server
 	requires SQLite 3.3.9 or newer.
-        The <command>b10-xfrin</command> and <command>b10-xfrout</command>
-        modules require the libpython3 library and the Python
-        _sqlite3.so module.
+	The <command>b10-xfrin</command>, <command>b10-xfrout</command>,
+	and <command>b10-zonemgr</command> modules require the
+	libpython3 library and the Python _sqlite3.so module.
       </para></note>
 <!-- TODO: this will change ... -->
 
@@ -165,6 +165,15 @@
             </simpara>
           </listitem>
 
+          <listitem>
+            <simpara>
+              <command>b10-zonemgr</command> &mdash;
+              Secondary manager.
+	      This process keeps track of timers and other
+              necessary information for BIND 10 to act as a slave server.
+            </simpara>
+          </listitem>
+
         </itemizedlist>
       </para>
 
@@ -650,8 +659,9 @@ var/
       The <command>bind10</command> master process will also start up
       <command>b10-cmdctl</command> for admins to communicate with the
       system, <command>b10-auth</command> for Authoritative DNS service,
-      <command>b10-xfrin</command> for inbound DNS zone transfers.
-      and <command>b10-xfrout</command> for outbound DNS zone transfers.
+      <command>b10-xfrin</command> for inbound DNS zone transfers,
+      <command>b10-xfrout</command> for outbound DNS zone transfers,
+      and <command>b10-zonemgr</command> for secondary service.
     </para>
 
     <section id="start">
@@ -1173,14 +1183,14 @@ TODO
       transfer. When received, it is stored in the BIND 10
       data store, and its records can be served by
       <command>b10-auth</command>.
-      This allows the BIND 10 server to provide
-      <quote>secondary</quote> service.
+      In combination with <command>b10-zonemgr</command> (for
+      automated SOA checks), this allows the BIND 10 server to
+      provide <quote>secondary</quote> service.
     </para>
 
     <note><simpara>
      The current development release of BIND 10 only supports
      AXFR. (IXFR is not supported.) 
-     It also does not yet support automated SOA checks.
     </simpara></note>
 
     <para>
@@ -1204,12 +1214,13 @@ TODO
       sends the zone.
       This is used to provide master DNS service to share zones
       to secondary name servers.
+      The <command>b10-xfrout</command> is also used to send
+      NOTIFY messages to slaves.
     </para>
 
     <note><simpara>
      The current development release of BIND 10 only supports
      AXFR. (IXFR is not supported.) 
-     It also does not yet support NOTIFY.
      Access control is not yet provided.
     </simpara></note>
 
@@ -1226,6 +1237,31 @@ what is XfroutClient xfr_client??
 
   </chapter>
 
+  <chapter id="zonemgr">
+    <title>Secondary Manager</title>
+
+    <para>
+      The <command>b10-zonemgr</command> process is started by
+      <command>bind10</command>.
+      It keeps track of SOA refresh, retry, and expire timers
+      and other details for BIND 10 to perform as a slave.
+      When the <command>b10-auth</command> authoritative DNS server
+      receives a NOTIFY message, <command>b10-zonemgr</command>
+      may tell <command>b10-xfrin</command> to do a refresh
+      to start an inbound zone transfer.
+      The secondary manager resets its counters when a new zone is
+      transferred in.
+    </para>
+
+    <note><simpara>
+     Access control (such as allowing notifies) is not yet provided.
+     The primary/secondary service is not yet complete.
+    </simpara></note>
+
+<!-- TODO: lots to describe for zonemgr -->
+
+  </chapter>
+
 <!-- TODO: how to help: run unit tests, join lists, review trac tickets -->
 
   <!-- <index>    <title>Index</title> </index> -->

+ 4 - 1
src/bin/Makefile.am

@@ -1 +1,4 @@
-SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout usermgr zonemgr
+SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
+	usermgr zonemgr stats tests
+
+check-recursive: all-recursive

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

@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = . tests benchmarks
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin

+ 96 - 10
src/bin/auth/asio_link.cc

@@ -64,24 +64,50 @@ IOAddress::toText() const {
     return (asio_address_.to_string());
 }
 
-// Note: this implementation is optimized for the case where this object
-// is created from an ASIO endpoint object in a receiving code path
-// by avoiding to make a copy of the base endpoint.  For TCP it may not be
-// a bug deal, but when we receive UDP packets at a high rate, the copy
-// overhead might be significant.
+/// \brief The \c TCPEndpoint class is a concrete derived class of
+/// \c IOEndpoint that represents an endpoint of a TCP connection.
+///
+/// In the current implementation, an object of this class is always
+/// instantiated within the wrapper routines.  Applications are expected to
+/// get access to the object via the abstract base class, \c IOEndpoint.
+/// This design may be changed when we generalize the wrapper interface.
+///
+/// Note: this implementation is optimized for the case where this object
+/// is created from an ASIO endpoint object in a receiving code path
+/// by avoiding to make a copy of the base endpoint.  For TCP it may not be
+/// a big deal, but when we receive UDP packets at a high rate, the copy
+/// overhead might be significant.
 class TCPEndpoint : public IOEndpoint {
 public:
+    ///
+    /// \name Constructors and Destructor
+    ///
+    //@{
+    /// \brief Constructor from a pair of address and port.
+    ///
+    /// \param address The IP address of the endpoint.
+    /// \param port The TCP port number of the endpoint.
     TCPEndpoint(const IOAddress& address, const unsigned short port) :
         asio_endpoint_placeholder_(
             new tcp::endpoint(ip::address::from_string(address.toText()),
                               port)),
         asio_endpoint_(*asio_endpoint_placeholder_)
     {}
+
+    /// \brief Constructor from an ASIO TCP endpoint.
+    ///
+    /// This constructor is designed to be an efficient wrapper for the
+    /// corresponding ASIO class, \c tcp::endpoint.
+    ///
+    /// \param asio_endpoint The ASIO representation of the TCP endpoint.
     TCPEndpoint(const tcp::endpoint& asio_endpoint) :
         asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
     {}
-        
+
+    /// \brief The destructor.        
     ~TCPEndpoint() { delete asio_endpoint_placeholder_; }
+    //@}
+
     virtual IOAddress getAddress() const {
         return (asio_endpoint_.address());
     }
@@ -90,18 +116,41 @@ private:
     const tcp::endpoint& asio_endpoint_;
 };
 
+/// \brief The \c UDPEndpoint class is a concrete derived class of
+/// \c IOEndpoint that represents an endpoint of a UDP packet.
+///
+/// Other notes about \c TCPEndpoint applies to this class, too.
 class UDPEndpoint : public IOEndpoint {
 public:
+    ///
+    /// \name Constructors and Destructor.
+    ///
+    //@{
+    /// \brief Constructor from a pair of address and port.
+    ///
+    /// \param address The IP address of the endpoint.
+    /// \param port The UDP port number of the endpoint.
     UDPEndpoint(const IOAddress& address, const unsigned short port) :
         asio_endpoint_placeholder_(
             new udp::endpoint(ip::address::from_string(address.toText()),
                               port)),
         asio_endpoint_(*asio_endpoint_placeholder_)
     {}
+
+    /// \brief Constructor from an ASIO UDP endpoint.
+    ///
+    /// This constructor is designed to be an efficient wrapper for the
+    /// corresponding ASIO class, \c udp::endpoint.
+    ///
+    /// \param asio_endpoint The ASIO representation of the UDP endpoint.
     UDPEndpoint(const udp::endpoint& asio_endpoint) :
         asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
     {}
+
+    /// \brief The destructor.
     ~UDPEndpoint() { delete asio_endpoint_placeholder_; }
+    //@}
+
     virtual IOAddress getAddress() const {
         return (asio_endpoint_.address());
     }
@@ -124,37 +173,74 @@ IOEndpoint::create(const int protocol, const IOAddress& address,
               protocol);
 }
 
+/// \brief The \c TCPSocket class is a concrete derived class of
+/// \c IOSocket that represents a TCP socket.
+///
+/// In the current implementation, an object of this class is always
+/// instantiated within the wrapper routines.  Applications are expected to
+/// get access to the object via the abstract base class, \c IOSocket.
+/// This design may be changed when we generalize the wrapper interface.
 class TCPSocket : public IOSocket {
 private:
     TCPSocket(const TCPSocket& source);
     TCPSocket& operator=(const TCPSocket& source);
 public:
+    /// \brief Constructor from an ASIO TCP socket.
+    ///
+    /// \param socket The ASIO representation of the TCP socket.
     TCPSocket(tcp::socket& socket) : socket_(socket) {}
+
     virtual int getNative() const { return (socket_.native()); }
     virtual int getProtocol() const { return (IPPROTO_TCP); }
 private:
     tcp::socket& socket_;
 };
 
+/// \brief The \c UDPSocket class is a concrete derived class of
+/// \c IOSocket that represents a UDP socket.
+///
+/// Other notes about \c TCPSocket applies to this class, too.
 class UDPSocket : public IOSocket {
 private:
     UDPSocket(const UDPSocket& source);
     UDPSocket& operator=(const UDPSocket& source);
 public:
+    /// \brief Constructor from an ASIO UDP socket.
+    ///
+    /// \param socket The ASIO representation of the UDP socket.
     UDPSocket(udp::socket& socket) : socket_(socket) {}
+
     virtual int getNative() const { return (socket_.native()); }
     virtual int getProtocol() const { return (IPPROTO_UDP); }
 private:
     udp::socket& socket_;
 };
 
+/// \brief The \c DummySocket class is a concrete derived class of
+/// \c IOSocket that is not associated with any real socket.
+///
+/// This main purpose of this class is tests, where it may be desirable to
+/// instantiate an \c IOSocket object without involving system resource
+/// allocation such as real network sockets.
 class DummySocket : public IOSocket {
 private:
     DummySocket(const DummySocket& source);
     DummySocket& operator=(const DummySocket& source);
 public:
+    /// \brief Constructor from the protocol number.
+    ///
+    /// The protocol must validly identify a standard network protocol.
+    /// For example, to specify TCP \c protocol must be \c IPPROTO_TCP.
+    ///
+    /// \param protocol The network protocol number for the socket.
     DummySocket(const int protocol) : protocol_(protocol) {}
+
+    /// \brief A dummy derived method of \c IOSocket::getNative().
+    ///
+    /// This version of method always returns -1 as the object is not
+    /// associated with a real (native) socket.
     virtual int getNative() const { return (-1); }
+
     virtual int getProtocol() const { return (protocol_); }
 private:
     const int protocol_;
@@ -197,8 +283,8 @@ public:
     void start() {
         // Check for queued configuration commands
         if (auth_server_ != NULL &&
-            auth_server_->configSession()->hasQueuedMsgs()) {
-            auth_server_->configSession()->checkCommand();
+            auth_server_->getConfigSession()->hasQueuedMsgs()) {
+            auth_server_->getConfigSession()->checkCommand();
         }
         async_read(socket_, asio::buffer(data_, TCP_MESSAGE_LENGTHSIZE),
                    boost::bind(&TCPClient::headerRead, this,
@@ -385,8 +471,8 @@ public:
     {
         // Check for queued configuration commands
         if (auth_server_ != NULL &&
-            auth_server_->configSession()->hasQueuedMsgs()) {
-            auth_server_->configSession()->checkCommand();
+            auth_server_->getConfigSession()->hasQueuedMsgs()) {
+            auth_server_->getConfigSession()->checkCommand();
         }
         if (!error && bytes_recvd > 0) {
             const UDPEndpoint remote_endpoint(sender_endpoint_);

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

@@ -24,10 +24,13 @@
 #include <exceptions/exceptions.h>
 
 #include <dns/buffer.h>
+#include <dns/edns.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/question.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rrset.h>
 #include <dns/rrttl.h>
 #include <dns/message.h>
@@ -164,7 +167,6 @@ makeErrorMessage(Message& message, MessageRenderer& renderer,
     message.setQid(qid);
     message.setOpcode(opcode);
     message.setHeaderFlag(MessageFlag::QR());
-    message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
     if (rd) {
         message.setHeaderFlag(MessageFlag::RD());
     }
@@ -193,6 +195,16 @@ AuthSrv::getVerbose() const {
 }
 
 void
+AuthSrv::setCacheSlots(const size_t slots) {
+    impl_->cache_.setSlots(slots);
+}
+
+size_t
+AuthSrv::getCacheSlots() const {
+    return (impl_->cache_.getSlots());
+}
+
+void
 AuthSrv::setXfrinSession(AbstractSession* xfrin_session) {
     impl_->xfrin_session_ = xfrin_session;
 }
@@ -203,7 +215,7 @@ AuthSrv::setConfigSession(ModuleCCSession* config_session) {
 }
 
 ModuleCCSession*
-AuthSrv::configSession() const {
+AuthSrv::getConfigSession() const {
     return (impl_->config_session_);
 }
 
@@ -292,14 +304,21 @@ bool
 AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
                                 MessageRenderer& response_renderer)
 {
-    const bool dnssec_ok = message.isDNSSECSupported();
-    const uint16_t remote_bufsize = message.getUDPSize();
+    ConstEDNSPtr remote_edns = message.getEDNS();
+    const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
+    const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
+        Message::DEFAULT_MAX_UDPSIZE; 
 
     message.makeResponse();
     message.setHeaderFlag(MessageFlag::AA());
     message.setRcode(Rcode::NOERROR());
-    message.setDNSSECSupported(dnssec_ok);
-    message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
+
+    if (remote_edns) {
+        EDNSPtr local_edns = EDNSPtr(new EDNS());
+        local_edns->setDNSSECAwareness(dnssec_ok);
+        local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
+        message.setEDNS(local_edns);
+    }
 
     try {
         Query query(message, cache_, dnssec_ok);

+ 114 - 2
src/bin/auth/auth_srv.h

@@ -38,8 +38,30 @@ namespace asio_link {
 class IOMessage;
 }
 
+/// \brief The implementation class for the \c AuthSrv class using the pimpl
+/// idiom.
 class AuthSrvImpl;
 
+/// \brief The authoritative nameserver class.
+///
+/// \c AuthSrv is a concrete class that implements authoritative DNS server
+/// protocol processing.
+/// An \c AuthSrv object is primarily responsible for handling incoming DNS
+/// requests: It parses the request and dispatches subsequent processing to
+/// the corresponding module (which may be an internal library or a separate
+/// process) depending on the request type.  For normal queries, the
+/// \c AuthSrv object searches configured data sources for the answer to the
+/// query, and builds a response containing the answer.
+///
+/// This class uses the "pimpl" idiom, and hides detailed implementation
+/// through the \c impl_ pointer (which points to an instance of the
+/// \c AuthSrvImpl class).  An \c AuthSrv object is supposed to exist for quite
+/// a long period, and only a few \c AuthSrv objects will be created (in fact,
+/// in this current implementation there will only be one object), so the
+/// construction overhead of this approach should be acceptable.
+///
+/// The design of this class is still in flux.  It's quite likely to change
+/// in future versions.
 class AuthSrv {
     ///
     /// \name Constructors, Assignment Operator and Destructor.
@@ -67,12 +89,101 @@ public:
     bool processMessage(const asio_link::IOMessage& io_message,
                         isc::dns::Message& message,
                         isc::dns::MessageRenderer& response_renderer);
-    void setVerbose(bool on);
+
+    /// \brief Enable or disable verbose logging.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param on \c true to enable verbose logging; \c false to disable
+    /// verbose logging.
+    void setVerbose(const bool on);
+
+    /// \brief Returns the logging verbosity of the \c AuthSrv object.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return \c true if verbose logging is enabled; otherwise \c false.
     bool getVerbose() const;
+
+    /// \brief Updates the data source for the \c AuthSrv object.
+    ///
+    /// This method installs or replaces the data source that the \c AuthSrv
+    /// object refers to for query processing.
+    /// Although the method name is generic, the only thing it does is to
+    /// update the data source information.
+    /// If there is a data source installed, it will be replaced with the
+    /// new one.
+    ///
+    /// In the current implementation, the SQLite data source is assumed.
+    /// The \c config parameter will simply be passed to the initialization
+    /// routine of the \c Sqlite3DataSrc class.
+    ///
+    /// On success this method returns a data \c Element (in the form of a
+    /// pointer like object) indicating the successful result,
+    /// i.e., {"result": [0]}.
+    /// Otherwise, it returns a data \c Element explaining the error:
+    /// {"result": [1, <error-description>]}.
+    ///
+    /// This method is mostly exception free (error conditions are represented
+    /// via the return value).  But it may still throw a standard exception
+    /// if memory allocation fails inside the method.
+    /// When a standard exception is thrown or an implementation specific
+    /// exception is triggered and caught internally, this function provides
+    /// the strong exception guarantee: Unless everything succeeds, currently
+    /// installed data source (if any) won't be replaced.
+    ///
+    /// \param config An immutable pointer-like object to a data \c Element,
+    /// possibly containing the data source information to be used.
+    /// \return An immutable pointer-like object to a data \c Element
+    /// containing the result of the update operation.
     isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
-    isc::config::ModuleCCSession* configSession() const;
+
+    /// \param Returns the command and configuration session for the
+    /// \c AuthSrv.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A pointer to \c ModuleCCSession object stored in the
+    /// \c AuthSrv object.  In this implementation it could be NULL.
+    isc::config::ModuleCCSession* getConfigSession() const;
+
+    /// \brief Set the command and configuration session for the \c AuthSrv.
+    ///
+    /// Note: this interface is tentative.  We'll revisit the ASIO and session
+    /// frameworks, at which point the session will probably be passed on
+    /// construction of the server.
+    /// In the current implementation, this method is expected to be called
+    /// exactly once as part of initialization.  If this method is called
+    /// multiple times, previously specified session is silently overridden.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param config_session A pointer to \c ModuleCCSession object to receive
+    /// control commands and configuration updates.
     void setConfigSession(isc::config::ModuleCCSession* config_session);
 
+    /// \brief Set or update the size (number of slots) of hot spot cache.
+    ///
+    /// If the specified size is 0, it means the size will be unlimited.
+    /// The specified size is recorded even if the cache is disabled; the
+    /// new size will be effective when the cache is enabled.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param slots The number of cache slots.
+    void setCacheSlots(const size_t slots);
+
+    /// \brief Get the current size (number of slots) of hot spot cache.
+    ///
+    /// It always returns the recorded size regardless of the cache is enabled.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return The current number of cache slots.
+    size_t getCacheSlots() const;
+
+    /// \brief Set the communication session with a separate process for
+    /// outgoing zone transfers.
     ///
     /// Note: this interface is tentative.  We'll revisit the ASIO and session
     /// frameworks, at which point the session will probably be passed on
@@ -85,6 +196,7 @@ public:
     /// Ownership isn't transferred: the caller is responsible for keeping
     /// this object to be valid while the server object is working and for
     /// disconnecting the session and destroying the object when the server
+    /// is shutdown.
     ///
     void setXfrinSession(isc::cc::AbstractSession* xfrin_session);
 private:

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

@@ -137,6 +137,7 @@ configuration is not defined\&.
 \fBb10-cmdctl\fR(8),
 \fBb10-loadzone\fR(8),
 \fBb10-msgq\fR(8),
+\fBb10-zonemgr\fR(8),
 \fBbind10\fR(8),
 BIND 10 Guide\&.
 .SH "HISTORY"

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

@@ -201,6 +201,9 @@
         <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
+        <refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
         <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citetitle>BIND 10 Guide</citetitle>.

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

@@ -1,7 +1,10 @@
+SUBDIRS = testdata .
+
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/auth/tests/testdata\"
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
@@ -36,25 +39,3 @@ run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
-
-EXTRA_DIST =  testdata/badExampleQuery_fromWire
-EXTRA_DIST += testdata/badExampleQuery_fromWire.spec
-EXTRA_DIST += testdata/example.com
-EXTRA_DIST += testdata/examplequery_fromWire
-EXTRA_DIST += testdata/examplequery_fromWire.spec
-EXTRA_DIST += testdata/example.sqlite3
-EXTRA_DIST += testdata/iqueryresponse_fromWire
-EXTRA_DIST += testdata/iqueryresponse_fromWire.spec
-EXTRA_DIST += testdata/multiquestion_fromWire
-EXTRA_DIST += testdata/multiquestion_fromWire.spec
-EXTRA_DIST += testdata/queryBadEDNS_fromWire
-EXTRA_DIST += testdata/queryBadEDNS_fromWire.spec
-EXTRA_DIST += testdata/shortanswer_fromWire
-EXTRA_DIST += testdata/shortanswer_fromWire.spec
-EXTRA_DIST += testdata/shortmessage_fromWire
-EXTRA_DIST += testdata/shortquestion_fromWire
-EXTRA_DIST += testdata/shortresponse_fromWire
-EXTRA_DIST += testdata/simplequery_fromWire
-EXTRA_DIST += testdata/simplequery_fromWire.spec
-EXTRA_DIST += testdata/simpleresponse_fromWire
-EXTRA_DIST += testdata/simpleresponse_fromWire.spec

+ 34 - 13
src/bin/auth/tests/auth_srv_unittest.cc

@@ -22,6 +22,8 @@
 #include <dns/name.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 
@@ -114,7 +116,7 @@ protected:
     AuthSrvTest() : server(true, xfrout),
                     request_message(Message::RENDER),
                     parse_message(Message::PARSE), default_qid(0x1035),
-                    opcode(Opcode(Opcode::QUERY())), qname("www.example.com"),
+                    opcode(Opcode::QUERY()), qname("www.example.com"),
                     qclass(RRClass::IN()), qtype(RRType::A()),
                     io_message(NULL), endpoint(NULL), request_obuffer(0),
                     request_renderer(request_obuffer),
@@ -280,6 +282,7 @@ AuthSrvTest::createRequestMessage(const Opcode& opcode,
 {
     request_message.clear(Message::RENDER);
     request_message.setOpcode(opcode);
+    request_message.setRcode(Rcode::NOERROR());
     request_message.setQid(default_qid);
     request_message.addQuestion(Question(request_name, rrclass, rrtype));
 }
@@ -341,7 +344,7 @@ TEST_F(AuthSrvTest, unsupportedRequest) {
             i == Opcode::NOTIFY().getCode()) {
             continue;
         }
-        createDataFromFile("simplequery_fromWire");
+        createDataFromFile("simplequery_fromWire.wire");
         data[2] = ((i << 3) & 0xff);
 
         parse_message.clear(Message::PARSE);
@@ -363,7 +366,7 @@ TEST_F(AuthSrvTest, verbose) {
 
 // Multiple questions.  Should result in FORMERR.
 TEST_F(AuthSrvTest, multiQuestion) {
-    createDataFromFile("multiquestion_fromWire");
+    createDataFromFile("multiquestion_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
     headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
@@ -393,7 +396,7 @@ TEST_F(AuthSrvTest, shortMessage) {
 // or malformed or could otherwise cause a protocol error.
 TEST_F(AuthSrvTest, response) {
     // A valid (although unusual) response
-    createDataFromFile("simpleresponse_fromWire");
+    createDataFromFile("simpleresponse_fromWire.wire");
     EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                            response_renderer));
 
@@ -404,7 +407,7 @@ TEST_F(AuthSrvTest, response) {
                                            response_renderer));
 
     // A response to iquery.  must be dropped rather than returning NOTIMP.
-    createDataFromFile("iqueryresponse_fromWire");
+    createDataFromFile("iqueryresponse_fromWire.wire");
     EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                            response_renderer));
 }
@@ -422,7 +425,7 @@ TEST_F(AuthSrvTest, shortQuestion) {
 
 // Query with a broken answer section
 TEST_F(AuthSrvTest, shortAnswer) {
-    createDataFromFile("shortanswer_fromWire");
+    createDataFromFile("shortanswer_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
 
@@ -441,17 +444,24 @@ TEST_F(AuthSrvTest, shortAnswer) {
 
 // Query with unsupported version of EDNS.
 TEST_F(AuthSrvTest, ednsBadVers) {
-    createDataFromFile("queryBadEDNS_fromWire");
+    createDataFromFile("queryBadEDNS_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
 
-    // The response must have an EDNS OPT RR in the additional section.
+    // The response must have an EDNS OPT RR in the additional section, but
+    // it will be added automatically at the render time.
     // Note that the DNSSEC DO bit is cleared even if this bit in the query
     // is set.  This is a limitation of the current implementation.
     headerCheck(parse_message, default_qid, Rcode::BADVERS(), opcode.getCode(),
                 QR_FLAG, 1, 0, 0, 1);
-    EXPECT_EQ(4096, parse_message.getUDPSize());
-    EXPECT_FALSE(parse_message.isDNSSECSupported());
+    EXPECT_FALSE(parse_message.getEDNS()); // EDNS isn't added at this point
+
+    parse_message.clear(Message::PARSE);
+    InputBuffer ib(response_renderer.getData(), response_renderer.getLength());
+    parse_message.fromWire(ib);
+    EXPECT_EQ(Rcode::BADVERS(), parse_message.getRcode());
+    EXPECT_TRUE(parse_message.getEDNS());
+    EXPECT_FALSE(parse_message.getEDNS()->getDNSSECAwareness());
 }
 
 TEST_F(AuthSrvTest, AXFROverUDP) {
@@ -577,6 +587,7 @@ TEST_F(AuthSrvTest, notifyForCHClass) {
 TEST_F(AuthSrvTest, notifyEmptyQuestion) {
     request_message.clear(Message::RENDER);
     request_message.setOpcode(Opcode::NOTIFY());
+    request_message.setRcode(Rcode::NOERROR());
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setQid(default_qid);
     request_message.toWire(request_renderer);
@@ -715,7 +726,7 @@ TEST_F(AuthSrvTest, updateConfig) {
     // query for existent data in the installed data source.  The resulting
     // response should have the AA flag on, and have an RR in each answer
     // and authority section.
-    createDataFromFile("examplequery_fromWire");
+    createDataFromFile("examplequery_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
     headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
@@ -729,7 +740,7 @@ TEST_F(AuthSrvTest, datasourceFail) {
     // tool and the data source itself naively accept it).  This will result
     // in a SERVFAIL response, and the answer and authority sections should
     // be empty.
-    createDataFromFile("badExampleQuery_fromWire");
+    createDataFromFile("badExampleQuery_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
     headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
@@ -744,10 +755,20 @@ TEST_F(AuthSrvTest, updateConfigFail) {
     updateConfig(&server, BADCONFIG_TESTDB, false);
 
     // The original data source should still exist.
-    createDataFromFile("examplequery_fromWire");
+    createDataFromFile("examplequery_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
     headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                 QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
+
+TEST_F(AuthSrvTest, cacheSlots) {
+    // simple check for the get/set operations
+    server.setCacheSlots(10);    // 10 = arbitrary choice
+    EXPECT_EQ(10, server.getCacheSlots());
+
+    // 0 is a valid size
+    server.setCacheSlots(0);
+    EXPECT_EQ(00, server.getCacheSlots());
+}
 }

+ 2 - 2
src/bin/auth/tests/run_unittests.cc

@@ -19,10 +19,10 @@
 #include <dns/tests/unittest_util.h>
 
 int
-main(int argc, char* argv[])
-{
+main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
+    isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
 
     return (RUN_ALL_TESTS());
 }

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

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 sbin_SCRIPTS = bind10
 CLEANFILES = bind10 bind10.pyc

+ 22 - 2
src/bin/bind10/bind10.8

@@ -1,7 +1,7 @@
 '\" t
 .\"     Title: bind10
 .\"    Author: [see the "AUTHORS" section]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\" Generator: DocBook XSL Stylesheets v1.76.0 <http://docbook.sf.net/>
 .\"      Date: July 29, 2010
 .\"    Manual: BIND10
 .\"    Source: BIND10
@@ -9,6 +9,15 @@
 .\"
 .TH "BIND10" "8" "July 29, 2010" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" disable hyphenation
@@ -22,7 +31,7 @@
 bind10 \- BIND 10 boss process
 .SH "SYNOPSIS"
 .HP \w'\fBbind10\fR\ 'u
-\fBbind10\fR [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-address\ \fR\fB\fIaddress\fR\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-verbose\fR]
+\fBbind10\fR [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-address\ \fR\fB\fIaddress\fR\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-verbose\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -86,6 +95,15 @@ to run as\&.
 must be initially ran as the root user to use this option\&. The default is to run as the current user\&.
 .RE
 .PP
+\fB\-\-pretty\-name \fR\fB\fIname\fR\fR
+.RS 4
+The name this process should have in tools like
+\fBps\fR
+or
+\fBtop\fR\&. This is handy if you have multiple versions/installations of
+\fBbind10\fR\&.
+.RE
+.PP
 \fB\-v\fR, \fB\-\-verbose\fR
 .RS 4
 Display more about what is going on for
@@ -101,6 +119,8 @@ and its child processes\&.
 \fBb10-cmdctl\fR(8),
 \fBb10-msgq\fR(8),
 \fBb10-xfrin\fR(8),
+\fBb10-xfrout\fR(8),
+\fBb10-zonemgr\fR(8),
 BIND 10 Guide\&.
 .SH "HISTORY"
 .PP

+ 83 - 51
src/bin/bind10/bind10.py.in

@@ -63,9 +63,19 @@ import pwd
 import posix
 
 import isc.cc
+import isc.net.parse
+import isc.utils.process
+
+# Assign this process some longer name
+isc.utils.process.rename(sys.argv[0])
 
 # This is the version that gets displayed to the user.
-__version__ = "v20100531"
+# The VERSION string consists of the module name, the module version
+# number, and the overall BIND 10 version number (set in configure.ac).
+VERSION = "bind10 20100916 (BIND 10 @PACKAGE_VERSION@)"
+
+# This is for bind10.boottime of stats module
+_BASETIME = time.gmtime()
 
 class RestartSchedule:
     """
@@ -174,35 +184,10 @@ class ProcessInfo:
     def respawn(self):
         self._spawn()
 
-class IPAddr:
-    """Stores an IPv4 or IPv6 address."""
-    family = None
-    addr = None
-
-    def __init__(self, addr):
-        try:
-            a = socket.inet_pton(socket.AF_INET, addr)
-            self.family = socket.AF_INET
-            self.addr = a
-            return
-        except:
-            pass
-
-        try:
-            a = socket.inet_pton(socket.AF_INET6, addr)
-            self.family = socket.AF_INET6
-            self.addr = a
-            return
-        except Exception as e:
-            raise e
-    
-    def __str__(self):
-        return socket.inet_ntop(self.family, self.addr)
-
 class BoB:
     """Boss of BIND class."""
     
-    def __init__(self, msgq_socket_file=None, auth_port=5300, address='',
+    def __init__(self, msgq_socket_file=None, auth_port=5300, address=None,
                  nocache=False, verbose=False, setuid=None, username=None):
         """Initialize the Boss of BIND. This is a singleton (only one
         can run).
@@ -216,7 +201,7 @@ class BoB:
         self.auth_port = auth_port
         self.address = None
         if address:
-            self.address = IPAddr(address)
+            self.address = address
         self.cc_session = None
         self.ccs = None
         self.processes = {}
@@ -418,6 +403,27 @@ class BoB:
             sys.stdout.write("[bind10] Started b10-zonemgr(PID %d)\n" % 
                              zonemgr.pid)
 
+        # start b10-stats
+        stats_args = ['b10-stats']
+        if self.verbose:
+            sys.stdout.write("[bind10] Starting b10-stats\n")
+            stats_args += ['-v']
+        try:
+            statsd = ProcessInfo("b10-stats", stats_args,
+                                 c_channel_env)
+        except Exception as e:
+            c_channel.process.kill()
+            bind_cfgd.process.kill()
+            xfrout.process.kill()
+            auth.process.kill()
+            xfrind.process.kill()
+            zonemgr.process.kill()
+            return "Unable to start b10-stats; " + str(e)
+
+        self.processes[statsd.pid] = statsd
+        if self.verbose:
+            sys.stdout.write("[bind10] Started b10-stats (PID %d)\n" % statsd.pid)
+
         # start the b10-cmdctl
         # XXX: we hardcode port 8080
         cmdctl_args = ['b10-cmdctl']
@@ -434,6 +440,7 @@ class BoB:
             auth.process.kill()
             xfrind.process.kill()
             zonemgr.process.kill()
+            statsd.process.kill()
             return "Unable to start b10-cmdctl; " + str(e)
         self.processes[cmd_ctrld.pid] = cmd_ctrld
         if self.verbose:
@@ -453,6 +460,7 @@ class BoB:
         self.cc_session.group_sendmsg(cmd, "Boss", "Xfrout")
         self.cc_session.group_sendmsg(cmd, "Boss", "Xfrin")
         self.cc_session.group_sendmsg(cmd, "Boss", "Zonemgr")
+        self.cc_session.group_sendmsg(cmd, "Boss", "Stats")
 
     def stop_process(self, process):
         """Stop the given process, friendly-like."""
@@ -576,7 +584,7 @@ def reaper(signal_number, stack_frame):
     # the Python signal handler has been set up to write
     # down a pipe, waking up our select() bit
     pass
-                   
+
 def get_signame(signal_number):
     """Return the symbolic name for a signal."""
     for sig in dir(signal):
@@ -598,27 +606,29 @@ def fatal_signal(signal_number, stack_frame):
 def check_port(option, opt_str, value, parser):
     """Function to insure that the port we are passed is actually 
     a valid port number. Used by OptionParser() on startup."""
-    if not re.match('^(6553[0-5]|655[0-2]\d|65[0-4]\d\d|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{0,3}|0)$', value):
-        raise OptionValueError("%s requires a port number (0-65535)" % opt_str)
-    if (opt_str == '-m' or opt_str == '--msgq-port'):
-        parser.values.msgq_port = value
-    elif (opt_str == '-p' or opt_str == '--port'):
-        parser.values.auth_port = value
-    else:
-        raise OptionValueError("Unknown option " + opt_str)
-  
+    try:
+        if opt_str in ['-p', '--port']:
+            parser.values.auth_port = isc.net.parse.port_parse(value)
+        else:
+            raise OptionValueError("Unknown option " + opt_str)
+    except ValueError as e:
+        raise OptionValueError(str(e))
+
 def check_addr(option, opt_str, value, parser):
     """Function to insure that the address we are passed is actually 
     a valid address. Used by OptionParser() on startup."""
     try:
-        IPAddr(value)
-    except:
+        if opt_str in ['-a', '--address']:
+            parser.values.address = isc.net.parse.addr_parse(value)
+        else:
+            raise OptionValueError("Unknown option " + opt_str)
+    except ValueError:
         raise OptionValueError("%s requires a valid IPv4 or IPv6 address" % opt_str)
-    if (opt_str == '-a' or opt_str == '--address'):
-        parser.values.address = value
-    else:
-        raise OptionValueError("Unknown option " + opt_str)
-  
+
+def process_rename(option, opt_str, value, parser):
+    """Function that renames the process if it is requested by a option."""
+    isc.utils.process.rename(value)
+
 def main():
     global options
     global boss_of_bind
@@ -627,7 +637,7 @@ def main():
 
 
     # Parse any command-line options.
-    parser = OptionParser(version=__version__)
+    parser = OptionParser(version=VERSION)
     parser.add_option("-a", "--address", dest="address", type="string",
                       action="callback", callback=check_addr, default='',
                       help="address the b10-auth daemon will use (default: listen on all addresses)")
@@ -636,14 +646,17 @@ def main():
                       help="UNIX domain socket file the b10-msgq daemon will use")
     parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
                       default=False, help="disable hot-spot cache in b10-auth")
-    parser.add_option("-p", "--port", dest="auth_port", type="string",
-                      action="callback", callback=check_port, default="5300",
+    parser.add_option("-p", "--port", dest="auth_port", type="int",
+                      action="callback", callback=check_port, default=5300,
                       help="port the b10-auth daemon will use (default 5300)")
     parser.add_option("-u", "--user", dest="user",
                       type="string", default=None,
                       help="Change user after startup (must run as root)")
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
                       help="display more about what is going on")
+    parser.add_option("--pretty-name", type="string", action="callback",
+                      callback=process_rename,
+                      help="Set the process name (displayed in ps, top, ...)")
     (options, args) = parser.parse_args()
     if args:
         parser.print_help()
@@ -680,7 +693,7 @@ def main():
 
     # Announce startup.
     if options.verbose:
-        sys.stdout.write("BIND 10 %s\n" % __version__)
+        sys.stdout.write("%s\n" % VERSION)
 
     # TODO: set process name, perhaps by:
     #       http://code.google.com/p/procname/
@@ -697,8 +710,11 @@ def main():
     signal.signal(signal.SIGINT, fatal_signal)
     signal.signal(signal.SIGTERM, fatal_signal)
 
+    # Block SIGPIPE, as we don't want it to end this process
+    signal.signal(signal.SIGPIPE, signal.SIG_IGN)
+
     # Go bob!
-    boss_of_bind = BoB(options.msgq_socket_file, int(options.auth_port),
+    boss_of_bind = BoB(options.msgq_socket_file, options.auth_port,
                        options.address, options.nocache, options.verbose,
                        setuid, username)
     startup_result = boss_of_bind.startup()
@@ -707,6 +723,17 @@ def main():
         sys.exit(1)
     sys.stdout.write("[bind10] BIND 10 started\n")
 
+    # send "bind10.boot_time" to b10-stats
+    time.sleep(1) # wait a second
+    if options.verbose:
+        sys.stdout.write("[bind10] send \"bind10.boot_time\" to b10-stats\n")
+    cmd = isc.config.ccsession.create_command('set', 
+            { "stats_data": {
+              'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
+              }
+            })
+    boss_of_bind.cc_session.group_sendmsg(cmd, 'Stats')
+
     # In our main loop, we check for dead processes or messages 
     # on the c-channel.
     wakeup_fd = wakeup_pipe[0]
@@ -735,7 +762,12 @@ def main():
 
         for fd in rlist + xlist:
             if fd == ccs_fd:
-                boss_of_bind.ccs.check_command()
+                try:
+                    boss_of_bind.ccs.check_command()
+                except isc.cc.session.ProtocolError:
+                    if options.verbose:
+                        sys.stderr.write("[bind10] msgq channel disappeared.\n")
+                    break
             elif fd == wakeup_fd:
                 os.read(wakeup_fd, 32)
 

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

@@ -56,6 +56,7 @@
       <arg><option>--no-cache</option></arg>
       <arg><option>--port <replaceable>number</replaceable></option></arg>
       <arg><option>--user <replaceable>user</replaceable></option></arg>
+      <arg><option>--pretty-name <replaceable>name</replaceable></option></arg>
       <arg><option>--verbose</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -149,6 +150,17 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>--pretty-name <replaceable>name</replaceable></option></term>
+
+        <listitem>
+          <para>The name this process should have in tools like
+          <command>ps</command> or <command>top</command>. This
+          is handy if you have multiple versions/installations
+          of <command>bind10</command>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-v</option>, <option>--verbose</option></term>
         <listitem>
 	  <para>Display more about what is going on for
@@ -189,6 +201,12 @@
       <citerefentry>
         <refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-xfrout</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
       <citetitle>BIND 10 Guide</citetitle>.
     </para>
   </refsect1>

+ 1 - 1
src/bin/bind10/run_bind10.sh.in

@@ -20,7 +20,7 @@ export PYTHON_EXEC
 
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 
-PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:$PATH
+PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:$PATH
 export PATH
 
 PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs

+ 23 - 0
src/bin/bind10/tests/args_test.py

@@ -130,5 +130,28 @@ class TestBossArgs(unittest.TestCase):
         x = bob.wait()
         self.assertTrue(bob.wait() == 0)
 
+    def testPrettyName(self):
+        """Try the --pretty-name option."""
+        CMD_PRETTY_NAME = b'bob-name-test'
+        bob = subprocess.Popen(args=(BIND10_EXE, '--pretty-name',
+            CMD_PRETTY_NAME), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        started_ok = self._waitForString(bob, '[bind10] BIND 10 started')
+        self.assertTrue(started_ok)
+        ps = subprocess.Popen(args=("ps", "axo", "pid,comm"),
+                              stdout=subprocess.PIPE)
+        s = ps.stdout.readline()
+        command = None
+        while True:
+            s = ps.stdout.readline()
+            if s == '': break
+            (pid,comm) = s.split(None, 1)
+            if int(pid) == bob.pid:
+                command = comm
+                break
+        self.assertEqual(command, CMD_PRETTY_NAME + b'\n')
+        time.sleep(0.1)
+        bob.terminate()
+        bob.wait()
+
 if __name__ == '__main__':
     unittest.main()

+ 3 - 24
src/bin/bind10/tests/bind10_test.py

@@ -1,4 +1,4 @@
-from bind10 import ProcessInfo, BoB, IPAddr
+from bind10 import ProcessInfo, BoB
 
 # XXX: environment tests are currently disabled, due to the preprocessor
 #      setup that we have now complicating the environment
@@ -8,6 +8,7 @@ import sys
 import os
 import signal
 import socket
+from isc.net.addr import IPAddr
 
 class TestProcessInfo(unittest.TestCase):
     def setUp(self):
@@ -72,28 +73,6 @@ class TestProcessInfo(unittest.TestCase):
         self.assertTrue(type(pi.pid) is int)
         self.assertNotEqual(pi.pid, old_pid)
 
-class TestIPAddr(unittest.TestCase):
-    def test_v6ok(self):
-        addr = IPAddr('2001:4f8::1')
-        self.assertEqual(addr.family, socket.AF_INET6)
-        self.assertEqual(addr.addr, socket.inet_pton(socket.AF_INET6, '2001:4f8::1'))
-
-    def test_v4ok(self):
-        addr = IPAddr('127.127.127.127')
-        self.assertEqual(addr.family, socket.AF_INET)
-        self.assertEqual(addr.addr, socket.inet_aton('127.127.127.127'))
-
-    def test_badaddr(self):
-        self.assertRaises(socket.error, IPAddr, 'foobar')
-        self.assertRaises(socket.error, IPAddr, 'foo::bar')
-        self.assertRaises(socket.error, IPAddr, '123')
-        self.assertRaises(socket.error, IPAddr, '123.456.789.0')
-        self.assertRaises(socket.error, IPAddr, '127/8')
-        self.assertRaises(socket.error, IPAddr, '0/0')
-        self.assertRaises(socket.error, IPAddr, '1.2.3.4/32')
-        self.assertRaises(socket.error, IPAddr, '0')
-        self.assertRaises(socket.error, IPAddr, '')
-
 class TestBoB(unittest.TestCase):
     def test_init(self):
         bob = BoB()
@@ -127,7 +106,7 @@ class TestBoB(unittest.TestCase):
         self.assertEqual(bob.runnable, False)
 
     def test_init_alternate_address(self):
-        bob = BoB(None, 5300, '127.127.127.127')
+        bob = BoB(None, 5300, IPAddr('127.127.127.127'))
         self.assertEqual(bob.verbose, False)
         self.assertEqual(bob.auth_port, 5300)
         self.assertEqual(bob.msgq_socket_file, None)

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

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 bin_SCRIPTS = bindctl
 man_MANS = bindctl.1

+ 3 - 0
src/bin/bindctl/bindctl-source.py.in

@@ -24,6 +24,9 @@ from bindctl.moduleinfo import *
 from bindctl.bindcmd import *
 import pprint
 from optparse import OptionParser, OptionValueError
+import isc.utils.process
+
+isc.utils.process.rename()
 
 __version__ = 'Bindctl'
 

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

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 

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

@@ -21,9 +21,12 @@ import sys; sys.path.append ('@@PYTHONPATH@@')
 
 from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError
 from isc.cc import SessionError
+import isc.utils.process
 import signal
 import os
 
+isc.utils.process.rename()
+
 # If B10_FROM_SOURCE is set in the environment, we use data files
 # from a directory relative to that, otherwise we use the ones
 # installed on the system

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

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
@@ -15,7 +15,7 @@ CMDCTL_CONFIGURATIONS += cmdctl-keyfile.pem cmdctl-certfile.pem
 
 b10_cmdctl_DATA = $(CMDCTL_CONFIGURATIONS)
 b10_cmdctl_DATA += cmdctl.spec
- 
+
 EXTRA_DIST = $(CMDCTL_CONFIGURATIONS)
 
 CLEANFILES=	b10-cmdctl cmdctl.pyc cmdctl.spec

+ 23 - 17
src/bin/cmdctl/cmdctl.py.in

@@ -1,6 +1,7 @@
 #!@PYTHON@
 
 # Copyright (C) 2010  Internet Systems Consortium.
+# Copyright (C) 2010  CZ NIC
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -42,6 +43,8 @@ import random
 import time
 import signal
 from isc.config import ccsession
+import isc.net.parse
+import isc.utils.process
 from optparse import OptionParser, OptionValueError
 from hashlib import sha1
 try:
@@ -49,6 +52,8 @@ try:
 except ImportError:
     import dummy_threading as threading
 
+isc.utils.process.rename()
+
 __version__ = 'BIND10'
 URL_PATTERN = re.compile('/([\w]+)(?:/([\w]+))?/?')
 CONFIG_DATA_URL = 'config_data'
@@ -320,8 +325,8 @@ class CommandControl():
     def _handle_msg_from_msgq(self):
         '''Process all the received commands with module session. '''
         while self._serving:
-            self._module_cc.check_command() 
- 
+            self._module_cc.check_command(False)
+
     def _parse_command_result(self, rcode, reply):
         '''Ignore the error reason when command rcode isn't 0, '''
         if rcode != 0:
@@ -380,6 +385,7 @@ class CommandControl():
     def send_command(self, module_name, command_name, params = None):
         '''Send the command from bindctl to proper module. '''
         errstr = 'unknown error'
+        answer = None
         if self._verbose:
             self.log_info("Begin send command '%s' to module '%s'" %(command_name, module_name))
 
@@ -390,7 +396,10 @@ class CommandControl():
             msg = ccsession.create_command(command_name, params)
             seq = self._cc.group_sendmsg(msg, module_name)
             #TODO, it may be blocked, msqg need to add a new interface waiting in timeout.
-            answer, env = self._cc.group_recvmsg(False, seq)
+            try:
+                answer, env = self._cc.group_recvmsg(False, seq)
+            except isc.cc.session.SessionTimeout:
+                errstr = "Module '%s' not responding" % module_name
 
         if self._verbose:
             self.log_info("Finish send command '%s' to module '%s'" % (command_name, module_name))
@@ -410,7 +419,6 @@ class CommandControl():
             except ccsession.ModuleCCSessionError as mcse:
                 errstr = str("Error in ccsession answer:") + str(mcse)
                 self.log_info(errstr)
-        
         return 1, {'error': errstr}
     
     def log_info(self, msg):
@@ -558,22 +566,17 @@ def run(addr = 'localhost', port = 8080, idle_timeout = 1200, verbose = False):
     httpd.serve_forever()
 
 def check_port(option, opt_str, value, parser):
-    if (value < 0) or (value > 65535):
-        raise OptionValueError('%s requires a port number (0-65535)' % opt_str)
-    parser.values.port = value
+    try:
+        parser.values.port = isc.net.parse.port_parse(value)
+    except ValueError as e:
+        raise OptionValueError(str(e))
 
 def check_addr(option, opt_str, value, parser):
-    ipstr = value
-    ip_family = socket.AF_INET
-    if (ipstr.find(':') != -1):
-        ip_family = socket.AF_INET6
-
     try:
-        socket.inet_pton(ip_family, ipstr)
-    except:
-        raise OptionValueError("%s invalid ip address" % ipstr)
-
-    parser.values.addr = value
+        isc.net.parse.addr_parse(value)
+        parser.values.addr = value
+    except ValueError as e:
+        raise OptionValueError(str(e))
 
 def set_cmd_options(parser):
     parser.add_option('-p', '--port', dest = 'port', type = 'int',
@@ -602,6 +605,9 @@ if __name__ == '__main__':
     except isc.cc.SessionError as err:
         sys.stderr.write("[b10-cmdctl] Error creating b10-cmdctl, "
                          "is the command channel daemon running?\n")        
+    except isc.cc.SessionTimeout:
+        sys.stderr.write("[b10-cmdctl] Error creating b10-cmdctl, "
+                         "is the configuration manager running?\n")        
     except KeyboardInterrupt:
         sys.stderr.write("[b10-cmdctl] exit from Cmdctl\n")
     except CmdctlException as err:

+ 2 - 0
src/bin/host/host.cc

@@ -28,6 +28,8 @@
 #include <dns/name.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 #include <dns/rrset.h>

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

@@ -1,5 +1,4 @@
-SUBDIRS = tests/correct
-SUBDIRS += tests/error
+SUBDIRS = . tests/correct tests/error
 bin_SCRIPTS = b10-loadzone
 
 CLEANFILES = b10-loadzone

+ 4 - 0
src/bin/loadzone/b10-loadzone.py.in

@@ -18,9 +18,13 @@
 import sys; sys.path.append ('@@PYTHONPATH@@')
 import re, getopt
 import isc.datasrc
+import isc.utils.process
 from isc.datasrc.master import MasterFile
 import time
 import os
+
+isc.utils.process.rename()
+
 #########################################################################
 # usage: print usage note and exit
 #########################################################################

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

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
  

+ 7 - 0
src/bin/msgq/msgq.py.in

@@ -31,9 +31,12 @@ import select
 import pprint
 import random
 from optparse import OptionParser, OptionValueError
+import isc.utils.process
 
 import isc.cc
 
+isc.utils.process.rename()
+
 # This is the version that gets displayed to the user.
 __version__ = "v20091030 (Paving the DNS Parking Lot)"
 
@@ -139,6 +142,10 @@ class MsgQ:
 
     def setup_listener(self):
         """Set up the listener socket.  Internal function."""
+        if self.verbose:
+            sys.stdout.write("[b10-msgq] Setting up socket at %s\n" %
+                             self.socket_file)
+
         self.listen_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
         
         if os.path.exists(self.socket_file):

+ 3 - 0
src/bin/usermgr/b10-cmdctl-usermgr.py.in

@@ -25,6 +25,9 @@ import csv
 import getpass
 import getopt
 import sys
+import isc.utils.process
+
+isc.utils.process.rename()
 
 VERSION_NUMBER = 'bind10'
 DEFAULT_FILE = 'cmdctl-accounts.csv'

+ 3 - 5
src/bin/xfrin/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
@@ -7,10 +7,11 @@ pkglibexec_SCRIPTS = b10-xfrin
 b10_xfrindir = $(DESTDIR)$(pkgdatadir)
 b10_xfrin_DATA = xfrin.spec
 
-CLEANFILES = b10-xfrin xfrin.pyc xfrin.spec
+CLEANFILES = b10-xfrin xfrin.pyc 
 
 man_MANS = b10-xfrin.8
 EXTRA_DIST = $(man_MANS) b10-xfrin.xml
+EXTRA_DIST += xfrin.spec
 
 if ENABLE_MAN
 
@@ -19,9 +20,6 @@ b10-xfrin.8: b10-xfrin.xml
 
 endif
 
-xfrin.spec: xfrin.spec.pre
-	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrin.spec.pre >$@
-
 # TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-xfrin: xfrin.py

+ 1 - 0
src/bin/xfrin/TODO

@@ -66,4 +66,5 @@
 17. Do zone transfer from notifyfrom address first, if it's one master of the zone.
 18. Check soa serial first when doing zone refreshment.
 19. Add configuration items to seperate zone, including ACL, multiple masters, etc.
+20. Be able to cancel the ongoing zone transfer, and be able to disable zone transfer.
 

+ 45 - 10
src/bin/xfrin/b10-xfrin.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-xfrin
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: March 17, 2010
+.\"      Date: September 8, 2010
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-XFRIN" "8" "March 17, 2010" "BIND10" "BIND10"
+.TH "B10\-XFRIN" "8" "September 8, 2010" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -47,7 +47,6 @@ The Y1 prototype release only supports AXFR\&. IXFR is not implemented\&.
 .sp .5v
 .RE
 .PP
-
 This daemon communicates with BIND 10 over a
 \fBb10-msgq\fR(8)
 C\-Channel connection\&. If this connection is not established,
@@ -60,24 +59,59 @@ receives its configurations from
 \fBb10-cfgmgr\fR(8)\&.
 .SH "CONFIGURATION AND COMMANDS"
 .PP
-The configurable setting is
+The configurable settings are:
+.PP
+\fImaster_addr\fR
+The default is 127\&.0\&.0\&.1\&.
+.PP
+\fImaster_port\fR
+The default is 53\&.
+.PP
 \fItransfers\-in\fR
-which defines the maximum number of inbound zone transfers that can run concurrently\&. The default is 10\&.
+defines the maximum number of inbound zone transfers that can run concurrently\&. The default is 10\&.
 .PP
 The configuration commands are:
 .PP
 
-\fBshutdown\fR
-stops all incoming zone transfers and exits
-\fBb10\-xfrin\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+\fBnotify\fR
+is sent by
+\fBb10-zonemgr\fR(8)
+when a DNS NOTIFY message is received to initiate a zone transfer\&.
+This is an internal command and not exposed to the administrator\&.
+.PP
+
+\fBrefresh\fR
+triggers the transfer in for a single zone\&. It is the same as
+\fBretransfer\fR
+except it checks the SOA serial first\&.
+This is an internal command and not exposed to the administrator\&.
+
+.PP
+
+\fBrefresh_from_zonemgr\fR
+is sent by
+\fBb10-zonemgr\fR(8)
+according to the SOA\'s REFRESH time to tell
+\fBb10\-xfrin\fR
+that the zone needs to do a zone refresh\&. This is an internal command and not exposed to the administrator\&.
 .PP
 
 \fBretransfer\fR
 triggers the transfer in for a single zone without checking the zone\'s serial number\&. It has the following arguments:
 \fIzone_name\fR
-to define the zone to request and
+to define the zone to request,
+\fIzone_class\fR
+to define the class (defaults to
+\(lqIN\(rq),
 \fImaster\fR
-to define the IP address of the authoritative server to transfer from\&.
+to define the IP address of the authoritative server to transfer from, and
+\fIport\fR
+to define the port number on the authoritative server (defaults to 53)\&.
+.PP
+
+\fBshutdown\fR
+stops all incoming zone transfers and exits
+\fBb10\-xfrin\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
 .if n \{\
 .sp
 .\}
@@ -99,6 +133,7 @@ This prototype version uses SQLite3 as its data source backend\&. Future version
 
 \fBb10-cfgmgr\fR(8),
 \fBb10-msgq\fR(8),
+\fBb10-zonemgr\fR(8),
 \fBbind10\fR(8),
 BIND 10 Guide\&.
 .SH "HISTORY"

+ 62 - 17
src/bin/xfrin/b10-xfrin.xml

@@ -21,7 +21,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>March 17, 2010</date>
+    <date>September 8, 2010</date>
   </refentryinfo>
 
   <refmeta>
@@ -68,7 +68,6 @@
     </simpara></note>
 
     <para>
-<!-- TODO: does it really use msgq? what for? -->
       This daemon communicates with BIND 10 over a
       <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       C-Channel connection.  If this connection is not established,
@@ -85,42 +84,85 @@
   <refsect1>
     <title>CONFIGURATION AND COMMANDS</title>
     <para>
-      The configurable setting is <varname>transfers-in</varname>
-      which defines the maximum number of inbound zone transfers
+      The configurable settings are:
+    </para>
+
+    <para><varname>master_addr</varname>
+<!-- TODO: how can there be a single setting for this? -->
+       The default is 127.0.0.1.
+    </para>
+
+    <para><varname>master_port</varname>
+<!-- TODO: what if custom is needed per zone? -->
+      The default is 53.
+    </para>
+
+    <para><varname>transfers-in</varname>
+      defines the maximum number of inbound zone transfers
       that can run concurrently. The default is 10.
     </para>
 
 <!-- TODO: formating -->
-<!-- TODO: refresh is code but not in spec -->
-<!-- schedule immediate maintenance for a zone(check soa serial ) -->
     <para>
       The configuration commands are:
     </para>
+
+    <para>
+      <command>notify</command> is sent by
+      <citerefentry><refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      when a DNS NOTIFY message is received to initiate a zone
+      transfer.
+<!-- TODO: document that zonemgr or xfrin checks if it needs to or not -->
+      This is an internal command and not exposed to the administrator.
+<!-- not defined in spec -->
+    </para>
+
     <para>
-      <command>shutdown</command> stops all incoming zone transfers
-      and exits <command>b10-xfrin</command>. (Note that the BIND 10
-      boss process will restart this service.)
+      <command>refresh</command> triggers the transfer in for
+      a single zone.
+      It is the same as <command>retransfer</command> except it
+      checks the SOA serial first.
+<!-- TODO more detail -->
+      This is an internal command and not exposed to the administrator.
+<!-- not defined in spec -->
+<!-- TODO: refresh is code but not in spec, see trac ticket #328 -->
     </para>
+
+    <para>
+      <command>refresh_from_zonemgr</command> is sent by
+      <citerefentry><refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      according to the SOA's REFRESH time
+      to tell <command>b10-xfrin</command> that the zone needs to do
+      a zone refresh.
+      This is an internal command and not exposed to the administrator. 
+<!-- not defined in spec -->
+    </para>
+
     <para>
       <command>retransfer</command> triggers the transfer in for
       a single zone without checking the zone's serial number.
       It has the following arguments: <varname>zone_name</varname>
-      to define the zone to request and <varname>master</varname>
-      to define the IP address of the authoritative server to
-      transfer from.
+      to define the zone to request,
+      <varname>zone_class</varname> to define the class (defaults to
+      <quote>IN</quote>),
+      <varname>master</varname> to define the IP address of
+      the authoritative server to transfer from,
+      and <varname>port</varname> to define the port number on the
+      authoritative server (defaults to 53).
+<!-- TODO: note: not documenting db_file since that will be removed. -->
      </para>
 <!-- TODO: later hostname for master? -->
 
+    <para>
+      <command>shutdown</command> stops all incoming zone transfers
+      and exits <command>b10-xfrin</command>. (Note that the BIND 10
+      boss process will restart this service.)
+    </para>
 <!-- TODO:
 add a usage example of xfrin -->
 
 <!-- TODO:
 
-port (defaults to 53)
-db_file (defaults to zone.sqlite3) --> <!-- TODO: fix this -->
-
-<!-- TODO:
-
  later it will can be triggered by :
 1.  Notify message from auth server.
 2.  Schedule zone transfer (determined by Refresh/Expire time in SOA record)
@@ -182,6 +224,9 @@ operation
         <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
+        <refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
         <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citetitle>BIND 10 Guide</citetitle>.

+ 5 - 5
src/bin/xfrin/tests/xfrin_test.py

@@ -421,21 +421,21 @@ class TestXfrin(unittest.TestCase):
         name, rrclass = self._do_parse_zone_name_class()
         master_addrinfo = self._do_parse_master_port()
         db_file = self.args.get('db_file')
-        self.assertEqual(master_addrinfo[4][1], int(TEST_MASTER_PORT))
+        self.assertEqual(master_addrinfo[2][1], int(TEST_MASTER_PORT))
         self.assertEqual(name, TEST_ZONE_NAME)
         self.assertEqual(rrclass, TEST_RRCLASS)
-        self.assertEqual(master_addrinfo[4][0], TEST_MASTER_IPV4_ADDRESS)
+        self.assertEqual(master_addrinfo[2][0], TEST_MASTER_IPV4_ADDRESS)
         self.assertEqual(db_file, TEST_DB_FILE)
 
     def test_parse_cmd_params_default_port(self):
         del self.args['port']
         master_addrinfo = self._do_parse_master_port()
-        self.assertEqual(master_addrinfo[4][1], 53)
+        self.assertEqual(master_addrinfo[2][1], 53)
 
     def test_parse_cmd_params_ip6master(self):
         self.args['master'] = TEST_MASTER_IPV6_ADDRESS
         master_addrinfo = self._do_parse_master_port()
-        self.assertEqual(master_addrinfo[4][0], TEST_MASTER_IPV6_ADDRESS)
+        self.assertEqual(master_addrinfo[2][0], TEST_MASTER_IPV6_ADDRESS)
 
     def test_parse_cmd_params_chclass(self):
         self.args['zone_class'] = 'CH'
@@ -454,7 +454,7 @@ class TestXfrin(unittest.TestCase):
         # master address is mandatory.
         del self.args['master']
         master_addrinfo = self._do_parse_master_port()
-        self.assertEqual(master_addrinfo[4][0], DEFAULT_MASTER)
+        self.assertEqual(master_addrinfo[2][0], DEFAULT_MASTER)
 
     def test_parse_cmd_params_bad_ip4(self):
         self.args['master'] = '3.3.3.3.3'

+ 28 - 30
src/bin/xfrin/xfrin.py.in

@@ -1,6 +1,7 @@
 #!@PYTHON@
 
 # Copyright (C) 2010  Internet Systems Consortium.
+# Copyright (C) 2010  CZ NIC
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -29,6 +30,8 @@ import random
 from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
 from isc.notify import notify_out
+import isc.net.parse
+import isc.utils.process
 try:
     from pydnspp import *
 except ImportError as e:
@@ -36,6 +39,8 @@ except ImportError as e:
     # must keep running, so we warn about it and move forward.
     sys.stderr.write('[b10-xfrin] failed to import DNS module: %s\n' % str(e))
 
+isc.utils.process.rename()
+
 # If B10_FROM_BUILD is set in the environment, we use data files
 # from a directory relative to that, otherwise we use the ones
 # installed on the system
@@ -90,7 +95,7 @@ class XfrinConnection(asyncore.dispatcher):
         self.setblocking(1)
         self._shutdown_event = shutdown_event
         self._verbose = verbose
-        self._master_address = master_addrinfo[4]
+        self._master_address = master_addrinfo[2]
 
     def connect_to_master(self):
         '''Connect to master in TCP.'''
@@ -326,10 +331,14 @@ def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file,
     sock_map = {}
     conn = XfrinConnection(sock_map, zone_name, rrclass, db_file,
                            shutdown_event, master_addrinfo, verbose)
+    ret = XFRIN_FAIL
     if conn.connect_to_master():
         ret = conn.do_xfrin(check_soa)
-        server.publish_xfrin_news(zone_name, rrclass, ret)
-
+    
+    # Publish the zone transfer result news, so zonemgr can reset the
+    # zone timer, and xfrout can notify the zone's slaves if the result
+    # is success.
+    server.publish_xfrin_news(zone_name, rrclass, ret)
     xfrin_recorder.decrement(zone_name)
 
 
@@ -393,20 +402,20 @@ class Xfrin:
         '''This is a straightforward wrapper for cc.check_command, 
         but provided as a separate method for the convenience 
         of unit tests.'''
-        self._module_cc.check_command()
+        self._module_cc.check_command(False)
 
     def config_handler(self, new_config):
         self._max_transfers_in = new_config.get("transfers_in") or self._max_transfers_in
         if ('master_addr' in new_config) or ('master_port' in new_config):
-            # Check if the new master is valid, there should be library for check it.
-            # and user should change the port and address together.
+            # User should change the port and address together.
             try:
                 addr = new_config.get('master_addr') or self._master_addr
                 port = new_config.get('master_port') or self._master_port
-                check_addr_port(addr, port)
+                isc.net.parse.addr_parse(addr)
+                isc.net.parse.port_parse(port)
                 self._master_addr = addr
                 self._master_port = port
-            except:
+            except ValueError:
                 errmsg = "bad format for zone's master: " + str(new_config)
                 log_error(errmsg)
                 return create_answer(1, errmsg)
@@ -435,7 +444,7 @@ class Xfrin:
                 # specify the notifyfrom address and port, according the RFC1996, zone
                 # transfer should starts first from the notifyfrom, but now, let 'TODO' it.
                 (zone_name, rrclass) = self._parse_zone_name_and_class(args)
-                (master_addr) = check_addr_port(self._master_addr, self._master_port)
+                (master_addr) = build_addr_info(self._master_addr, self._master_port)
                 ret = self.xfrin_start(zone_name, 
                                        rrclass, 
                                        self._get_db_file(),
@@ -483,7 +492,7 @@ class Xfrin:
     def _parse_master_and_port(self, args):
         port = args.get('port') or self._master_port
         master = args.get('master') or self._master_addr
-        return check_addr_port(master, port)
+        return build_addr_info(master, port)
  
     def _get_db_file(self):
         #TODO, the db file path should be got in auth server's configuration
@@ -556,31 +565,20 @@ def set_signal_handler():
     signal.signal(signal.SIGTERM, signal_handler)
     signal.signal(signal.SIGINT, signal_handler)
 
-def check_addr_port(addrstr, portstr):
-    # XXX: Linux (glibc)'s getaddrinfo incorrectly accepts numeric port
-    # string larger than 65535.  So we need to explicit validate it separately.
+def build_addr_info(addrstr, portstr):
+    """
+    Return tuple (family, socktype, sockaddr) for given address and port.
+    IPv4 and IPv6 are the only supported addresses now, so sockaddr will be
+    (address, port). The socktype is socket.SOCK_STREAM for now.
+    """
     try:
-        portnum = int(portstr)
-        if portnum < 0 or portnum > 65535:
-            raise ValueError("invalid port number (out of range): " + portstr)
+        port = isc.net.parse.port_parse(portstr)
+        addr = isc.net.parse.addr_parse(addrstr)
+        return (addr.family, socket.SOCK_STREAM, (addrstr, port))
     except ValueError as err:
         raise XfrinException("failed to resolve master address/port=%s/%s: %s" %
                              (addrstr, portstr, str(err)))
 
-    try:
-        addrinfo = socket.getaddrinfo(addrstr, portstr, socket.AF_UNSPEC,
-                                      socket.SOCK_STREAM, socket.IPPROTO_TCP,
-                                      socket.AI_NUMERICHOST|
-                                      socket.AI_NUMERICSERV)
-    except socket.gaierror as err:
-        raise XfrinException("failed to resolve master address/port=%s/%s: %s" %
-                             (addrstr, portstr, str(err)))
-    if len(addrinfo) != 1:
-        # with the parameters above the result must be uniquely determined.
-        errmsg = "unexpected result for address/port resolution for %s:%s"
-        raise XfrinException(errmsg % (addrstr, portstr))
-    return addrinfo[0]
-
 def set_cmd_options(parser):
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
             help="display more about what is going on")

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

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 

+ 2 - 1
src/bin/xfrout/TODO

@@ -1 +1,2 @@
-Add unittest code.
+Add unittest code.
+Be able to cancel the outgoing zone transfer, and also be able to disable outgoing zone transfer.

+ 18 - 9
src/bin/xfrout/b10-xfrout.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-xfrout
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: April 20, 2010
+.\"      Date: September 8, 2010
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-XFROUT" "8" "April 20, 2010" "BIND10" "BIND10"
+.TH "B10\-XFROUT" "8" "September 8, 2010" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -27,7 +27,7 @@ b10-xfrout \- Outbound DNS zone transfer service
 .PP
 The
 \fBb10\-xfrout\fR
-daemon provides the BIND 10 outgoing DNS zone transfer service\&. Normally it is started by the
+daemon provides the BIND 10 outgoing DNS zone transfer service\&. It is also used to send outgoing NOTIFY messages\&. Normally it is started by the
 \fBbind10\fR(8)
 boss process\&. When the
 \fBb10\-auth\fR
@@ -67,13 +67,13 @@ receives its configurations from
 The configurable settings are:
 .PP
 
-\fItransfers\-out\fR
-defines the maximum number of outgoing zone transfers that can run concurrently\&. The default is 10\&.
-.PP
-
 \fIdb_file\fR
 defines the path to the SQLite3 data store file\&. The default is
 /usr/local/var/bind10\-devel/zone\&.sqlite3\&.
+.PP
+
+\fItransfers_out\fR
+defines the maximum number of outgoing zone transfers that can run concurrently\&. The default is 10\&.
 .if n \{\
 .sp
 .\}
@@ -91,25 +91,34 @@ This prototype version uses SQLite3 as its data source backend\&. Future version
 .sp .5v
 .RE
 .PP
-The configuration command is:
+The configuration commands are:
 .PP
 
 \fBshutdown\fR
 stops all outbound zone transfers and exits
 \fBb10\-xfrout\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+.PP
+
+\fBzone_new_data_ready\fR
+is sent from
+\fBb10-xfrin\fR(8)
+to indicate that the zone transferred in successfully\&. This triggers
+\fBb10\-xfrout\fR
+to send NOTIFY message(s)\&. This is an internal command and not exposed to the administrator\&.
 .SH "SEE ALSO"
 .PP
 
 \fBb10-auth\fR(8),
 \fBb10-cfgmgr\fR(8),
 \fBb10-msgq\fR(8),
+\fBb10-xfrin\fR(8),
 \fBbind10\fR(8),
 BIND 10 Guide\&.
 .SH "HISTORY"
 .PP
 The
 \fBb10\-xfrout\fR
-daemon was implemented in March 2010 by Zhang Likun of CNNIC for the ISC BIND 10 project\&.
+daemon was first implemented in March 2010 by Zhang Likun of CNNIC for the ISC BIND 10 project\&.
 .SH "COPYRIGHT"
 .br
 Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")

+ 22 - 4
src/bin/xfrout/b10-xfrout.xml

@@ -21,7 +21,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>April 20, 2010</date>
+    <date>September 8, 2010</date>
   </refentryinfo>
 
   <refmeta>
@@ -54,6 +54,7 @@
     <title>DESCRIPTION</title>
     <para>The <command>b10-xfrout</command> daemon provides the BIND 10
       outgoing DNS zone transfer service.
+      It is also used to send outgoing NOTIFY messages.
       Normally it is started by the
       <citerefentry><refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       boss process.
@@ -97,6 +98,7 @@
       defines the path to the SQLite3 data store file.
       The default is
       <filename>/usr/local/var/bind10-devel/zone.sqlite3</filename>.
+<!-- TODO: db_file will be removed -->
     </para>
     <para>
       <varname>transfers_out</varname>
@@ -104,6 +106,9 @@
       that can run concurrently. The default is 10.
     </para>
 
+<!-- TODO: log configurations not documented yet in here. jreed
+     has some but waiting on decisions ... -->
+
     <note><simpara>
       This prototype version uses SQLite3 as its data source backend.
       Future versions will be configurable, supporting multiple
@@ -112,7 +117,7 @@
 
 <!-- TODO: formating -->
     <para>
-      The configuration command is:
+      The configuration commands are:
     </para>
     <para>
       <command>shutdown</command> stops all outbound zone transfers
@@ -120,6 +125,16 @@
       boss process will restart this service.)
     </para>
 
+    <para>
+      <command>zone_new_data_ready</command> is sent from
+      <citerefentry><refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      to indicate that the zone transferred in successfully.
+      This triggers <command>b10-xfrout</command> to send NOTIFY
+      message(s).
+      This is an internal command and not exposed to the administrator.
+<!-- not defined in spec -->
+    </para>
+
   </refsect1>
 
 <!--
@@ -161,6 +176,9 @@
         <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
+        <refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
         <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citetitle>BIND 10 Guide</citetitle>.
@@ -170,8 +188,8 @@
   <refsect1>
     <title>HISTORY</title>
     <para>
-      The <command>b10-xfrout</command> daemon was implemented in March 2010
-      by Zhang Likun of CNNIC for the ISC BIND 10 project.
+      The <command>b10-xfrout</command> daemon was first implemented
+      in March 2010 by Zhang Likun of CNNIC for the ISC BIND 10 project.
     </para>
   </refsect1>
 </refentry><!--

+ 52 - 6
src/bin/xfrout/tests/xfrout_test.py

@@ -40,8 +40,12 @@ class MySocket():
         return len(data)
 
     def readsent(self):
-        result = self.sendqueue[:]
-        del self.sendqueue[:]
+        if len(self.sendqueue) >= 2:
+            size = 2 + struct.unpack("!H", self.sendqueue[:2])[0]
+        else:
+            size = 0
+        result = self.sendqueue[:size]
+        self.sendqueue = self.sendqueue[size:]
         return result
     
     def read_msg(self):
@@ -133,7 +137,7 @@ class TestXfroutSession(unittest.TestCase):
 
         msg = self.getmsg()
         msg.make_response()
-        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa)
+        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 0)
         get_msg = self.sock.read_msg()
 
         self.assertEqual(get_msg.get_rr_count(Section.QUESTION()), 1)
@@ -148,10 +152,52 @@ class TestXfroutSession(unittest.TestCase):
         rdata = answer.get_rdata()
         self.assertEqual(rdata[0].to_text(), self.soa_record[7])
 
-    def test_get_message_len(self):
+    def test_trigger_send_message_with_last_soa(self):
+        rrset_a = RRset(Name("example.com"), RRClass.IN(), RRType.A(), RRTTL(3600))
+        rrset_a.add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.1"))
+        rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+
         msg = self.getmsg()
-        msg.make_response()  
-        self.assertEqual(self.xfrsess._get_message_len(msg), 29)
+        msg.make_response()
+
+        msg.add_rrset(Section.ANSWER(), rrset_a)
+        # give the function a value that is larger than MAX-len(rrset)
+        self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_soa, 65520)
+
+        # this should have triggered the sending of two messages
+        # (1 with the rrset we added manually, and 1 that triggered
+        # the sending in _with_last_soa)
+        get_msg = self.sock.read_msg()
+        self.assertEqual(get_msg.get_rr_count(Section.QUESTION()), 1)
+        self.assertEqual(get_msg.get_rr_count(Section.ANSWER()), 1)
+        self.assertEqual(get_msg.get_rr_count(Section.AUTHORITY()), 0)
+
+        answer = get_msg.get_section(Section.ANSWER())[0]
+        self.assertEqual(answer.get_name().to_text(), "example.com.")
+        self.assertEqual(answer.get_class(), RRClass("IN"))
+        self.assertEqual(answer.get_type().to_text(), "A")
+        rdata = answer.get_rdata()
+        self.assertEqual(rdata[0].to_text(), "192.0.2.1")
+
+        get_msg = self.sock.read_msg()
+        self.assertEqual(get_msg.get_rr_count(Section.QUESTION()), 0)
+        self.assertEqual(get_msg.get_rr_count(Section.ANSWER()), 1)
+        self.assertEqual(get_msg.get_rr_count(Section.AUTHORITY()), 0)
+
+        #answer_rrset_iter = section_iter(get_msg, section.ANSWER())
+        answer = get_msg.get_section(Section.ANSWER())[0]
+        self.assertEqual(answer.get_name().to_text(), "example.com.")
+        self.assertEqual(answer.get_class(), RRClass("IN"))
+        self.assertEqual(answer.get_type().to_text(), "SOA")
+        rdata = answer.get_rdata()
+        self.assertEqual(rdata[0].to_text(), self.soa_record[7])
+
+        # and it should not have sent anything else
+        self.assertEqual(0, len(self.sock.sendqueue))
+
+    def test_get_rrset_len(self):
+        rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
+        self.assertEqual(82, get_rrset_len(rrset_soa))
 
     def test_zone_is_empty(self):
         global sqlite3_ds

+ 45 - 43
src/bin/xfrout/xfrout.py.in

@@ -1,6 +1,7 @@
 #!@PYTHON@
 
 # Copyright (C) 2010  Internet Systems Consortium.
+# Copyright (C) 2010  CZ NIC
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -27,8 +28,9 @@ from socketserver import *
 import os
 from isc.config.ccsession import *
 from isc.log.log import *
-from isc.cc import SessionError
+from isc.cc import SessionError, SessionTimeout
 from isc.notify import notify_out
+import isc.utils.process
 import socket
 import select
 import errno
@@ -41,6 +43,8 @@ except ImportError as e:
     # must keep running, so we warn about it and move forward.
     sys.stderr.write('[b10-xfrout] failed to import DNS or XFR module: %s\n' % str(e))
 
+isc.utils.process.rename()
+
 if "B10_FROM_BUILD" in os.environ:
     SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrout"
     AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
@@ -57,6 +61,15 @@ AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
 MAX_TRANSFERS_OUT = 10
 VERBOSE_MODE = False
 
+XFROUT_MAX_MESSAGE_SIZE = 65535
+
+def get_rrset_len(rrset):
+    """Returns the wire length of the given RRset"""
+    bytes = bytearray()
+    rrset.to_wire(bytes)
+    return len(bytes)
+
+
 class XfroutSession(BaseRequestHandler):
     def __init__(self, request, client_address, server, log):
         # The initializer for the superclass may call functions
@@ -121,10 +134,8 @@ class XfroutSession(BaseRequestHandler):
 
 
     def _send_message(self, sock, msg):
-        #obuf = output_buffer(0)
-        #render = message_render(obuf)
         render = MessageRenderer()
-        render.set_length_limit(65535)
+        render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
         msg.to_wire(render)
         header_len = struct.pack('H', socket.htons(render.get_length()))
         self._send_data(sock, header_len)
@@ -227,34 +238,20 @@ class XfroutSession(BaseRequestHandler):
         rrset_.add_rdata(rdata_)
         return rrset_
          
-    def _send_message_with_last_soa(self, msg, sock, rrset_soa):
+    def _send_message_with_last_soa(self, msg, sock, rrset_soa, message_upper_len):
         '''Add the SOA record to the end of message. If it can't be
         added, a new message should be created to send out the last soa .
         '''
+        rrset_len = get_rrset_len(rrset_soa)
 
-        render = MessageRenderer()
-        msg.to_wire(render)
-        old_message_len = render.get_length()
-        msg.add_rrset(Section.ANSWER(), rrset_soa)
-
-        msg.to_wire(render)
-        message_len = render.get_length()
-
-        if message_len != old_message_len:
-            self._send_message(sock, msg)
+        if message_upper_len + rrset_len < XFROUT_MAX_MESSAGE_SIZE:
+            msg.add_rrset(Section.ANSWER(), rrset_soa)
         else:
+            self._send_message(sock, msg)
             msg = self._clear_message(msg)
             msg.add_rrset(Section.ANSWER(), rrset_soa)
-            self._send_message(sock, msg)
-
-    def _get_message_len(self, msg):
-        '''Get message length, every time need do like this? Actually there should be 
-        a better way, I need check with jinmei later.
-        '''
 
-        render = MessageRenderer()
-        msg.to_wire(render)
-        return render.get_length()
+        self._send_message(sock, msg)
 
 
     def _reply_xfrout_query(self, msg, sock, zone_name):
@@ -265,9 +262,8 @@ class XfroutSession(BaseRequestHandler):
         rrset_soa = self._create_rrset_from_db_record(soa_record)
         msg.add_rrset(Section.ANSWER(), rrset_soa)
 
-        old_message_len = 0
-        # TODO, Since add_rrset() return nothing when rrset can't be added, so I have to compare
-        # the message length to know if the rrset has been added sucessfully.
+        message_upper_len = get_rrset_len(rrset_soa)
+
         for rr_data in sqlite3_ds.get_zone_datas(zone_name, self.server.get_db_file()):
             if  self.server._shutdown_event.is_set(): # Check if xfrout is shutdown
                 self._log.log_message("error", "shutdown!")
@@ -277,19 +273,22 @@ class XfroutSession(BaseRequestHandler):
                 continue
 
             rrset_ = self._create_rrset_from_db_record(rr_data)
-            msg.add_rrset(Section.ANSWER(), rrset_)
-            message_len = self._get_message_len(msg)
-            if message_len != old_message_len:
-                old_message_len = message_len
+
+            # We calculate the maximum size of the RRset (i.e. the
+            # size without compression) and use that to see if we
+            # may have reached the limit
+            rrset_len = get_rrset_len(rrset_)
+            if message_upper_len + rrset_len < XFROUT_MAX_MESSAGE_SIZE:
+                msg.add_rrset(Section.ANSWER(), rrset_)
+                message_upper_len += rrset_len
                 continue
 
             self._send_message(sock, msg)
             msg = self._clear_message(msg)
             msg.add_rrset(Section.ANSWER(), rrset_) # Add the rrset to the new message
-            old_message_len = 0
-
-        self._send_message_with_last_soa(msg, sock, rrset_soa)
+            message_upper_len = rrset_len
 
+        self._send_message_with_last_soa(msg, sock, rrset_soa, message_upper_len)
 
 class UnixSockServer(ThreadingUnixStreamServer):
     '''The unix domain socket server which accept xfr query sent from auth server.'''
@@ -315,8 +314,8 @@ class UnixSockServer(ThreadingUnixStreamServer):
         If it's not a socket file or nobody is listening
         , it will be removed. If it can't be removed, exit from python. '''
         if self._sock_file_in_use(sock_file):
-            print("[b10-xfrout] Fail to start xfrout process, unix socket" 
-                  " file '%s' is being used by another xfrout process" % sock_file)
+            sys.stderr.write("[b10-xfrout] Fail to start xfrout process, unix socket" 
+                  " file '%s' is being used by another xfrout process\n" % sock_file)
             sys.exit(0)
         else:
             if not os.path.exists(sock_file):
@@ -325,7 +324,7 @@ class UnixSockServer(ThreadingUnixStreamServer):
             try:
                 os.unlink(sock_file)
             except OSError as err:
-                print('[b10-xfrout] Fail to remove file ' + sock_file, err)
+                sys.stderr.write('[b10-xfrout] Fail to remove file %s: %s\n' % (sock_file, err))
                 sys.exit(0)
    
     def _sock_file_in_use(self, sock_file):
@@ -409,9 +408,9 @@ class XfroutServer:
         self._listen_sock_file = UNIX_SOCKET_FILE 
         self._shutdown_event = threading.Event()
         self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
-        self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
         self._config_data = self._cc.get_full_config()
         self._cc.start()
+        self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
         self._log = isc.log.NSLogger(self._config_data.get('log_name'), self._config_data.get('log_file'),
                                 self._config_data.get('log_severity'), self._config_data.get('log_versions'),
                                 self._config_data.get('log_max_bytes'), True)
@@ -496,7 +495,7 @@ class XfroutServer:
     def run(self):
         '''Get and process all commands sent from cfgmgr or other modules. '''
         while not self._shutdown_event.is_set():
-            self._cc.check_command()
+            self._cc.check_command(False)
 
 
 xfrout_server = None
@@ -525,12 +524,15 @@ if '__main__' == __name__:
         xfrout_server = XfroutServer()
         xfrout_server.run()
     except KeyboardInterrupt:
-        sys.stderr.write("[b10-xfrout] exit xfrout process")
+        sys.stderr.write("[b10-xfrout] exit xfrout process\n")
     except SessionError as e:
-        sys.stderr.write("[b10-xfrout] Error creating xfrout," 
-                           "is the command channel daemon running?")
+        sys.stderr.write("[b10-xfrout] Error creating xfrout, "
+                           "is the command channel daemon running?\n")
+    except SessionTimeout as e:
+        sys.stderr.write("[b10-xfrout] Error creating xfrout, " 
+                           "is the configuration manager running?\n")
     except ModuleCCSessionError as e:
-        sys.stderr.write("info", '[b10-xfrout] exit xfrout process:', e)
+        sys.stderr.write("[b10-xfrout] exit xfrout process:%s\n" % str(e))
 
     if xfrout_server:
         xfrout_server.shutdown()

+ 11 - 1
src/bin/zonemgr/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
@@ -9,6 +9,16 @@ b10_zonemgr_DATA = zonemgr.spec
 
 CLEANFILES = b10-zonemgr zonemgr.pyc zonemgr.spec
 
+man_MANS = b10-zonemgr.8
+EXTRA_DIST = $(man_MANS) b10-zonemgr.xml
+
+if ENABLE_MAN
+
+b10-zonemgr.8: b10-zonemgr.xml
+	xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-zonemgr.xml
+
+endif
+
 zonemgr.spec: zonemgr.spec.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" zonemgr.spec.pre >$@
 

+ 1 - 0
src/bin/zonemgr/TODO

@@ -1,5 +1,6 @@
 1. Zonemgr should support adding/deleting zones dynamically.
 2. Make zonemgr  has customizable configurations for LOWERBOUND_REFRESH, LOWERBOUND_RETRY, MAX_TRANSFER_TIMEOUT, REFRESH_OFFSET, RETRY_OFFSET, EXPIRED_OFFSET, and/or jitter?
 3. There should be one way to see the current counters/timers and other data for each zone managed by zonemgr.
+4. There should be one way to turn off zonemgr.(Does user really need it? not sure what's the purpose of user)
 
 

+ 49 - 17
src/bin/zonemgr/tests/zonemgr_test.py

@@ -27,6 +27,11 @@ ZONE_NAME_CLASS3_IN = ("example.com", "IN")
 ZONE_NAME_CLASS1_CH = ("sd.cn.", "CH")
 ZONE_NAME_CLASS2_IN = ("tw.cn", "IN")
 
+MAX_TRANSFER_TIMEOUT = 14400
+LOWERBOUND_REFRESH = 10
+LOWERBOUND_RETRY = 5 
+JITTER_SCOPE = 0.10
+
 class ZonemgrTestException(Exception):
     pass
 
@@ -42,15 +47,20 @@ class MyZonemgrRefresh(ZonemgrRefresh):
     def __init__(self):
         self._cc = MySession()
         self._db_file = "initdb.file"
+        current_time = time.time()
+        self._max_transfer_timeout = MAX_TRANSFER_TIMEOUT
+        self._lowerbound_refresh = LOWERBOUND_REFRESH
+        self._lowerbound_retry = LOWERBOUND_RETRY
+        self._jitter_scope = JITTER_SCOPE
         self._zonemgr_refresh_info = { 
          ('sd.cn.', 'IN'): {
-         'last_refresh_time': 1280474398.822142,
-         'next_refresh_time': 1280481598.822153, 
+         'last_refresh_time': current_time,
+         'next_refresh_time': current_time + 6500, 
          'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600', 
          'zone_state': 0},
          ('tw.cn', 'CH'): {
-         'last_refresh_time': 1280474399.116421, 
-         'next_refresh_time': 1280481599.116433, 
+         'last_refresh_time': current_time, 
+         'next_refresh_time': current_time + 6900, 
          'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600', 
          'zone_state': 0}
         } 
@@ -311,6 +321,11 @@ class TestZonemgrRefresh(unittest.TestCase):
         self.assertTrue((time1 + 3 * 3600 / 4) <= next_refresh_time)
         self.assertTrue(next_refresh_time <= time2 + 3600)
         self.assertEqual(ZONE_OK, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
+
+        self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"] = time1 - 2419200 
+        self.zone_refresh.zone_refresh_fail(ZONE_NAME_CLASS1_IN)
+        self.assertEqual(ZONE_EXPIRED, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
+
         self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ("org.cn.", "CH"))
         self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_IN) 
 
@@ -332,17 +347,6 @@ class TestZonemgrRefresh(unittest.TestCase):
         zone_need_refresh = self.zone_refresh._find_need_do_refresh_zone()
         self.assertEqual(ZONE_NAME_CLASS1_IN, zone_need_refresh)
 
-        self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"] = time1 - 2419200
-        self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"] = ZONE_EXPIRED
-        zone_need_refresh = self.zone_refresh._find_need_do_refresh_zone()
-        self.assertEqual(None, zone_need_refresh)
-
-        self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"] = ZONE_REFRESHING
-        self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["notify_master"] = "192.168.0.1"
-        zone_need_refresh = self.zone_refresh._find_need_do_refresh_zone()
-        self.assertEqual(ZONE_NAME_CLASS1_IN, zone_need_refresh)
-        self.assertEqual(ZONE_EXPIRED, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
-
         self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS2_CH]["refresh_timeout"] = time1 
         zone_need_refresh = self.zone_refresh._find_need_do_refresh_zone()
         self.assertEqual(ZONE_NAME_CLASS2_CH, zone_need_refresh)
@@ -402,6 +406,19 @@ class TestZonemgrRefresh(unittest.TestCase):
         self.assertTrue("refresh_timeout" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
         self.assertTrue(zone_state == ZONE_REFRESHING)
 
+    def test_update_config_data(self):
+        config_data = {
+                    "lowerbound_refresh" : 60,
+                    "lowerbound_retry" : 30,
+                    "max_transfer_timeout" : 19800,
+                    "jitter_scope" : 0.25
+                }
+        self.zone_refresh.update_config_data(config_data)
+        self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
+        self.assertEqual(30, self.zone_refresh._lowerbound_retry)
+        self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
+        self.assertEqual(0.25, self.zone_refresh._jitter_scope)
+
 
     def tearDown(self):
         sys.stdout = self.stdout_backup
@@ -422,10 +439,16 @@ class MyZonemgr(Zonemgr):
 
     def __init__(self):
         self._db_file = "initdb.file"
+        self._zone_refresh = None
         self._shutdown_event = threading.Event()
         self._cc = MySession()
         self._module_cc = MyCCSession()
-        self._config_data = {"zone_name" : "org.cn", "zone_class" : "CH", "master" : "127.0.0.1"}
+        self._config_data = {
+                    "lowerbound_refresh" : 10, 
+                    "lowerbound_retry" : 5, 
+                    "max_transfer_timeout" : 14400,
+                    "jitter_scope" : 0.1
+                    }
 
     def _start_zone_refresh_timer(self):
         pass
@@ -436,12 +459,21 @@ class TestZonemgr(unittest.TestCase):
         self.zonemgr = MyZonemgr()
 
     def test_config_handler(self):
-        config_data1 = {"zone_name" : "sd.cn.", "zone_class" : "CH", "master" : "192.168.1.1"}
+        config_data1 = {
+                    "lowerbound_refresh" : 60, 
+                    "lowerbound_retry" : 30, 
+                    "max_transfer_timeout" : 14400,
+                    "jitter_scope" : 0.1
+                    }
         self.zonemgr.config_handler(config_data1)
         self.assertEqual(config_data1, self.zonemgr._config_data)
         config_data2 = {"zone_name" : "sd.cn.", "port" : "53", "master" : "192.168.1.1"}
         self.zonemgr.config_handler(config_data2)
         self.assertEqual(config_data1, self.zonemgr._config_data)
+        # jitter should not be bigger than half of the original value
+        config_data3 = {"jitter_scope" : 0.7}
+        self.zonemgr.config_handler(config_data3)
+        self.assertEqual(0.5, self.zonemgr._config_data.get("jitter_scope"))
 
     def test_get_db_file(self):
         self.assertEqual("initdb.file", self.zonemgr.get_db_file())

+ 55 - 39
src/bin/zonemgr/zonemgr.py.in

@@ -1,6 +1,7 @@
 #!@PYTHON@
 
 # Copyright (C) 2010  Internet Systems Consortium.
+# Copyright (C) 2010  CZ NIC
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -36,6 +37,9 @@ import errno
 from isc.datasrc import sqlite3_ds
 from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
+import isc.utils.process
+
+isc.utils.process.rename()
 
 # If B10_FROM_BUILD is set in the environment, we use data files
 # from a directory relative to that, otherwise we use the ones
@@ -69,13 +73,6 @@ ZONE_OK = 0
 ZONE_REFRESHING = 1
 ZONE_EXPIRED = 2
 
-# smallest refresh timeout
-LOWERBOUND_REFRESH = 10
-# smallest retry timeout
-LOWERBOUND_RETRY = 5
-# max zone transfer timeout
-MAX_TRANSFER_TIMEOUT = 14400
-
 # offsets of fields in the SOA RDATA
 REFRESH_OFFSET = 3
 RETRY_OFFSET = 4
@@ -97,10 +94,11 @@ class ZonemgrRefresh:
     do zone refresh.
     """
 
-    def __init__(self, cc, db_file, slave_socket):
+    def __init__(self, cc, db_file, slave_socket, config_data):
         self._cc = cc
         self._socket = slave_socket 
         self._db_file = db_file
+        self.update_config_data(config_data)
         self._zonemgr_refresh_info = {} 
         self._build_zonemgr_refresh_info()
     
@@ -118,25 +116,26 @@ class ZonemgrRefresh:
         return time.time()
 
     def _set_zone_timer(self, zone_name_class, max, jitter):
-        """Set zone next refresh time."""
+        """Set zone next refresh time. 
+        jitter should not be bigger than half the original value."""
         self._set_zone_next_refresh_time(zone_name_class, self._get_current_time() + \
                                             self._random_jitter(max, jitter))
 
     def _set_zone_refresh_timer(self, zone_name_class):
         """Set zone next refresh time after zone refresh success.
-           now + refresh*3/4 <= next_refresh_time <= now + refresh
+           now + refresh - jitter  <= next_refresh_time <= now + refresh
            """
         zone_refresh_time = float(self._get_zone_soa_rdata(zone_name_class).split(" ")[REFRESH_OFFSET])
-        zone_refresh_time = max(LOWERBOUND_REFRESH, zone_refresh_time)
-        self._set_zone_timer(zone_name_class, zone_refresh_time, (1 * zone_refresh_time) / 4)
+        zone_refresh_time = max(self._lowerbound_refresh, zone_refresh_time)
+        self._set_zone_timer(zone_name_class, zone_refresh_time, self._jitter_scope * zone_refresh_time)
 
     def _set_zone_retry_timer(self, zone_name_class):
         """Set zone next refresh time after zone refresh fail.
-           now + retry*3/4 <= next_refresh_time <= now + retry
+           now + retry - jitter <= next_refresh_time <= now + retry
            """
         zone_retry_time = float(self._get_zone_soa_rdata(zone_name_class).split(" ")[RETRY_OFFSET])
-        zone_retry_time = max(LOWERBOUND_RETRY, zone_retry_time)
-        self._set_zone_timer(zone_name_class, zone_retry_time, (1 * zone_retry_time) / 4)
+        zone_retry_time = max(self._lowerbound_retry, zone_retry_time)
+        self._set_zone_timer(zone_name_class, zone_retry_time, self._jitter_scope * zone_retry_time)
 
     def _set_zone_notify_timer(self, zone_name_class):
         """Set zone next refresh time after receiving notify
@@ -167,7 +166,11 @@ class ZonemgrRefresh:
             raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
                                    "belong to zonemgr" % zone_name_class)
             return
-        self._set_zone_state(zone_name_class, ZONE_OK)
+        # Is zone expired?
+        if (self._zone_is_expired(zone_name_class)):
+            self._set_zone_state(zone_name_class, ZONE_EXPIRED)
+        else:
+            self._set_zone_state(zone_name_class, ZONE_OK)
         self._set_zone_retry_timer(zone_name_class)
 
     def zone_handle_notify(self, zone_name_class, master):
@@ -261,7 +264,7 @@ class ZonemgrRefresh:
         try:
             self._cc.group_sendmsg(msg, module_name)
         except socket.error:
-            sys.err.write("[b10-zonemgr] Failed to send to module %s, the session has been closed." % module_name) 
+            sys.stderr.write("[b10-zonemgr] Failed to send to module %s, the session has been closed." % module_name) 
 
     def _find_need_do_refresh_zone(self):
         """Find the first zone need do refresh, if no zone need
@@ -269,25 +272,14 @@ class ZonemgrRefresh:
         """
         zone_need_refresh = None
         for zone_name_class in self._zonemgr_refresh_info.keys():
-            # Does the zone expired?
-            if (ZONE_EXPIRED != self._get_zone_state(zone_name_class) and 
-                self._zone_is_expired(zone_name_class)):
-                log_msg("Zone (%s, %s) is expired." % zone_name_class)
-                self._set_zone_state(zone_name_class, ZONE_EXPIRED)
-
             zone_state = self._get_zone_state(zone_name_class)
-            # If zone is expired and doesn't receive notify, skip the zone
-            if (ZONE_EXPIRED == zone_state and 
-                (not self._get_zone_notifier_master(zone_name_class))):
-                continue
-
             # If hasn't received refresh response but are within refresh timeout, skip the zone
             if (ZONE_REFRESHING == zone_state and
                 (self._get_zone_refresh_timeout(zone_name_class) > self._get_current_time())):
                 continue
                     
             # Get the zone with minimum next_refresh_time 
-            if ((None == zone_need_refresh) or 
+            if ((zone_need_refresh is None) or 
                 (self._get_zone_next_refresh_time(zone_name_class) < 
                  self._get_zone_next_refresh_time(zone_need_refresh))):
                 zone_need_refresh = zone_name_class
@@ -303,7 +295,7 @@ class ZonemgrRefresh:
         """Do zone refresh."""
         log_msg("Do refresh for zone (%s, %s)." % zone_name_class)
         self._set_zone_state(zone_name_class, ZONE_REFRESHING)
-        self._set_zone_refresh_timeout(zone_name_class, self._get_current_time() + MAX_TRANSFER_TIMEOUT) 
+        self._set_zone_refresh_timeout(zone_name_class, self._get_current_time() + self._max_transfer_timeout) 
         notify_master = self._get_zone_notifier_master(zone_name_class)
         # If the zone has notify master, send notify command to xfrin module
         if notify_master:
@@ -332,13 +324,13 @@ class ZonemgrRefresh:
         while True:
             # Zonemgr has no zone.
             if self._zone_mgr_is_empty():
-                time.sleep(LOWERBOUND_RETRY) # A better time?
+                time.sleep(self._lowerbound_retry) # A better time?
                 continue
 
             zone_need_refresh = self._find_need_do_refresh_zone()
-            # If don't get zone with minimum next refresh time, set timer timeout = LOWERBOUND_REFRESH
+            # If don't get zone with minimum next refresh time, set timer timeout = lowerbound_retry 
             if not zone_need_refresh:
-                timeout = LOWERBOUND_RETRY
+                timeout = self._lowerbound_retry 
             else:
                 timeout = self._get_zone_next_refresh_time(zone_need_refresh) - self._get_current_time()
                 if (timeout < 0):
@@ -361,15 +353,23 @@ class ZonemgrRefresh:
                     raise ZonemgrException("[b10-zonemgr] Error with select(): %s\n" % e)
                     break
 
+    def update_config_data(self, new_config):
+        """ update ZonemgrRefresh config """
+        self._lowerbound_refresh = new_config.get('lowerbound_refresh')
+        self._lowerbound_retry = new_config.get('lowerbound_retry')
+        self._max_transfer_timeout = new_config.get('max_transfer_timeout')
+        self._jitter_scope = new_config.get('jitter_scope')
+
 
 class Zonemgr:
     """Zone manager class."""
     def __init__(self):
+        self._zone_refresh = None
         self._setup_session()
         self._db_file = self.get_db_file()
         # Create socket pair for communicating between main thread and zonemgr timer thread 
         self._master_socket, self._slave_socket = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
-        self._zone_refresh= ZonemgrRefresh(self._cc, self._db_file, self._slave_socket)
+        self._zone_refresh = ZonemgrRefresh(self._cc, self._db_file, self._slave_socket, self._config_data)
         self._start_zone_refresh_timer()
 
         self._lock = threading.Lock()
@@ -391,6 +391,10 @@ class Zonemgr:
                                                   self.command_handler)
         self._module_cc.add_remote_config(AUTH_SPECFILE_LOCATION)
         self._config_data = self._module_cc.get_full_config()
+        # jitter should not be bigger than half of the original value
+        if self._config_data.get('jitter_scope') > 0.5:
+            self._config_data['jitter_scope'] = 0.5
+            log_msg("[b10-zonemgr] jitter_scope should not be bigger than 0.5.") 
         self._module_cc.start()
 
     def get_db_file(self):
@@ -417,13 +421,22 @@ class Zonemgr:
             th.join()
 
     def config_handler(self, new_config):
-        """Update config data."""
+        """ Update config data. """
         answer = create_answer(0)
         for key in new_config:
             if key not in self._config_data:
                 answer = create_answer(1, "Unknown config data: " + str(key))
                 continue
+            # jitter should not be bigger than half of the original value
+            if key == 'jitter_scope':
+                if new_config.get(key) > 0.5:
+                    new_config[key] = 0.5
+                    log_msg("[b10-zonemgr] jitter_scope should not be bigger than 0.5.") 
             self._config_data[key] = new_config[key]
+
+        if (self._zone_refresh):
+            self._zone_refresh.update_config_data(self._config_data)
+
         return answer
 
     def _parse_cmd_params(self, args, command):
@@ -485,7 +498,7 @@ class Zonemgr:
 
     def run(self):
         while not self._shutdown_event.is_set():
-            self._module_cc.check_command()
+            self._module_cc.check_command(False)
 
 zonemgrd = None
 
@@ -513,12 +526,15 @@ if '__main__' == __name__:
         zonemgrd = Zonemgr()
         zonemgrd.run()
     except KeyboardInterrupt:
-        sys.stderr.write("[b10-zonemgr] exit zonemgr process")
+        sys.stderr.write("[b10-zonemgr] exit zonemgr process\n")
     except isc.cc.session.SessionError as e:
         sys.stderr.write("[b10-zonemgr] Error creating zonemgr, " 
-                           "is the command channel daemon running?")
+                           "is the command channel daemon running?\n")
+    except isc.cc.session.SessionTimeout as e:
+        sys.stderr.write("[b10-zonemgr] Error creating zonemgr, " 
+                           "is the configuration manager running?\n")
     except isc.config.ModuleCCSessionError as e:
-        sys.stderr.write("info", "[b10-zonemgr] exit zonemgr process:", e)
+        sys.stderr.write("[b10-zonemgr] exit zonemgr process: %s\n" % str(e))
 
     if zonemgrd:
         zonemgrd.shutdown()

+ 24 - 0
src/bin/zonemgr/zonemgr.spec.pre.in

@@ -2,6 +2,30 @@
   "module_spec": {
      "module_name": "Zonemgr",
       "config_data":[
+       {
+         "item_name": "lowerbound_refresh",
+         "item_type": "integer",
+         "item_optional": false,
+         "item_default": 10
+       },
+       {
+         "item_name": "lowerbound_retry",
+         "item_type": "integer",
+         "item_optional": false,
+         "item_default": 5 
+       },
+       {
+         "item_name": "max_transfer_timeout",
+         "item_type": "integer",
+         "item_optional": false,
+         "item_default": 14400 
+       },
+       {
+         "item_name": "jitter_scope",
+         "item_type": "real",
+         "item_optional": false,
+         "item_default": 0.25
+       }
       ],
       "commands": [
         {

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

@@ -26,6 +26,8 @@
 #include <dns/name.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rrtype.h>
 #include <dns/rrclass.h>
 #include <dns/question.h>

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

@@ -22,6 +22,8 @@
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 
@@ -80,7 +82,6 @@ public:
         EXPECT_EQ(0, message.getQid());
         EXPECT_EQ(Opcode::QUERY(), message.getOpcode());
         EXPECT_EQ(Rcode::NOERROR(), message.getRcode());
-        EXPECT_EQ(Rcode::NOERROR(), message.getRcode());
         EXPECT_FALSE(message.getHeaderFlag(MessageFlag::QR()));
         EXPECT_FALSE(message.getHeaderFlag(MessageFlag::AA()));
         EXPECT_EQ(1, message.getRRCount(Section::QUESTION()));

+ 12 - 8
src/lib/cc/data.cc

@@ -184,7 +184,8 @@ Element::find(const std::string& identifier UNUSED_PARAM,
 
 namespace {
 inline void
-throwJSONError(const std::string& error, const std::string& file, int line, int pos)
+throwJSONError(const std::string& error, const std::string& file, int line,
+               int pos)
 {
     std::stringstream ss;
     ss << error << " in " + file + ":" << line << ":" << pos;
@@ -427,13 +428,15 @@ from_stringstream_null(std::istream &in, const std::string& file,
 }
 
 ElementPtr
-from_stringstream_string(std::istream& in, const std::string& file, int& line, int& pos)
+from_stringstream_string(std::istream& in, const std::string& file, int& line,
+                         int& pos)
 {
     return (Element::create(str_from_stringstream(in, file, line, pos)));
 }
 
 ElementPtr
-from_stringstream_list(std::istream &in, const std::string& file, int& line, int& pos)
+from_stringstream_list(std::istream &in, const std::string& file, int& line,
+                       int& pos)
 {
     char c = 0;
     ElementPtr list = Element::createList();
@@ -484,8 +487,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
 }
 
 std::string
-Element::typeToName(Element::types type)
-{
+Element::typeToName(Element::types type) {
     switch (type) {
     case Element::integer:
         return (std::string("integer"));
@@ -538,14 +540,16 @@ Element::fromJSON(std::istream& in) throw(JSONError) {
 }
 
 ElementPtr
-Element::fromJSON(std::istream& in, const std::string& file_name) throw(JSONError)
+Element::fromJSON(std::istream& in, const std::string& file_name)
+    throw(JSONError)
 {
     int line = 1, pos = 1;
     return (fromJSON(in, file_name, line, pos));
 }
 
 ElementPtr
-Element::fromJSON(std::istream &in, const std::string& file, int& line, int& pos) throw(JSONError)
+Element::fromJSON(std::istream &in, const std::string& file, int& line,
+                  int& pos) throw(JSONError)
 {
     char c = 0;
     ElementPtr element;
@@ -892,7 +896,7 @@ merge(ElementPtr element, ConstElementPtr other) {
         isc_throw(TypeError, "merge arguments not MapElements");
     }
     
-    std::map<std::string, ConstElementPtr> m = other->mapValue();
+    const std::map<std::string, ConstElementPtr>& m = other->mapValue();
     for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
          it != m.end() ; ++it) {
         if ((*it).second && (*it).second->getType() != Element::null) {

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

@@ -18,8 +18,7 @@ TESTS += run_unittests
 # (TODO: these need to be completed and moved to tests/)
 run_unittests_SOURCES = data_unittests.cc session_unittests.cc run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-# TODO: remove PTHREAD_LDFLAGS (and from configure too)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(PTHREAD_LDFLAGS)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD +=  $(top_builddir)/src/lib/cc/libcc.la

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

@@ -17,8 +17,7 @@
 #include <gtest/gtest.h>
 
 int
-main(int argc, char* argv[])
-{
+main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     return (RUN_ALL_TESTS());
 }

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

@@ -1,4 +1,4 @@
-SUBDIRS = . testdata tests
+SUBDIRS = . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
@@ -8,45 +8,3 @@ lib_LTLIBRARIES = libcfgclient.la
 libcfgclient_la_SOURCES = config_data.h config_data.cc module_spec.h module_spec.cc ccsession.cc ccsession.h
 
 CLEANFILES = *.gcno *.gcda
-
-EXTRA_DIST =  testdata/b10-config-bad1.db
-EXTRA_DIST += testdata/b10-config-bad2.db
-EXTRA_DIST += testdata/b10-config-bad3.db
-EXTRA_DIST += testdata/b10-config-bad4.db
-EXTRA_DIST += testdata/b10-config.db.master #.db will be auto-generated
-EXTRA_DIST += testdata/data22_1.data
-EXTRA_DIST += testdata/data22_2.data
-EXTRA_DIST += testdata/data22_3.data
-EXTRA_DIST += testdata/data22_4.data
-EXTRA_DIST += testdata/data22_5.data
-EXTRA_DIST += testdata/data22_6.data
-EXTRA_DIST += testdata/data22_7.data
-EXTRA_DIST += testdata/data22_8.data
-EXTRA_DIST += testdata/spec1.spec
-EXTRA_DIST += testdata/spec2.spec
-EXTRA_DIST += testdata/spec3.spec
-EXTRA_DIST += testdata/spec4.spec
-EXTRA_DIST += testdata/spec5.spec
-EXTRA_DIST += testdata/spec6.spec
-EXTRA_DIST += testdata/spec7.spec
-EXTRA_DIST += testdata/spec8.spec
-EXTRA_DIST += testdata/spec9.spec
-EXTRA_DIST += testdata/spec10.spec
-EXTRA_DIST += testdata/spec11.spec
-EXTRA_DIST += testdata/spec12.spec
-EXTRA_DIST += testdata/spec13.spec
-EXTRA_DIST += testdata/spec14.spec
-EXTRA_DIST += testdata/spec15.spec
-EXTRA_DIST += testdata/spec16.spec
-EXTRA_DIST += testdata/spec17.spec
-EXTRA_DIST += testdata/spec18.spec
-EXTRA_DIST += testdata/spec19.spec
-EXTRA_DIST += testdata/spec20.spec
-EXTRA_DIST += testdata/spec21.spec
-EXTRA_DIST += testdata/spec22.spec
-EXTRA_DIST += testdata/spec23.spec
-EXTRA_DIST += testdata/spec24.spec
-EXTRA_DIST += testdata/spec25.spec
-EXTRA_DIST += testdata/spec26.spec
-EXTRA_DIST += testdata/spec27.spec
-EXTRA_DIST += testdata/spec28.spec

+ 2 - 4
src/lib/config/ccsession.cc

@@ -333,8 +333,7 @@ ModuleCCSession::checkCommand() {
 }
 
 std::string
-ModuleCCSession::addRemoteConfig(const std::string& spec_file_name)
-{
+ModuleCCSession::addRemoteConfig(const std::string& spec_file_name) {
     ModuleSpec rmod_spec = readModuleSpecification(spec_file_name);
     std::string module_name = rmod_spec.getFullSpec()->get("module_name")->stringValue();
     ConfigData rmod_config = ConfigData(rmod_spec);
@@ -362,8 +361,7 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_file_name)
 }
 
 void
-ModuleCCSession::removeRemoteConfig(const std::string& module_name)
-{
+ModuleCCSession::removeRemoteConfig(const std::string& module_name) {
     std::map<std::string, ConfigData>::iterator it;
 
     it = remote_module_configs_.find(module_name);

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

@@ -1,3 +1,5 @@
+SUBDIRS = testdata .
+
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -24,12 +26,9 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD =  $(GTEST_LDADD)
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD += libfake_session.la
 run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
-# link *only* to data.o from lib/cc (more importantly, don't link in
-# the session class provided there, since we use our own fake_session
-# here)
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/data.o
 
 endif
 

+ 1 - 2
src/lib/config/tests/ccsession_unittests.cc

@@ -164,8 +164,7 @@ TEST_F(CCSessionTest, session1) {
     EXPECT_EQ(0, session.getMsgQueue()->size());
 }
 
-TEST_F(CCSessionTest, session2)
-{
+TEST_F(CCSessionTest, session2) {
     EXPECT_EQ(false, session.haveSubscription("Spec2", "*"));
     ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL);
     EXPECT_EQ(true, session.haveSubscription("Spec2", "*"));

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

@@ -12,4 +12,4 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#define TEST_DATA_PATH "@abs_srcdir@/../testdata"
+#define TEST_DATA_PATH "@abs_srcdir@/testdata"

+ 2 - 4
src/lib/config/tests/module_spec_unittests.cc

@@ -105,8 +105,7 @@ TEST(ModuleSpec, SpecfileItems) {
                    "badname is not a valid type name");
 }
 
-TEST(ModuleSpec, SpecfileConfigData)
-{
+TEST(ModuleSpec, SpecfileConfigData) {
     module_spec_error("spec7.spec",
                    "module_name missing in {  }");
     module_spec_error("spec8.spec",
@@ -117,8 +116,7 @@ TEST(ModuleSpec, SpecfileConfigData)
                    "commands is not a list of elements");
 }
 
-TEST(ModuleSpec, SpecfileCommands)
-{
+TEST(ModuleSpec, SpecfileCommands) {
     module_spec_error("spec17.spec",
                    "command_name missing in { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\" }");
     module_spec_error("spec18.spec",

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

@@ -17,8 +17,7 @@
 #include <gtest/gtest.h>
 
 int
-main(int argc, char* argv[])
-{
+main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     return (RUN_ALL_TESTS());
 }

+ 1 - 0
src/lib/datasrc/data_source.cc

@@ -32,6 +32,7 @@
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/name.h>
+#include <dns/rcode.h>
 #include <dns/rdataclass.h>
 #include <dns/rrset.h>
 #include <dns/rrsetlist.h>

+ 2 - 4
src/lib/datasrc/static_datasrc.cc

@@ -110,14 +110,12 @@ StaticDataSrcImpl::StaticDataSrcImpl() :
                                "0 28800 7200 604800 86400"));
 }
 
-StaticDataSrc::StaticDataSrc()
-{
+StaticDataSrc::StaticDataSrc() {
     setClass(RRClass::CH());
     impl_ = new StaticDataSrcImpl;
 }
 
-StaticDataSrc::~StaticDataSrc()
-{
+StaticDataSrc::~StaticDataSrc() {
     delete impl_;
 }
 

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

@@ -26,6 +26,8 @@
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
 #include <dns/question.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
 #include <dns/rrclass.h>

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

@@ -19,6 +19,7 @@
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/name.h>
+#include <dns/opcode.h>
 #include <dns/rrtype.h>
 #include <dns/rrclass.h>
 

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

@@ -19,8 +19,7 @@
 #include <dns/tests/unittest_util.h>
 
 int
-main(int argc, char* argv[])
-{
+main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
 

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

@@ -63,11 +63,14 @@ libdns___la_SOURCES += util/binary_from_base32hex.h
 libdns___la_SOURCES += util/base16_from_binary.h util/binary_from_base16.h
 libdns___la_SOURCES += buffer.h
 libdns___la_SOURCES += dnssectime.h dnssectime.cc
+libdns___la_SOURCES += edns.h edns.cc
 libdns___la_SOURCES += exceptions.h exceptions.cc
 libdns___la_SOURCES += util/hex.h
 libdns___la_SOURCES += message.h message.cc
 libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
 libdns___la_SOURCES += name.h name.cc
+libdns___la_SOURCES += opcode.h opcode.cc
+libdns___la_SOURCES += rcode.h rcode.cc
 libdns___la_SOURCES += rdata.h rdata.cc
 libdns___la_SOURCES += rrclass.cc
 libdns___la_SOURCES += rrparamregistry.h
@@ -88,8 +91,8 @@ rrparamregistry.cc: rrparamregistry-placeholder.cc
 rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: Makefile
 	./gen-rdatacode.py
 
-libdns++_includedir = $(includedir)/dns
-libdns++_include_HEADERS = \
+libdns___includedir = $(includedir)/dns
+libdns___include_HEADERS = \
 	buffer.h \
 	dnssectime.h \
 	exceptions.h \

+ 1 - 1
src/lib/dns/exceptions.cc

@@ -15,7 +15,7 @@
 // $Id$
 
 #include <dns/exceptions.h>
-#include <dns/message.h>
+#include <dns/rcode.h>
 
 namespace isc {
 namespace dns {

+ 6 - 8
src/lib/dns/gen-rdatacode.py.in

@@ -113,7 +113,7 @@ class MessageRenderer;\n\n'''
         if re.match('\s+// BEGIN_COMMON_MEMBERS$', line):
             content += '''
     explicit ''' + type_utxt + '''(const std::string& type_str);
-    explicit ''' + type_utxt + '''(InputBuffer& buffer, size_t rdata_len);
+    ''' + type_utxt + '''(InputBuffer& buffer, size_t rdata_len);
     ''' + type_utxt + '''(const ''' + type_utxt + '''& other);
     virtual std::string toText() const;
     virtual void toWire(OutputBuffer& buffer) const;
@@ -129,12 +129,13 @@ def import_definitions(classcode2txt, typecode2txt, typeandclass):
     global rdatadef_mtime
     global rdatahdr_mtime
 
+    if classdir_mtime < getmtime('@srcdir@/rdata'):
+        classdir_mtime = getmtime('@srcdir@/rdata')
+
     for dir in list(os.listdir('@srcdir@/rdata')):
         classdir = '@srcdir@/rdata' + os.sep + dir
         m = re_typecode.match(dir)
         if os.path.isdir(classdir) and (m != None or dir == 'generic'):
-            if classdir_mtime < getmtime(classdir):
-                classdir_mtime = getmtime(classdir)
             if dir == 'generic':
                 class_txt = 'generic'
                 class_code = generic_code
@@ -219,8 +220,7 @@ def generate_typeclasscode(fileprefix, basemtime, code2txt, type_or_class):
         codetxt = code2txt[code].upper()
         declarationtxt += ' ' * 4 + 'static const RR' + cap_key + '& ' + codetxt + '();\n'
         deftxt += '''inline const RR''' + cap_key + '''&
-RR''' + cap_key + '''::''' + codetxt + '''()
-{
+RR''' + cap_key + '''::''' + codetxt + '''() {
     static RR''' + cap_key + ''' ''' + lower_key + '''(''' + code + ''');
     return (''' + lower_key + ''');
 }\n
@@ -284,9 +284,7 @@ if __name__ == "__main__":
         generate_rdatadef('@builddir@/rdataclass.cc', rdatadef_mtime)
         generate_rdatahdr('@builddir@/rdataclass.h', rdata_declarations,
                           rdatahdr_mtime)
-        generate_typeclasscode('rrtype',
-                               max(rdatadef_mtime, rdatahdr_mtime),
-                               typecode2txt, 'Type')
+        generate_typeclasscode('rrtype', rdatahdr_mtime, typecode2txt, 'Type')
         generate_typeclasscode('rrclass', classdir_mtime,
                                classcode2txt, 'Class')
         generate_rrparam('rrparamregistry', rdatahdr_mtime)

+ 141 - 292
src/lib/dns/message.cc

@@ -28,10 +28,13 @@
 #include <exceptions/exceptions.h>
 
 #include <dns/buffer.h>
+#include <dns/edns.h>
 #include <dns/exceptions.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/question.h>
 #include <dns/rdataclass.h>
 #include <dns/rrclass.h>
@@ -61,14 +64,6 @@ const flags_t FLAG_RA = 0x0080;
 const flags_t FLAG_AD = 0x0020;
 const flags_t FLAG_CD = 0x0010;
 
-//
-// EDNS related constants
-//
-const flags_t EXTFLAG_MASK = 0xffff;
-const flags_t EXTFLAG_DO = 0x8000;
-const uint32_t EXTRCODE_MASK = 0xff000000; 
-const uint32_t EDNSVERSION_MASK = 0x00ff0000;
-
 const unsigned int OPCODE_MASK = 0x7800;
 const unsigned int OPCODE_SHIFT = 11;
 const unsigned int RCODE_MASK = 0x000f;
@@ -76,84 +71,6 @@ const unsigned int FLAG_MASK = 0x8ff0;
 
 const unsigned int MESSAGE_REPLYPRESERVE = (FLAG_RD | FLAG_CD);
 
-const Rcode rcodes[] = {
-    Rcode::NOERROR(),
-    Rcode::FORMERR(),
-    Rcode::SERVFAIL(),
-    Rcode::NXDOMAIN(),
-    Rcode::NOTIMP(),
-    Rcode::REFUSED(),
-    Rcode::YXDOMAIN(),
-    Rcode::YXRRSET(),
-    Rcode::NXRRSET(),
-    Rcode::NOTAUTH(),
-    Rcode::NOTZONE(),
-    Rcode::RESERVED11(),
-    Rcode::RESERVED12(),
-    Rcode::RESERVED13(),
-    Rcode::RESERVED14(),
-    Rcode::RESERVED15(),
-    Rcode::BADVERS()
-};
-
-const char *rcodetext[] = {
-    "NOERROR",
-    "FORMERR",
-    "SERVFAIL",
-    "NXDOMAIN",
-    "NOTIMP",
-    "REFUSED",
-    "YXDOMAIN",
-    "YXRRSET",
-    "NXRRSET",
-    "NOTAUTH",
-    "NOTZONE",
-    "RESERVED11",
-    "RESERVED12",
-    "RESERVED13",
-    "RESERVED14",
-    "RESERVED15",
-    "BADVERS"
-};
-
-const Opcode* opcodes[] = {
-    &Opcode::QUERY(),
-    &Opcode::IQUERY(),
-    &Opcode::STATUS(),
-    &Opcode::RESERVED3(),
-    &Opcode::NOTIFY(),
-    &Opcode::UPDATE(),
-    &Opcode::RESERVED6(),
-    &Opcode::RESERVED7(),
-    &Opcode::RESERVED8(),
-    &Opcode::RESERVED9(),
-    &Opcode::RESERVED10(),
-    &Opcode::RESERVED11(),
-    &Opcode::RESERVED12(),
-    &Opcode::RESERVED13(),
-    &Opcode::RESERVED14(),
-    &Opcode::RESERVED15()
-};
-
-const char *opcodetext[] = {
-    "QUERY",
-    "IQUERY",
-    "STATUS",
-    "RESERVED3",
-    "NOTIFY",
-    "UPDATE",
-    "RESERVED6",
-    "RESERVED7",
-    "RESERVED8",
-    "RESERVED9",
-    "RESERVED10",
-    "RESERVED11",
-    "RESERVED12",
-    "RESERVED13",
-    "RESERVED14",
-    "RESERVED15"
-};
-
 const char *sectiontext[] = {
     "QUESTION",
     "ANSWER",
@@ -162,28 +79,6 @@ const char *sectiontext[] = {
 };
 }
 
-string
-Opcode::toText() const {
-    return (opcodetext[code_]);
-}
-
-Rcode::Rcode(uint16_t code) : code_(code) {
-    if (code_ > MAX_RCODE) {
-        isc_throw(OutOfRange, "Rcode is too large to construct");
-    }
-}
-
-string
-Rcode::toText() const {
-    if (code_ < sizeof(rcodetext) / sizeof (const char *)) {
-        return (rcodetext[code_]);
-    }
-
-    ostringstream oss;
-    oss << code_;
-    return (oss.str());
-}
-
 namespace {
 inline unsigned int
 sectionCodeToId(const Section& section) {
@@ -200,20 +95,24 @@ public:
     // for efficiency?
     Message::Mode mode_;
     qid_t qid_;
-    Rcode rcode_;
+
+    // We want to use NULL for [op,r]code_ to mean the code being not
+    // correctly parsed or set.  We store the real code object in
+    // xxcode_placeholder_ and have xxcode_ refer to it when the object
+    // is valid.
+    const Rcode* rcode_;
+    Rcode rcode_placeholder_;
     const Opcode* opcode_;
+    Opcode opcode_placeholder_;
+
     flags_t flags_;
-    bool dnssec_ok_;
 
     bool header_parsed_;
     static const unsigned int SECTION_MAX = 4; // TODO: revisit this design
     int counts_[SECTION_MAX];   // TODO: revisit this definition
     vector<QuestionPtr> questions_;
     vector<RRsetPtr> rrsets_[SECTION_MAX];
-    RRsetPtr remote_edns_;
-    uint16_t remote_udpsize_;
-    RRsetPtr local_edns_;
-    uint16_t udpsize_;
+    ConstEDNSPtr edns_;
 
 #ifdef notyet
     // tsig/sig0: TODO
@@ -221,12 +120,16 @@ public:
 #endif
 
     void init();
+    void setOpcode(const Opcode& opcode);
+    void setRcode(const Rcode& rcode);
     int parseQuestion(InputBuffer& buffer);
     int parseSection(const Section& section, InputBuffer& buffer);
 };
 
 MessageImpl::MessageImpl(Message::Mode mode) :
-    mode_(mode), rcode_(Rcode::NOERROR())
+    mode_(mode),
+    rcode_placeholder_(Rcode(0)), // as a placeholder the value doesn't matter
+    opcode_placeholder_(Opcode(0)) // ditto
 {
     init();
 }
@@ -235,13 +138,9 @@ void
 MessageImpl::init() {
     flags_ = 0;
     qid_ = 0;
-    rcode_ = Rcode::NOERROR();  // XXX
+    rcode_ = NULL;
     opcode_ = NULL;
-    dnssec_ok_ = false;
-    remote_edns_ = RRsetPtr();
-    remote_udpsize_ = Message::DEFAULT_MAX_UDPSIZE;
-    local_edns_ = RRsetPtr();
-    udpsize_ = Message::DEFAULT_MAX_UDPSIZE;
+    edns_ = EDNSPtr();
 
     for (int i = 0; i < SECTION_MAX; ++i) {
         counts_[i] = 0;
@@ -254,6 +153,18 @@ MessageImpl::init() {
     rrsets_[sectionCodeToId(Section::ADDITIONAL())].clear();
 }
 
+void
+MessageImpl::setOpcode(const Opcode& opcode) {
+    opcode_placeholder_ = opcode;
+    opcode_ = &opcode_placeholder_;
+}
+
+void
+MessageImpl::setRcode(const Rcode& rcode) {
+    rcode_placeholder_ = rcode;
+    rcode_ = &rcode_placeholder_;
+}
+
 Message::Message(Mode mode) :
     impl_(new MessageImpl(mode))
 {}
@@ -285,38 +196,6 @@ Message::clearHeaderFlag(const MessageFlag& flag) {
     impl_->flags_ &= ~flag.getBit();
 }
 
-bool
-Message::isDNSSECSupported() const {
-    return (impl_->dnssec_ok_);
-}
-
-void
-Message::setDNSSECSupported(bool on) {
-    if (impl_->mode_ != Message::RENDER) {
-        isc_throw(InvalidMessageOperation,
-                  "setDNSSECSupported performed in non-render mode");
-    }
-    impl_->dnssec_ok_ = on;
-}
-
-uint16_t
-Message::getUDPSize() const {
-    return (impl_->udpsize_);
-}
-
-void
-Message::setUDPSize(uint16_t size) {
-    if (impl_->mode_ != Message::RENDER) {
-        isc_throw(InvalidMessageOperation,
-                  "setUDPSize performed in non-render mode");
-    }
-    if (size < DEFAULT_MAX_UDPSIZE) {
-        isc_throw(InvalidMessageUDPSize,
-                  "Specified UDP message size is too small");
-    }
-    impl_->udpsize_ = size;
-}
-
 qid_t
 Message::getQid() const {
     return (impl_->qid_);
@@ -333,7 +212,10 @@ Message::setQid(qid_t qid) {
 
 const Rcode&
 Message::getRcode() const {
-    return (impl_->rcode_);
+    if (impl_->rcode_ == NULL) {
+        isc_throw(InvalidMessageOperation, "getRcode attempted before set");
+    }
+    return (*impl_->rcode_);
 }
 
 void
@@ -342,11 +224,14 @@ Message::setRcode(const Rcode& rcode) {
         isc_throw(InvalidMessageOperation,
                   "setRcode performed in non-render mode");
     }
-    impl_->rcode_ = rcode;
+    impl_->setRcode(rcode);
 }
 
 const Opcode&
 Message::getOpcode() const {
+    if (impl_->opcode_ == NULL) {
+        isc_throw(InvalidMessageOperation, "getOpcode attempted before set");
+    }
     return (*impl_->opcode_);
 }
 
@@ -356,7 +241,21 @@ Message::setOpcode(const Opcode& opcode) {
         isc_throw(InvalidMessageOperation,
                   "setOpcode performed in non-render mode");
     }
-    impl_->opcode_ = &opcode;
+    impl_->setOpcode(opcode);
+}
+
+ConstEDNSPtr
+Message::getEDNS() const {
+    return (impl_->edns_);
+}
+
+void
+Message::setEDNS(ConstEDNSPtr edns) {
+    if (impl_->mode_ != Message::RENDER) {
+        isc_throw(InvalidMessageOperation,
+                  "setEDNS performed in non-render mode");
+    }
+    impl_->edns_ = edns;
 }
 
 unsigned int
@@ -441,60 +340,20 @@ struct RenderSection {
 };
 }
 
-namespace {
-bool
-addEDNS(MessageImpl* mimpl, MessageRenderer& renderer) {
-    const bool is_query = ((mimpl->flags_ & MessageFlag::QR().getBit()) == 0); 
-
-    // If this is a reply, add EDNS either when the request had it, or
-    // if the Rcode is BADVERS, which is EDNS specific.
-    // XXX: this logic is tricky.  We should revisit this later.
-    if (!is_query) {
-        if (mimpl->remote_edns_ == NULL && mimpl->rcode_ != Rcode::BADVERS()) {
-            return (false);
-        }
-    } else {
-        // For queries, we add EDNS only when necessary:
-        // Local UDP size is not the default value, or
-        // DNSSEC DO bit is to be set, or
-        // Extended Rcode is to be specified.
-        if (mimpl->udpsize_ == Message::DEFAULT_MAX_UDPSIZE &&
-            !mimpl->dnssec_ok_ &&
-            mimpl->rcode_.getCode() < 0x10) {
-            return (false);
-        }
-    }
-
-    // If adding the OPT RR would exceed the size limit, don't do it.
-    // 11 = len(".") + type(2byte) + class(2byte) + TTL(4byte) + RDLEN(2byte)
-    // (RDATA is empty in this simple implementation)
-    if (renderer.getLength() + 11 > renderer.getLengthLimit()) {
-        return (false);
-    }
-
-    // Render EDNS OPT RR
-    uint32_t extrcode_flags = ((mimpl->rcode_.getCode() & 0xff0) << 24);
-    if (mimpl->dnssec_ok_) {
-        extrcode_flags |= 0x8000; // set DO bit
-    }
-    mimpl->local_edns_ = RRsetPtr(new RRset(Name::ROOT_NAME(),
-                                            RRClass(mimpl->udpsize_),
-                                            RRType::OPT(),
-                                            RRTTL(extrcode_flags)));
-    // We don't support any options in this simple implementation
-    mimpl->local_edns_->addRdata(ConstRdataPtr(new generic::OPT()));
-    mimpl->local_edns_->toWire(renderer);
-
-    return (true);
-}
-}
-
 void
 Message::toWire(MessageRenderer& renderer) {
     if (impl_->mode_ != Message::RENDER) {
         isc_throw(InvalidMessageOperation,
                   "Message rendering attempted in non render mode");
     }
+    if (impl_->rcode_ == NULL) {
+        isc_throw(InvalidMessageOperation,
+                  "Message rendering attempted without Rcode set");
+    }
+    if (impl_->opcode_ == NULL) {
+        isc_throw(InvalidMessageOperation,
+                  "Message rendering attempted without Opcode set");
+    }
 
     // reserve room for the header
     renderer.skip(HEADERLEN);
@@ -528,12 +387,20 @@ Message::toWire(MessageRenderer& renderer) {
                      RenderSection<RRsetPtr>(renderer, false)).getTotalCount();
     }
 
-    // Added EDNS OPT RR if necessary (we want to avoid hardcoding specialized
-    // logic, see the parser case)
-    if (!renderer.isTruncated() && addEDNS(this->impl_, renderer)) {
-        ++arcount;
+    // Add EDNS OPT RR if necessary.  Basically, we add it only when EDNS
+    // has been explicitly set.  However, if the RCODE would require it and
+    // no EDNS has been set we generate a temporary local EDNS and use it.
+    if (!renderer.isTruncated()) {
+        ConstEDNSPtr local_edns = impl_->edns_;
+        if (!local_edns && impl_->rcode_->getExtendedCode() != 0) {
+            local_edns = ConstEDNSPtr(new EDNS());
+        }
+        if (local_edns) {
+            arcount += local_edns->toWire(renderer,
+                                          impl_->rcode_->getExtendedCode());
+        }
     }
-
+ 
     // Adjust the counter buffer.
     // XXX: these may not be equal to the number of corresponding entries
     // in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR
@@ -553,7 +420,7 @@ Message::toWire(MessageRenderer& renderer) {
 
     uint16_t codes_and_flags =
         (impl_->opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK;
-    codes_and_flags |= (impl_->rcode_.getCode() & RCODE_MASK);
+    codes_and_flags |= (impl_->rcode_->getCode() & RCODE_MASK);
     codes_and_flags |= (impl_->flags_ & FLAG_MASK);
     renderer.writeUint16At(codes_and_flags, header_pos);
     header_pos += sizeof(uint16_t);
@@ -582,8 +449,8 @@ Message::parseHeader(InputBuffer& buffer) {
 
     impl_->qid_ = buffer.readUint16();
     const uint16_t codes_and_flags = buffer.readUint16();
-    impl_->opcode_ = opcodes[((codes_and_flags & OPCODE_MASK) >> OPCODE_SHIFT)];
-    impl_->rcode_ = rcodes[(codes_and_flags & RCODE_MASK)];
+    impl_->setOpcode(Opcode((codes_and_flags & OPCODE_MASK) >> OPCODE_SHIFT));
+    impl_->setRcode(Rcode(codes_and_flags & RCODE_MASK));
     impl_->flags_ = (codes_and_flags & FLAG_MASK);
     impl_->counts_[Section::QUESTION().getCode()] = buffer.readUint16();
     impl_->counts_[Section::ANSWER().getCode()] = buffer.readUint16();
@@ -657,6 +524,34 @@ struct MatchRR : public unary_function<RRsetPtr, bool> {
 };
 }
 
+// Note about design decision:
+// we need some type specific processing here, including EDNS and TSIG.
+// how much we should generalize/hardcode the special logic is subject
+// to discussion.  In terms of modularity it would be ideal to introduce
+// an abstract class (say "MessageAttribute") and let other such
+// concrete notions as EDNS or TSIG inherit from it.  Then we would
+// just do:
+// message->addAttribute(rrtype, rrclass, buffer);
+// to create and attach type-specific concrete object to the message.
+//
+// A major downside of this approach is, as usual, complexity due to
+// indirection and performance penalty.  Also, it may not be so easy
+// to separate the processing logic because in many cases we'll need
+// parse context for which the message class is responsible (e.g.
+// to check the EDNS OPT RR only appears in the additional section,
+// and appears only once).
+//
+// Another point to consider is that we may not need so many special
+// types other than EDNS and TSIG (and when and if we implement it,
+// SIG(0)); newer optional attributes of the message would more likely
+// be standardized as new flags or options of EDNS.  If that's the case,
+// introducing an abstract class with all the overhead and complexity
+// may not make much sense.
+//
+// Conclusion: don't over-generalize type-specific logic for now.
+// introduce separate concrete classes, and move context-independent
+// logic to that class; processing logic dependent on parse context
+// is hardcoded here.
 int
 MessageImpl::parseSection(const Section& section, InputBuffer& buffer) {
     unsigned int added = 0;
@@ -678,60 +573,36 @@ MessageImpl::parseSection(const Section& section, InputBuffer& buffer) {
         const size_t rdlen = buffer.readUint16();
         ConstRdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen);
 
-        // XXX: we wanted to avoid hardcoding type-specific logic here,
-        // but this would be the fastest way for a proof-of-concept
-        // implementation.  We'll revisit this part later.
         if (rrtype == RRType::OPT()) {
             if (section != Section::ADDITIONAL()) {
                 isc_throw(DNSMessageFORMERR,
                           "EDNS OPT RR found in an invalid section");
             }
-            if (remote_edns_ != NULL) {
+            if (edns_) {
                 isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
             }
-            if (((ttl.getValue() & EDNSVERSION_MASK) >> 16) >
-                Message::EDNS_SUPPORTED_VERSION) {
-                // XXX: we should probably not reject the message yet, because
-                // it's better to let the requestor know the responder-side
-                // highest version as indicated in Section 4.6 of RFC2671.
-                // This is probably because why BIND 9 does the version check
-                // in the client code.
-                // This is a TODO item.  Right now we simply reject it.
-                const unsigned int ver =
-                    (ttl.getValue() & EDNSVERSION_MASK) >> 16;
-                isc_throw(DNSMessageBADVERS, "unsupported EDNS version: " <<
-                          ver);
-            }
-            if (name != Name::ROOT_NAME()) {
-                isc_throw(DNSMessageFORMERR,
-                          "invalid owner name for EDNS OPT RR");
-            }
-
-            remote_edns_ = RRsetPtr(new RRset(name, rrclass, rrtype, ttl));
-            remote_edns_->addRdata(rdata);
 
-            dnssec_ok_ = (((ttl.getValue() & EXTFLAG_MASK) & EXTFLAG_DO) != 0);
-            if (rrclass.getCode() > Message::DEFAULT_MAX_UDPSIZE) {
-                udpsize_ = rrclass.getCode();
-            }
-            rcode_ = Rcode(((ttl.getValue() & EXTRCODE_MASK) >> 20) |
-                           rcode_.getCode());
+            uint8_t extended_rcode;
+            edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl,
+                                                  *rdata, extended_rcode));
+            setRcode(Rcode(rcode_->getCode(), extended_rcode));
             continue;
-        }
-
-        vector<RRsetPtr>::iterator it =
-            find_if(rrsets_[sectionCodeToId(section)].begin(),
-                    rrsets_[sectionCodeToId(section)].end(),
-                    MatchRR(name, rrtype, rrclass));
-        if (it != rrsets_[sectionCodeToId(section)].end()) {
-            (*it)->setTTL(min((*it)->getTTL(), ttl));
-            (*it)->addRdata(rdata);
         } else {
-            RRsetPtr rrset = RRsetPtr(new RRset(name, rrclass, rrtype, ttl)); 
-            rrset->addRdata(rdata);
-            rrsets_[sectionCodeToId(section)].push_back(rrset);
+            vector<RRsetPtr>::iterator it =
+                find_if(rrsets_[sectionCodeToId(section)].begin(),
+                        rrsets_[sectionCodeToId(section)].end(),
+                        MatchRR(name, rrtype, rrclass));
+            if (it != rrsets_[sectionCodeToId(section)].end()) {
+                (*it)->setTTL(min((*it)->getTTL(), ttl));
+                (*it)->addRdata(rdata);
+            } else {
+                RRsetPtr rrset =
+                    RRsetPtr(new RRset(name, rrclass, rrtype, ttl)); 
+                rrset->addRdata(rdata);
+                rrsets_[sectionCodeToId(section)].push_back(rrset);
+            }
+            ++added;
         }
-        ++added;
     }
 
     return (added);
@@ -755,11 +626,20 @@ struct SectionFormatter {
 
 string
 Message::toText() const {
+    if (impl_->rcode_ == NULL) {
+        isc_throw(InvalidMessageOperation,
+                  "Message::toText() attempted without Rcode set");
+    }
+    if (impl_->opcode_ == NULL) {
+        isc_throw(InvalidMessageOperation,
+                  "Message::toText() attempted without Opcode set");
+    }
+
     string s;
 
     s += ";; ->>HEADER<<- opcode: " + impl_->opcode_->toText();
     // for simplicity we don't consider extended rcode (unlike BIND9)
-    s += ", status: " + impl_->rcode_.toText();
+    s += ", status: " + impl_->rcode_->toText();
     s += ", id: " + boost::lexical_cast<string>(impl_->qid_);
     s += "\n;; flags: ";
     if (getHeaderFlag(MessageFlag::QR()))
@@ -786,30 +666,14 @@ Message::toText() const {
         lexical_cast<string>(impl_->counts_[Section::AUTHORITY().getCode()]);
 
     unsigned int arcount = impl_->counts_[Section::ADDITIONAL().getCode()];
-    RRsetPtr edns_rrset;
-    if (!getHeaderFlag(MessageFlag::QR()) && impl_->remote_edns_ != NULL) {
-        edns_rrset = impl_->remote_edns_;
+    if (impl_->edns_ != NULL) {
         ++arcount;
     }
     s += ", ADDITIONAL: " + lexical_cast<string>(arcount) + "\n";
 
-    if (edns_rrset != NULL) {
+    if (impl_->edns_ != NULL) {
         s += "\n;; OPT PSEUDOSECTION:\n";
-        s += "; EDNS: version: ";
-        s += lexical_cast<string>(
-            (edns_rrset->getTTL().getValue() & 0x00ff0000) >> 16);
-        s += ", flags:";
-        if ((edns_rrset->getTTL().getValue() & 0x8000) != 0) {
-            s += " do";
-        }
-        const uint32_t mbz = edns_rrset->getTTL().getValue() & ~0x8000 & 0xffff;
-        if (mbz != 0) {
-            s += "; MBZ: " + lexical_cast<string>(mbz) + ", udp: ";
-        } else {
-            s += "; udp: " +
-                lexical_cast<string>(edns_rrset->getClass().getCode());
-        }
-        s += "\n";
+        s += impl_->edns_->toText();
     }
 
     if (!impl_->questions_.empty()) {
@@ -851,8 +715,7 @@ Message::clear(Mode mode) {
 }
 
 void
-Message::makeResponse()
-{
+Message::makeResponse() {
     if (impl_->mode_ != Message::PARSE) {
         isc_throw(InvalidMessageOperation,
                   "makeResponse() is performed in non-parse mode");
@@ -860,11 +723,7 @@ Message::makeResponse()
 
     impl_->mode_ = Message::RENDER;
 
-    impl_->dnssec_ok_ = false;
-    impl_->remote_udpsize_ = impl_->udpsize_;
-    impl_->local_edns_ = RRsetPtr();
-    impl_->udpsize_ = DEFAULT_MAX_UDPSIZE;
-
+    impl_->edns_ = EDNSPtr();
     impl_->flags_ &= MESSAGE_REPLYPRESERVE;
     setHeaderFlag(MessageFlag::QR());
 
@@ -1005,16 +864,6 @@ Message::endSection(const Section& section) const {
 }
 
 ostream&
-operator<<(ostream& os, const Opcode& opcode) {
-    return (os << opcode.toText());
-}
-
-ostream&
-operator<<(ostream& os, const Rcode& rcode) {
-    return (os << rcode.toText());
-}
-
-ostream&
 operator<<(ostream& os, const Message& message) {
     return (os << message.toText());
 }

+ 52 - 368
src/lib/dns/message.h

@@ -25,6 +25,7 @@
 
 #include <exceptions/exceptions.h>
 
+#include <dns/edns.h>
 #include <dns/question.h>
 #include <dns/rrset.h>
 
@@ -81,6 +82,8 @@ class InputBuffer;
 class MessageRenderer;
 class Message;
 class MessageImpl;
+class Opcode;
+class Rcode;
 
 template <typename T>
 struct SectionIteratorImpl;
@@ -111,358 +114,47 @@ private:
 };
 
 inline const MessageFlag&
-MessageFlag::QR()
-{
+MessageFlag::QR() {
     static MessageFlag f(0x8000);
     return (f);
 }
 
 inline const MessageFlag&
-MessageFlag::AA()
-{
+MessageFlag::AA() {
     static MessageFlag f(0x0400);
     return (f);
 }
 
 inline const MessageFlag&
-MessageFlag::TC()
-{
+MessageFlag::TC() {
     static MessageFlag f(0x0200);
     return (f);
 }
 
 inline const MessageFlag&
-MessageFlag::RD()
-{
+MessageFlag::RD() {
     static MessageFlag f(0x0100);
     return (f);
 }
 
 inline const MessageFlag&
-MessageFlag::RA()
-{
+MessageFlag::RA() {
     static MessageFlag f(0x0080);
     return (f);
 }
 
 inline const MessageFlag&
-MessageFlag::AD()
-{
+MessageFlag::AD() {
     static MessageFlag f(0x0020);
     return (f);
 }
 
 inline const MessageFlag&
-MessageFlag::CD()
-{
+MessageFlag::CD() {
     static MessageFlag f(0x0010);
     return (f);
 }
 
-/// \brief The \c Opcode class objects represent standard OPCODEs
-/// of the header section of DNS messages.
-///
-/// Note: since there are only 15 possible values, it may make more sense to
-/// simply define an enum type to represent these values.
-///
-/// Constant objects are defined for standard flags.
-class Opcode {
-public:
-    uint16_t getCode() const { return (code_); }
-    bool operator==(const Opcode& other) const
-    { return (code_ == other.code_); }
-    bool operator!=(const Opcode& other) const
-    { return (code_ != other.code_); }
-    std::string toText() const;
-    static const Opcode& QUERY();
-    static const Opcode& IQUERY();
-    static const Opcode& STATUS();
-    static const Opcode& RESERVED3();
-    static const Opcode& NOTIFY();
-    static const Opcode& UPDATE();
-    static const Opcode& RESERVED6();
-    static const Opcode& RESERVED7();
-    static const Opcode& RESERVED8();
-    static const Opcode& RESERVED9();
-    static const Opcode& RESERVED10();
-    static const Opcode& RESERVED11();
-    static const Opcode& RESERVED12();
-    static const Opcode& RESERVED13();
-    static const Opcode& RESERVED14();
-    static const Opcode& RESERVED15();
-private:
-    Opcode(uint16_t code) : code_(code) {}
-    uint16_t code_;
-};
-
-inline const Opcode&
-Opcode::QUERY()
-{
-    static Opcode c(0);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::IQUERY()
-{
-    static Opcode c(1);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::STATUS()
-{
-    static Opcode c(2);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED3()
-{
-    static Opcode c(3);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::NOTIFY()
-{
-    static Opcode c(4);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::UPDATE()
-{
-    static Opcode c(5);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED6()
-{
-    static Opcode c(6);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED7()
-{
-    static Opcode c(7);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED8()
-{
-    static Opcode c(8);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED9()
-{
-    static Opcode c(9);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED10()
-{
-    static Opcode c(10);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED11()
-{
-    static Opcode c(11);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED12()
-{
-    static Opcode c(12);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED13()
-{
-    static Opcode c(13);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED14()
-{
-    static Opcode c(14);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED15()
-{
-    static Opcode c(15);
-    return (c);
-}
-
-/// \brief The \c Rcode class objects represent standard Response Codes
-/// (RCODEs) of the header section of DNS messages, and extended response
-/// codes as defined in the EDNS specification.
-///
-/// Constant objects are defined for standard flags.
-class Rcode {
-public:
-    Rcode(uint16_t code);
-    uint16_t getCode() const { return (code_); }
-    bool operator==(const Rcode& other) const { return (code_ == other.code_); }
-    bool operator!=(const Rcode& other) const { return (code_ != other.code_); }
-    std::string toText() const;
-    static const Rcode& NOERROR();
-    static const Rcode& FORMERR();
-    static const Rcode& SERVFAIL();
-    static const Rcode& NXDOMAIN();
-    static const Rcode& NOTIMP();
-    static const Rcode& REFUSED();
-    static const Rcode& YXDOMAIN();
-    static const Rcode& YXRRSET();
-    static const Rcode& NXRRSET();
-    static const Rcode& NOTAUTH();
-    static const Rcode& NOTZONE();
-    static const Rcode& RESERVED11();
-    static const Rcode& RESERVED12();
-    static const Rcode& RESERVED13();
-    static const Rcode& RESERVED14();
-    static const Rcode& RESERVED15();
-    // Extended Rcodes follow (EDNS required):
-    static const Rcode& BADVERS();
-private:
-    uint16_t code_;
-
-    // EDNS-extended RCODEs are 12-bit unsigned integers.
-    static const uint16_t MAX_RCODE = 0xfff;
-};
-
-inline const Rcode&
-Rcode::NOERROR()
-{
-    static Rcode c(0);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::FORMERR()
-{
-    static Rcode c(1);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::SERVFAIL()
-{
-    static Rcode c(2);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::NXDOMAIN()
-{
-    static Rcode c(3);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::NOTIMP()
-{
-    static Rcode c(4);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::REFUSED()
-{
-    static Rcode c(5);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::YXDOMAIN()
-{
-    static Rcode c(6);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::YXRRSET()
-{
-    static Rcode c(7);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::NXRRSET()
-{
-    static Rcode c(8);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::NOTAUTH()
-{
-    static Rcode c(9);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::NOTZONE()
-{
-    static Rcode c(10);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::RESERVED11()
-{
-    static Rcode c(11);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::RESERVED12()
-{
-    static Rcode c(12);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::RESERVED13()
-{
-    static Rcode c(13);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::RESERVED14()
-{
-    static Rcode c(14);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::RESERVED15()
-{
-    static Rcode c(15);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::BADVERS()
-{
-    static Rcode c(16);
-    return (c);
-}
-
 /// \brief The \c Section class objects represent DNS message sections such
 /// as the header, question, or answer.
 ///
@@ -500,29 +192,25 @@ private:
 };
 
 inline const Section&
-Section::QUESTION()
-{
+Section::QUESTION() {
     static Section s(SECTION_QUESTION);
     return (s);
 }
 
 inline const Section&
-Section::ANSWER()
-{
+Section::ANSWER() {
     static Section s(SECTION_ANSWER);
     return (s);
 }
 
 inline const Section&
-Section::AUTHORITY()
-{
+Section::AUTHORITY() {
     static Section s(SECTION_AUTHORITY);
     return (s);
 }
 
 inline const Section&
-Section::ADDITIONAL()
-{
+Section::ADDITIONAL() {
     static Section s(SECTION_ADDITIONAL);
     return (s);
 }
@@ -627,45 +315,6 @@ public:
     /// \c setHeaderFlag() with an additional argument.
     void clearHeaderFlag(const MessageFlag& flag);
 
-    /// \brief Return whether the message sender indicates DNSSEC is supported.
-    /// If EDNS is included, this corresponds to the value of the DO bit.
-    /// Otherwise, DNSSEC is considered not supported.
-    bool isDNSSECSupported() const;
-
-    /// \brief Specify whether DNSSEC is supported in the message.
-    ///
-    /// Only allowed in the \c RENDER mode.
-    /// If EDNS is included in the message, the DO bit is set or cleared
-    /// according to the specified value of this method.
-    void setDNSSECSupported(bool on);
-
-    /// \brief Return the maximum buffer size of UDP messages for the sender
-    /// of the message.
-    ///
-    /// The semantics of this value is different based on the mode:
-    /// In the \c PARSE mode, it means the buffer size of the remote node;
-    /// in the \c RENDER mode, it means the buffer size of the local node.
-    ///
-    /// In either case, its value is the value of the UDP payload size field
-    /// of EDNS (when it's included) or \c DEFAULT_MAX_UDPSIZE.
-    ///
-    /// Note: this interface may be confusing and may have to be revisited.
-    uint16_t getUDPSize() const;
-
-    /// \brief Specify the maximum buffer size of UDP messages of the local
-    /// node.
-    ///
-    /// Only allowed in the \c RENDER mode.
-    /// If EDNS OPT RR is included in the message, its UDP payload size field
-    /// will be set to the specified value.
-    ///
-    /// Unless explicitly specified, \c DEFAULT_MAX_UDPSIZE will be assumed
-    /// for the maximum buffer size, regardless of whether EDNS OPT RR is
-    /// included or not.  This means if an application wants to send a message
-    /// with an EDNS OPT RR for specifying a larger UDP size, it must explicitly
-    /// specify the value using this method.
-    void setUDPSize(uint16_t size);
-
     /// \brief Return the query ID given in the header section of the message.
     qid_t getQid() const;
 
@@ -680,16 +329,27 @@ public:
     /// included).  In the \c PARSE mode, if the received message contains
     /// an EDNS OPT RR, the corresponding extended code is identified and
     /// returned.
+    ///
+    /// The message must have been properly parsed (in the case of the
+    /// \c PARSE mode) or an \c Rcode has been set (in the case of the
+    /// \c RENDER mode) beforehand.  Otherwise, an exception of class
+    /// \c InvalidMessageOperation will be thrown.
     const Rcode& getRcode() const;
 
-    /// \brief Return the Response Code of the message.
+    /// \brief Set the Response Code of the message.
     ///
     /// Only allowed in the \c RENDER mode.
+    ///
     /// If the specified code is an EDNS extended RCODE, an EDNS OPT RR will be
     /// included in the message.
     void setRcode(const Rcode& rcode);
 
     /// \brief Return the OPCODE given in the header section of the message.
+    ///
+    /// The message must have been properly parsed (in the case of the
+    /// \c PARSE mode) or an \c Opcode has been set (in the case of the
+    /// \c RENDER mode) beforehand.  Otherwise, an exception of class
+    /// \c InvalidMessageOperation will be thrown.
     const Opcode& getOpcode() const;
 
     /// \brief Set the OPCODE of the header section of the message.
@@ -697,6 +357,23 @@ public:
     /// Only allowed in the \c RENDER mode.
     void setOpcode(const Opcode& opcode);
 
+    /// \brief Return, if any, the EDNS associated with the message.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A shared pointer to the EDNS.  This will be a null shared
+    /// pointer if the message is not associated with EDNS.
+    ConstEDNSPtr getEDNS() const;
+
+    /// \brief Set EDNS for the message.
+    ///
+    /// Only allowed in the \c RENDER mode; otherwise an exception of class
+    /// \c InvalidMessageOperation will be thrown.
+    ///
+    /// \param edns A shared pointer to an \c EDNS object to be set in
+    /// \c Message.
+    void setEDNS(ConstEDNSPtr edns);
+
     /// \brief Returns the number of RRs contained in the given section.
     unsigned int getRRCount(const Section& section) const;
 
@@ -768,10 +445,19 @@ public:
     void makeResponse();
 
     /// \brief Convert the Message to a string.
+    ///
+    /// At least \c Opcode and \c Rcode must be validly set in the \c Message
+    /// (as a result of parse in the \c PARSE mode or by explicitly setting
+    /// in the \c RENDER mode);  otherwise, an exception of
+    /// class \c InvalidMessageOperation will be thrown.
     std::string toText() const;
 
     /// \brief Render the message in wire formant into a \c MessageRenderer
     /// object.
+    ///
+    /// This \c Message must be in the \c RENDER mode and both \c Opcode and
+    /// \c Rcode must have been set beforehand; otherwise, an exception of
+    /// class \c InvalidMessageOperation will be thrown.
     void toWire(MessageRenderer& renderer);
 
     /// \brief Parse the header section of the \c Message.
@@ -798,8 +484,6 @@ private:
     MessageImpl* impl_;
 };
 
-std::ostream& operator<<(std::ostream& os, const Opcode& opcode);
-std::ostream& operator<<(std::ostream& os, const Rcode& rcode);
 std::ostream& operator<<(std::ostream& os, const Message& message);
 }
 }

+ 2 - 2
src/lib/dns/name.h

@@ -143,8 +143,8 @@ public:
     ///
     /// This constructor simply initializes the object in the straightforward
     /// way.
-    explicit NameComparisonResult(int order, unsigned int nlabels,
-                                  NameRelation relation) :
+    NameComparisonResult(int order, unsigned int nlabels,
+                         NameRelation relation) :
         order_(order), nlabels_(nlabels), relation_(relation) {}
     //@}
 

+ 4 - 1
src/lib/dns/python/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -11,10 +11,13 @@ pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
 # directly included from source files, so these don't have their own
 # rules
 EXTRA_DIST = pydnspp_common.h
+EXTRA_DIST += edns_python.cc
 EXTRA_DIST += messagerenderer_python.cc
 EXTRA_DIST += message_python.cc
 EXTRA_DIST += rrclass_python.cc
 EXTRA_DIST += name_python.cc
+EXTRA_DIST += opcode_python.cc
+EXTRA_DIST += rcode_python.cc
 EXTRA_DIST += rrset_python.cc
 EXTRA_DIST += question_python.cc
 EXTRA_DIST += rrttl_python.cc

+ 74 - 678
src/lib/dns/python/message_python.cc

@@ -26,7 +26,6 @@ static PyObject* po_MessageTooShort;
 static PyObject* po_InvalidMessageSection;
 static PyObject* po_InvalidMessageOperation;
 static PyObject* po_InvalidMessageUDPSize;
-static PyObject* po_DNSMessageBADVERS;
 
 //
 // Definition of the classes
@@ -120,7 +119,6 @@ static PyTypeObject messageflag_type = {
     0                                   // tp_version_tag
 };
 
-
 static int
 MessageFlag_init(s_MessageFlag* self UNUSED_PARAM,
                  PyObject* args UNUSED_PARAM)
@@ -191,585 +189,6 @@ MessageFlag_CD(s_MessageFlag* self UNUSED_PARAM) {
 // End of MessageFlag wrapper
 //
 
-
-//
-// Opcode
-//
-class s_Opcode : public PyObject {
-public:
-    const Opcode* opcode;
-};
-
-static int Opcode_init(s_Opcode* self, PyObject* args);
-static void Opcode_destroy(s_Opcode* self);
-
-static PyObject* Opcode_getCode(s_Opcode* self);
-static PyObject* Opcode_toText(s_Opcode* self);
-static PyObject* Opcode_str(PyObject* self);
-static PyObject* Opcode_QUERY(s_Opcode* self);
-static PyObject* Opcode_IQUERY(s_Opcode* self);
-static PyObject* Opcode_STATUS(s_Opcode* self);
-static PyObject* Opcode_RESERVED3(s_Opcode* self);
-static PyObject* Opcode_NOTIFY(s_Opcode* self);
-static PyObject* Opcode_UPDATE(s_Opcode* self);
-static PyObject* Opcode_RESERVED6(s_Opcode* self);
-static PyObject* Opcode_RESERVED7(s_Opcode* self);
-static PyObject* Opcode_RESERVED8(s_Opcode* self);
-static PyObject* Opcode_RESERVED9(s_Opcode* self);
-static PyObject* Opcode_RESERVED10(s_Opcode* self);
-static PyObject* Opcode_RESERVED11(s_Opcode* self);
-static PyObject* Opcode_RESERVED12(s_Opcode* self);
-static PyObject* Opcode_RESERVED13(s_Opcode* self);
-static PyObject* Opcode_RESERVED14(s_Opcode* self);
-static PyObject* Opcode_RESERVED15(s_Opcode* self);
-static PyObject* Opcode_richcmp(s_Opcode* self, s_Opcode* other, int op);
-
-static PyMethodDef Opcode_methods[] = {
-    { "get_code", reinterpret_cast<PyCFunction>(Opcode_getCode), METH_NOARGS, "Returns the code value" },
-    { "to_text", reinterpret_cast<PyCFunction>(Opcode_toText), METH_NOARGS, "Returns the text representation" },
-    { "QUERY", reinterpret_cast<PyCFunction>(Opcode_QUERY), METH_NOARGS | METH_STATIC, "Creates a QUERY Opcode" },
-    { "IQUERY", reinterpret_cast<PyCFunction>(Opcode_IQUERY), METH_NOARGS | METH_STATIC, "Creates a IQUERY Opcode" },
-    { "STATUS", reinterpret_cast<PyCFunction>(Opcode_STATUS), METH_NOARGS | METH_STATIC, "Creates a STATUS Opcode" },
-    { "RESERVED3", reinterpret_cast<PyCFunction>(Opcode_RESERVED3), METH_NOARGS | METH_STATIC, "Creates a RESERVED3 Opcode" },
-    { "NOTIFY", reinterpret_cast<PyCFunction>(Opcode_NOTIFY), METH_NOARGS | METH_STATIC, "Creates a NOTIFY Opcode" },
-    { "UPDATE", reinterpret_cast<PyCFunction>(Opcode_UPDATE), METH_NOARGS | METH_STATIC, "Creates a UPDATE Opcode" },
-    { "RESERVED6", reinterpret_cast<PyCFunction>(Opcode_RESERVED6), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED7", reinterpret_cast<PyCFunction>(Opcode_RESERVED7), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED8", reinterpret_cast<PyCFunction>(Opcode_RESERVED8), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED9", reinterpret_cast<PyCFunction>(Opcode_RESERVED9), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED10", reinterpret_cast<PyCFunction>(Opcode_RESERVED10), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED11", reinterpret_cast<PyCFunction>(Opcode_RESERVED11), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED12", reinterpret_cast<PyCFunction>(Opcode_RESERVED12), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED13", reinterpret_cast<PyCFunction>(Opcode_RESERVED13), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED14", reinterpret_cast<PyCFunction>(Opcode_RESERVED14), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED15", reinterpret_cast<PyCFunction>(Opcode_RESERVED15), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { NULL, NULL, 0, NULL }
-};
-
-static PyTypeObject opcode_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.Opcode",
-    sizeof(s_Opcode),                   // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)Opcode_destroy,         // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    Opcode_str,                         // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The Opcode class objects represent standard OPCODEs "
-    "of the header section of DNS messages.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    (richcmpfunc)Opcode_richcmp,        // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    Opcode_methods,                     // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)Opcode_init,              // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
-
-static int
-Opcode_init(s_Opcode* self UNUSED_PARAM, PyObject* args UNUSED_PARAM) {
-    PyErr_SetString(PyExc_NotImplementedError,
-                    "Opcode can't be built directly");
-    return (-1);
-}
-
-static void
-Opcode_destroy(s_Opcode* self) {
-    // We only use the consts from Opcode, so don't
-    // delete self->opcode here
-    self->opcode = NULL;
-    Py_TYPE(self)->tp_free(self);
-}
-
-static PyObject*
-Opcode_getCode(s_Opcode* self) {
-    return (Py_BuildValue("I", self->opcode->getCode()));
-}
-
-static PyObject*
-Opcode_toText(s_Opcode* self) {
-    return (Py_BuildValue("s", self->opcode->toText().c_str()));
-}
-
-static PyObject*
-Opcode_str(PyObject* self) {
-    // Simply call the to_text method we already defined
-    return (PyObject_CallMethod(self,
-                               const_cast<char*>("to_text"),
-                                const_cast<char*>("")));
-}
-
-static PyObject*
-Opcode_createStatic(const Opcode& opcode) {
-    s_Opcode* ret = PyObject_New(s_Opcode, &opcode_type);
-    if (ret != NULL) {
-        ret->opcode = &opcode;
-    }
-    return (ret);
-}
-
-static PyObject*
-Opcode_QUERY(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::QUERY()));
-}
-
-static PyObject*
-Opcode_IQUERY(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::IQUERY()));
-}
-
-static PyObject*
-Opcode_STATUS(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::STATUS()));
-}
-
-static PyObject*
-Opcode_RESERVED3(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED3()));
-}
-
-static PyObject*
-Opcode_NOTIFY(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::NOTIFY()));
-}
-
-static PyObject*
-Opcode_UPDATE(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::UPDATE()));
-}
-
-static PyObject*
-Opcode_RESERVED6(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED6()));
-}
-
-static PyObject*
-Opcode_RESERVED7(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED7()));
-}
-
-static PyObject*
-Opcode_RESERVED8(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED8()));
-}
-
-static PyObject*
-Opcode_RESERVED9(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED9()));
-}
-
-static PyObject*
-Opcode_RESERVED10(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED10()));
-}
-
-static PyObject*
-Opcode_RESERVED11(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED11()));
-}
-
-static PyObject*
-Opcode_RESERVED12(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED12()));
-}
-
-static PyObject*
-Opcode_RESERVED13(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED13()));
-}
-
-static PyObject*
-Opcode_RESERVED14(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED14()));
-}
-
-static PyObject*
-Opcode_RESERVED15(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED15()));
-}
-
-static PyObject* 
-Opcode_richcmp(s_Opcode* self, s_Opcode* other, int op) {
-    bool c = false;
-
-    // Check for null and if the types match. If different type,
-    // simply return False
-    if (!other || (self->ob_type != other->ob_type)) {
-        Py_RETURN_FALSE;
-    }
-
-    // Only equals and not equals here, unorderable type
-    switch (op) {
-    case Py_LT:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
-        return (NULL);
-        break;
-    case Py_LE:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
-        return (NULL);
-        break;
-    case Py_EQ:
-        c = (*self->opcode == *other->opcode);
-        break;
-    case Py_NE:
-        c = (*self->opcode != *other->opcode);
-        break;
-    case Py_GT:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
-        return (NULL);
-        break;
-    case Py_GE:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
-        return (NULL);
-        break;
-    }
-    if (c)
-        Py_RETURN_TRUE;
-    else
-        Py_RETURN_FALSE;
-}
-
-//
-// End of Opcode wrapper
-//
-
-//
-// Rcode
-//
-
-// We added a helper variable static_code here
-// Since we can create Rcodes dynamically with Rcode(int), but also
-// use the static globals (Rcode::NOERROR() etc), we use this
-// variable to see if the code came from one of the latter, in which
-// case Rcode_destroy should not free it (the other option is to
-// allocate new Rcodes for every use of the static ones, but this
-// seems more efficient).
-class s_Rcode : public PyObject {
-public:
-    const Rcode* rcode;
-    bool static_code;
-};
-
-static int Rcode_init(s_Rcode* self, PyObject* args);
-static void Rcode_destroy(s_Rcode* self);
-
-static PyObject* Rcode_getCode(s_Rcode* self);
-static PyObject* Rcode_toText(s_Rcode* self);
-static PyObject* Rcode_str(PyObject* self);
-static PyObject* Rcode_NOERROR(s_Rcode* self);
-static PyObject* Rcode_FORMERR(s_Rcode* self);
-static PyObject* Rcode_SERVFAIL(s_Rcode* self);
-static PyObject* Rcode_NXDOMAIN(s_Rcode* self);
-static PyObject* Rcode_NOTIMP(s_Rcode* self);
-static PyObject* Rcode_REFUSED(s_Rcode* self);
-static PyObject* Rcode_YXDOMAIN(s_Rcode* self);
-static PyObject* Rcode_YXRRSET(s_Rcode* self);
-static PyObject* Rcode_NXRRSET(s_Rcode* self);
-static PyObject* Rcode_NOTAUTH(s_Rcode* self);
-static PyObject* Rcode_NOTZONE(s_Rcode* self);
-static PyObject* Rcode_RESERVED11(s_Rcode* self);
-static PyObject* Rcode_RESERVED12(s_Rcode* self);
-static PyObject* Rcode_RESERVED13(s_Rcode* self);
-static PyObject* Rcode_RESERVED14(s_Rcode* self);
-static PyObject* Rcode_RESERVED15(s_Rcode* self);
-static PyObject* Rcode_BADVERS(s_Rcode* self);
-static PyObject* Rcode_richcmp(s_Rcode* self, s_Rcode* other, int op);
-
-static PyMethodDef Rcode_methods[] = {
-    { "get_code", reinterpret_cast<PyCFunction>(Rcode_getCode), METH_NOARGS, "Returns the code value" },
-    { "to_text", reinterpret_cast<PyCFunction>(Rcode_toText), METH_NOARGS, "Returns the text representation" },
-    { "NOERROR", reinterpret_cast<PyCFunction>(Rcode_NOERROR), METH_NOARGS | METH_STATIC, "Creates a NOERROR Rcode" },
-    { "FORMERR", reinterpret_cast<PyCFunction>(Rcode_FORMERR), METH_NOARGS | METH_STATIC, "Creates a FORMERR Rcode" },
-    { "SERVFAIL", reinterpret_cast<PyCFunction>(Rcode_SERVFAIL), METH_NOARGS | METH_STATIC, "Creates a SERVFAIL Rcode" },
-    { "NXDOMAIN", reinterpret_cast<PyCFunction>(Rcode_NXDOMAIN), METH_NOARGS | METH_STATIC, "Creates a NXDOMAIN Rcode" },
-    { "NOTIMP", reinterpret_cast<PyCFunction>(Rcode_NOTIMP), METH_NOARGS | METH_STATIC, "Creates a NOTIMP Rcode" },
-    { "REFUSED", reinterpret_cast<PyCFunction>(Rcode_REFUSED), METH_NOARGS | METH_STATIC, "Creates a REFUSED Rcode" },
-    { "YXDOMAIN", reinterpret_cast<PyCFunction>(Rcode_YXDOMAIN), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "YXRRSET", reinterpret_cast<PyCFunction>(Rcode_YXRRSET), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "NXRRSET", reinterpret_cast<PyCFunction>(Rcode_NXRRSET), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "NOTAUTH", reinterpret_cast<PyCFunction>(Rcode_NOTAUTH), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "NOTZONE", reinterpret_cast<PyCFunction>(Rcode_NOTZONE), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "RESERVED11", reinterpret_cast<PyCFunction>(Rcode_RESERVED11), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "RESERVED12", reinterpret_cast<PyCFunction>(Rcode_RESERVED12), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "RESERVED13", reinterpret_cast<PyCFunction>(Rcode_RESERVED13), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "RESERVED14", reinterpret_cast<PyCFunction>(Rcode_RESERVED14), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "RESERVED15", reinterpret_cast<PyCFunction>(Rcode_RESERVED15), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "BADVERS", reinterpret_cast<PyCFunction>(Rcode_BADVERS), METH_NOARGS | METH_STATIC, "Creates a BADVERS Rcode" },
-    { NULL, NULL, 0, NULL }
-};
-
-static PyTypeObject rcode_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.Rcode",
-    sizeof(s_Rcode),                    // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)Rcode_destroy,          // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    Rcode_str,                          // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The Rcode class objects represent standard RCODEs"
-    "of the header section of DNS messages.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    (richcmpfunc)Rcode_richcmp,         // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    Rcode_methods,                      // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)Rcode_init,               // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
-
-static int
-Rcode_init(s_Rcode* self UNUSED_PARAM, PyObject* args UNUSED_PARAM) {
-    uint16_t code = 0;
-    if (PyArg_ParseTuple(args, "h", &code)) {
-        try {
-            self->rcode = new Rcode(code);
-            self->static_code = false;
-        } catch (const isc::OutOfRange&) {
-            PyErr_SetString(PyExc_OverflowError,
-                            "rcode out of range");
-            return (-1);
-        }
-        return (0);
-    } else {
-        return (-1);
-    }
-}
-
-static void
-Rcode_destroy(s_Rcode* self) {
-    // Depending on whether we created the rcode or are referring
-    // to a global static one, we do or do not delete self->rcode here
-    if (!self->static_code) {
-        delete self->rcode;
-    }
-    self->rcode = NULL;
-    Py_TYPE(self)->tp_free(self);
-}
-
-static PyObject*
-Rcode_getCode(s_Rcode* self) {
-    return (Py_BuildValue("I", self->rcode->getCode()));
-}
-
-static PyObject*
-Rcode_toText(s_Rcode* self) {
-    return (Py_BuildValue("s", self->rcode->toText().c_str()));
-}
-
-static PyObject*
-Rcode_str(PyObject* self) {
-    // Simply call the to_text method we already defined
-    return (PyObject_CallMethod(self,
-                               const_cast<char*>("to_text"),
-                                const_cast<char*>("")));
-}
-
-static PyObject*
-Rcode_createStatic(const Rcode& rcode) {
-    s_Rcode* ret = PyObject_New(s_Rcode, &rcode_type);
-    if (ret != NULL) {
-        ret->rcode = &rcode;
-        ret->static_code = true;
-    }
-    return (ret);
-}
-
-static PyObject*
-Rcode_NOERROR(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NOERROR()));
-}
-
-static PyObject*
-Rcode_FORMERR(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::FORMERR()));
-}
-
-static PyObject*
-Rcode_SERVFAIL(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::SERVFAIL()));
-}
-
-static PyObject*
-Rcode_NXDOMAIN(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NXDOMAIN()));
-}
-
-static PyObject*
-Rcode_NOTIMP(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NOTIMP()));
-}
-
-static PyObject*
-Rcode_REFUSED(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::REFUSED()));
-}
-
-static PyObject*
-Rcode_YXDOMAIN(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::YXDOMAIN()));
-}
-
-static PyObject*
-Rcode_YXRRSET(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::YXRRSET()));
-}
-
-static PyObject*
-Rcode_NXRRSET(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NXRRSET()));
-}
-
-static PyObject*
-Rcode_NOTAUTH(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NOTAUTH()));
-}
-
-static PyObject*
-Rcode_NOTZONE(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NOTZONE()));
-}
-
-static PyObject*
-Rcode_RESERVED11(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::RESERVED11()));
-}
-
-static PyObject*
-Rcode_RESERVED12(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::RESERVED12()));
-}
-
-static PyObject*
-Rcode_RESERVED13(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::RESERVED13()));
-}
-
-static PyObject*
-Rcode_RESERVED14(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::RESERVED14()));
-}
-
-static PyObject*
-Rcode_RESERVED15(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::RESERVED15()));
-}
-
-static PyObject*
-Rcode_BADVERS(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::BADVERS()));
-}
-
-static PyObject* 
-Rcode_richcmp(s_Rcode* self, s_Rcode* other, int op) {
-    bool c = false;
-
-    // Check for null and if the types match. If different type,
-    // simply return False
-    if (!other || (self->ob_type != other->ob_type)) {
-        Py_RETURN_FALSE;
-    }
-
-    // Only equals and not equals here, unorderable type
-    switch (op) {
-    case Py_LT:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
-        return (NULL);
-        break;
-    case Py_LE:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
-        return (NULL);
-        break;
-    case Py_EQ:
-        c = (*self->rcode == *other->rcode);
-        break;
-    case Py_NE:
-        c = (*self->rcode != *other->rcode);
-        break;
-    case Py_GT:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
-        return (NULL);
-        break;
-    case Py_GE:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
-        return (NULL);
-        break;
-    }
-    if (c)
-        Py_RETURN_TRUE;
-    else
-        Py_RETURN_FALSE;
-}
-
-//
-// End of Rcode wrapper
-//
-
-
 //
 // Section
 //
@@ -973,16 +392,14 @@ static void Message_destroy(s_Message* self);
 static PyObject* Message_getHeaderFlag(s_Message* self, PyObject* args);
 static PyObject* Message_setHeaderFlag(s_Message* self, PyObject* args);
 static PyObject* Message_clearHeaderFlag(s_Message* self, PyObject* args);
-static PyObject* Message_isDNSSECSupported(s_Message* self);
-static PyObject* Message_setDNSSECSupported(s_Message* self, PyObject* args);
-static PyObject* Message_getUDPSize(s_Message* self);
-static PyObject* Message_setUDPSize(s_Message* self, PyObject* args);
 static PyObject* Message_getQid(s_Message* self);
 static PyObject* Message_setQid(s_Message* self, PyObject* args);
 static PyObject* Message_getRcode(s_Message* self);
 static PyObject* Message_setRcode(s_Message* self, PyObject* args);
 static PyObject* Message_getOpcode(s_Message* self);
 static PyObject* Message_setOpcode(s_Message* self, PyObject* args);
+static PyObject* Message_getEDNS(s_Message* self);
+static PyObject* Message_setEDNS(s_Message* self, PyObject* args);
 static PyObject* Message_getRRCount(s_Message* self, PyObject* args);
 // use direct iterators for these? (or simply lists for now?)
 static PyObject* Message_getQuestion(s_Message* self);
@@ -1019,35 +436,6 @@ static PyMethodDef Message_methods[] = {
       "Sets the specified header flag bit to 0. The message must be in "
       "RENDER mode. If not, an InvalidMessageOperation is raised. "
       "Takes a MessageFlag object as the only argument." },
-    { "is_dnssec_supported", reinterpret_cast<PyCFunction>(Message_isDNSSECSupported), METH_NOARGS,
-      "Returns True if the message sender indicates DNSSEC is supported. "
-      "If EDNS is included, this corresponds to the value of the DO bit. "
-      "Otherwise, DNSSEC is considered not supported." },
-    { "set_dnssec_supported", reinterpret_cast<PyCFunction>(Message_setDNSSECSupported), METH_VARARGS,
-      "Specify whether DNSSEC is supported in the message. "
-      "The message must be in RENDER mode. If not, an "
-      "InvalidMessageOperation is raised."
-      "If EDNS is included in the message, the DO bit is set or cleared "
-      "according to given argument (True or False) of this method."},
-    { "get_udp_size", reinterpret_cast<PyCFunction>(Message_getUDPSize), METH_NOARGS,
-      "Return the maximum buffer size of UDP messages for the sender "
-      "of the message.\n\n"
-      "The semantics of this value is different based on the mode:\n"
-      "In the PARSE mode, it means the buffer size of the remote node;\n"
-      "in the RENDER mode, it means the buffer size of the local node.\n\n"
-      "In either case, its value is the value of the UDP payload size field "
-      "of EDNS (when it's included) or DEFAULT_MAX_UDPSIZE." },
-    { "set_udp_size", reinterpret_cast<PyCFunction>(Message_setUDPSize), METH_VARARGS,
-      "Specify the maximum buffer size of UDP messages of the local "
-      "node. If the message is not in RENDER mode, an "
-      "InvalidMessageOperation is raised.\n\n"
-      "If EDNS OPT RR is included in the message, its UDP payload size field "
-      "will be set to the specified value.\n"
-      "Unless explicitly specified, DEFAULT_MAX_UDPSIZE will be assumed "
-      "for the maximum buffer size, regardless of whether EDNS OPT RR is "
-      "included or not.  This means if an application wants to send a message "
-      "with an EDNS OPT RR for specifying a larger UDP size, it must explicitly "
-      "specify the value using this method. "},
     { "get_qid", reinterpret_cast<PyCFunction>(Message_getQid), METH_NOARGS,
       "Returns the query id" },
     { "set_qid", reinterpret_cast<PyCFunction>(Message_setQid), METH_VARARGS,
@@ -1066,6 +454,12 @@ static PyMethodDef Message_methods[] = {
       "Sets the message opcode (an Opcode object).\n"
       "If the message is not in RENDER mode, an "
       "InvalidMessageOperation is raised."},
+    { "get_edns", reinterpret_cast<PyCFunction>(Message_getEDNS), METH_NOARGS,
+      "Return, if any, the EDNS associated with the message."
+    },
+    { "set_edns", reinterpret_cast<PyCFunction>(Message_setEDNS), METH_VARARGS,
+      "Set EDNS for the message."
+    },
     { "get_rr_count", reinterpret_cast<PyCFunction>(Message_getRRCount), METH_VARARGS,
       "Returns the number of RRs contained in the given section." },
     { "get_question", reinterpret_cast<PyCFunction>(Message_getQuestion), METH_NOARGS,
@@ -1248,57 +642,6 @@ Message_clearHeaderFlag(s_Message* self, PyObject* args) {
 }
 
 static PyObject*
-Message_isDNSSECSupported(s_Message* self) {
-    if (self->message->isDNSSECSupported()) {
-        Py_RETURN_TRUE;
-    } else {
-        Py_RETURN_FALSE;
-    }
-}
-
-static PyObject*
-Message_setDNSSECSupported(s_Message* self, PyObject* args) {
-    PyObject *b;
-    if (!PyArg_ParseTuple(args, "O!", &PyBool_Type, &b)) {
-        return (NULL);
-    }
-    try {
-        if (b == Py_True) {
-            self->message->setDNSSECSupported(true);
-        } else {
-            self->message->setDNSSECSupported(false);
-        }
-        Py_RETURN_NONE;
-    } catch (const InvalidMessageOperation& imo) {
-        PyErr_SetString(po_InvalidMessageOperation, imo.what());
-        return (NULL);
-    }
-}
-
-static PyObject*
-Message_getUDPSize(s_Message* self) {
-    return (Py_BuildValue("I", self->message->getUDPSize()));
-}
-
-static PyObject*
-Message_setUDPSize(s_Message* self, PyObject* args) {
-    uint16_t size;
-    if (!PyArg_ParseTuple(args, "H", &size)) {
-        return (NULL);
-    }
-    try {
-        self->message->setUDPSize(size);
-        Py_RETURN_NONE;
-    } catch (const InvalidMessageUDPSize& imus) {
-        PyErr_SetString(po_InvalidMessageUDPSize, imus.what());
-        return (NULL);
-    } catch (const InvalidMessageOperation& imo) {
-        PyErr_SetString(po_InvalidMessageOperation, imo.what());
-        return (NULL);
-    }
-}
-
-static PyObject*
 Message_getQid(s_Message* self) {
     return (Py_BuildValue("I", self->message->getQid()));
 }
@@ -1324,12 +667,18 @@ Message_getRcode(s_Message* self) {
 
     rcode = static_cast<s_Rcode*>(rcode_type.tp_alloc(&rcode_type, 0));
     if (rcode != NULL) {
-        rcode->rcode = new Rcode(self->message->getRcode());
-        if (rcode->rcode == NULL)
-          {
+        rcode->rcode = NULL;
+        try {
+            rcode->rcode = new Rcode(self->message->getRcode());
+        } catch (const InvalidMessageOperation& imo) {
+            PyErr_SetString(po_InvalidMessageOperation, imo.what());
+        } catch (...) {
+            PyErr_SetString(po_IscException, "Unexpected exception");
+        }
+        if (rcode->rcode == NULL) {
             Py_DECREF(rcode);
             return (NULL);
-          }
+        }
     }
 
     return (rcode);
@@ -1356,15 +705,18 @@ Message_getOpcode(s_Message* self) {
 
     opcode = static_cast<s_Opcode*>(opcode_type.tp_alloc(&opcode_type, 0));
     if (opcode != NULL) {
-        // Note that we do not new and delete for opcodes.
-        // all rcodes point to the statics defined in
-        // message.cc
-        opcode->opcode = &self->message->getOpcode();
-        if (opcode->opcode == NULL)
-          {
+        opcode->opcode = NULL;
+        try {
+            opcode->opcode = new Opcode(self->message->getOpcode());
+        } catch (const InvalidMessageOperation& imo) {
+            PyErr_SetString(po_InvalidMessageOperation, imo.what());
+        } catch (...) {
+            PyErr_SetString(po_IscException, "Unexpected exception");
+        }
+        if (opcode->opcode == NULL) {
             Py_DECREF(opcode);
             return (NULL);
-          }
+        }
     }
 
     return (opcode);
@@ -1386,6 +738,41 @@ Message_setOpcode(s_Message* self, PyObject* args) {
 }
 
 static PyObject*
+Message_getEDNS(s_Message* self) {
+    s_EDNS* edns;
+    EDNS* edns_body;
+    ConstEDNSPtr src = self->message->getEDNS();
+
+    if (!src) {
+        Py_RETURN_NONE;
+    }
+    if ((edns_body = new(nothrow) EDNS(*src)) == NULL) {
+        return (PyErr_NoMemory());
+    }
+    edns = static_cast<s_EDNS*>(opcode_type.tp_alloc(&edns_type, 0));
+    if (edns != NULL) {
+        edns->edns = edns_body;
+    }
+
+    return (edns);
+}
+
+static PyObject*
+Message_setEDNS(s_Message* self, PyObject* args) {
+    s_EDNS* edns;
+    if (!PyArg_ParseTuple(args, "O!", &edns_type, &edns)) {
+        return (NULL);
+    }
+    try {
+        self->message->setEDNS(EDNSPtr(new EDNS(*edns->edns)));
+        Py_RETURN_NONE;
+    } catch (const InvalidMessageOperation& imo) {
+        PyErr_SetString(po_InvalidMessageOperation, imo.what());
+        return (NULL);
+    }
+}
+
+static PyObject*
 Message_getRRCount(s_Message* self, PyObject* args) {
     s_Section *section;
     if (!PyArg_ParseTuple(args, "O!", &section_type, &section)) {
@@ -1520,7 +907,16 @@ Message_makeResponse(s_Message* self) {
 static PyObject*
 Message_toText(s_Message* self) {
     // Py_BuildValue makes python objects from native data
-    return (Py_BuildValue("s", self->message->toText().c_str()));
+    try {
+        return (Py_BuildValue("s", self->message->toText().c_str()));
+    } catch (const InvalidMessageOperation& imo) {
+        PyErr_Clear();
+        PyErr_SetString(po_InvalidMessageOperation, imo.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(po_IscException, "Unexpected exception");
+        return (NULL);
+    }
 }
 
 static PyObject*
@@ -1535,7 +931,7 @@ static PyObject*
 Message_toWire(s_Message* self, PyObject* args) {
     s_MessageRenderer* mr;
     
-    if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         try {
             self->message->toWire(*mr->messagerenderer);
             // If we return NULL it is seen as an error, so use this for

+ 10 - 1
src/lib/dns/python/messagerenderer_python.cc

@@ -42,7 +42,7 @@ static PyObject* MessageRenderer_getLengthLimit(s_MessageRenderer* self);
 // TODO: set/get compressmode
 static PyObject* MessageRenderer_setTruncated(s_MessageRenderer* self);
 static PyObject* MessageRenderer_setLengthLimit(s_MessageRenderer* self, PyObject* args);
-
+static PyObject* MessageRenderer_clear(s_MessageRenderer* self);
 
 static PyMethodDef MessageRenderer_methods[] = {
     { "get_data", reinterpret_cast<PyCFunction>(MessageRenderer_getData), METH_NOARGS,
@@ -57,6 +57,9 @@ static PyMethodDef MessageRenderer_methods[] = {
       "Sets truncated to true" },
     { "set_length_limit", reinterpret_cast<PyCFunction>(MessageRenderer_setLengthLimit), METH_VARARGS,
       "Sets the length limit of the data to the given number" },
+    { "clear", reinterpret_cast<PyCFunction>(MessageRenderer_clear),
+      METH_NOARGS,
+      "Clear the internal buffer and other internal resources." },
     { NULL, NULL, 0, NULL }
 };
 
@@ -175,6 +178,12 @@ MessageRenderer_setLengthLimit(s_MessageRenderer* self,
     Py_RETURN_NONE;
 }
 
+static PyObject*
+MessageRenderer_clear(s_MessageRenderer* self) {
+    self->messagerenderer->clear();
+    Py_RETURN_NONE;
+}
+
 // end of MessageRenderer
 
 

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

@@ -421,7 +421,7 @@ Name_toWire(s_Name* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(name_bytes);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->name->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns
@@ -437,7 +437,7 @@ static PyObject*
 Name_compare(s_Name* self, PyObject* args) {
     s_Name* other;
 
-    if (!PyArg_ParseTuple(args, "O!", &name_type, (PyObject* *) &other))
+    if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
         return (NULL);
 
     s_NameComparisonResult* ret = PyObject_New(s_NameComparisonResult, &name_comparison_result_type);
@@ -452,7 +452,7 @@ static PyObject*
 Name_equals(s_Name* self, PyObject* args) {
     s_Name* other;
 
-    if (!PyArg_ParseTuple(args, "O!", &name_type, (PyObject* *) &other))
+    if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
         return (NULL);
 
     if (self->name->equals(*other->name))
@@ -565,7 +565,7 @@ static PyObject*
 Name_concatenate(s_Name* self, PyObject* args) {
     s_Name* other;
 
-    if (!PyArg_ParseTuple(args, "O!", &name_type, (PyObject**) &other))
+    if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
         return (NULL);
 
     s_Name* ret = PyObject_New(s_Name, &name_type);

+ 25 - 1
src/lib/dns/python/pydnspp.cc

@@ -40,8 +40,12 @@
 
 #include <dns/python/pydnspp_common.h>
 
-// For our 'general' isc::Exception
+// For our 'general' isc::Exceptions
 static PyObject* po_IscException;
+static PyObject* po_InvalidParameter;
+
+// For our own isc::dns::Exception
+static PyObject* po_DNSMessageBADVERS;
 
 // order is important here!
 #include <dns/python/messagerenderer_python.cc>
@@ -53,6 +57,9 @@ static PyObject* po_IscException;
 #include <dns/python/rrset_python.cc>          // needs Rdata, RRTTL
 #include <dns/python/question_python.cc>       // needs RRClass, RRType, RRTTL,
                                                // Name
+#include <dns/python/opcode_python.cc>
+#include <dns/python/rcode_python.cc>
+#include <dns/python/edns_python.cc>           // needs Messagerenderer, Rcode
 #include <dns/python/message_python.cc>        // needs RRset, Question
 
 //
@@ -81,9 +88,14 @@ PyInit_pydnspp(void) {
         return (NULL);
     }
 
+    // Add the exceptions to the class
     po_IscException = PyErr_NewException("pydnspp.IscException", NULL, NULL);
     PyModule_AddObject(mod, "IscException", po_IscException);
 
+    po_InvalidParameter = PyErr_NewException("pydnspp.InvalidParameter",
+                                             NULL, NULL);
+    PyModule_AddObject(mod, "InvalidParameter", po_InvalidParameter);
+
     // for each part included above, we call its specific initializer
 
     if (!initModulePart_Name(mod)) {
@@ -118,10 +130,22 @@ PyInit_pydnspp(void) {
         return (NULL);
     }
 
+    if (!initModulePart_Opcode(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_Rcode(mod)) {
+        return (NULL);
+    }
+
     if (!initModulePart_Message(mod)) {
         return (NULL);
     }
 
+    if (!initModulePart_EDNS(mod)) {
+        return (NULL);
+    }
+
     return (mod);
 }
 

+ 1 - 2
src/lib/dns/python/question_python.cc

@@ -254,8 +254,7 @@ Question_toWire(s_Question* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(n);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type,
-                                reinterpret_cast<PyObject**>(&mr))) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->question->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns

+ 11 - 2
src/lib/dns/python/rdata_python.cc

@@ -146,12 +146,21 @@ Rdata_init(s_Rdata* self, PyObject* args) {
     s_RRType* rrtype;
     s_RRClass* rrclass;
     const char* s;
-    
+    const char* data;
+    Py_ssize_t len;
+
+    // Create from string
     if (PyArg_ParseTuple(args, "O!O!s", &rrtype_type, &rrtype,
                                         &rrclass_type, &rrclass,
                                         &s)) {
         self->rdata = createRdata(*rrtype->rrtype, *rrclass->rrclass, s);
         return (0);
+    } else if (PyArg_ParseTuple(args, "O!O!y#", &rrtype_type, &rrtype,
+                                &rrclass_type, &rrclass, &data, &len)) {
+        InputBuffer input_buffer(data, len);
+        self->rdata = createRdata(*rrtype->rrtype, *rrclass->rrclass,
+                                  input_buffer, len);
+        return (0);
     }
 
     return (-1);
@@ -195,7 +204,7 @@ Rdata_toWire(s_Rdata* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(rd_bytes);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->rdata->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns

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

@@ -236,7 +236,7 @@ RRClass_toWire(s_RRClass* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(n);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->rrclass->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns

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

@@ -315,7 +315,7 @@ RRset_toWire(s_RRset* self, PyObject* args) {
             // to prevent memory leak
             Py_DECREF(n);
             return (result);
-        } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+        } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
             self->rrset->toWire(*mr->messagerenderer);
             // If we return NULL it is seen as an error, so use this for
             // None returns

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

@@ -235,7 +235,7 @@ RRTTL_toWire(s_RRTTL* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(n);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->rrttl->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns

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

@@ -273,7 +273,7 @@ RRType_toWire(s_RRType* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(n);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->rrtype->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns

+ 7 - 3
src/lib/dns/python/tests/Makefile.am

@@ -1,7 +1,10 @@
-PYTESTS = message_python_test.py
+PYTESTS = edns_python_test.py
+PYTESTS += message_python_test.py
 PYTESTS += messagerenderer_python_test.py
 PYTESTS += name_python_test.py
 PYTESTS += question_python_test.py
+PYTESTS += opcode_python_test.py
+PYTESTS += rcode_python_test.py
 PYTESTS += rdata_python_test.py
 PYTESTS += rrclass_python_test.py
 PYTESTS += rrset_python_test.py
@@ -9,6 +12,7 @@ PYTESTS += rrttl_python_test.py
 PYTESTS += rrtype_python_test.py
 
 EXTRA_DIST = $(PYTESTS)
+EXTRA_DIST += testutil.py
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
 # required by loadable python modules.
@@ -23,8 +27,8 @@ PYCOVERAGE = $(PYTHON)
 check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
-	TESTDATA_PATH=$(abs_top_srcdir)/src/lib/dns/tests/testdata \
+	env PYTHONPATH=$(abs_top_srcdir)/src/lib/dns/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+	TESTDATA_PATH=$(abs_top_srcdir)/src/lib/dns/tests/testdata:$(abs_top_builddir)/src/lib/dns/tests/testdata \
 	$(LIBRARY_PATH_PLACEHOLDER) \
 	$(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
 	done

+ 42 - 273
src/lib/dns/python/tests/message_python_test.py

@@ -20,7 +20,7 @@
 import unittest
 import os
 from pydnspp import *
-
+from testutil import *
 
 class MessageFlagTest(unittest.TestCase):
     def test_init(self):
@@ -35,163 +35,6 @@ class MessageFlagTest(unittest.TestCase):
         self.assertEqual(0x0020, MessageFlag.AD().get_bit())
         self.assertEqual(0x0010, MessageFlag.CD().get_bit())
 
-class OpcodeTest(unittest.TestCase):
-    def test_init(self):
-        self.assertRaises(NotImplementedError, Opcode)
-
-    def test_get_code(self):
-        self.assertEqual(0, Opcode.QUERY().get_code())
-        self.assertEqual(1, Opcode.IQUERY().get_code())
-        self.assertEqual(2, Opcode.STATUS().get_code())
-        self.assertEqual(3, Opcode.RESERVED3().get_code())
-        self.assertEqual(4, Opcode.NOTIFY().get_code())
-        self.assertEqual(5, Opcode.UPDATE().get_code())
-        self.assertEqual(6, Opcode.RESERVED6().get_code())
-        self.assertEqual(7, Opcode.RESERVED7().get_code())
-        self.assertEqual(8, Opcode.RESERVED8().get_code())
-        self.assertEqual(9, Opcode.RESERVED9().get_code())
-        self.assertEqual(10, Opcode.RESERVED10().get_code())
-        self.assertEqual(11, Opcode.RESERVED11().get_code())
-        self.assertEqual(12, Opcode.RESERVED12().get_code())
-        self.assertEqual(13, Opcode.RESERVED13().get_code())
-        self.assertEqual(14, Opcode.RESERVED14().get_code())
-        self.assertEqual(15, Opcode.RESERVED15().get_code())
-
-    def test_to_text(self):
-        self.assertEqual("QUERY", Opcode.QUERY().to_text())
-        self.assertEqual("QUERY", str(Opcode.QUERY()))
-        self.assertEqual("IQUERY", Opcode.IQUERY().to_text())
-        self.assertEqual("STATUS", Opcode.STATUS().to_text())
-        self.assertEqual("RESERVED3", Opcode.RESERVED3().to_text())
-        self.assertEqual("NOTIFY", Opcode.NOTIFY().to_text())
-        self.assertEqual("UPDATE", Opcode.UPDATE().to_text())
-        self.assertEqual("RESERVED6", Opcode.RESERVED6().to_text())
-        self.assertEqual("RESERVED7", Opcode.RESERVED7().to_text())
-        self.assertEqual("RESERVED8", Opcode.RESERVED8().to_text())
-        self.assertEqual("RESERVED9", Opcode.RESERVED9().to_text())
-        self.assertEqual("RESERVED10", Opcode.RESERVED10().to_text())
-        self.assertEqual("RESERVED11", Opcode.RESERVED11().to_text())
-        self.assertEqual("RESERVED12", Opcode.RESERVED12().to_text())
-        self.assertEqual("RESERVED13", Opcode.RESERVED13().to_text())
-        self.assertEqual("RESERVED14", Opcode.RESERVED14().to_text())
-        self.assertEqual("RESERVED15", Opcode.RESERVED15().to_text())
-
-    def test_richcmp(self):
-        o1 = Opcode.QUERY()
-        o2 = Opcode.NOTIFY()
-        o3 = Opcode.NOTIFY()
-        self.assertTrue(o2 == o3)
-        self.assertFalse(o2 != o3)
-        self.assertTrue(o1 != o2)
-        self.assertFalse(o1 == 1)
-        self.assertFalse(o1 == o2)
-        # can't use assertRaises here...
-        try:
-            o1 < o2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            o1 <= o2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            o1 > o2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            o1 >= o2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-
-class RcodeTest(unittest.TestCase):
-    def test_init(self):
-        self.assertRaises(TypeError, Rcode, "wrong")
-        self.assertRaises(OverflowError, Rcode, 65536)
-        self.assertEqual(Rcode(0).get_code(), 0)
-    
-        self.assertEqual(0, Rcode(0).get_code())
-        self.assertEqual(0xfff, Rcode(0xfff).get_code()) # possible max code
-    
-        # should fail on attempt of construction with an out of range code
-        self.assertRaises(OverflowError, Rcode, 0x1000)
-        self.assertRaises(OverflowError, Rcode, 0xffff)
-
-    def test_get_code(self):
-        self.assertEqual(0, Rcode.NOERROR().get_code())
-        self.assertEqual(1, Rcode.FORMERR().get_code())
-        self.assertEqual(2, Rcode.SERVFAIL().get_code())
-        self.assertEqual(3, Rcode.NXDOMAIN().get_code())
-        self.assertEqual(4, Rcode.NOTIMP().get_code())
-        self.assertEqual(5, Rcode.REFUSED().get_code())
-        self.assertEqual(6, Rcode.YXDOMAIN().get_code())
-        self.assertEqual(7, Rcode.YXRRSET().get_code())
-        self.assertEqual(8, Rcode.NXRRSET().get_code())
-        self.assertEqual(9, Rcode.NOTAUTH().get_code())
-        self.assertEqual(10, Rcode.NOTZONE().get_code())
-        self.assertEqual(11, Rcode.RESERVED11().get_code())
-        self.assertEqual(12, Rcode.RESERVED12().get_code())
-        self.assertEqual(13, Rcode.RESERVED13().get_code())
-        self.assertEqual(14, Rcode.RESERVED14().get_code())
-        self.assertEqual(15, Rcode.RESERVED15().get_code())
-        self.assertEqual(16, Rcode.BADVERS().get_code())
-
-    def test_to_text(self):
-        self.assertEqual("NOERROR", Rcode(0).to_text())
-        self.assertEqual("NOERROR", str(Rcode(0)))
-        self.assertEqual("FORMERR", Rcode(1).to_text())
-        self.assertEqual("SERVFAIL", Rcode(2).to_text())
-        self.assertEqual("NXDOMAIN", Rcode(3).to_text())
-        self.assertEqual("NOTIMP", Rcode(4).to_text())
-        self.assertEqual("REFUSED", Rcode(5).to_text())
-        self.assertEqual("YXDOMAIN", Rcode(6).to_text())
-        self.assertEqual("YXRRSET", Rcode(7).to_text())
-        self.assertEqual("NXRRSET", Rcode(8).to_text())
-        self.assertEqual("NOTAUTH", Rcode(9).to_text())
-        self.assertEqual("NOTZONE", Rcode(10).to_text())
-        self.assertEqual("RESERVED11", Rcode(11).to_text())
-        self.assertEqual("RESERVED12", Rcode(12).to_text())
-        self.assertEqual("RESERVED13", Rcode(13).to_text())
-        self.assertEqual("RESERVED14", Rcode(14).to_text())
-        self.assertEqual("RESERVED15", Rcode(15).to_text())
-        self.assertEqual("BADVERS", Rcode(16).to_text())
-        
-        self.assertEqual("17", Rcode(Rcode.BADVERS().get_code() + 1).to_text())
-        self.assertEqual("4095", Rcode(0xfff).to_text())
-
-    def test_richcmp(self):
-        r1 = Rcode.NOERROR()
-        r2 = Rcode.FORMERR()
-        r3 = Rcode.FORMERR()
-        self.assertTrue(r2 == r3)
-        self.assertTrue(r1 != r2)
-        self.assertFalse(r1 == r2)
-        self.assertFalse(r1 != 1)
-        # can't use assertRaises here...
-        try:
-            r1 < r2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            r1 <= r2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            r1 > r2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            r1 >= r2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-
 class SectionTest(unittest.TestCase):
 
     def test_init(self):
@@ -240,19 +83,6 @@ if "TESTDATA_PATH" in os.environ:
 else:
     testdata_path = "../tests/testdata"
 
-def read_wire_data(filename):
-    data = bytes()
-    file = open(testdata_path + os.sep + filename, "r")
-    for line in file:
-        line = line.strip()
-        if line == "" or line.startswith("#"):
-            pass
-        else:
-            cur_data = bytes.fromhex(line)
-            data += cur_data
-
-    return data
-
 def factoryFromFile(message, file):
     data = read_wire_data(file)
     message.from_wire(data)
@@ -316,28 +146,6 @@ class MessageTest(unittest.TestCase):
         self.assertRaises(InvalidMessageOperation,
                           self.p.clear_header_flag, MessageFlag.AA())
 
-    def test_set_DNSSEC_supported(self):
-        self.assertRaises(TypeError, self.r.set_dnssec_supported, "wrong")
-
-        self.assertFalse(self.r.is_dnssec_supported())
-        self.r.set_dnssec_supported(True)
-        self.assertTrue(self.r.is_dnssec_supported())
-        self.r.set_dnssec_supported(False)
-        self.assertFalse(self.r.is_dnssec_supported())
-
-        self.assertRaises(InvalidMessageOperation,
-                          self.p.set_dnssec_supported, True)
-        self.assertRaises(InvalidMessageOperation,
-                          self.p.set_dnssec_supported, False)
-
-    def test_set_udp_size(self):
-        self.assertRaises(TypeError, self.r.set_udp_size, "wrong")
-        self.assertRaises(InvalidMessageUDPSize, self.r.set_udp_size, 0)
-        self.assertRaises(InvalidMessageUDPSize, self.r.set_udp_size, 65536)
-        self.assertRaises(InvalidMessageOperation, self.p.set_udp_size, 1024)
-        self.r.set_udp_size(2048)
-        self.assertEqual(2048, self.r.get_udp_size())
-
     def test_set_qid(self):
         self.assertRaises(TypeError, self.r.set_qid, "wrong")
         self.assertRaises(InvalidMessageOperation,
@@ -355,6 +163,7 @@ class MessageTest(unittest.TestCase):
         self.assertRaises(InvalidMessageOperation,
                           self.p.set_rcode, rcode)
         
+        self.assertRaises(InvalidMessageOperation, self.p.get_rcode)
 
     def test_set_opcode(self):
         self.assertRaises(TypeError, self.r.set_opcode, "wrong")
@@ -366,6 +175,26 @@ class MessageTest(unittest.TestCase):
         self.assertRaises(InvalidMessageOperation,
                           self.p.set_opcode, opcode)
 
+        self.assertRaises(InvalidMessageOperation, self.p.get_opcode)
+
+    def test_get_edns(self):
+        self.assertEqual(None, self.p.get_edns())
+
+        message_parse = Message(Message.PARSE)
+        factoryFromFile(message_parse, "message_fromWire10.wire")
+        edns = message_parse.get_edns()
+        self.assertEqual(0, edns.get_version())
+        self.assertEqual(4096, edns.get_udp_size())
+        self.assertTrue(edns.get_dnssec_awareness())
+
+    def test_set_edns(self):
+        self.assertRaises(InvalidMessageOperation, self.p.set_edns, EDNS())
+
+        edns = EDNS()
+        edns.set_udp_size(1024)
+        self.r.set_edns(edns)
+        self.assertEqual(1024, self.r.get_edns().get_udp_size())
+
     def test_get_section(self):
         self.assertRaises(TypeError, self.r.get_section, "wrong")
 
@@ -430,6 +259,16 @@ class MessageTest(unittest.TestCase):
         self.assertEqual(b'\x105\x85\x00\x00\x01\x00\x02\x00\x00\x00\x00\x04test\x07example\x03com\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02\x02',
                          renderer.get_data())
 
+    def test_to_wire_without_opcode(self):
+        self.r.set_rcode(Rcode.NOERROR())
+        self.assertRaises(InvalidMessageOperation, self.r.to_wire,
+                          MessageRenderer())
+
+    def test_to_wire_without_rcode(self):
+        self.r.set_opcode(Opcode.QUERY())
+        self.assertRaises(InvalidMessageOperation, self.r.to_wire,
+                          MessageRenderer())
+
     def test_to_text(self):
         message_render = create_message()
         
@@ -447,6 +286,14 @@ test.example.com. 3600 IN A 192.0.2.2
         self.assertEqual(msg_str, message_render.to_text())
         self.assertEqual(msg_str, str(message_render))
 
+    def test_to_text_without_opcode(self):
+        self.r.set_rcode(Rcode.NOERROR())
+        self.assertRaises(InvalidMessageOperation, self.r.to_text)
+
+    def test_to_text_without_rcode(self):
+        self.r.set_opcode(Opcode.QUERY())
+        self.assertRaises(InvalidMessageOperation, self.r.to_text)
+
     def test_from_wire(self):
         self.assertRaises(TypeError, self.r.from_wire, 1)
         self.assertRaises(InvalidMessageOperation,
@@ -487,93 +334,15 @@ test.example.com. 3600 IN A 192.0.2.2
         self.assertEqual("192.0.2.2", rdata[1].to_text())
         self.assertEqual(2, len(rdata))
 
-    def test_GetEDNS0DOBit(self):
-        message_parse = Message(Message.PARSE)
-        ## Without EDNS0, DNSSEC is considered to be unsupported.
-        factoryFromFile(message_parse, "message_fromWire1")
-        self.assertFalse(message_parse.is_dnssec_supported())
-    
-        ## If DO bit is on, DNSSEC is considered to be supported.
-        message_parse.clear(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire2")
-        self.assertTrue(message_parse.is_dnssec_supported())
-    
-        ## If DO bit is off, DNSSEC is considered to be unsupported.
-        message_parse.clear(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire3")
-        self.assertFalse(message_parse.is_dnssec_supported())
-    
-    def test_SetEDNS0DOBit(self):
-        # By default, it's false, and we can enable/disable it.
-        message_parse = Message(Message.PARSE)
-        message_render = Message(Message.RENDER)
-        self.assertFalse(message_render.is_dnssec_supported())
-        message_render.set_dnssec_supported(True)
-        self.assertTrue(message_render.is_dnssec_supported())
-        message_render.set_dnssec_supported(False)
-        self.assertFalse(message_render.is_dnssec_supported())
-    
-        ## A message in the parse mode doesn't allow this flag to be set.
-        self.assertRaises(InvalidMessageOperation,
-                          message_parse.set_dnssec_supported,
-                          True)
-        ## Once converted to the render mode, it works as above
-        message_parse.make_response()
-        self.assertFalse(message_parse.is_dnssec_supported())
-        message_parse.set_dnssec_supported(True)
-        self.assertTrue(message_parse.is_dnssec_supported())
-        message_parse.set_dnssec_supported(False)
-        self.assertFalse(message_parse.is_dnssec_supported())
-    
-    def test_GetEDNS0UDPSize(self):
-        # Without EDNS0, the default max UDP size is used.
-        message_parse = Message(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire1")
-        self.assertEqual(Message.DEFAULT_MAX_UDPSIZE, message_parse.get_udp_size())
-    
-        ## If the size specified in EDNS0 > default max, use it.
-        message_parse.clear(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire2")
-        self.assertEqual(4096, message_parse.get_udp_size())
-    
-        ## If the size specified in EDNS0 < default max, keep using the default.
-        message_parse.clear(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire8")
-        self.assertEqual(Message.DEFAULT_MAX_UDPSIZE, message_parse.get_udp_size())
-    
-    def test_SetEDNS0UDPSize(self):
-        # The default size if unspecified
-        message_render = Message(Message.RENDER)
-        message_parse = Message(Message.PARSE)
-        self.assertEqual(Message.DEFAULT_MAX_UDPSIZE, message_render.get_udp_size())
-        # A common buffer size with EDNS, should succeed
-        message_render.set_udp_size(4096)
-        self.assertEqual(4096, message_render.get_udp_size())
-        # Unusual large value, but accepted
-        message_render.set_udp_size(0xffff)
-        self.assertEqual(0xffff, message_render.get_udp_size())
-        # Too small is value is rejected
-        self.assertRaises(InvalidMessageUDPSize, message_render.set_udp_size, 511)
-    
-        # A message in the parse mode doesn't allow the set operation.
-        self.assertRaises(InvalidMessageOperation, message_parse.set_udp_size, 4096)
-        ## Once converted to the render mode, it works as above.
-        message_parse.make_response()
-        message_parse.set_udp_size(4096)
-        self.assertEqual(4096, message_parse.get_udp_size())
-        message_parse.set_udp_size(0xffff)
-        self.assertEqual(0xffff, message_parse.get_udp_size())
-        self.assertRaises(InvalidMessageUDPSize, message_parse.set_udp_size, 511)
-    
     def test_EDNS0ExtCode(self):
         # Extended Rcode = BADVERS
         message_parse = Message(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire10")
+        factoryFromFile(message_parse, "message_fromWire10.wire")
         self.assertEqual(Rcode.BADVERS(), message_parse.get_rcode())
     
         # Maximum extended Rcode
         message_parse.clear(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire11")
+        factoryFromFile(message_parse, "message_fromWire11.wire")
         self.assertEqual(0xfff, message_parse.get_rcode().get_code())
     
     def test_BadEDNS0(self):

+ 1 - 0
src/lib/dns/python/tests/messagerenderer_python_test.py

@@ -32,6 +32,7 @@ class MessageRendererTest(unittest.TestCase):
         message = Message(Message.RENDER)
         message.set_qid(123)
         message.set_opcode(Opcode.QUERY())
+        message.set_rcode(Rcode.NOERROR())
         message.add_question(Question(name, c, t))
 
         self.message1 = message

+ 1 - 14
src/lib/dns/python/tests/question_python_test.py

@@ -20,25 +20,13 @@
 import unittest
 import os
 from pydnspp import *
+from testutil import *
 
 if "TESTDATA_PATH" in os.environ:
     testdata_path = os.environ["TESTDATA_PATH"]
 else:
     testdata_path = "../tests/testdata"
 
-def read_wire_data(filename):
-    data = bytes()
-    file = open(testdata_path + os.sep + filename, "r")
-    for line in file:
-        line = line.strip()
-        if line == "" or line.startswith("#"):
-            pass
-        else:
-            cur_data = bytes.fromhex(line)
-            data += cur_data
-
-    return data
-
 def question_from_wire(file, position = 0):
     data = read_wire_data(file)
     return Question(data, position)
@@ -102,7 +90,6 @@ class QuestionTest(unittest.TestCase):
         wiredata = read_wire_data("question_toWire2")
         self.assertEqual(renderer.get_data(), wiredata)
         self.assertRaises(TypeError, self.test_question1.to_wire, 1)
-    
 
 if __name__ == '__main__':
     unittest.main()

+ 4 - 8
src/lib/dns/question.cc

@@ -42,15 +42,13 @@ Question::Question(InputBuffer& buffer) :
 }
 
 string
-Question::toText() const
-{
+Question::toText() const {
     return (name_.toText() + " " + rrclass_.toText() + " " +
             rrtype_.toText() + "\n");
 }
 
 unsigned int
-Question::toWire(OutputBuffer& buffer) const
-{
+Question::toWire(OutputBuffer& buffer) const {
     name_.toWire(buffer);
     rrtype_.toWire(buffer);
     rrclass_.toWire(buffer);    // number of "entries", which is always 1
@@ -59,8 +57,7 @@ Question::toWire(OutputBuffer& buffer) const
 }
 
 unsigned int
-Question::toWire(MessageRenderer& renderer) const
-{
+Question::toWire(MessageRenderer& renderer) const {
     renderer.writeName(name_);
     rrtype_.toWire(renderer);
     rrclass_.toWire(renderer);
@@ -69,8 +66,7 @@ Question::toWire(MessageRenderer& renderer) const
 }
 
 ostream&
-operator<<(std::ostream& os, const Question& question)
-{
+operator<<(std::ostream& os, const Question& question) {
     os << question.toText();
     return (os);
 }

+ 11 - 22
src/lib/dns/rdata.cc

@@ -82,8 +82,7 @@ createRdata(const RRType& rrtype, const RRClass& rrclass, const Rdata& source)
 }
 
 int
-compareNames(const Name& n1, const Name& n2)
-{
+compareNames(const Name& n1, const Name& n2) {
     size_t len1 = n1.getLength();
     size_t len2 = n2.getLength();
     size_t cmplen = min(len1, len2);
@@ -107,8 +106,7 @@ struct GenericImpl {
     vector<uint8_t> data_;
 };
 
-Generic::Generic(InputBuffer& buffer, size_t rdata_len)
-{
+Generic::Generic(InputBuffer& buffer, size_t rdata_len) {
     if (rdata_len > MAX_RDLENGTH) {
         isc_throw(InvalidRdataLength, "RDLENGTH too large");
     }
@@ -119,8 +117,7 @@ Generic::Generic(InputBuffer& buffer, size_t rdata_len)
     impl_ = new GenericImpl(data);
 }
 
-Generic::Generic(const string& rdata_string)
-{
+Generic::Generic(const string& rdata_string) {
     istringstream iss(rdata_string);
     string unknown_mark;
     iss >> unknown_mark;
@@ -181,8 +178,7 @@ Generic::Generic(const string& rdata_string)
     impl_ = new GenericImpl(data);
 }
 
-Generic::~Generic()
-{
+Generic::~Generic() {
     delete impl_;
 }
 
@@ -191,8 +187,7 @@ Generic::Generic(const Generic& source) :
 {}
 
 Generic&
-Generic::operator=(const Generic& source)
-{
+Generic::operator=(const Generic& source) {
     if (impl_ == source.impl_) {
         return (*this);
     }
@@ -218,8 +213,7 @@ private:
 }
 
 string
-Generic::toText() const
-{
+Generic::toText() const {
     ostringstream oss;
 
     oss << "\\# " << impl_->data_.size() << " ";
@@ -231,21 +225,18 @@ Generic::toText() const
 }
 
 void
-Generic::toWire(OutputBuffer& buffer) const
-{
+Generic::toWire(OutputBuffer& buffer) const {
     buffer.writeData(&impl_->data_[0], impl_->data_.size());
 }
 
 void
-Generic::toWire(MessageRenderer& renderer) const
-{
+Generic::toWire(MessageRenderer& renderer) const {
     renderer.writeData(&impl_->data_[0], impl_->data_.size());
 }
 
 namespace {
 inline int
-compare_internal(const GenericImpl& lhs, const GenericImpl& rhs)
-{
+compare_internal(const GenericImpl& lhs, const GenericImpl& rhs) {
     size_t this_len = lhs.data_.size();
     size_t other_len = rhs.data_.size();
     size_t len = (this_len < other_len) ? this_len : other_len;
@@ -262,16 +253,14 @@ compare_internal(const GenericImpl& lhs, const GenericImpl& rhs)
 }
 
 int
-Generic::compare(const Rdata& other) const
-{
+Generic::compare(const Rdata& other) const {
     const Generic& other_rdata = dynamic_cast<const Generic&>(other);
 
     return (compare_internal(*impl_, *other_rdata.impl_));
 }
 
 std::ostream&
-operator<<(std::ostream& os, const Generic& rdata)
-{
+operator<<(std::ostream& os, const Generic& rdata) {
     return (os << rdata.toText());
 }
 } // end of namespace generic

+ 6 - 12
src/lib/dns/rdata/ch_3/a_1.cc

@@ -30,13 +30,11 @@ using namespace std;
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
-A::A(const string& addrstr UNUSED_PARAM)
-{
+A::A(const string& addrstr UNUSED_PARAM) {
     // TBD
 }
 
-A::A(InputBuffer& buffer UNUSED_PARAM, size_t rdata_len UNUSED_PARAM)
-{
+A::A(InputBuffer& buffer UNUSED_PARAM, size_t rdata_len UNUSED_PARAM) {
     // TBD
 }
 
@@ -45,27 +43,23 @@ A::A(const A& source UNUSED_PARAM) : Rdata() {
 }
 
 void
-A::toWire(OutputBuffer& buffer UNUSED_PARAM) const
-{
+A::toWire(OutputBuffer& buffer UNUSED_PARAM) const {
     // TBD
 }
 
 void
-A::toWire(MessageRenderer& renderer UNUSED_PARAM) const
-{
+A::toWire(MessageRenderer& renderer UNUSED_PARAM) const {
     // TBD
 }
 
 string
-A::toText() const
-{
+A::toText() const {
     // TBD
     isc_throw(InvalidRdataText, "Not implemented yet");
 }
 
 int
-A::compare(const Rdata& other UNUSED_PARAM) const
-{
+A::compare(const Rdata& other UNUSED_PARAM) const {
     return (0);                 // dummy.  TBD
 }
 

+ 5 - 10
src/lib/dns/rdata/generic/cname_5.cc

@@ -49,34 +49,29 @@ CNAME::CNAME(const Name& cname) :
 {}
 
 void
-CNAME::toWire(OutputBuffer& buffer) const
-{
+CNAME::toWire(OutputBuffer& buffer) const {
     cname_.toWire(buffer);
 }
 
 void
-CNAME::toWire(MessageRenderer& renderer) const
-{
+CNAME::toWire(MessageRenderer& renderer) const {
     renderer.writeName(cname_);
 }
 
 string
-CNAME::toText() const
-{
+CNAME::toText() const {
     return (cname_.toText());
 }
 
 int
-CNAME::compare(const Rdata& other) const
-{
+CNAME::compare(const Rdata& other) const {
     const CNAME& other_cname = dynamic_cast<const CNAME&>(other);
 
     return (compareNames(cname_, other_cname.cname_));
 }
 
 const Name&
-CNAME::getCname() const
-{
+CNAME::getCname() const {
     return (cname_);
 }
 

+ 5 - 10
src/lib/dns/rdata/generic/dname_39.cc

@@ -49,34 +49,29 @@ DNAME::DNAME(const Name& dname) :
 {}
 
 void
-DNAME::toWire(OutputBuffer& buffer) const
-{
+DNAME::toWire(OutputBuffer& buffer) const {
     dname_.toWire(buffer);
 }
 
 void
-DNAME::toWire(MessageRenderer& renderer) const
-{
+DNAME::toWire(MessageRenderer& renderer) const {
     renderer.writeName(dname_);
 }
 
 string
-DNAME::toText() const
-{
+DNAME::toText() const {
     return (dname_.toText());
 }
 
 int
-DNAME::compare(const Rdata& other) const
-{
+DNAME::compare(const Rdata& other) const {
     const DNAME& other_dname = dynamic_cast<const DNAME&>(other);
 
     return (compareNames(dname_, other_dname.dname_));
 }
 
 const Name&
-DNAME::getDname() const
-{
+DNAME::getDname() const {
     return (dname_);
 }
 

+ 7 - 14
src/lib/dns/rdata/generic/dnskey_48.cc

@@ -103,8 +103,7 @@ DNSKEY::DNSKEY(const DNSKEY& source) :
 {}
 
 DNSKEY&
-DNSKEY::operator=(const DNSKEY& source)
-{
+DNSKEY::operator=(const DNSKEY& source) {
     if (impl_ == source.impl_) {
         return (*this);
     }
@@ -116,14 +115,12 @@ DNSKEY::operator=(const DNSKEY& source)
     return (*this);
 }
 
-DNSKEY::~DNSKEY()
-{
+DNSKEY::~DNSKEY() {
     delete impl_;
 }
 
 string
-DNSKEY::toText() const
-{
+DNSKEY::toText() const {
     return (boost::lexical_cast<string>(static_cast<int>(impl_->flags_)) +
         " " + boost::lexical_cast<string>(static_cast<int>(impl_->protocol_)) +
         " " + boost::lexical_cast<string>(static_cast<int>(impl_->algorithm_)) +
@@ -131,8 +128,7 @@ DNSKEY::toText() const
 }
 
 void
-DNSKEY::toWire(OutputBuffer& buffer) const
-{
+DNSKEY::toWire(OutputBuffer& buffer) const {
     buffer.writeUint16(impl_->flags_);
     buffer.writeUint8(impl_->protocol_);
     buffer.writeUint8(impl_->algorithm_);
@@ -140,8 +136,7 @@ DNSKEY::toWire(OutputBuffer& buffer) const
 }
 
 void
-DNSKEY::toWire(MessageRenderer& renderer) const
-{
+DNSKEY::toWire(MessageRenderer& renderer) const {
     renderer.writeUint16(impl_->flags_);
     renderer.writeUint8(impl_->protocol_);
     renderer.writeUint8(impl_->algorithm_);
@@ -149,8 +144,7 @@ DNSKEY::toWire(MessageRenderer& renderer) const
 }
 
 int
-DNSKEY::compare(const Rdata& other) const
-{
+DNSKEY::compare(const Rdata& other) const {
     const DNSKEY& other_dnskey = dynamic_cast<const DNSKEY&>(other);
 
     if (impl_->flags_ != other_dnskey.impl_->flags_) {
@@ -176,8 +170,7 @@ DNSKEY::compare(const Rdata& other) const
 }
 
 uint16_t
-DNSKEY::getTag() const
-{
+DNSKEY::getTag() const {
     if (impl_->algorithm_ == 1) {
         int len = impl_->keydata_.size();
         return ((impl_->keydata_[len - 3] << 8) + impl_->keydata_[len - 2]);

+ 7 - 14
src/lib/dns/rdata/generic/ds_43.cc

@@ -77,8 +77,7 @@ DS::DS(const string& ds_str) :
     impl_ = new DSImpl(tag, algorithm, digest_type, digest);
 }
 
-DS::DS(InputBuffer& buffer, size_t rdata_len)
-{
+DS::DS(InputBuffer& buffer, size_t rdata_len) {
     if (rdata_len < 4) {
         isc_throw(InvalidRdataLength, "DS too short");
     }
@@ -99,8 +98,7 @@ DS::DS(const DS& source) :
 {}
 
 DS&
-DS::operator=(const DS& source)
-{
+DS::operator=(const DS& source) {
     if (impl_ == source.impl_) {
         return (*this);
     }
@@ -112,14 +110,12 @@ DS::operator=(const DS& source)
     return (*this);
 }
 
-DS::~DS()
-{
+DS::~DS() {
     delete impl_;
 }
 
 string
-DS::toText() const
-{
+DS::toText() const {
     using namespace boost;
     return (lexical_cast<string>(static_cast<int>(impl_->tag_)) +
         " " + lexical_cast<string>(static_cast<int>(impl_->algorithm_)) +
@@ -128,8 +124,7 @@ DS::toText() const
 }
 
 void
-DS::toWire(OutputBuffer& buffer) const
-{
+DS::toWire(OutputBuffer& buffer) const {
     buffer.writeUint16(impl_->tag_);
     buffer.writeUint8(impl_->algorithm_);
     buffer.writeUint8(impl_->digest_type_);
@@ -137,8 +132,7 @@ DS::toWire(OutputBuffer& buffer) const
 }
 
 void
-DS::toWire(MessageRenderer& renderer) const
-{
+DS::toWire(MessageRenderer& renderer) const {
     renderer.writeUint16(impl_->tag_);
     renderer.writeUint8(impl_->algorithm_);
     renderer.writeUint8(impl_->digest_type_);
@@ -146,8 +140,7 @@ DS::toWire(MessageRenderer& renderer) const
 }
 
 int
-DS::compare(const Rdata& other) const
-{
+DS::compare(const Rdata& other) const {
     const DS& other_ds = dynamic_cast<const DS&>(other);
 
     if (impl_->tag_ != other_ds.impl_->tag_) {

+ 6 - 12
src/lib/dns/rdata/generic/mx_15.cc

@@ -67,28 +67,24 @@ MX::MX(const MX& other) :
 {}
 
 void
-MX::toWire(OutputBuffer& buffer) const
-{
+MX::toWire(OutputBuffer& buffer) const {
     buffer.writeUint16(preference_);
     mxname_.toWire(buffer);
 }
 
 void
-MX::toWire(MessageRenderer& renderer) const
-{
+MX::toWire(MessageRenderer& renderer) const {
     renderer.writeUint16(preference_);
     renderer.writeName(mxname_);
 }
 
 string
-MX::toText() const
-{
+MX::toText() const {
     return (lexical_cast<string>(preference_) + " " + mxname_.toText());
 }
 
 int
-MX::compare(const Rdata& other) const
-{
+MX::compare(const Rdata& other) const {
     const MX& other_mx = dynamic_cast<const MX&>(other);
 
     if (preference_ < other_mx.preference_) {
@@ -101,14 +97,12 @@ MX::compare(const Rdata& other) const
 }
 
 const Name&
-MX::getMXName() const
-{
+MX::getMXName() const {
     return (mxname_);
 }
 
 uint16_t
-MX::getMXPref() const
-{
+MX::getMXPref() const {
     return (preference_);
 }
 

+ 1 - 1
src/lib/dns/rdata/generic/mx_15.h

@@ -35,7 +35,7 @@ public:
     // BEGIN_COMMON_MEMBERS
     // END_COMMON_MEMBERS
 
-    explicit MX(uint16_t preference, const Name& mxname);
+    MX(uint16_t preference, const Name& mxname);
 
     ///
     /// Specialized methods

+ 5 - 10
src/lib/dns/rdata/generic/ns_2.cc

@@ -45,34 +45,29 @@ NS::NS(const NS& other) :
 {}
 
 void
-NS::toWire(OutputBuffer& buffer) const
-{
+NS::toWire(OutputBuffer& buffer) const {
     nsname_.toWire(buffer);
 }
 
 void
-NS::toWire(MessageRenderer& renderer) const
-{
+NS::toWire(MessageRenderer& renderer) const {
     renderer.writeName(nsname_);
 }
 
 string
-NS::toText() const
-{
+NS::toText() const {
     return (nsname_.toText());
 }
 
 int
-NS::compare(const Rdata& other) const
-{
+NS::compare(const Rdata& other) const {
     const NS& other_ns = dynamic_cast<const NS&>(other);
 
     return (compareNames(nsname_, other_ns.nsname_));
 }
 
 const Name&
-NS::getNSName() const
-{
+NS::getNSName() const {
     return (nsname_);
 }
 

+ 0 - 0
src/lib/dns/rdata/generic/nsec3_50.cc


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