[svn] commit: r2454 - in /branches/trac191: ./ doc/ doc/guide/ src/bin/auth/ src/bin/auth/tests/ src/bin/bind10/ src/bin/bind10/tests/ src/bin/bindctl/ src/bin/bindctl/tests/ src/bin/cmdctl/ src/bin/cmdctl/tests/ src/bin/host/ src/bin/loadzone/ src/bin/loadzone/testdata/ src/bin/loadzone/tests/ src/bin/xfrin/ src/bin/xfrin/tests/ src/bin/xfrout/ src/bin/xfrout/tests/ src/lib/ src/lib/cc/ src/lib/config/ src/lib/config/tests/ src/lib/datasrc/ src/lib/datasrc/tests/ src/lib/datasrc/tests/testdata/ src/lib/dns/ src/lib/dns/python/ src/lib/dns/rdata/generic/ src/lib/dns/tests/ src/lib/python/isc/ src/lib/python/isc/cc/ src/lib/python/isc/cc/tests/ src/lib/python/isc/config/ src/lib/python/isc/config/tests/ src/lib/python/isc/datasrc/ src/lib/python/isc/dns/ src/lib/python/isc/log/ src/lib/xfr/ tools/

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Jul 9 10:04:59 UTC 2010


Author: naokikambe
Date: Fri Jul  9 10:04:58 2010
New Revision: 2454

Log:
merge from trunk head

Added:
    branches/trac191/src/bin/bind10/tests/args_test.py
      - copied unchanged from r2453, trunk/src/bin/bind10/tests/args_test.py
    branches/trac191/src/bin/cmdctl/cmdctl.spec.pre.in
      - copied unchanged from r2453, trunk/src/bin/cmdctl/cmdctl.spec.pre.in
    branches/trac191/src/bin/loadzone/tests/
      - copied from r2453, trunk/src/bin/loadzone/tests/
    branches/trac191/src/lib/datasrc/cache.cc
      - copied unchanged from r2453, trunk/src/lib/datasrc/cache.cc
    branches/trac191/src/lib/datasrc/cache.h
      - copied unchanged from r2453, trunk/src/lib/datasrc/cache.h
    branches/trac191/src/lib/datasrc/tests/cache_unittest.cc
      - copied unchanged from r2453, trunk/src/lib/datasrc/tests/cache_unittest.cc
    branches/trac191/src/lib/datasrc/tests/testdata/q_example_dnskey
      - copied unchanged from r2453, trunk/src/lib/datasrc/tests/testdata/q_example_dnskey
    branches/trac191/src/lib/dns/python/
      - copied from r2453, trunk/src/lib/dns/python/
    branches/trac191/src/lib/python/isc/cc/tests/message_test.py
      - copied unchanged from r2453, trunk/src/lib/python/isc/cc/tests/message_test.py
    branches/trac191/src/lib/python/isc/dns/
      - copied from r2453, trunk/src/lib/python/isc/dns/
    branches/trac191/src/lib/python/isc/log/
      - copied from r2453, trunk/src/lib/python/isc/log/
    branches/trac191/src/lib/xfr/fdshare_python.cc
      - copied unchanged from r2453, trunk/src/lib/xfr/fdshare_python.cc
Removed:
    branches/trac191/src/bin/bindctl/bindctl.pem
    branches/trac191/src/bin/cmdctl/cmdctl.spec
    branches/trac191/src/bin/loadzone/testdata/
    branches/trac191/src/lib/dns/python_dns.cc
    branches/trac191/src/lib/python/isc/cc/tests/test.py
Modified:
    branches/trac191/   (props changed)
    branches/trac191/ChangeLog
    branches/trac191/README
    branches/trac191/configure.ac
    branches/trac191/doc/Doxyfile
    branches/trac191/doc/guide/bind10-guide.html
    branches/trac191/doc/guide/bind10-guide.xml
    branches/trac191/src/bin/auth/Makefile.am
    branches/trac191/src/bin/auth/asio_link.cc
    branches/trac191/src/bin/auth/asio_link.h
    branches/trac191/src/bin/auth/auth_srv.cc
    branches/trac191/src/bin/auth/auth_srv.h
    branches/trac191/src/bin/auth/main.cc
    branches/trac191/src/bin/auth/tests/Makefile.am
    branches/trac191/src/bin/auth/tests/auth_srv_unittest.cc
    branches/trac191/src/bin/bind10/bind10.py.in
    branches/trac191/src/bin/bind10/run_bind10.sh.in
    branches/trac191/src/bin/bind10/tests/bind10_test.in
    branches/trac191/src/bin/bind10/tests/bind10_test.py
    branches/trac191/src/bin/bindctl/Makefile.am
    branches/trac191/src/bin/bindctl/TODO
    branches/trac191/src/bin/bindctl/bindcmd.py
    branches/trac191/src/bin/bindctl/bindctl-source.py.in
    branches/trac191/src/bin/bindctl/cmdparse.py
    branches/trac191/src/bin/bindctl/exception.py
    branches/trac191/src/bin/bindctl/tests/bindctl_test.py
    branches/trac191/src/bin/cmdctl/Makefile.am
    branches/trac191/src/bin/cmdctl/TODO
    branches/trac191/src/bin/cmdctl/cmdctl.py.in
    branches/trac191/src/bin/cmdctl/tests/Makefile.am
    branches/trac191/src/bin/cmdctl/tests/cmdctl_test.in
    branches/trac191/src/bin/cmdctl/tests/cmdctl_test.py
    branches/trac191/src/bin/host/host.cc
    branches/trac191/src/bin/loadzone/Makefile.am
    branches/trac191/src/bin/loadzone/b10-loadzone.py.in
    branches/trac191/src/bin/loadzone/run_loadzone.sh.in
    branches/trac191/src/bin/xfrin/   (props changed)
    branches/trac191/src/bin/xfrin/tests/Makefile.am
    branches/trac191/src/bin/xfrin/tests/xfrin_test.py
    branches/trac191/src/bin/xfrin/xfrin.py.in
    branches/trac191/src/bin/xfrout/run_b10-xfrout.sh.in
    branches/trac191/src/bin/xfrout/tests/Makefile.am
    branches/trac191/src/bin/xfrout/tests/xfrout_test.py
    branches/trac191/src/bin/xfrout/xfrout.py.in
    branches/trac191/src/bin/xfrout/xfrout.spec.pre.in
    branches/trac191/src/lib/Makefile.am
    branches/trac191/src/lib/cc/   (props changed)
    branches/trac191/src/lib/cc/Makefile.am
    branches/trac191/src/lib/cc/data.cc
    branches/trac191/src/lib/cc/data.h
    branches/trac191/src/lib/cc/data_unittests.cc
    branches/trac191/src/lib/cc/session.cc
    branches/trac191/src/lib/cc/session_unittests.cc
    branches/trac191/src/lib/config/ccsession.cc
    branches/trac191/src/lib/config/config_data.cc
    branches/trac191/src/lib/config/config_data.h
    branches/trac191/src/lib/config/module_spec.cc
    branches/trac191/src/lib/config/module_spec.h
    branches/trac191/src/lib/config/tests/Makefile.am
    branches/trac191/src/lib/config/tests/ccsession_unittests.cc
    branches/trac191/src/lib/config/tests/config_data_unittests.cc
    branches/trac191/src/lib/config/tests/fake_session.cc
    branches/trac191/src/lib/config/tests/fake_session.h
    branches/trac191/src/lib/config/tests/module_spec_unittests.cc
    branches/trac191/src/lib/datasrc/   (props changed)
    branches/trac191/src/lib/datasrc/Makefile.am
    branches/trac191/src/lib/datasrc/data_source.cc
    branches/trac191/src/lib/datasrc/data_source.h
    branches/trac191/src/lib/datasrc/query.cc
    branches/trac191/src/lib/datasrc/query.h
    branches/trac191/src/lib/datasrc/sqlite3_datasrc.cc
    branches/trac191/src/lib/datasrc/sqlite3_datasrc.h
    branches/trac191/src/lib/datasrc/static_datasrc.cc
    branches/trac191/src/lib/datasrc/static_datasrc.h
    branches/trac191/src/lib/datasrc/tests/Makefile.am
    branches/trac191/src/lib/datasrc/tests/datasrc_unittest.cc
    branches/trac191/src/lib/datasrc/tests/query_unittest.cc
    branches/trac191/src/lib/datasrc/tests/sqlite3_unittest.cc
    branches/trac191/src/lib/datasrc/tests/static_unittest.cc
    branches/trac191/src/lib/datasrc/tests/test_datasrc.cc
    branches/trac191/src/lib/datasrc/tests/test_datasrc.h
    branches/trac191/src/lib/dns/   (props changed)
    branches/trac191/src/lib/dns/Makefile.am
    branches/trac191/src/lib/dns/message.cc
    branches/trac191/src/lib/dns/message.h
    branches/trac191/src/lib/dns/question.h
    branches/trac191/src/lib/dns/rdata/generic/rrsig_46.cc   (props changed)
    branches/trac191/src/lib/dns/rrsetlist.cc
    branches/trac191/src/lib/dns/rrsetlist.h
    branches/trac191/src/lib/dns/tests/   (props changed)
    branches/trac191/src/lib/dns/tests/base64_unittest.cc
    branches/trac191/src/lib/dns/tests/question_unittest.cc
    branches/trac191/src/lib/dns/tests/rrsetlist_unittest.cc
    branches/trac191/src/lib/python/isc/Makefile.am
    branches/trac191/src/lib/python/isc/__init__.py
    branches/trac191/src/lib/python/isc/cc/message.py
    branches/trac191/src/lib/python/isc/cc/tests/Makefile.am
    branches/trac191/src/lib/python/isc/cc/tests/session_test.py
    branches/trac191/src/lib/python/isc/config/ccsession.py
    branches/trac191/src/lib/python/isc/config/cfgmgr.py
    branches/trac191/src/lib/python/isc/config/tests/ccsession_test.py
    branches/trac191/src/lib/python/isc/config/tests/cfgmgr_test.py
    branches/trac191/src/lib/python/isc/config/tests/unittest_fakesession.py
    branches/trac191/src/lib/python/isc/datasrc/master.py
    branches/trac191/src/lib/xfr/Makefile.am
    branches/trac191/src/lib/xfr/fd_share.cc
    branches/trac191/src/lib/xfr/fd_share.h
    branches/trac191/src/lib/xfr/python_xfr.cc
    branches/trac191/src/lib/xfr/xfrout_client.cc
    branches/trac191/tools/import_boost.sh

Modified: branches/trac191/ChangeLog
==============================================================================
--- branches/trac191/ChangeLog (original)
+++ branches/trac191/ChangeLog Fri Jul  9 10:04:58 2010
@@ -1,3 +1,103 @@
+  73.	[bug]		jelte
+  	Fixed a bug where in bindctl, locally changed settings were
+	reset when the list of running modules is updated. (Trac #285,
+	r2452)
+
+  72.	[build]		jinmei
+	Added -R when linking python wrapper modules to libpython when
+	possible.  This helps build BIND 10 on platforms that install
+	libpython whose path is unknown to run-time loader.  NetBSD is a
+	known such platform. (Trac #148, r2427)
+
+  71.  [func]		each
+  	Add "-a" (address) option to bind10 to specify an address for
+	the auth server to listen on.
+
+  70.  [func]		each
+  	Added a hot-spot cache to libdatasrc to speed up access to
+	repeatedly-queried data and reduce the number of queries to
+	the underlying database; this should substantially improve
+	performance.  Also added a "-n" ("no cache") option to
+	bind10 and b10-auth to disable the cache if needed.
+	(Trac #192, svn r2383)
+
+bind10-devel-20100701 released on July 1, 2010
+
+  69.  [func]*		jelte
+	Added python wrappers for libdns++ (isc::dns), and libxfr. This
+	removes the dependency on Boost.Python. The wrappers don't
+	completely implement all functionality, but the high-level API
+	is wrapped, and current modules use it now.
+	(Trac #181, svn r2361)
+
+  68.  [func]		zhanglikun
+	Add options -c(--certificate-chain) to bindctl. Override class
+	HTTPSConnection to support server certificate validation.
+	Add support to cmdctl.spec file, now there are three configurable 
+	items for cmdctl: 'key_file', 'cert_file' and 'accounts_file', 
+	all of them can be changed in runtime.
+	(Trac #127, svn r2357)
+
+  67.  [func]		zhanglikun
+	Make bindctl's command parser only do minimal check.
+	Parameter value can be a sequence of non-space characters,
+	or a string surrounded by quotation marks (these marks can
+	be a part of the value string in escaped form). Make error
+	message be more friendly. (If there is some error in
+	parameter's value, the parameter name will be provided).
+	Refactor function login_to_cmdctl() in class BindCmdInterpreter:
+	avoid using Exception to catch all exceptions.
+	(Trac #220, svn r2356)
+
+  66.  [bug]		each
+	Check for duplicate RRsets before inserting data into a message
+	section; this, among other things, will prevent multiple copies
+	of the same CNAME from showing up when there's a loop.  (Trac #69,
+	svn r2350)
+    
+  65.  [func]		shentingting
+	Various loadzone improvements: allow optional comment for
+	$TTL, allow optional origin and comment for $INCLUDE, allow
+	optional comment for $ORIGIN, support BIND9 extension of
+	time units for TTLs, and fix bug to not use class as part
+	of label name when records don't have a label but do have
+	a class.  Added verbose options to exactly what is happening
+	with loadzone.  Added loadzone test suite of different file
+	formats to load.
+	(Trac #197, #199, #244, #161, #198, #174, #175, svn r2340)
+
+  64.  [func]		jerry
+	Added python logging framework. It is for testing and
+	experimenting with logging ideas. Currently, it supports
+	three channels (file, syslog and stderr) and five levels
+	(debug, info, warning, error and critical).
+	(Trac #176, svn r2338)
+
+  63.  [func]		shane
+	Added initial support for setuid(), using the "-u" flag. This will
+	be replaced in the future, but for now provides a reasonable 
+	starting point.
+	(Trac #180, svn r2330)
+
+  62.  [func]		jelte
+	bin/xfrin: Use the database_file as configured in Auth to transfers
+	bin/xfrout: Use the database_file as configured in Auth to transfers
+
+  61.  [bug]		jelte
+	bin/auth: Enable b10-auth to be launched in source tree
+	(i.e. use a zone database file relative to that)
+
+  60.	[build]		jinmei
+	Supported SunStudio C++ compiler.  Note: gtest still doesn't work.
+	(Trac #251, svn r2310)
+
+  59.	[bug]		jinmei
+	lib/datasrc,bin/auth: The authoritative server could return a
+	SERVFAIL with a partial answer if it finds a data source broken
+	while looking for an answer.  This can happen, for example, if a
+	zone that doesn't have an NS RR is configured and loaded as a
+	sqlite3 data source. (Trac #249, r2286)
+
   58.	[bug]		jinmei
 	Worked around an interaction issue between ASIO and standard C++
 	library headers.  Without this ASIO didn't work: sometimes the
@@ -40,7 +140,7 @@
 
 bind10-devel-20100602 released on June 2, 2010
 
-  51.   [build]         jelte
+  51.   [build]		jelte
 	lib/python: Add bind10_config.py module for paths and
 	possibly other configure-time variables. Allow some components
 	to find spec files in build tree when ran from source.
@@ -156,7 +256,7 @@
 
   24.	[func]
 	Support case-sensitive name compression in MessageRenderer.
-	(svn r1704)
+	(Trac #142, svn r1704)
 
   23.	[func]
 	Support a simple name with possible compression. (svn r1701)

Modified: branches/trac191/README
==============================================================================
--- branches/trac191/README (original)
+++ branches/trac191/README Fri Jul  9 10:04:58 2010
@@ -3,21 +3,21 @@
 BIND is the popular implementation of a DNS server, developer
 interfaces, and DNS tools. BIND 10 is a rewrite of BIND 9. BIND 10
 is written in C++ and Python and provides a modular environment
-for serving and maintaining DNS.
+for serving, maintaining, and developing DNS.
 
 BIND10-devel is new development leading up to the production
 BIND 10 release. It contains prototype code and experimental
 interfaces. Nevertheless it is ready to use now for testing the
-new BIND 10 infrastructure ideas. The Year 1 (Y1) deliverable of
-the five year plan is described here:
-
-        http://bind10.isc.org/wiki/Year1Deliverable
+new BIND 10 infrastructure ideas. The Year 2 milestones of the
+five year plan are described here:
+
+	https://bind10.isc.org/wiki/Year2Milestones
 
 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.
+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/

Modified: branches/trac191/configure.ac
==============================================================================
--- branches/trac191/configure.ac (original)
+++ branches/trac191/configure.ac Fri Jul  9 10:04:58 2010
@@ -2,18 +2,44 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20100602, bind10-dev at isc.org)
+AC_INIT(bind10-devel, 20100701, bind10-dev at isc.org)
 AC_CONFIG_SRCDIR(README)
 AM_INIT_AUTOMAKE
 AC_CONFIG_HEADERS([config.h])
 
 # Checks for programs.
 AC_PROG_CXX
-AC_PROG_CC
 AC_PROG_LIBTOOL
 
 # Use C++ language
-AC_LANG_CPLUSPLUS
+AC_LANG([C++])
+
+# Identify the compiler: this check must be after AC_PROG_CXX and AC_LANG.
+AM_CONDITIONAL(USE_GXX, test "X${GXX}" = "Xyes")
+AC_CHECK_DECL([__SUNPRO_CC], [SUNCXX="yes"], [SUNCXX="no"])
+
+# Linker options
+
+# check -R rather than gcc specific -rpath to be as portable as possible.
+AC_MSG_CHECKING([whether -R flag is available in linker])
+LDFLAGS_SAVED="$LDFLAGS"
+LDFLAGS="$LDFLAGS -R/usr/lib"
+AC_TRY_LINK([],[],
+	[ AC_MSG_RESULT(yes)
+		rpath_available=yes
+	],[ AC_MSG_RESULT(no)
+	rpath_available=no
+	])
+LDFLAGS=$LDFLAGS_SAVED
+
+# OS dependent compiler flags
+case "$host" in
+*-solaris*)
+	# Solaris requires special definitions to get some standard libraries
+	# (e.g. getopt(3)) available with common used header files.
+	CPPFLAGS="$CPPFLAGS -D_XPG4_2 -D__EXTENSIONS__"
+	;;
+esac
 
 m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3 python3.1])
 AC_ARG_WITH([pythonpath],
@@ -71,11 +97,29 @@
 		AC_MSG_WARN([${PYTHON}-config does not exist or is not executable, so we could not detect python development environment.  Your system may require an additional package (e.g. "python3-dev").  Alternatively, if you are sure you have python headers and libraries, define PYTHON_INCLUDES and PYTHON_LDFLAGS and run this script.])
 	fi
 fi
+
+# Some OSes including NetBSD don't install libpython.so in a well known path.
+# To avoid requiring dynamic library path with our python wrapper loadable
+# modules, we embed the path to the modules when possible.  We do this even
+# when the path is known in the common operational environment (e.g. when
+# it's stored in a common "hint" file) for simplicity.
+if test $rpath_available = yes; then
+	python_rpath=
+	for flag in ${PYTHON_LDFLAGS}; do
+		python_rpath="${python_rpath} `echo $flag | sed -ne 's/^\(\-L\)/-R/p'`"
+	done
+	PYTHON_LDFLAGS="${PYTHON_LDFLAGS} ${python_rpath}"
+fi
+
 AC_SUBST(PYTHON_INCLUDES)
 AC_SUBST(PYTHON_LDFLAGS)
 
-# Check for python library (not absolutely mandatory, but needed for
-# Boost.Python when we use it.  See below.)
+CPPFLAGS_SAVED="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS ${PYTHON_INCLUDES}"
+AC_CHECK_HEADERS([Python.h],, AC_MSG_ERROR([Missing Python.h]))
+CPPFLAGS="$CPPFLAGS_SAVED"
+
+# Check for python library.  Needed for Python-wrapper libraries.
 LDFLAGS_SAVED="$LDFLAGS"
 LDFLAGS="$LDFLAGS $PYTHON_LDFLAGS"
 python_bin="python${PYTHON_VERSION}"
@@ -84,11 +128,12 @@
 	PYTHON_LIB="-l$python_lib"
 fi
 AC_SUBST(PYTHON_LIB)
+LDFLAGS=$LDFLAGS_SAVED
 
 # TODO: check for _sqlite3.py module
 
-#
-# B10_CXXFLAGS is the default C++ compiler flags.  This will (and should) be
+# Compiler dependent settings: define some mandatory CXXFLAGS here.
+# We also use a separate variable B10_CXXFLAGS.  This will (and should) be
 # used as the default value for each specifc AM_CXXFLAGS:
 # AM_CXXFLAGS = $(B10_CXXFLAGS)
 # AM_CXXFLAGS += ... # add module specific flags
@@ -97,17 +142,24 @@
 # gcc's -Wno-XXX option must be specified after -Wall or -Wextra, we cannot
 # specify the default warning flags in CXXFLAGS and let specific modules
 # "override" the default.
-#
-B10_CXXFLAGS=
-
-if test "X$GCC" = "Xyes"; then
-B10_CXXFLAGS="-g -Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
+
+CXXFLAGS=-g
+werror_ok=0
+
+# SunStudio compiler requires special compiler options for boost
+# (http://blogs.sun.com/sga/entry/boost_mini_howto)
+if test "$SUNCXX" = "yes"; then
+CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic"
+fi
+
+# gcc specific settings:
+if test "X$GXX" = "Xyes"; then
+B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
 UNUSED_PARAM_ATTRIBUTE='__attribute__((unused))'
 
 # Certain versions of gcc (g++) have a bug that incorrectly warns about
 # the use of anonymous name spaces even if they're closed in a single
 # translation unit.  For these versions we have to disable -Werror.
-werror_ok=0
 CXXFLAGS_SAVED="$CXXFLAGS"
 CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror"
 AC_MSG_CHECKING(for in-TU anonymous namespace breakage)
@@ -118,13 +170,14 @@
 	 B10_CXXFLAGS="$B10_CXXFLAGS -Werror"],
 	[AC_MSG_RESULT(yes)])
 CXXFLAGS="$CXXFLAGS_SAVED"
-fi				dnl GCC = yes
+
+fi				dnl GXX = yes
 
 AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1)
 AC_DEFINE_UNQUOTED(UNUSED_PARAM, $UNUSED_PARAM_ATTRIBUTE, Define to compiler keyword indicating a function argument is intentionally unused)
 
 # produce PIC unless we disable shared libraries. need this for python bindings.
-if test $enable_shared != "no" -a "X$GCC" = "Xyes"; then
+if test $enable_shared != "no" -a "X$GXX" = "Xyes"; then
    B10_CXXFLAGS="$B10_CXXFLAGS -fPIC"
 fi
 
@@ -210,54 +263,6 @@
    BOOST_LDFLAGS="-L$withval"
    fi])
 AC_SUBST(BOOST_LDFLAGS)
-
-# Check availability of the Boost Python library
-
-AC_MSG_CHECKING([for boost::python library])
-AC_ARG_WITH([boost-python],
-AC_HELP_STRING([--with-boost-python],
-  [specify whether to use the boost python library]),
-  [with_boost_python="$withval"], [with_boost_python="auto"])
-if test "$with_boost_python" != "no"; then
-	if test "$with_boost_python" != "auto" -a "X$PYTHON_LIB" = X; then
-		AC_MSG_ERROR([Boost.Python requested but python library is not available])
-	fi
-	LDFLAGS_SAVED="$LDFLAGS"
-	LIBS_SAVED="$LIBS"
-	CPPFLAGS_SAVED="$CPPFLAGS"
-	CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
-
-	for BOOST_TRY_LIB in boost_python boost_python-mt; do
-		LDFLAGS="$LDFLAGS_SAVED ${BOOST_LDFLAGS} ${PYTHON_LDFLAGS}"
-		LIBS="$LIBS_SAVED -l${BOOST_TRY_LIB} ${PYTHON_LIB}"
-		AC_TRY_LINK([#include <boost/python/module.hpp>
-	      using namespace boost::python;
-	      BOOST_PYTHON_MODULE(test) { throw "Boost::Python test."; }],
-			[ return 0; ],
-			[ AC_MSG_RESULT(yes)
-			BOOST_PYTHON_LIB="-l${BOOST_TRY_LIB}"
-			],[])
-		if test "X${BOOST_PYTHON_LIB}" != X; then
-        		break
-		fi
-	done
-
-	LDFLAGS="$LDFLAGS_SAVED"
-	CPPFLAGS="$CPPFLAGS_SAVED"
-	LIBS="$LIBS_SAVED"
-fi
-
-if test "X${BOOST_PYTHON_LIB}" = X; then
-	AC_MSG_RESULT(no)
-	if test "$with_boost_python" = "yes"; then
-	   AC_MSG_ERROR([boost python library is requested but not found])
-	fi
-else
-	AC_DEFINE(HAVE_BOOST_PYTHON, 1, Define to 1 if boost python library is available)
-fi
-
-AM_CONDITIONAL(HAVE_BOOST_PYTHON, test "X${BOOST_PYTHON_LIB}" != X)
-AC_SUBST(BOOST_PYTHON_LIB)
 
 #
 # Check availability of gtest, which will be used for unit tests.
@@ -364,7 +369,7 @@
 # run time performance.  Hpefully we can find a better solution or the ASIO
 # code will be updated by the time we really need it.
 AC_CHECK_HEADERS(sys/devpoll.h, ac_cv_have_devpoll=yes, ac_cv_have_devpoll=no)
-if test "X$ac_cv_have_devpoll" = "Xyes" -a "X$GCC" = "Xyes"; then
+if test "X$ac_cv_have_devpoll" = "Xyes" -a "X$GXX" = "Xyes"; then
 	CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_DEV_POLL=1"
 fi
 
@@ -396,6 +401,8 @@
                  src/bin/cfgmgr/tests/Makefile
                  src/bin/host/Makefile
                  src/bin/loadzone/Makefile
+                 src/bin/loadzone/tests/correct/Makefile
+                 src/bin/loadzone/tests/error/Makefile
                  src/bin/msgq/Makefile
                  src/bin/msgq/tests/Makefile
                  src/bin/auth/Makefile
@@ -416,10 +423,14 @@
                  src/lib/python/isc/cc/tests/Makefile
                  src/lib/python/isc/config/Makefile
                  src/lib/python/isc/config/tests/Makefile
+                 src/lib/python/isc/log/Makefile
+                 src/lib/python/isc/log/tests/Makefile
                  src/lib/config/Makefile
                  src/lib/config/tests/Makefile
                  src/lib/dns/Makefile
                  src/lib/dns/tests/Makefile
+                 src/lib/dns/python/Makefile
+                 src/lib/dns/python/tests/Makefile
                  src/lib/exceptions/Makefile
                  src/lib/datasrc/Makefile
                  src/lib/datasrc/tests/Makefile
@@ -430,6 +441,7 @@
            src/bin/cmdctl/cmdctl.py
            src/bin/cmdctl/run_b10-cmdctl.sh
            src/bin/cmdctl/tests/cmdctl_test
+           src/bin/cmdctl/cmdctl.spec.pre
            src/bin/xfrin/tests/xfrin_test
            src/bin/xfrin/xfrin.py
            src/bin/xfrin/xfrin.spec.pre
@@ -451,6 +463,8 @@
            src/bin/bindctl/bindctl-source.py
            src/bin/bindctl/tests/bindctl_test
            src/bin/loadzone/run_loadzone.sh
+           src/bin/loadzone/tests/correct/correct_test.sh
+           src/bin/loadzone/tests/error/error_test.sh
            src/bin/loadzone/b10-loadzone.py
            src/bin/usermgr/run_b10-cmdctl-usermgr.sh
            src/bin/usermgr/b10-cmdctl-usermgr.py
@@ -462,6 +476,7 @@
            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
+           src/lib/python/isc/log/tests/log_test
            src/lib/dns/gen-rdatacode.py
            src/lib/python/bind10_config.py
            src/lib/dns/tests/testdata/gen-wiredata.py
@@ -480,6 +495,8 @@
            chmod +x src/bin/bindctl/tests/bindctl_test
            chmod +x src/bin/bindctl/run_bindctl.sh
            chmod +x src/bin/loadzone/run_loadzone.sh
+           chmod +x src/bin/loadzone/tests/correct/correct_test.sh
+           chmod +x src/bin/loadzone/tests/error/error_test.sh
            chmod +x src/bin/usermgr/run_b10-cmdctl-usermgr.sh
            chmod +x src/bin/msgq/run_msgq.sh
            chmod +x src/bin/msgq/tests/msgq_test
@@ -507,8 +524,10 @@
   CXXFLAGS:      $CXXFLAGS
   B10_CXXFLAGS:  $B10_CXXFLAGS
 dnl includes too
-  Boost Python:  $BOOST_PYTHON_LIB
-  SQLite:	 $SQLITE_CFLAGS
+  Python:        ${PYTHON_INCLUDES}
+                 ${PYTHON_LDFLAGS}
+                 ${PYTHON_LIB}
+  SQLite:        $SQLITE_CFLAGS
                  $SQLITE_LIBS
 
 Features:

Modified: branches/trac191/doc/Doxyfile
==============================================================================
--- branches/trac191/doc/Doxyfile (original)
+++ branches/trac191/doc/Doxyfile Fri Jul  9 10:04:58 2010
@@ -568,7 +568,7 @@
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions
+INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

Modified: branches/trac191/doc/guide/bind10-guide.html
==============================================================================
--- branches/trac191/doc/guide/bind10-guide.html (original)
+++ branches/trac191/doc/guide/bind10-guide.html Fri Jul  9 10:04:58 2010
@@ -2,7 +2,7 @@
         The most up-to-date version of this document, along with other documents
         for BIND 10, can be found at
         <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.
-      </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230342718">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230342746">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230328220">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230328405">Download Tar File</a></span></dt><dt><s
 pan class="section"><a href="#id1168230327606">Retrieve from Subversion</a></span></dt><dt><span class="section"><a href="#id1168230327666">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230328711">Build</a></span></dt><dt><span class="section"><a href="#id1168230328726">Install</a></span></dt><dt><span class="section"><a href="#id1168230328757">Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a href="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specifi
 cation for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230329323">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230329388">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230329419">Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230342718">S
 upported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230342746">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></div><p>
+      </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230342718">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230342746">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230328220">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230328405">Download Tar File</a></span></dt><dt><s
 pan class="section"><a href="#id1168230328425">Retrieve from Subversion</a></span></dt><dt><span class="section"><a href="#id1168230328485">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230328596">Build</a></span></dt><dt><span class="section"><a href="#id1168230328611">Install</a></span></dt><dt><span class="section"><a href="#id1168230328642">Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a href="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specifi
 cation for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230329208">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230329273">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230329303">Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230342718">S
 upported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230342746">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></div><p>
       BIND is the popular implementation of a DNS server, developer
       interfaces, and DNS tools.
       BIND 10 is a rewrite of BIND 9.  BIND 10 is written in C++ and Python
@@ -33,7 +33,7 @@
 	requires SQLite 3.3.9 or newer.
         The <span class="command"><strong>b10-xfrin</strong></span> and <span class="command"><strong>b10-xfrout</strong></span>
 	modules require the libboost library,
-        Boost Python library, libpython3 library,
+        libpython3 library,
 	and the Python _sqlite3.so module.
       </p></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
           Some operating systems do not provide these dependencies
@@ -117,7 +117,7 @@
       and, of course, DNS. These include detailed developer
       documentation and code examples.
 
-    </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230328220">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230328405">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230327606">Retrieve from Subversion</a></span></dt><dt><span class="section"><a href="#id1168230327666">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230328711">Build</a></span></dt><dt><span class="section"><a href="#id1168230328726">Install</a></span></dt><dt><span class="section"><a href="#id1168230328757">Install
  Hierarchy</a></span></dt></dl></dd></dl></div><div class="section" title="Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230328220"></a>Building Requirements</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+    </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230328220">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230328405">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230328425">Retrieve from Subversion</a></span></dt><dt><span class="section"><a href="#id1168230328485">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230328596">Build</a></span></dt><dt><span class="section"><a href="#id1168230328611">Install</a></span></dt><dt><span class="section"><a href="#id1168230328642">Install
  Hierarchy</a></span></dt></dl></dd></dl></div><div class="section" title="Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230328220"></a>Building Requirements</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
             Some operating systems have split their distribution packages into
             a run-time and a development package.  You will need to install
             the development package versions, which include header files and
@@ -128,7 +128,7 @@
   
   
         </p><p>
-	  The Boost Library, Boost Python Library, Python Library,
+	  The Boost Library, Python Library,
 	  and Python _sqlite3 module are required to enable the
 	  Xfrout and Xfrin support.
         </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
@@ -136,7 +136,7 @@
           for Python 3.1.
         </p></div><p>
           Building BIND 10 also requires a C++ compiler and
-          standard development headers.
+          standard development headers, make, and pkg-config.
           BIND 10 builds have been tested with GCC g++ 3.4.3, 4.1.2,
           4.1.3, 4.2.1, 4.3.2, and 4.4.1.
         </p></div><div class="section" title="Quick start"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="quickstart"></a>Quick start</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
@@ -185,7 +185,7 @@
           The BIND 10 releases are available as tar file downloads from
           <a class="ulink" href="ftp://ftp.isc.org/isc/bind10/" target="_top">ftp://ftp.isc.org/isc/bind10/</a>.
           Periodic development snapshots may also be available.
-        </p></div><div class="section" title="Retrieve from Subversion"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230327606"></a>Retrieve from Subversion</h3></div></div></div><p>
+        </p></div><div class="section" title="Retrieve from Subversion"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230328425"></a>Retrieve from Subversion</h3></div></div></div><p>
           Downloading this "bleeding edge" code is recommended only for
           developers or advanced users.  Using development code in a production
           environment is not recommended.
@@ -217,7 +217,7 @@
           <span class="command"><strong>autoheader</strong></span>,
           <span class="command"><strong>automake</strong></span>,
           and related commands.
-        </p></div><div class="section" title="Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230327666"></a>Configure before the build</h3></div></div></div><p>
+        </p></div><div class="section" title="Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230328485"></a>Configure before the build</h3></div></div></div><p>
           BIND 10 uses the GNU Build System to discover build environment
           details.
           To generate the makefiles using the defaults, simply run:
@@ -230,7 +230,6 @@
                 default is <code class="filename">/usr/local/</code>).
               </dd><dt><span class="term">--with-boost-include</span></dt><dd>Define the path to find the Boost headers.
               </dd><dt><span class="term">--with-boost-lib</span></dt><dd>Define the path to find the Boost library.
-              </dd><dt><span class="term">--with-boost-python</span></dt><dd>Define to use the Boost Python library.
               </dd><dt><span class="term">--with-pythonpath</span></dt><dd>Define the path to Python 3.1 if it is not in the
                 standard execution path.
               </dd><dt><span class="term">--with-gtest</span></dt><dd>Enable building the C++ Unit Tests using the
@@ -239,25 +238,23 @@
               </dd></dl></div><p>
 
         </p><p>
-          For example, the following configures it
-    build with Boost Python support (for Python DNS library),
+          For example, the following configures it to
     find the Boost headers and library, find the
     Python interpreter, and sets the installation location:
 
           </p><pre class="screen">$ <strong class="userinput"><code>./configure --with-boost-lib=/usr/pkg/lib \
       --with-boost-include=/usr/pkg/include \
-      --with-boost-python \
       --with-pythonpath=/usr/pkg/bin/python3.1 \
       --prefix=/opt/bind10</code></strong></pre><p>
         </p><p>
           If the configure fails, it may be due to missing or old
           dependencies.
-        </p></div><div class="section" title="Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230328711"></a>Build</h3></div></div></div><p>
+        </p></div><div class="section" title="Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230328596"></a>Build</h3></div></div></div><p>
     After the configure step is complete, to build the executables
     from the C++ code and prepare the Python scripts, run:
 
           </p><pre class="screen">$ <strong class="userinput"><code>make</code></strong></pre><p>
-        </p></div><div class="section" title="Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230328726"></a>Install</h3></div></div></div><p>
+        </p></div><div class="section" title="Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230328611"></a>Install</h3></div></div></div><p>
           To install the BIND 10 executables, support files,
           and documentation, run:
           </p><pre class="screen">$ <strong class="userinput"><code>make install</code></strong></pre><p>
@@ -266,7 +263,7 @@
 	  Python and Python shared libraries, you may need to
 	  configure your run-time linker to find them (such as
 	  setting LD_LIBRARY_PATH).
-        </p></div></div><div class="section" title="Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230328757"></a>Install Hierarchy</h3></div></div></div><p>
+        </p></div></div><div class="section" title="Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230328642"></a>Install Hierarchy</h3></div></div></div><p>
           The following is the layout of the complete BIND 10 installation:
           </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
                 <code class="filename">bin/</code> —
@@ -478,7 +475,7 @@
       the details and relays (over a <span class="command"><strong>b10-msgq</strong></span> command
       channel) the configuration on to the specified module.
     </p><p>
-    </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230329323">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230329388">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230329419">Loading Master Zones Files</a></span></dt></dl></div><p>
+    </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230329208">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230329273">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230329303">Loading Master Zones Files</a></span></dt></dl></div><p>
       The <span class="command"><strong>b10-auth</strong></span> is the authoritative DNS server.
       It supports EDNS0 and DNSSEC. It supports IPv6.
       Normally it is started by the <span class="command"><strong>bind10</strong></span> master
@@ -486,7 +483,7 @@
     </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
       This development prototype release listens on all interfaces
       and the non-standard port 5300.
-    </p></div><div class="section" title="Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230329323"></a>Server Configurations</h2></div></div></div><p>
+    </p></div><div class="section" title="Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230329208"></a>Server Configurations</h2></div></div></div><p>
         <span class="command"><strong>b10-auth</strong></span> is configured via the
         <span class="command"><strong>b10-cfgmgr</strong></span> configuration manager.
         The module name is <span class="quote">“<span class="quote">Auth</span>”</span>.
@@ -506,7 +503,7 @@
         </p><div class="variablelist"><dl><dt><span class="term">shutdown</span></dt><dd>Stop the authoritative DNS server.
               </dd></dl></div><p>
 
-      </p></div><div class="section" title="Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230329388"></a>Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+      </p></div><div class="section" title="Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230329273"></a>Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
         For the development prototype release, <span class="command"><strong>b10-auth</strong></span>
         only supports the SQLite3 data source backend.
         Upcoming versions will be able to use multiple different
@@ -519,7 +516,7 @@
         The default is <code class="filename">/usr/local/var/</code>.)
   This data file location may be changed by defining the
   <span class="quote">“<span class="quote">database_file</span>”</span> configuration.
-      </p></div><div class="section" title="Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230329419"></a>Loading Master Zones Files</h2></div></div></div><p>
+      </p></div><div class="section" title="Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230329303"></a>Loading Master Zones Files</h2></div></div></div><p>
         RFC 1035 style DNS master zone files may imported
         into a BIND 10 data source by using the
         <span class="command"><strong>b10-loadzone</strong></span> utility.

Modified: branches/trac191/doc/guide/bind10-guide.xml
==============================================================================
--- branches/trac191/doc/guide/bind10-guide.xml (original)
+++ branches/trac191/doc/guide/bind10-guide.xml Fri Jul  9 10:04:58 2010
@@ -76,7 +76,7 @@
 	requires SQLite 3.3.9 or newer.
         The <command>b10-xfrin</command> and <command>b10-xfrout</command>
 	modules require the libboost library,
-        Boost Python library, libpython3 library,
+        libpython3 library,
 	and the Python _sqlite3.so module.
       </para></note>
 <!-- TODO: this will change ... -->
@@ -272,7 +272,7 @@
         </para>
 
         <para>
-	  The Boost Library, Boost Python Library, Python Library,
+	  The Boost Library, Python Library,
 	  and Python _sqlite3 module are required to enable the
 	  Xfrout and Xfrin support.
         </para>
@@ -284,7 +284,7 @@
 
         <para>
           Building BIND 10 also requires a C++ compiler and
-          standard development headers.
+          standard development headers, make, and pkg-config.
           BIND 10 builds have been tested with GCC g++ 3.4.3, 4.1.2,
           4.1.3, 4.2.1, 4.3.2, and 4.4.1.
         </para>
@@ -495,14 +495,6 @@
           </varlistentry>
 
           <varlistentry>
-            <term>--with-boost-python</term>
-            <listitem> 
-              <simpara>Define to use the Boost Python library.
-              </simpara>
-            </listitem> 
-          </varlistentry>
-
-          <varlistentry>
             <term>--with-pythonpath</term>
             <listitem> 
               <simpara>Define the path to Python 3.1 if it is not in the
@@ -527,14 +519,12 @@
   <!-- TODO: lcov -->
 
         <para>
-          For example, the following configures it
-    build with Boost Python support (for Python DNS library),
+          For example, the following configures it to
     find the Boost headers and library, find the
     Python interpreter, and sets the installation location:
 
           <screen>$ <userinput>./configure --with-boost-lib=/usr/pkg/lib \
       --with-boost-include=/usr/pkg/include \
-      --with-boost-python \
       --with-pythonpath=/usr/pkg/bin/python3.1 \
       --prefix=/opt/bind10</userinput></screen>
         </para>

Modified: branches/trac191/src/bin/auth/Makefile.am
==============================================================================
--- branches/trac191/src/bin/auth/Makefile.am (original)
+++ branches/trac191/src/bin/auth/Makefile.am Fri Jul  9 10:04:58 2010
@@ -36,7 +36,10 @@
 libasio_link_a_SOURCES = asio_link.cc asio_link.h
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
-libasio_link_a_CXXFLAGS = $(AM_CXXFLAGS) -Wno-unused-parameter
+libasio_link_a_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_GXX
+libasio_link_a_CXXFLAGS += -Wno-unused-parameter
+endif
 libasio_link_a_CPPFLAGS = $(AM_CPPFLAGS)
 
 BUILT_SOURCES = spec_config.h 
@@ -47,13 +50,11 @@
 b10_auth_LDADD =  $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a
 b10_auth_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns++.a
 b10_auth_LDADD += $(top_builddir)/src/lib/config/.libs/libcfgclient.a
-b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.a
+b10_auth_LDADD += $(top_builddir)/src/lib/cc/.libs/libcc.a
 b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
 b10_auth_LDADD += $(top_builddir)/src/bin/auth/libasio_link.a
 b10_auth_LDADD += $(SQLITE_LIBS)
-if HAVE_BOOST_PYTHON
 b10_auth_LDADD += $(top_builddir)/src/lib/xfr/.libs/libxfr.a
-endif
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
 # and can't use @datadir@ because doesn't expand default ${prefix}

Modified: branches/trac191/src/bin/auth/asio_link.cc
==============================================================================
--- branches/trac191/src/bin/auth/asio_link.cc (original)
+++ branches/trac191/src/bin/auth/asio_link.cc Fri Jul  9 10:04:58 2010
@@ -16,22 +16,22 @@
 
 #include <config.h>
 
+#include <unistd.h>             // for some IPC/network system calls
 #include <asio.hpp>
+#include <boost/lexical_cast.hpp>
 #include <boost/bind.hpp>
 
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
 
-#if defined(HAVE_BOOST_PYTHON)
-#define USE_XFROUT
 #include <xfr/xfrout_client.h>
-#endif
 
 #include <asio_link.h>
 
 #include "spec_config.h"        // for XFROUT.  should not be here.
 #include "auth_srv.h"
+#include "common.h"
 
 using namespace asio;
 using ip::udp;
@@ -39,15 +39,15 @@
 
 using namespace std;
 using namespace isc::dns;
-#ifdef USE_XFROUT
 using namespace isc::xfr;
-#endif
 
 namespace {
 // As a short term workaround, we have XFROUT specific code.  We should soon
 // refactor the code with some abstraction so that we can separate this level
 // details from the (AS)IO module.
-#ifdef USE_XFROUT
+
+// This was contained in an ifdef USE_XFROUT, but we should really check
+// live if we do xfrout
 //TODO. The sample way for checking axfr query, the code should be merged to auth server class
 bool
 check_axfr_query(char* const msg_data, const uint16_t msg_len) {
@@ -64,6 +64,7 @@
 }
 
 //TODO. Send the xfr query to xfrout module, the code should be merged to auth server class
+//BIGGERTODO: stop using hardcoded install-path locations! 
 void
 dispatch_axfr_query(const int tcp_sock, char const axfr_query[],
                     const uint16_t query_len)
@@ -75,6 +76,9 @@
         path = UNIX_SOCKET_FILE;
     }
     
+    if (getenv("B10_FROM_BUILD")) {
+        path = string(getenv("B10_FROM_BUILD")) + "/auth_xfrout_conn";
+    }
     XfroutClient xfr_client(path);
     try {
         xfr_client.connect();
@@ -84,10 +88,9 @@
     }
     catch (const exception & err) {
         //if (verbose_mode)
-        cerr << "error handle xfr query:" << err.what() << endl;
-    }
-}
-#endif
+        cerr << "error handle xfr query " << UNIX_SOCKET_FILE << ":" << err.what() << endl;
+    }
+}
 }
 
 namespace asio_link {
@@ -140,13 +143,11 @@
     {
         if (!error) {
             InputBuffer dnsbuffer(data_, bytes_transferred);
-#ifdef USE_XFROUT
             if (check_axfr_query(data_, bytes_transferred)) {
                 dispatch_axfr_query(socket_.native(), data_, bytes_transferred); 
                 // start to get new query ?
                 start();
             } else {
-#endif          
                 if (auth_server_->processMessage(dnsbuffer, dns_message_,
                                                 response_renderer_, false)) {
                     responselen_buffer_.writeUint16(
@@ -160,9 +161,7 @@
                 } else {
                     delete this;
                 }
-#ifdef USE_XFROUT
             }
-#endif
         } else {
             delete this;
         }
@@ -203,7 +202,7 @@
 class TCPServer {
 public:
     TCPServer(AuthSrv* auth_server, io_service& io_service,
-              int af, short port) :
+              int af, uint16_t port) :
         auth_server_(auth_server), io_service_(io_service),
         acceptor_(io_service_), listening_(new TCPClient(auth_server_,
                                                          io_service_))
@@ -215,6 +214,23 @@
         if (af == AF_INET6) {
             acceptor_.set_option(ip::v6_only(true));
         }
+        acceptor_.set_option(tcp::acceptor::reuse_address(true));
+        acceptor_.bind(endpoint);
+        acceptor_.listen();
+        acceptor_.async_accept(listening_->getSocket(),
+                               boost::bind(&TCPServer::handleAccept, this,
+                                           listening_, placeholders::error));
+    }
+
+    TCPServer(AuthSrv* auth_server, io_service& io_service,
+              asio::ip::address addr, uint16_t port) :
+        auth_server_(auth_server),
+        io_service_(io_service), acceptor_(io_service_),
+        listening_(new TCPClient(auth_server, io_service_))
+    {
+        tcp::endpoint endpoint(addr, port);
+        acceptor_.open(endpoint.protocol());
+
         acceptor_.set_option(tcp::acceptor::reuse_address(true));
         acceptor_.bind(endpoint);
         acceptor_.listen();
@@ -251,7 +267,7 @@
 class UDPServer {
 public:
     UDPServer(AuthSrv* auth_server, io_service& io_service,
-              int af, short port) :
+              int af, uint16_t port) :
         auth_server_(auth_server),
         io_service_(io_service),
         socket_(io_service, af == AF_INET6 ? udp::v6() : udp::v4()),
@@ -267,6 +283,18 @@
         } else {
             socket_.bind(udp::endpoint(udp::v4(), port));
         }
+        startReceive();
+    }
+
+    UDPServer(AuthSrv* auth_server, io_service& io_service,
+              asio::ip::address addr, uint16_t port) :
+        auth_server_(auth_server), io_service_(io_service),
+        socket_(io_service, addr.is_v6() ? udp::v6() : udp::v4()),
+        response_buffer_(0),
+        response_renderer_(response_buffer_),
+        dns_message_(Message::PARSE)
+    {
+        socket_.bind(udp::endpoint(addr, port));
         startReceive();
     }
 
@@ -350,7 +378,7 @@
 
 class IOServiceImpl {
 public:
-    IOServiceImpl(AuthSrv* auth_server, const char* port,
+    IOServiceImpl(AuthSrv* auth_server, const char* address, const char* port,
                   const bool use_ipv4, const bool use_ipv6);
     ~IOServiceImpl();
     asio::io_service io_service_;
@@ -361,25 +389,59 @@
     TCPServer* tcp6_server_;
 };
 
-IOServiceImpl::IOServiceImpl(AuthSrv* auth_server, const char* const port,
-                             const bool use_ipv4, const bool use_ipv6) :
+IOServiceImpl::IOServiceImpl(AuthSrv* auth_server, const char* const address,
+                             const char* const port, const bool use_ipv4,
+                             const bool use_ipv6) :
     auth_server_(auth_server), udp4_server_(NULL), udp6_server_(NULL),
     tcp4_server_(NULL), tcp6_server_(NULL)
 {
     ServerSet servers;
-    short portnum = atoi(port);
-
-    if (use_ipv4) {
-        servers.udp4_server = new UDPServer(auth_server, io_service_,
-                                            AF_INET, portnum);
-        servers.tcp4_server = new TCPServer(auth_server, io_service_,
-                                            AF_INET, portnum);
-    }
-    if (use_ipv6) {
-        servers.udp6_server = new UDPServer(auth_server, io_service_,
-                                            AF_INET6, portnum);
-        servers.tcp6_server = new TCPServer(auth_server, io_service_,
-                                            AF_INET6, portnum);
+    uint16_t portnum = atoi(port);
+
+    try {
+        portnum = boost::lexical_cast<uint16_t>(port);
+    } catch (const std::exception& ex) {
+        isc_throw(FatalError, "[b10-auth] Invalid port number '"
+                              << port << "'");
+    }
+
+    if (address != NULL) {
+        asio::ip::address addr = asio::ip::address::from_string(address);
+
+        if ((addr.is_v6() && !use_ipv6)) {
+            isc_throw(FatalError,
+                      "[b10-auth] Error: -4 conflicts with " << addr);
+        }
+
+        if ((addr.is_v4() && !use_ipv4)) {
+            isc_throw(FatalError,
+                      "[b10-auth] Error: -6 conflicts with " << addr);
+        }
+
+        if (addr.is_v4()) {
+            servers.udp4_server = new UDPServer(auth_server, io_service_,
+                                                addr, portnum);
+            servers.tcp4_server = new TCPServer(auth_server, io_service_,
+                                                addr, portnum);
+         } else {
+            servers.udp6_server = new UDPServer(auth_server, io_service_,
+                                                addr, portnum);
+            servers.tcp6_server = new TCPServer(auth_server, io_service_,
+                                                addr, portnum);
+        }
+    } else {
+        if (use_ipv4) {
+            servers.udp4_server = new UDPServer(auth_server, io_service_,
+                                                AF_INET, portnum);
+            servers.tcp4_server = new TCPServer(auth_server, io_service_,
+                                                AF_INET, portnum);
+        }
+        if (use_ipv6) {
+            servers.udp6_server = new UDPServer(auth_server, io_service_,
+                                                AF_INET6, portnum);
+            servers.tcp6_server = new TCPServer(auth_server, io_service_,
+                                                AF_INET6, portnum);
+        }
     }
 
     // Now we don't have to worry about exception, and need to make sure that
@@ -397,9 +459,10 @@
     delete tcp6_server_;
 }
 
-IOService::IOService(AuthSrv* auth_server, const char* const port,
-                     const bool use_ipv4, const bool use_ipv6) {
-    impl_ = new IOServiceImpl(auth_server, port, use_ipv4, use_ipv6);
+IOService::IOService(AuthSrv* auth_server, const char* const address,
+                     const char* const port, const bool use_ipv4,
+                     const bool use_ipv6) {
+    impl_ = new IOServiceImpl(auth_server, address, port, use_ipv4, use_ipv6);
 }
 
 IOService::~IOService() {

Modified: branches/trac191/src/bin/auth/asio_link.h
==============================================================================
--- branches/trac191/src/bin/auth/asio_link.h (original)
+++ branches/trac191/src/bin/auth/asio_link.h Fri Jul  9 10:04:58 2010
@@ -24,8 +24,9 @@
 
 class IOService {
 public:
-    IOService(AuthSrv* auth_server, const char* port, bool use_ipv4,
-              bool use_ipv6);
+    IOService(AuthSrv* auth_server,
+              const char* const address, const char* const port,
+              const bool use_ipv4, const bool use_ipv6);
     ~IOService();
     void run();
     void stop();

Modified: branches/trac191/src/bin/auth/auth_srv.cc
==============================================================================
--- branches/trac191/src/bin/auth/auth_srv.cc (original)
+++ branches/trac191/src/bin/auth/auth_srv.cc Fri Jul  9 10:04:58 2010
@@ -60,7 +60,7 @@
     AuthSrvImpl(const AuthSrvImpl& source);
     AuthSrvImpl& operator=(const AuthSrvImpl& source);
 public:
-    AuthSrvImpl();
+    AuthSrvImpl(const bool use_cache);
 
     isc::data::ElementPtr setDbFile(const isc::data::ElementPtr config);
 
@@ -76,9 +76,13 @@
 
     /// Currently non-configurable, but will be.
     static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
+
+    /// Hot spot cache
+    isc::datasrc::HotCache cache_;
 };
 
-AuthSrvImpl::AuthSrvImpl() : cs_(NULL), verbose_mode_(false)
+AuthSrvImpl::AuthSrvImpl(const bool use_cache) :
+    cs_(NULL), verbose_mode_(false)
 {
     // cur_datasrc_ is automatically initialized by the default constructor,
     // effectively being an empty (sqlite) data source.  once ccsession is up
@@ -86,9 +90,12 @@
 
     // add static data source
     data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
-}
-
-AuthSrv::AuthSrv() : impl_(new AuthSrvImpl) {
+
+    // enable or disable the cache
+    cache_.setEnabled(use_cache);
+}
+
+AuthSrv::AuthSrv(const bool use_cache) : impl_(new AuthSrvImpl(use_cache)) {
 }
 
 AuthSrv::~AuthSrv() {
@@ -239,11 +246,12 @@
     message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
 
     try {
-        Query query(message, dnssec_ok);
+        Query query(message, impl_->cache_, dnssec_ok);
         impl_->data_sources_.doQuery(query);
     } catch (const Exception& ex) {
         if (impl_->verbose_mode_) {
-            cerr << "[b10-auth] Internal error, returning SERVFAIL: " << ex.what() << endl;
+            cerr << "[b10-auth] Internal error, returning SERVFAIL: " <<
+                ex.what() << endl;
         }
         makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
                          impl_->verbose_mode_);
@@ -273,9 +281,22 @@
         bool is_default;
         string item("database_file");
         ElementPtr value = cs_->getValue(is_default, item);
+        final = Element::createMap();
+
+        // If the value is the default, and we are running from
+        // a specific directory ('from build'), we need to use
+        // a different value than the default (which may not exist)
+        // (btw, this should not be done here in the end, i think
+        //  the from-source script should have a check for this,
+        //  but for that we need offline access to config, so for
+        //  now this is a decent solution)
+        if (is_default && getenv("B10_FROM_BUILD")) {
+            value = Element::create(string(getenv("B10_FROM_BUILD")) +
+                                    "/bind10_zones.sqlite3");
+        }
+        final->set(item, value);
+
         db_file_ = value->stringValue();
-        final = Element::createFromString("{}");
-        final->set(item, value);
     } else {
         return (answer);
     }

Modified: branches/trac191/src/bin/auth/auth_srv.h
==============================================================================
--- branches/trac191/src/bin/auth/auth_srv.h (original)
+++ branches/trac191/src/bin/auth/auth_srv.h Fri Jul  9 10:04:58 2010
@@ -43,7 +43,7 @@
     AuthSrv(const AuthSrv& source);
     AuthSrv& operator=(const AuthSrv& source);
 public:
-    explicit AuthSrv();
+    explicit AuthSrv(const bool use_cache);
     ~AuthSrv();
     //@}
     /// \return \c true if the \message contains a response to be returned;

Modified: branches/trac191/src/bin/auth/main.cc
==============================================================================
--- branches/trac191/src/bin/auth/main.cc (original)
+++ branches/trac191/src/bin/auth/main.cc Fri Jul  9 10:04:58 2010
@@ -86,7 +86,7 @@
 
 void
 usage() {
-    cerr << "Usage: b10-auth [-p port] [-4|-6]" << endl;
+    cerr << "Usage: b10-auth [-p port] [-4|-6] [-nv]" << endl;
     exit(1);
 }
 } // end of anonymous namespace
@@ -95,9 +95,10 @@
 main(int argc, char* argv[]) {
     int ch;
     const char* port = DNSPORT;
-    bool use_ipv4 = true, use_ipv6 = true;
+    const char* address = NULL;
+    bool use_ipv4 = true, use_ipv6 = true, cache = true;
 
-    while ((ch = getopt(argc, argv, "46p:v")) != -1) {
+    while ((ch = getopt(argc, argv, "46np:v")) != -1) {
         switch (ch) {
         case '4':
             // Note that -4 means "ipv4 only", we need to set "use_ipv6" here,
@@ -109,6 +110,12 @@
         case '6':
             // The same note as -4 applies.
             use_ipv4 = false;
+            break;
+        case 'n':
+            cache = false;
+            break;
+        case 'a':
+            address = optarg;
             break;
         case 'p':
             port = optarg;
@@ -142,11 +149,11 @@
             specfile = string(AUTH_SPECFILE_LOCATION);
         }
 
-        auth_server = new AuthSrv;
+        auth_server = new AuthSrv(cache);
         auth_server->setVerbose(verbose_mode);
 
-        io_service = new asio_link::IOService(auth_server, port, use_ipv4,
-                                              use_ipv6);
+        io_service = new asio_link::IOService(auth_server, address, port,
+                                              use_ipv4, use_ipv6);
 
         ModuleCCSession cs(specfile, io_service->get_io_service(),
                            my_config_handler, my_command_handler);

Modified: branches/trac191/src/bin/auth/tests/Makefile.am
==============================================================================
--- branches/trac191/src/bin/auth/tests/Makefile.am (original)
+++ branches/trac191/src/bin/auth/tests/Makefile.am Fri Jul  9 10:04:58 2010
@@ -22,7 +22,7 @@
 run_unittests_LDADD +=  $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a
 run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/.libs/libdns++.a
 run_unittests_LDADD += $(top_builddir)/src/lib/config/.libs/libcfgclient.a
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.a
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/.libs/libcc.a
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
 endif
 

Modified: branches/trac191/src/bin/auth/tests/auth_srv_unittest.cc
==============================================================================
--- branches/trac191/src/bin/auth/tests/auth_srv_unittest.cc (original)
+++ branches/trac191/src/bin/auth/tests/auth_srv_unittest.cc Fri Jul  9 10:04:58 2010
@@ -44,7 +44,7 @@
 
 class AuthSrvTest : public ::testing::Test {
 protected:
-    AuthSrvTest() : request_message(Message::RENDER),
+    AuthSrvTest() : server(true), request_message(Message::RENDER),
                     parse_message(Message::PARSE), default_qid(0x1035),
                     opcode(Opcode(Opcode::QUERY())), qname("www.example.com"),
                     qclass(RRClass::IN()), qtype(RRType::A()), ibuffer(NULL),
@@ -236,7 +236,7 @@
              const bool expect_success)
 {
     const ElementPtr config_answer =
-        server->updateConfig(Element::createFromString(dbfile));
+        server->updateConfig(Element::fromJSON(dbfile));
     EXPECT_EQ(Element::map, config_answer->getType());
     EXPECT_TRUE(config_answer->contains("result"));
 

Modified: branches/trac191/src/bin/bind10/bind10.py.in
==============================================================================
--- branches/trac191/src/bin/bind10/bind10.py.in (original)
+++ branches/trac191/src/bin/bind10/bind10.py.in Fri Jul  9 10:04:58 2010
@@ -56,7 +56,11 @@
 import time
 import select
 import random
+import socket
 from optparse import OptionParser, OptionValueError
+import io
+import pwd
+import posix
 
 import isc.cc
 
@@ -108,21 +112,38 @@
             when = time.time()
         return max(when, self.restart_time)
 
+class ProcessInfoError(Exception): pass
+
 class ProcessInfo:
     """Information about a process"""
 
     dev_null = open(os.devnull, "w")
 
     def __init__(self, name, args, env={}, dev_null_stdout=False,
-                 dev_null_stderr=False):
+                 dev_null_stderr=False, uid=None, username=None):
         self.name = name 
         self.args = args
         self.env = env
         self.dev_null_stdout = dev_null_stdout
         self.dev_null_stderr = dev_null_stderr
         self.restart_schedule = RestartSchedule()
+        self.uid = uid
+        self.username = username
         self._spawn()
 
+    def _setuid(self):
+        """Function used before running a program that needs to run as a
+        different user."""
+        if self.uid is not None:
+            try:
+                posix.setuid(self.uid)
+            except OSError as e:
+                if e.errno == errno.EPERM:
+                    # if we failed to change user due to permission report that
+                    raise ProcessInfoError("Unable to change to user %s (uid %d)" % (self.username, self.uid))
+                else:
+                    # otherwise simply re-raise whatever error we found
+                    raise
 
     def _spawn(self):
         if self.dev_null_stdout:
@@ -138,24 +159,51 @@
         # on construction (self.env).
         spawn_env = os.environ
         spawn_env.update(self.env)
-        if not 'B10_FROM_SOURCE' in os.environ:
+        if 'B10_FROM_SOURCE' not in os.environ:
             spawn_env['PATH'] = "@@LIBEXECDIR@@:" + spawn_env['PATH']
         self.process = subprocess.Popen(self.args,
                                         stdin=subprocess.PIPE,
                                         stdout=spawn_stdout,
                                         stderr=spawn_stderr,
                                         close_fds=True,
-                                        env=spawn_env,)
+                                        env=spawn_env,
+                                        preexec_fn=self._setuid)
         self.pid = self.process.pid
         self.restart_schedule.set_run_start_time()
 
     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, verbose=False):
+    def __init__(self, msgq_socket_file=None, auth_port=5300, address='',
+                 nocache=False, verbose=False, setuid=None, username=None):
         """Initialize the Boss of BIND. This is a singleton (only one
         can run).
         
@@ -166,11 +214,17 @@
         self.verbose = verbose
         self.msgq_socket_file = msgq_socket_file
         self.auth_port = auth_port
+        self.address = None
+        if address:
+            self.address = IPAddr(address)
         self.cc_session = None
         self.ccs = None
         self.processes = {}
         self.dead_processes = {}
         self.runnable = False
+        self.uid = setuid
+        self.username = username
+        self.nocache = nocache
 
     def config_handler(self, new_config):
         if self.verbose:
@@ -225,12 +279,14 @@
                 sys.stdout.write("[bind10] Starting b10-msgq\n")
         try:
             c_channel = ProcessInfo("b10-msgq", ["b10-msgq"], c_channel_env,
-                                    True, not self.verbose)
+                                    True, not self.verbose, uid=self.uid,
+                                    username=self.username)
         except Exception as e:
             return "Unable to start b10-msgq; " + str(e)
         self.processes[c_channel.pid] = c_channel
         if self.verbose:
-            sys.stdout.write("[bind10] Started b10-msgq (PID %d)\n" % c_channel.pid)
+            sys.stdout.write("[bind10] Started b10-msgq (PID %d)\n" % 
+                             c_channel.pid)
 
         # now connect to the c-channel
         cc_connect_start = time.time()
@@ -250,7 +306,8 @@
             sys.stdout.write("[bind10] Starting b10-cfgmgr\n")
         try:
             bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
-                                    c_channel_env)
+                                    c_channel_env, uid=self.uid,
+                                    username=self.username)
         except Exception as e:
             c_channel.process.kill()
             return "Unable to start b10-cfgmgr; " + str(e)
@@ -272,30 +329,20 @@
         if self.verbose:
             sys.stdout.write("[bind10] ccsession started\n")
 
-        # start the xfrout before auth-server, to make sure every xfr-query can
-        # be processed properly.
-        xfrout_args = ['b10-xfrout']
-        if self.verbose:
-            sys.stdout.write("[bind10] Starting b10-xfrout\n")
-            xfrout_args += ['-v']
-        try:
-            xfrout = ProcessInfo("b10-xfrout", xfrout_args, 
-                                 c_channel_env )
-        except Exception as e:
-            c_channel.process.kill()
-            bind_cfgd.process.kill()
-            return "Unable to start b10-xfrout; " + str(e)
-        self.processes[xfrout.pid] = xfrout
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-xfrout (PID %d)\n" % xfrout.pid)
-
         # start b10-auth
         # XXX: this must be read from the configuration manager in the future
         authargs = ['b10-auth', '-p', str(self.auth_port)]
-        if self.verbose:
-            sys.stdout.write("[bind10] Starting b10-auth using port %d\n" %
+        if self.address:
+            authargs += ['-a', str(self.address)]
+        if self.nocache:
+            authargs += ['-n']
+        if self.verbose:
+            authargs += ['-v']
+            sys.stdout.write("Starting b10-auth using port %d" %
                              self.auth_port)
-            authargs += ['-v']
+            if self.address:
+                sys.stdout.write(" on %s" % str(self.address))
+            sys.stdout.write("\n")
         try:
             auth = ProcessInfo("b10-auth", authargs,
                                c_channel_env)
@@ -307,6 +354,28 @@
         self.processes[auth.pid] = auth
         if self.verbose:
             sys.stdout.write("[bind10] Started b10-auth (PID %d)\n" % auth.pid)
+
+        # everything after the authoritative server can run as non-root
+        if self.uid is not None:
+            posix.setuid(self.uid)
+
+        # start the xfrout before auth-server, to make sure every xfr-query can
+        # be processed properly.
+        xfrout_args = ['b10-xfrout']
+        if self.verbose:
+            sys.stdout.write("[bind10] Starting b10-xfrout\n")
+            xfrout_args += ['-v']
+        try:
+            xfrout = ProcessInfo("b10-xfrout", xfrout_args, 
+                                 c_channel_env )
+        except Exception as e:
+            c_channel.process.kill()
+            bind_cfgd.process.kill()
+            return "Unable to start b10-xfrout; " + str(e)
+        self.processes[xfrout.pid] = xfrout
+        if self.verbose:
+            sys.stdout.write("[bind10] Started b10-xfrout (PID %d)\n" % 
+                             xfrout.pid)
 
         # start b10-xfrin
         xfrin_args = ['b10-xfrin']
@@ -324,7 +393,8 @@
             return "Unable to start b10-xfrin; " + str(e)
         self.processes[xfrind.pid] = xfrind
         if self.verbose:
-            sys.stdout.write("[bind10] Started b10-xfrin (PID %d)\n" % xfrind.pid)
+            sys.stdout.write("[bind10] Started b10-xfrin (PID %d)\n" % 
+                             xfrind.pid)
 
         # start b10-stats
         stats_args = ['b10-stats']
@@ -365,7 +435,8 @@
             return "Unable to start b10-cmdctl; " + str(e)
         self.processes[cmd_ctrld.pid] = cmd_ctrld
         if self.verbose:
-            sys.stdout.write("[bind10] Started b10-cmdctl (PID %d)\n" % cmd_ctrld.pid)
+            sys.stdout.write("[bind10] Started b10-cmdctl (PID %d)\n" % 
+                             cmd_ctrld.pid)
 
         self.runnable = True
 
@@ -374,7 +445,7 @@
     def stop_all_processes(self):
         """Stop all processes."""
         cmd = { "command": ['shutdown']}
-        self.cc_session.group_sendmsg(cmd, 'Boss', 'Cmd-Ctrld')
+        self.cc_session.group_sendmsg(cmd, 'Boss', 'Cmdctl')
         self.cc_session.group_sendmsg(cmd, "Boss", "ConfigManager")
         self.cc_session.group_sendmsg(cmd, "Boss", "Auth")
         self.cc_session.group_sendmsg(cmd, "Boss", "Xfrout")
@@ -457,11 +528,16 @@
                 sys.stdout.write("[bind10] Unknown child pid %d exited.\n" % pid)
 
     def restart_processes(self):
-        """Restart any dead processes."""
+        """Restart any dead processes.
+        Returns the time when the next process is ready to be restarted. 
+          If the server is shutting down, returns 0.
+          If there are no processes, returns None.
+        The values returned can be safely passed into select() as the 
+        timeout value."""
         next_restart = None
         # if we're shutting down, then don't restart
         if not self.runnable:
-            return next_restart
+            return 0
         # otherwise look through each dead process and try to restart
         still_dead = {}
         now = time.time()
@@ -529,20 +605,76 @@
     else:
         raise OptionValueError("Unknown option " + opt_str)
   
+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:
+        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 main():
     global options
     global boss_of_bind
+    # Enforce line buffering on stdout, even when not a TTY
+    sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
+
+
     # Parse any command-line options.
     parser = OptionParser(version=__version__)
-    parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
-                      help="display more about what is going on")
+    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)")
+    parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
+                      type="string", default=None,
+                      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",
                       help="port the b10-auth daemon will use (default 5300)")
-    parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
+    parser.add_option("-u", "--user", dest="user",
                       type="string", default=None,
-                      help="UNIX domain socket file the b10-msgq daemon will use")
+                      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")
     (options, args) = parser.parse_args()
+    if args:
+        parser.print_help()
+        sys.exit(1)
+
+    # Check user ID.
+    setuid = None
+    username = None
+    if options.user:
+        # Try getting information about the user, assuming UID passed.
+        try:
+            pw_ent = pwd.getpwuid(int(options.user))
+            setuid = pw_ent.pw_uid
+            username = pw_ent.pw_name
+        except ValueError:
+            pass
+        except KeyError:
+            pass
+
+        # Next try getting information about the user, assuming user name 
+        # passed.
+        # If the information is both a valid user name and user number, we
+        # prefer the name because we try it second. A minor point, hopefully.
+        try:
+            pw_ent = pwd.getpwnam(options.user)
+            setuid = pw_ent.pw_uid
+            username = pw_ent.pw_name
+        except KeyError:
+            pass
+
+        if setuid is None:
+            sys.stderr.write("bind10: invalid user: '%s'\n" % options.user)
+            sys.exit(1)
 
     # Announce startup.
     if options.verbose:
@@ -565,11 +697,13 @@
 
     # Go bob!
     boss_of_bind = BoB(options.msgq_socket_file, int(options.auth_port),
-                       options.verbose)
+                       options.address, options.nocache, options.verbose,
+                       setuid, username)
     startup_result = boss_of_bind.startup()
     if startup_result:
         sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
         sys.exit(1)
+    sys.stdout.write("[bind10] BIND 10 started\n")
 
     # In our main loop, we check for dead processes or messages 
     # on the c-channel.
@@ -606,6 +740,7 @@
     # shutdown
     signal.signal(signal.SIGCHLD, signal.SIG_DFL)
     boss_of_bind.shutdown()
+    sys.exit(0)
 
 if __name__ == "__main__":
     main()

Modified: branches/trac191/src/bin/bind10/run_bind10.sh.in
==============================================================================
--- branches/trac191/src/bin/bind10/run_bind10.sh.in (original)
+++ branches/trac191/src/bin/bind10/run_bind10.sh.in Fri Jul  9 10:04:58 2010
@@ -23,7 +23,8 @@
 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:$PATH
 export PATH
 
-PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/xfr/.libs
+PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs
+#PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/xfr/.libs
 export PYTHONPATH
 
 B10_FROM_SOURCE=@abs_top_srcdir@

Modified: branches/trac191/src/bin/bind10/tests/bind10_test.in
==============================================================================
--- branches/trac191/src/bin/bind10/tests/bind10_test.in (original)
+++ branches/trac191/src/bin/bind10/tests/bind10_test.in Fri Jul  9 10:04:58 2010
@@ -27,5 +27,6 @@
 export PYTHONPATH
 
 cd ${BIND10_PATH}/tests
-exec ${PYTHON_EXEC} -O bind10_test.py $*
+${PYTHON_EXEC} -O bind10_test.py $*
+exec ${PYTHON_EXEC} -O args_test.py $*
 

Modified: branches/trac191/src/bin/bind10/tests/bind10_test.py
==============================================================================
--- branches/trac191/src/bin/bind10/tests/bind10_test.py (original)
+++ branches/trac191/src/bin/bind10/tests/bind10_test.py Fri Jul  9 10:04:58 2010
@@ -1,4 +1,4 @@
-from bind10 import ProcessInfo, BoB
+from bind10 import ProcessInfo, BoB, IPAddr
 
 # XXX: environment tests are currently disabled, due to the preprocessor
 #      setup that we have now complicating the environment
@@ -7,6 +7,7 @@
 import sys
 import os
 import signal
+import socket
 
 class TestProcessInfo(unittest.TestCase):
     def setUp(self):
@@ -71,12 +72,36 @@
         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()
         self.assertEqual(bob.verbose, False)
         self.assertEqual(bob.msgq_socket_file, None)
+        self.assertEqual(bob.auth_port, 5300)
         self.assertEqual(bob.cc_session, None)
+        self.assertEqual(bob.address, None)
         self.assertEqual(bob.processes, {})
         self.assertEqual(bob.dead_processes, {})
         self.assertEqual(bob.runnable, False)
@@ -90,6 +115,27 @@
         self.assertEqual(bob.dead_processes, {})
         self.assertEqual(bob.runnable, False)
 
+    def test_init_alternate_auth_port(self):
+        bob = BoB(None, 9999)
+        self.assertEqual(bob.verbose, False)
+        self.assertEqual(bob.msgq_socket_file, None)
+        self.assertEqual(bob.auth_port, 9999)
+        self.assertEqual(bob.cc_session, None)
+        self.assertEqual(bob.address, None)
+        self.assertEqual(bob.processes, {})
+        self.assertEqual(bob.dead_processes, {})
+        self.assertEqual(bob.runnable, False)
+
+    def test_init_alternate_address(self):
+        bob = BoB(None, 5300, '127.127.127.127')
+        self.assertEqual(bob.verbose, False)
+        self.assertEqual(bob.auth_port, 5300)
+        self.assertEqual(bob.msgq_socket_file, None)
+        self.assertEqual(bob.cc_session, None)
+        self.assertEqual(bob.address.addr, socket.inet_aton('127.127.127.127'))
+        self.assertEqual(bob.processes, {})
+        self.assertEqual(bob.dead_processes, {})
+        self.assertEqual(bob.runnable, False)
     # verbose testing...
 
 if __name__ == '__main__':

Modified: branches/trac191/src/bin/bindctl/Makefile.am
==============================================================================
--- branches/trac191/src/bin/bindctl/Makefile.am (original)
+++ branches/trac191/src/bin/bindctl/Makefile.am Fri Jul  9 10:04:58 2010
@@ -9,8 +9,6 @@
 pythondir = $(pyexecdir)/bindctl
 
 bindctldir = $(DESTDIR)$(pkgdatadir)
-bindctl_DATA = bindctl.pem
-EXTRA_DIST += bindctl.pem
 
 CLEANFILES = bindctl
 
@@ -26,14 +24,3 @@
 	       -e "s|@@SYSCONFDIR@@|@sysconfdir@|" \
 	       -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bindctl-source.py >$@
 	chmod a+x $@
-
-if INSTALL_CONFIGURATIONS
-
-# TODO: permissions handled later
-install-data-local:
-	$(mkinstalldirs) $(DESTDIR)/@sysconfdir@/@PACKAGE@   
-	if test ! -f $(DESTDIR)$(sysconfdir)/@PACKAGE@/bindctl.pem; then	\
-	  $(INSTALL_DATA) $(srcdir)/bindctl.pem $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ;	\
-	fi
-
-endif

Modified: branches/trac191/src/bin/bindctl/TODO
==============================================================================
--- branches/trac191/src/bin/bindctl/TODO (original)
+++ branches/trac191/src/bin/bindctl/TODO Fri Jul  9 10:04:58 2010
@@ -18,3 +18,4 @@
    If the default user is saved in file, its password shouldn't be saved in plaintext.
 7. Need to think of what exactly to do with responses received to commands
    (currently it simply print the map)
+8. Remove bindctl-source.py.in(replace with bindctl-source.py) when merging to trunk.

Modified: branches/trac191/src/bin/bindctl/bindcmd.py
==============================================================================
--- branches/trac191/src/bin/bindctl/bindcmd.py (original)
+++ branches/trac191/src/bin/bindctl/bindcmd.py Fri Jul  9 10:04:58 2010
@@ -36,6 +36,9 @@
 from hashlib import sha1
 import csv
 import ast
+import pwd
+import getpass
+import traceback
 
 try:
     from collections import OrderedDict
@@ -49,6 +52,8 @@
 except ImportError:
     my_readline = sys.stdin.readline
 
+CSV_FILE_NAME = 'default_user.csv'
+FAIL_TO_CONNECT_WITH_CMDCTL = "Fail to connect with b10-cmdctl module, is it running?"
 CONFIG_MODULE_NAME = 'config'
 CONST_BINDCTL_HELP = """
 usage: <module name> <command name> [param1 = value1 [, param2 = value2]]
@@ -58,10 +63,34 @@
 Type \"<module_name> <command_name> help\" for help on the specific command.
 \nAvailable module names: """
 
+class ValidatedHTTPSConnection(http.client.HTTPSConnection):
+    '''Overrides HTTPSConnection to support certification 
+    validation. '''
+    def __init__(self, host, ca_certs):
+        http.client.HTTPSConnection.__init__(self, host)
+        self.ca_certs = ca_certs
+
+    def connect(self):
+        ''' Overrides the connect() so that we do 
+        certificate validation. '''
+        sock = socket.create_connection((self.host, self.port),
+                                        self.timeout)
+        if self._tunnel_host:
+            self.sock = sock
+            self._tunnel()
+       
+        req_cert = ssl.CERT_NONE
+        if self.ca_certs:
+            req_cert = ssl.CERT_REQUIRED
+        self.sock = ssl.wrap_socket(sock, self.key_file,
+                                    self.cert_file,
+                                    cert_reqs=req_cert,
+                                    ca_certs=self.ca_certs)
+
 class BindCmdInterpreter(Cmd):
     """simple bindctl example."""    
 
-    def __init__(self, server_port = 'localhost:8080', pem_file = "bindctl.pem"):
+    def __init__(self, server_port = 'localhost:8080', pem_file = None):
         Cmd.__init__(self)
         self.location = ""
         self.prompt_end = '> '
@@ -70,19 +99,11 @@
         self.modules = OrderedDict()
         self.add_module_info(ModuleInfo("help", desc = "Get help for bindctl"))
         self.server_port = server_port
-        self.pem_file = pem_file
-        self._connect_to_cmd_ctrld()
+        self.conn = ValidatedHTTPSConnection(self.server_port,
+                                             ca_certs=pem_file)
         self.session_id = self._get_session_id()
-
-    def _connect_to_cmd_ctrld(self):
-        '''Connect to cmdctl in SSL context. '''
-        try:
-            self.conn = http.client.HTTPSConnection(self.server_port,
-                          cert_file=self.pem_file)
-        except  Exception as e:
-            print(e, "can't connect to %s, please make sure cmd-ctrld is running" %
-                  self.server_port)
-
+        self.config_data = None
+        
     def _get_session_id(self):
         '''Generate one session id for the connection. '''
         rand = os.urandom(16)
@@ -93,17 +114,59 @@
         return digest
     
     def run(self):
-        '''Parse commands inputted from user and send them to cmdctl. '''
+        '''Parse commands from user and send them to cmdctl. '''
         try:
             if not self.login_to_cmdctl():
-                return False
-
-            # Get all module information from cmd-ctrld
-            self.config_data = isc.config.UIModuleCCSession(self)
-            self._update_commands()
+                return 
+
             self.cmdloop()
+        except FailToLogin as err:
+            print(err)
+            print(FAIL_TO_CONNECT_WITH_CMDCTL)
+            traceback.print_exc()
         except KeyboardInterrupt:
-            return True
+            print('\nExit from bindctl')
+
+    def _get_saved_user_info(self, dir, file_name):
+        ''' Read all the available username and password pairs saved in 
+        file(path is "dir + file_name"), Return value is one list of elements
+        ['name', 'password'], If get information failed, empty list will be 
+        returned.'''
+        if (not dir) or (not os.path.exists(dir)):
+            return []
+
+        try:
+            csvfile = None
+            users = []
+            csvfile = open(dir + file_name)
+            users_info = csv.reader(csvfile)
+            for row in users_info:
+                users.append([row[0], row[1]])
+        except (IOError, IndexError) as e:
+            pass
+        finally:
+            if csvfile:
+                csvfile.close()
+            return users
+
+    def _save_user_info(self, username, passwd, dir, file_name):
+        ''' Save username and password in file "dir + file_name"
+        If it's saved properly, return True, or else return False. '''
+        try:
+            if not os.path.exists(dir):
+                os.mkdir(dir, 0o700)
+
+            csvfilepath = dir + file_name 
+            csvfile = open(csvfilepath, 'w')
+            os.chmod(csvfilepath, 0o600)
+            writer = csv.writer(csvfile)
+            writer.writerow([username, passwd])
+            csvfile.close()
+        except Exception as e:
+            print(e, "\nCannot write %s%s; default user is not stored" % (dir, file_name))
+            return False
+
+        return True
 
     def login_to_cmdctl(self):
         '''Login to cmdctl with the username and password inputted 
@@ -112,33 +175,21 @@
         time, username and password saved in 'default_user.csv' will be
         used first.
         '''
-        csvfile = None
-        bsuccess = False
-        try:
-            cvsfilepath = ""
-            if ('HOME' in os.environ):
-                cvsfilepath = os.environ['HOME']
-                cvsfilepath += os.sep + '.bind10' + os.sep
-            cvsfilepath += 'default_user.csv'
-            csvfile = open(cvsfilepath)
-            users = csv.reader(csvfile)
-            for row in users:
-                param = {'username': row[0], 'password' : row[1]}
+        csv_file_dir = pwd.getpwnam(getpass.getuser()).pw_dir
+        csv_file_dir += os.sep + '.bind10' + os.sep
+        users = self._get_saved_user_info(csv_file_dir, CSV_FILE_NAME)
+        for row in users:
+            param = {'username': row[0], 'password' : row[1]}
+            try:
                 response = self.send_POST('/login', param)
                 data = response.read().decode()
-                if response.status == http.client.OK:
-                    print(data + ' login as ' + row[0] )
-                    bsuccess = True
-                    break
-        except IOError as e:
-            pass
-        except Exception as e:
-            print(e)
-        finally:
-            if csvfile:
-                csvfile.close()
-            if bsuccess:
-                return True
+            except socket.error:
+                traceback.print_exc()
+                raise FailToLogin()
+
+            if response.status == http.client.OK:
+                print(data + ' login as ' + row[0] )
+                return True 
 
         count = 0
         print("[TEMP MESSAGE]: username :root  password :bind10")
@@ -151,33 +202,17 @@
             username = input("Username:")
             passwd = getpass.getpass()
             param = {'username': username, 'password' : passwd}
-            response = self.send_POST('/login', param)
-            data = response.read().decode()
-            print(data)
-            
+            try:
+                response = self.send_POST('/login', param)
+                data = response.read().decode()
+                print(data)
+            except socket.error as e:
+                traceback.print_exc()
+                raise FailToLogin()
+
             if response.status == http.client.OK:
-                cvsfilepath = ""
-                try:
-                    if ('HOME' in os.environ):
-                        cvsfilepath = os.environ['HOME']
-                        cvsfilepath += os.sep + '.bind10' + os.sep
-                        if not os.path.exists(cvsfilepath):
-                                os.mkdir(cvsfilepath, 0o700)
-                    else:
-                        print("Cannot determine location of $HOME. Not storing default user")
-                        return True
-                    cvsfilepath += 'default_user.csv'
-                    csvfile = open(cvsfilepath, 'w')
-                    os.chmod(cvsfilepath, 0o600)
-                    writer = csv.writer(csvfile)
-                    writer.writerow([username, passwd])
-                    csvfile.close()
-                except Exception as e:
-                    # just not store it
-                    print("Cannot write ~/.bind10/default_user.csv; default user is not stored")
-                    print(e)
+                self._save_user_info(username, passwd, csv_file_dir, CSV_FILE_NAME)
                 return True
-
 
     def _update_commands(self):
         '''Update the commands of all modules. '''
@@ -211,7 +246,22 @@
         headers = {"cookie" : self.session_id}
         self.conn.request('POST', url, param, headers)
         return self.conn.getresponse()
-        
+
+    def _update_all_modules_info(self):
+        ''' Get all modules' information from cmdctl, including
+        specification file and configuration data. This function
+        should be called before interpreting command line or complete-key
+        is entered. This may not be the best way to keep bindctl
+        and cmdctl share same modules information, but it works.'''
+        if self.config_data is not None:
+            self.config_data.update_specs_and_config()
+        else:
+            self.config_data = isc.config.UIModuleCCSession(self)
+        self._update_commands()
+
+    def precmd(self, line):
+        self._update_all_modules_info()
+        return line 
 
     def postcmd(self, stop, line):
         '''Update the prompt after every command'''
@@ -308,8 +358,11 @@
         if cmd.module != CONFIG_MODULE_NAME:
             for param_name in cmd.params:
                 param_spec = command_info.get_param_with_name(param_name).param_spec
-                cmd.params[param_name] = isc.config.config_data.convert_type(param_spec, cmd.params[param_name])
-
+                try:
+                    cmd.params[param_name] = isc.config.config_data.convert_type(param_spec, cmd.params[param_name])
+                except isc.cc.data.DataTypeError as e:
+                    raise isc.cc.data.DataTypeError('Invalid parameter value for \"%s\", the type should be \"%s\" \n' 
+                                                     % (param_name, param_spec['item_type']) + str(e))
     
     def _handle_cmd(self, cmd):
         '''Handle a command entered by the user'''
@@ -356,6 +409,7 @@
 
     def complete(self, text, state):
         if 0 == state:
+            self._update_all_modules_info()
             text = text.strip()
             hints = []
             cur_line = my_readline()
@@ -441,13 +495,14 @@
             cmd = BindCmdParse(line)
             self._validate_cmd(cmd)
             self._handle_cmd(cmd)
-        except BindCtlException as e:
-            print("Error! ", e)
-            self._print_correct_usage(e)
-        except isc.cc.data.DataTypeError as e:
-            print("Error! ", e)
-            self._print_correct_usage(e)
-            
+        except (IOError, http.client.HTTPException) as err:
+            print('Error!', err)
+            print(FAIL_TO_CONNECT_WITH_CMDCTL)
+        except BindCtlException as err:
+            print("Error! ", err)
+            self._print_correct_usage(err)
+        except isc.cc.data.DataTypeError as err:
+            print("Error! ", err)
             
     def _print_correct_usage(self, ept):        
         if isinstance(ept, CmdUnknownModuleSyntaxError):
@@ -556,7 +611,7 @@
         if (len(cmd.params) != 0):
             cmd_params = json.dumps(cmd.params)
 
-        print("send the message to cmd-ctrld")        
+        print("send the command to cmd-ctrld")        
         reply = self.send_POST(url, cmd.params)
         data = reply.read().decode()
         print("received reply:", data)

Modified: branches/trac191/src/bin/bindctl/bindctl-source.py.in
==============================================================================
--- branches/trac191/src/bin/bindctl/bindctl-source.py.in (original)
+++ branches/trac191/src/bin/bindctl/bindctl-source.py.in Fri Jul  9 10:04:58 2010
@@ -97,13 +97,16 @@
 
 def set_bindctl_options(parser):
     parser.add_option('-p', '--port', dest = 'port', type = 'int',
-            action = 'callback', callback=check_port,
-            default = '8080', help = 'port for cmdctl of bind10')
+                      action = 'callback', callback=check_port,
+                      default = '8080', help = 'port for cmdctl of bind10')
 
     parser.add_option('-a', '--address', dest = 'addr', type = 'string',
-            action = 'callback', callback=check_addr,
-            default = '127.0.0.1', help = 'IP address for cmdctl of bind10')
+                      action = 'callback', callback=check_addr,
+                      default = '127.0.0.1', help = 'IP address for cmdctl of bind10')
 
+    parser.add_option('-c', '--certificate-chain', dest = 'cert_chain', 
+                      type = 'string', action = 'store',
+                      help = 'PEM formatted server certificate validation chain file')
 
 if __name__ == '__main__':
     try:
@@ -111,14 +114,7 @@
         set_bindctl_options(parser)
         (options, args) = parser.parse_args()
         server_addr = options.addr + ':' + str(options.port)
-        # If B10_FROM_SOURCE is set in the environment, we use PEM file
-        # from a directory relative to that, otherwise we use the one
-        # installed on the system
-        if "B10_FROM_SOURCE" in os.environ:
-            SYSCONF_PATH = os.environ["B10_FROM_SOURCE"] + "/src/bin/bindctl"
-        else:
-            SYSCONF_PATH = "@@SYSCONFDIR@@/@PACKAGE@"
-        tool = BindCmdInterpreter(server_addr, pem_file = SYSCONF_PATH + "/bindctl.pem")
+        tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain)
         prepare_config_commands(tool)
         tool.run()
     except Exception as e:

Modified: branches/trac191/src/bin/bindctl/cmdparse.py
==============================================================================
--- branches/trac191/src/bin/bindctl/cmdparse.py (original)
+++ branches/trac191/src/bin/bindctl/cmdparse.py Fri Jul  9 10:04:58 2010
@@ -24,15 +24,19 @@
     from bindctl.mycollections import OrderedDict
 
 param_name_str = "^\s*(?P<param_name>[\w]+)\s*=\s*"
-param_value_str = "(?P<param_value>[\w\.:/-]+)"
-param_value_with_quota_str = "[\"\'](?P<param_value>[\w\.:, /-]+)[\"\']"
+
+# The value string can be a sequence without space or comma 
+# characters, or a string surroundedby quotation marks(such marks
+# can be part of string in an escaped form)
+#param_value_str  = "(?P<param_value>[\"\'].+?(?<!\\\)[\"\']|[^\'\"][^, ]+)"
+param_value_str  = "(?P<param_value>[^\'\" ][^, ]+)"
+param_value_with_quota_str  = "[\"\'](?P<param_value>.+?)(?<!\\\)[\"\']"
 next_params_str = "(?P<blank>\s*)(?P<comma>,?)(?P<next_params>.*)$"
 
 PARAM_WITH_QUOTA_PATTERN = re.compile(param_name_str + 
-                                      param_value_with_quota_str +
+                                      param_value_with_quota_str + 
                                       next_params_str)
 PARAM_PATTERN = re.compile(param_name_str + param_value_str + next_params_str)
-                           
 # Used for module and command name
 NAME_PATTERN = re.compile("^\s*(?P<name>[\w]+)(?P<blank>\s*)(?P<others>.*)$")
 
@@ -98,7 +102,6 @@
                 
             groups = PARAM_PATTERN.match(param_text) or \
                      PARAM_WITH_QUOTA_PATTERN.match(param_text)
-            
             if not groups:
                 # ok, fill in the params in the order entered
                 params = re.findall("([^\" ]+|\".*\")", param_text)

Modified: branches/trac191/src/bin/bindctl/exception.py
==============================================================================
--- branches/trac191/src/bin/bindctl/exception.py (original)
+++ branches/trac191/src/bin/bindctl/exception.py Fri Jul  9 10:04:58 2010
@@ -115,3 +115,10 @@
     def __str__(self):
         return str("Parameter '%s' is missed for command '%s' of module '%s'" % 
                    (self.param, self.command, self.module))
+
+
+class FailToLogin(BindCtlException):
+    def __str__(self):
+        return "Fail to login to cmdctl"
+
+

Modified: branches/trac191/src/bin/bindctl/tests/bindctl_test.py
==============================================================================
--- branches/trac191/src/bin/bindctl/tests/bindctl_test.py (original)
+++ branches/trac191/src/bin/bindctl/tests/bindctl_test.py Fri Jul  9 10:04:58 2010
@@ -16,6 +16,7 @@
 
 import unittest
 import isc.cc.data
+import os
 from bindctl import cmdparse
 from bindctl import bindcmd
 from bindctl.moduleinfo import *
@@ -50,12 +51,31 @@
             assert cmd.params["zone_name"] == "cnnic.cn"
             assert cmd.params["file"] == "cnnic.cn.file"
             assert cmd.params["master"] == '1.1.1.1'
+
+    def testCommandWithParamters_2(self):
+        '''Test whether the parameters in key=value can be parsed properly.'''
+        cmd = cmdparse.BindCmdParse('zone cmd name = 1:34::2')
+        self.assertEqual(cmd.params['name'], '1:34::2')
+
+        cmd = cmdparse.BindCmdParse('zone cmd name = 1\"\'34**&2 value=44\"\'\"')
+        self.assertEqual(cmd.params['name'], '1\"\'34**&2')
+        self.assertEqual(cmd.params['value'], '44\"\'\"')
+
+        cmd = cmdparse.BindCmdParse('zone cmd name = 1\"\'34**&2 ,value=  44\"\'\"')
+        self.assertEqual(cmd.params['name'], '1\"\'34**&2')
+        self.assertEqual(cmd.params['value'], '44\"\'\"')
             
+        cmd = cmdparse.BindCmdParse('zone cmd name =  1\'34**&2value=44\"\'\" value = \"==============\'')
+        self.assertEqual(cmd.params['name'], '1\'34**&2value=44\"\'\"')
+        self.assertEqual(cmd.params['value'], '==============')
+
+        cmd = cmdparse.BindCmdParse('zone cmd name =    \"1234, 567890 \" value ==&*/')
+        self.assertEqual(cmd.params['name'], '1234, 567890 ')
+        self.assertEqual(cmd.params['value'], '=&*/')
             
     def testCommandWithListParam(self):
-            cmd = cmdparse.BindCmdParse("zone set zone_name='cnnic.cn', master='1.1.1.1, 2.2.2.2'")
-            assert cmd.params["master"] == '1.1.1.1, 2.2.2.2'            
-        
+        cmd = cmdparse.BindCmdParse("zone set zone_name='cnnic.cn', master='1.1.1.1, 2.2.2.2'")
+        assert cmd.params["master"] == '1.1.1.1, 2.2.2.2'            
         
     def testCommandWithHelpParam(self):
         cmd = cmdparse.BindCmdParse("zone add help")
@@ -217,8 +237,32 @@
             assert self.random_names[i] == cmd_names[i+1]
             assert self.random_names[i] == module_names[i+1]
             i = i + 1
-        
-    
+    
+class FakeBindCmdInterpreter(bindcmd.BindCmdInterpreter):
+    def __init__(self):
+        pass
+
+class TestBindCmdInterpreter(unittest.TestCase):
+
+    def _create_invalid_csv_file(self, csvfilename):
+        import csv
+        csvfile = open(csvfilename, 'w')
+        writer = csv.writer(csvfile)
+        writer.writerow(['name1'])
+        writer.writerow(['name2'])
+        csvfile.close()
+
+    def test_get_saved_user_info(self):
+        cmd = FakeBindCmdInterpreter()
+        users = cmd._get_saved_user_info('/notexist', 'cvs_file.cvs')
+        self.assertEqual([], users)
+        
+        csvfilename = 'csv_file.csv'
+        self._create_invalid_csv_file(csvfilename)
+        users = cmd._get_saved_user_info('./', csvfilename)
+        self.assertEqual([], users)
+        os.remove(csvfilename)
+
 if __name__== "__main__":
     unittest.main()
     

Modified: branches/trac191/src/bin/cmdctl/Makefile.am
==============================================================================
--- branches/trac191/src/bin/cmdctl/Makefile.am (original)
+++ branches/trac191/src/bin/cmdctl/Makefile.am Fri Jul  9 10:04:58 2010
@@ -17,9 +17,8 @@
 b10_cmdctl_DATA += cmdctl.spec
  
 EXTRA_DIST = $(CMDCTL_CONFIGURATIONS)
-EXTRA_DIST += cmdctl.spec
 
-CLEANFILES=	b10-cmdctl cmdctl.pyc
+CLEANFILES=	b10-cmdctl cmdctl.pyc cmdctl.spec
 
 man_MANS = b10-cmdctl.8
 EXTRA_DIST += $(man_MANS) b10-cmdctl.xml
@@ -30,6 +29,9 @@
 	xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-cmdctl.xml
 
 endif
+
+cmdctl.spec: cmdctl.spec.pre
+	$(SED) -e "s|@@SYSCONFDIR@@|$(sysconfdir)|" cmdctl.spec.pre >$@
 
 # TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix

Modified: branches/trac191/src/bin/cmdctl/TODO
==============================================================================
--- branches/trac191/src/bin/cmdctl/TODO (original)
+++ branches/trac191/src/bin/cmdctl/TODO Fri Jul  9 10:04:58 2010
@@ -1,8 +1,6 @@
-. Refine code for b10-cmdctl.
-. Add value type check according module specification.
 . Add return code for RESTful API document of b10-cmdctl.
-. Add more unit tests for b10-cmdctl.
 . Update man page for b10-cmdctl?
+. Add check for the content of key/certificate file
+  (when cmdctl starts or is configured by bindctl).
+. Use only one msgq/session to communicate with other modules?
 
-. Add id to each command, so the receiver knows if the response is what it wants.
-. Make cmdctl can be configured through bindctl.(after id is added)

Modified: branches/trac191/src/bin/cmdctl/cmdctl.py.in
==============================================================================
--- branches/trac191/src/bin/cmdctl/cmdctl.py.in (original)
+++ branches/trac191/src/bin/cmdctl/cmdctl.py.in Fri Jul  9 10:04:58 2010
@@ -41,6 +41,7 @@
 import random
 import time
 import signal
+from isc.config import ccsession
 from optparse import OptionParser, OptionValueError
 from hashlib import sha1
 try:
@@ -50,29 +51,27 @@
 
 __version__ = 'BIND10'
 URL_PATTERN = re.compile('/([\w]+)(?:/([\w]+))?/?')
-
-# If B10_FROM_SOURCE is set in the environment, we use data files
+CONFIG_DATA_URL = 'config_data'
+MODULE_SPEC_URL = 'module_spec'
+
+
+# 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
-if "B10_FROM_SOURCE" in os.environ:
-    SPECFILE_PATH = os.environ["B10_FROM_SOURCE"] + "/src/bin/cmdctl"
-    SYSCONF_PATH = os.environ["B10_FROM_SOURCE"] + "/src/bin/cmdctl"
+if "B10_FROM_BUILD" in os.environ:
+    SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/cmdctl"
 else:
     PREFIX = "@prefix@"
     DATAROOTDIR = "@datarootdir@"
     SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
-    SYSCONF_PATH = "@sysconfdir@/@PACKAGE@".replace("${prefix}", PREFIX)
 SPECFILE_LOCATION = SPECFILE_PATH + os.sep + "cmdctl.spec"
-USER_INFO_FILE = SYSCONF_PATH + os.sep + "cmdctl-accounts.csv"
-PRIVATE_KEY_FILE = SYSCONF_PATH + os.sep + "cmdctl-keyfile.pem"
-CERTIFICATE_FILE = SYSCONF_PATH + os.sep + "cmdctl-certfile.pem"
-        
+
+class CmdctlException(Exception):
+    pass
+       
 class SecureHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
     '''https connection request handler.
-    Currently only GET and POST are supported.
-
-    '''
-
+    Currently only GET and POST are supported.  '''
     def do_GET(self):
         '''The client should send its session id in header with 
         the name 'cookie'
@@ -167,13 +166,13 @@
         user_name = user_info.get('username')
         if not user_name:
             return False, ["need user name"]
-        if not self.server.user_infos.get(user_name):
+        if not self.server.get_user_info(user_name):
             return False, ["user doesn't exist"]
 
         user_pwd = user_info.get('password')
         if not user_pwd:
             return False, ["need password"]
-        local_info = self.server.user_infos.get(user_name)
+        local_info = self.server.get_user_info(user_name)
         pwd_hashval = sha1((user_pwd + local_info[1]).encode())
         if pwd_hashval.hexdigest() != local_info[0]:
             return False, ["password doesn't match"] 
@@ -197,9 +196,6 @@
                 pass
 
         rcode, reply = self.server.send_command_to_module(mod, cmd, param)
-        if self.server._verbose:
-            print('[b10-cmdctl] Finish send message \'%s\' to module %s' % (cmd, mod))
-
         ret = http.client.OK
         if rcode != 0:
             ret = http.client.BAD_REQUEST
@@ -214,65 +210,167 @@
     '''Get all modules' config data/specification from configmanager.
     receive command from client and resend it to proper module.
     '''
-
-    def __init__(self, verbose = False):
+    def __init__(self, httpserver, verbose = False):
+        ''' httpserver: the http server which use the object of
+        CommandControl to communicate with other modules. '''
         self._verbose = verbose
-        self.cc = isc.cc.Session()
-        self.cc.group_subscribe('Cmd-Ctrld')
-        self.module_spec = self.get_module_specification()
-        self.config_data = self.get_config_data()
-
+        self._httpserver = httpserver
+        self._lock = threading.Lock()
+        self._setup_session()
+        self.modules_spec = self._get_modules_specification()
+        self._config_data = self._get_config_data_from_config_manager()
+        self._serving = True
+        self._start_msg_handle_thread()
+
+    def _setup_session(self):
+        '''Setup the session for receving the commands
+        sent from other modules. There are two sessions 
+        for cmdctl, one(self.module_cc) is used for receiving 
+        commands sent from other modules, another one (self._cc) 
+        is used to send the command from Bindctl or other tools 
+        to proper modules.''' 
+        self._cc = isc.cc.Session()
+        self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
+                                              self.config_handler,
+                                              self.command_handler)
+        self._module_name = self._module_cc.get_module_spec().get_module_name()
+        self._cmdctl_config_data = self._module_cc.get_full_config()
+        self._module_cc.start()
+    
+    def _accounts_file_check(self, filepath):
+        ''' Check whether the accounts file is valid, each row
+        should be a list with 3 items.'''
+        csvfile = None
+        errstr = None
+        try:
+            csvfile = open(filepath)
+            reader = csv.reader(csvfile)
+            for row in reader:
+                a = (row[0], row[1], row[2])
+        except (IOError, IndexError) as e:
+            errstr = 'Invalid accounts file: ' + str(e)
+        finally:
+            if csvfile:
+                csvfile.close()
+
+        return errstr
+
+    def _config_data_check(self, new_config):
+        ''' Check whether the new config data is valid or
+        not. '''
+        errstr = None
+        for key in new_config:
+            if key == 'version':
+                continue
+            elif key in ['key_file', 'cert_file']:
+                #TODO, only check whether the file exist,
+                # further check need to be done: eg. whether
+                # the private/certificate is valid.
+                path = new_config[key]
+                if not os.path.exists(path):
+                    errstr = "the file doesn't exist: " + path
+            elif key == 'accounts_file':
+                errstr = self._accounts_file_check(new_config[key])
+            else:
+                errstr = 'unknown config item: ' + key
+            
+            if errstr != None:
+                self.log_info('Fail to apply config data, ' + errstr) 
+                return ccsession.create_answer(1, errstr)
+
+        return ccsession.create_answer(0)
+
+    def config_handler(self, new_config):
+        answer = self._config_data_check(new_config)
+        rcode, val = ccsession.parse_answer(answer)
+        if rcode != 0:
+            return answer
+
+        with self._lock:
+            for key in new_config:
+                if key in self._cmdctl_config_data:
+                    self._cmdctl_config_data[key] = new_config[key]
+        return answer
+
+    def command_handler(self, command, args):
+        answer = ccsession.create_answer(0)
+        if command == ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE:
+            with self._lock:
+                self.modules_spec[args[0]] = args[1]
+
+        elif command == ccsession.COMMAND_SHUTDOWN:
+            #When cmdctl get 'shutdown' command from boss, 
+            #shutdown the outer httpserver.
+            self._httpserver.shutdown()
+            self._serving = False
+
+        elif command == 'print_settings':
+            answer = ccsession.create_answer(0, self._cmdctl_config_data)
+        else:
+            answer = ccsession.create_answer(1, 'unknown command: ' + command)
+
+        return answer
+
+    def _start_msg_handle_thread(self):
+        ''' Start one thread to handle received message from msgq.'''
+        td = threading.Thread(target=self._handle_msg_from_msgq)
+        td.daemon = True
+        td.start()
+
+    def _handle_msg_from_msgq(self):
+        '''Process all the received commands with module session. '''
+        while self._serving:
+            self._module_cc.check_command() 
+ 
     def _parse_command_result(self, rcode, reply):
         '''Ignore the error reason when command rcode isn't 0, '''
         if rcode != 0:
             return {}
         return reply
 
+    def _get_config_data_from_config_manager(self):
+        '''Get config data for all modules from configmanager '''
+        rcode, reply = self.send_command('ConfigManager', ccsession.COMMAND_GET_CONFIG)
+        return self._parse_command_result(rcode, reply)
+
+    def _update_config_data(self, module_name, command_name):
+        '''Get lastest config data for all modules from configmanager '''
+        if module_name == 'ConfigManager' and command_name == ccsession.COMMAND_SET_CONFIG:
+            data = self._get_config_data_from_config_manager()
+            with self._lock:
+                self._config_data = data
+
     def get_config_data(self):
-        '''Get config data for all modules from configmanager '''
-        rcode, reply = self.send_command('ConfigManager', isc.config.ccsession.COMMAND_GET_CONFIG)
+        with self._lock:
+            data = self._config_data
+        return data
+
+    def get_modules_spec(self):
+        with self._lock:
+            spec = self.modules_spec
+        return spec
+
+    def _get_modules_specification(self):
+        '''Get all the modules' specification files. '''
+        rcode, reply = self.send_command('ConfigManager', ccsession.COMMAND_GET_MODULE_SPEC)
         return self._parse_command_result(rcode, reply)
 
-
-    def update_config_data(self, module_name, command_name):
-        '''Get lastest config data for all modules from configmanager '''
-        if module_name == 'ConfigManager' and command_name == isc.config.ccsession.COMMAND_SET_CONFIG:
-            self.config_data = self.get_config_data()
-
-    def get_module_specification(self):
-        rcode, reply = self.send_command('ConfigManager', isc.config.ccsession.COMMAND_GET_MODULE_SPEC)
-        return self._parse_command_result(rcode, reply)
-
-    def handle_recv_msg(self):
-        '''Handle received message, if 'shutdown' is received, return False'''
-        (message, env) = self.cc.group_recvmsg(True)
-        command, arg = isc.config.ccsession.parse_command(message)
-        while command:
-            if command == isc.config.ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE:
-                self.module_spec[arg[0]] = arg[1]
-            elif command == isc.config.ccsession.COMMAND_SHUTDOWN:
-                return False
-            (message, env) = self.cc.group_recvmsg(True)
-            command, arg = isc.config.ccsession.parse_command(message)
-        
-        return True
-    
     def send_command_with_check(self, module_name, command_name, params = None):
         '''Before send the command to modules, check if module_name, command_name
         parameters are legal according the spec file of the module.
-        Return rcode, dict.
+        Return rcode, dict. TODO, the rcode should be defined properly.
         rcode = 0: dict is the correct returned value.
         rcode > 0: dict is : { 'error' : 'error reason' }
         '''
-
         # core module ConfigManager does not have a specification file
         if module_name == 'ConfigManager':
             return self.send_command(module_name, command_name, params)
 
-        if module_name not in self.module_spec.keys():
+        specs = self.get_modules_spec()
+        if module_name not in specs.keys():
             return 1, {'error' : 'unknown module'}
        
-        spec_obj = isc.config.module_spec.ModuleSpec(self.module_spec[module_name], False)
+        spec_obj = isc.config.module_spec.ModuleSpec(specs[module_name], False)
         errors = []
         if not spec_obj.validate_command(command_name, params, errors):
             return 1, {'error': errors[0]}
@@ -281,123 +379,157 @@
 
     def send_command(self, module_name, command_name, params = None):
         '''Send the command from bindctl to proper module. '''
-        
-        errstr = 'no error'
+        errstr = 'unknown error'
         if self._verbose:
-            self.log_info('[b10-cmdctl] send command \'%s\' to %s\n' %(command_name, module_name))
-        try:
-            msg = isc.config.ccsession.create_command(command_name, params)
-            seq = self.cc.group_sendmsg(msg, module_name)
+            self.log_info("Begin send command '%s' to module '%s'" %(command_name, module_name))
+
+        if module_name == self._module_name:
+            # Process the command sent to cmdctl directly. 
+            answer = self.command_handler(command_name, params)
+        else:
+            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)
-            if answer:
-                try:
-                    rcode, arg = isc.config.ccsession.parse_answer(answer)
-                    if rcode == 0:
-                        self.update_config_data(module_name, command_name)
-                        if arg != None:
-                            return rcode, arg
-                        else:
-                            return rcode, {}
+            answer, env = self._cc.group_recvmsg(False, seq)
+
+        if self._verbose:
+            self.log_info("Finish send command '%s' to module '%s'" % (command_name, module_name))
+
+        if answer:
+            try:
+                rcode, arg = ccsession.parse_answer(answer)
+                if rcode == 0:
+                    self._update_config_data(module_name, command_name)
+                    if arg != None:
+                        return rcode, arg
                     else:
-                        # todo: exception
-                        errstr = str(answer['result'][1])
-                except isc.config.ccsession.ModuleCCSessionError as mcse:
-                    errstr = str("Error in ccsession answer:") + str(mcse)
-                    self.log_info(answer)
-        except Exception as e:
-            errstr = str(e)
-            self.log_info('\'%s\':[b10-cmdctl] fail send command \'%s\' to %s\n' % (e, command_name, module_name))
+                        return rcode, {}
+                else:
+                    # TODO: exception
+                    errstr = str(answer['result'][1])
+            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):
         sys.stdout.write("[b10-cmdctl] %s\n" % str(msg))
 
+    def get_cmdctl_config_data(self):
+        ''' If running in source code tree, use keyfile, certificate
+        and user accounts file in source code. '''
+        if "B10_FROM_SOURCE" in os.environ:
+            sysconf_path = os.environ["B10_FROM_SOURCE"] + "/src/bin/cmdctl/"
+            accountsfile  = sysconf_path + "cmdctl-accounts.csv"
+            keyfile = sysconf_path + "cmdctl-keyfile.pem"
+            certfile = sysconf_path + "cmdctl-certfile.pem"
+            return (keyfile, certfile, accountsfile)
+
+        with self._lock:
+            keyfile = self._cmdctl_config_data.get('key_file')
+            certfile = self._cmdctl_config_data.get('cert_file')
+            accountsfile = self._cmdctl_config_data.get('accounts_file')
+
+        return (keyfile, certfile, accountsfile)
+
 class SecureHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
     '''Make the server address can be reused.'''
     allow_reuse_address = True
 
-    def __init__(self, server_address, RequestHandlerClass, idle_timeout = 1200, verbose = False):
+    def __init__(self, server_address, RequestHandlerClass, 
+                 CommandControlClass,
+                 idle_timeout = 1200, verbose = False):
         '''idle_timeout: the max idle time for login'''
         http.server.HTTPServer.__init__(self, server_address, RequestHandlerClass)
         self.user_sessions = {}
         self.idle_timeout = idle_timeout
-        self.cmdctrl = CommandControl()
-        self.__is_shut_down = threading.Event()
-        self.__serving = False
+        self.cmdctl = CommandControlClass(self, verbose)
         self._verbose = verbose
-        self.user_infos = {}
-        self._read_user_info()
-
-    def _read_user_info(self):
-        '''Read all user's name and its' password from csv file.'''
-        csvfile = None
-        try:
-            csvfile = open(USER_INFO_FILE)
-            reader = csv.reader(csvfile)
-            for row in reader:
-                self.user_infos[row[0]] = [row[1], row[2]]
-        except Exception as e:
-            self.log_info('[b10-cmdctl] Fail to read user information :\'%s\'\n' % e)                
-        finally:
-            if csvfile:
-                csvfile.close()
-        
+        self._lock = threading.Lock()
+        self._user_infos = {}
+        self._accounts_file = None
+
+    def _create_user_info(self, accounts_file):
+        '''Read all user's name and its' salt, hashed password 
+        from accounts file.'''
+        if (self._accounts_file == accounts_file) and (len(self._user_infos) > 0): 
+            return
+
+        with self._lock:
+            self._user_infos = {}
+            csvfile = None
+            try:
+                csvfile = open(accounts_file)
+                reader = csv.reader(csvfile)
+                for row in reader:
+                    self._user_infos[row[0]] = [row[1], row[2]]
+            except (IOError, IndexError) as e:
+                self.log_info("Fail to read user database, %s" % e)                
+            finally:
+                if csvfile:
+                    csvfile.close()
+
+        self._accounts_file = accounts_file
+        if len(self._user_infos) == 0:
+            self.log_info("Fail to get user information, will deny any user")                
+         
+    def get_user_info(self, username):
+        '''Get user's salt and hashed string. If the user
+        doesn't exist, return None, or else, the list 
+        [salt, hashed password] will be returned.'''
+        with self._lock:
+            info = self._user_infos.get(username)
+        return info
+
     def save_user_session_id(self, session_id):
-        # Record user's id and login time.
+        ''' Record user's id and login time. '''
         self.user_sessions[session_id] = time.time()
         
+    def _check_key_and_cert(self, key, cert):
+        # TODO, check the content of key/certificate file 
+        if not os.path.exists(key):
+            raise CmdctlException("key file '%s' doesn't exist " % key)
+
+        if not os.path.exists(cert):
+            raise CmdctlException("certificate file '%s' doesn't exist " % cert)
+
+    def _wrap_socket_in_ssl_context(self, sock, key, cert):
+        try:
+            self._check_key_and_cert(key, cert)
+            ssl_sock = ssl.wrap_socket(sock,
+                                      server_side = True,
+                                      certfile = cert,
+                                      keyfile = key,
+                                      ssl_version = ssl.PROTOCOL_SSLv23)
+            return ssl_sock 
+        except (ssl.SSLError, CmdctlException) as err :
+            self.log_info("Deny client's connection because %s" % str(err))
+            self.close_request(sock)
+            # raise socket error to finish the request
+            raise socket.error
+
     def get_request(self):
         '''Get client request socket and wrap it in SSL context. '''
+        key, cert, account_file = self.cmdctl.get_cmdctl_config_data()
+        self._create_user_info(account_file)
         newsocket, fromaddr = self.socket.accept()
-        try:
-            connstream = ssl.wrap_socket(newsocket,
-                                     server_side = True,
-                                     certfile = CERTIFICATE_FILE,
-                                     keyfile = PRIVATE_KEY_FILE,
-                                     ssl_version = ssl.PROTOCOL_SSLv23)
-            return (connstream, fromaddr)
-        except ssl.SSLError as e :
-            self.log_info('[b10-cmdctl] deny client\'s invalid connection:\'%s\'\n' % e)
-            self.close_request(newsocket)
-            # raise socket error to finish the request
-            raise socket.error
-            
+        ssl_sock = self._wrap_socket_in_ssl_context(newsocket, key, cert)
+        return (ssl_sock, fromaddr)
 
     def get_reply_data_for_GET(self, id, module):
         '''Currently only support the following three url GET request '''
         rcode, reply = http.client.NO_CONTENT, []        
         if not module:
-            if id == 'config_data':
-               rcode, reply = http.client.OK, self.cmdctrl.config_data
-            elif id == 'module_spec':
-                rcode, reply = http.client.OK, self.cmdctrl.module_spec
+            if id == CONFIG_DATA_URL:
+                rcode, reply = http.client.OK, self.cmdctl.get_config_data()
+            elif id == MODULE_SPEC_URL:
+                rcode, reply = http.client.OK, self.cmdctl.get_modules_spec()
         
         return rcode, reply 
 
-        
-    def serve_forever(self, poll_interval = 0.5):
-        '''Start cmdctl as one tcp server. '''
-        self.__serving = True
-        self.__is_shut_down.clear()
-        while self.__serving:
-            if not self.cmdctrl.handle_recv_msg():
-                break
-
-            r, w, e = select.select([self], [], [], poll_interval)
-            if r:
-                self._handle_request_noblock()
-
-        self.__is_shut_down.set()
-    
-    def shutdown(self):
-        self.__serving = False
-        self.__is_shut_down.wait()
-
-
     def send_command_to_module(self, module_name, command_name, params):
-        return self.cmdctrl.send_command_with_check(module_name, command_name, params)
+        return self.cmdctl.send_command_with_check(module_name, command_name, params)
    
     def log_info(self, msg):
         sys.stdout.write("[b10-cmdctl] %s\n" % str(msg))
@@ -417,7 +549,8 @@
     ''' Start cmdctl as one https server. '''
     if verbose:
         sys.stdout.write("[b10-cmdctl] starting on %s port:%d\n" %(addr, port))
-    httpd = SecureHTTPServer((addr, port), SecureHTTPRequestHandler, idle_timeout, verbose)
+    httpd = SecureHTTPServer((addr, port), SecureHTTPRequestHandler, 
+                             CommandControl, idle_timeout, verbose)
     httpd.serve_forever()
 
 def check_port(option, opt_str, value, parser):
@@ -453,13 +586,12 @@
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
             help="display more about what is going on")
 
-
 if __name__ == '__main__':
     try:
+        set_signal_handler()
         parser = OptionParser(version = __version__)
         set_cmd_options(parser)
         (options, args) = parser.parse_args()
-        set_signal_handler()
         run(options.addr, options.port, options.idle_timeout, options.verbose)
     except isc.cc.SessionError as se:
         sys.stderr.write("[b10-cmdctl] Error creating b10-cmdctl, "

Modified: branches/trac191/src/bin/cmdctl/tests/Makefile.am
==============================================================================
--- branches/trac191/src/bin/cmdctl/tests/Makefile.am (original)
+++ branches/trac191/src/bin/cmdctl/tests/Makefile.am Fri Jul  9 10:04:58 2010
@@ -8,5 +8,7 @@
 	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/bin/cmdctl \
+	CMDCTL_SPEC_PATH=$(abs_top_builddir)/src/bin/cmdctl \
+	CMDCTL_SRC_PATH=$(abs_top_srcdir)/src/bin/cmdctl \
 	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
 	done

Modified: branches/trac191/src/bin/cmdctl/tests/cmdctl_test.in
==============================================================================
--- branches/trac191/src/bin/cmdctl/tests/cmdctl_test.in (original)
+++ branches/trac191/src/bin/cmdctl/tests/cmdctl_test.in Fri Jul  9 10:04:58 2010
@@ -18,10 +18,10 @@
 PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
 export PYTHON_EXEC
 
-BINDCTL_TEST_PATH=@abs_top_srcdir@/src/bin/cmdctl/tests
+CMDCTL_TEST_PATH=@abs_top_srcdir@/src/bin/cmdctl/tests
 PYTHONPATH=@abs_top_srcdir@/src/bin/cmdctl
 export PYTHONPATH
 
-cd ${BINDCTL_TEST_PATH}
+cd ${CMDCTL_TEST_PATH}
 exec ${PYTHON_EXEC} -O cmdctl_test.py $*
 

Modified: branches/trac191/src/bin/cmdctl/tests/cmdctl_test.py
==============================================================================
--- branches/trac191/src/bin/cmdctl/tests/cmdctl_test.py (original)
+++ branches/trac191/src/bin/cmdctl/tests/cmdctl_test.py Fri Jul  9 10:04:58 2010
@@ -16,7 +16,16 @@
 
 import unittest
 import socket
+import tempfile
 from cmdctl import *
+
+SPEC_FILE_PATH = '..' + os.sep
+if 'CMDCTL_SPEC_PATH' in os.environ:
+    SPEC_FILE_PATH = os.environ['CMDCTL_SPEC_PATH'] + os.sep
+
+SRC_FILE_PATH = '..' + os.sep
+if 'CMDCTL_SRC_PATH' in os.environ:
+    SRC_FILE_PATH = os.environ['CMDCTL_SRC_PATH'] + os.sep
 
 # Rewrite the class for unittest.
 class MySecureHTTPRequestHandler(SecureHTTPRequestHandler):
@@ -42,17 +51,20 @@
         os.remove('tmp.file')
     
 
-class MySecureHTTPServer(SecureHTTPServer):
+class FakeSecureHTTPServer(SecureHTTPServer):
     def __init__(self):
         self.user_sessions = {}
+        self.cmdctl = FakeCommandControlForTestRequestHandler()
+        self._verbose = True 
+        self._user_infos = {}
         self.idle_timeout = 1200
-        self.cmdctrl = MyCommandControl()
-        self._verbose = False
-
-class MyCommandControl(CommandControl):
+        self._lock = threading.Lock()
+
+class FakeCommandControlForTestRequestHandler(CommandControl):
     def __init__(self):
-        self.config_data = {}
-        self.module_spec = {}
+        self._config_data = {}
+        self.modules_spec = {}
+        self._lock = threading.Lock()
 
     def send_command(self, mod, cmd, param):
         return 0, {}
@@ -60,14 +72,17 @@
 
 class TestSecureHTTPRequestHandler(unittest.TestCase):
     def setUp(self):
+        self.old_stdout = sys.stdout
+        sys.stdout = open(os.devnull, 'w')
         self.handler = MySecureHTTPRequestHandler()
-        self.handler.server = MySecureHTTPServer()
+        self.handler.server = FakeSecureHTTPServer()
         self.handler.server.user_sessions = {}
-        self.handler.server.user_infos = {}
+        self.handler.server._user_infos = {}
         self.handler.headers = {}
         self.handler.rfile = open("check.tmp", 'w+b')
 
     def tearDown(self):
+        sys.stdout = self.old_stdout
         self.handler.rfile.close()
         os.remove('check.tmp')
 
@@ -145,7 +160,7 @@
     def test_check_user_name_and_pwd(self):
         self.handler.headers = {}
         ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertTrue(ret == False)
+        self.assertFalse(ret)
         self.assertEqual(msg, ['invalid username or password'])
 
     def test_check_user_name_and_pwd_1(self):
@@ -154,9 +169,9 @@
         self.handler.headers['Content-Length'] = len
         self.handler.rfile.seek(0, 0)
 
-        self.handler.server.user_infos['root'] = ['aa', 'aaa']
-        ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertTrue(ret == False)
+        self.handler.server._user_infos['root'] = ['aa', 'aaa']
+        ret, msg = self.handler._check_user_name_and_pwd()
+        self.assertFalse(ret)
         self.assertEqual(msg, ['password doesn\'t match'])
 
     def test_check_user_name_and_pwd_2(self):
@@ -166,7 +181,7 @@
         self.handler.rfile.seek(0, 0)
 
         ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertTrue(ret == False)
+        self.assertFalse(ret)
         self.assertEqual(msg, ['invalid username or password'])
 
     def test_check_user_name_and_pwd_3(self):
@@ -176,7 +191,7 @@
         self.handler.rfile.seek(0, 0)
 
         ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertTrue(ret == False)
+        self.assertFalse(ret)
         self.assertEqual(msg, ['need user name'])
 
     def test_check_user_name_and_pwd_4(self):
@@ -185,9 +200,9 @@
         self.handler.headers['Content-Length'] = len
         self.handler.rfile.seek(0, 0)
 
-        self.handler.server.user_infos['root'] = ['aa', 'aaa']
-        ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertTrue(ret == False)
+        self.handler.server._user_infos['root'] = ['aa', 'aaa']
+        ret, msg = self.handler._check_user_name_and_pwd()
+        self.assertFalse(ret)
         self.assertEqual(msg, ['need password'])
 
     def test_check_user_name_and_pwd_5(self):
@@ -197,7 +212,7 @@
         self.handler.rfile.seek(0, 0)
 
         ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertTrue(ret == False)
+        self.assertFalse(ret)
         self.assertEqual(msg, ['user doesn\'t exist'])
 
     def test_do_POST(self):
@@ -247,8 +262,8 @@
 
         self.handler.rfile.seek(0, 0)
         self.handler.path = '/module/command'
-        self.handler.server.cmdctrl.module_spec = {}
-        self.handler.server.cmdctrl.module_spec['module'] = self._gen_module_spec()
+        self.handler.server.cmdctl.modules_spec = {}
+        self.handler.server.cmdctl.modules_spec['module'] = self._gen_module_spec()
         rcode, reply = self.handler._handle_post_request()
         self.assertEqual(http.client.OK, rcode)
 
@@ -259,10 +274,160 @@
 
         self.handler.rfile.seek(0, 0)
         self.handler.path = '/module/command'
-        self.handler.server.cmdctrl.module_spec = {}
-        self.handler.server.cmdctrl.module_spec['module'] = self._gen_module_spec()
+        self.handler.server.cmdctl.modules_spec = {}
+        self.handler.server.cmdctl.modules_spec['module'] = self._gen_module_spec()
         rcode, reply = self.handler._handle_post_request()
         self.assertEqual(http.client.BAD_REQUEST, rcode)
 
+class MyCommandControl(CommandControl):
+    def _get_modules_specification(self):
+        return {}
+
+    def _get_config_data_from_config_manager(self):
+        return {}
+
+    def _setup_session(self):
+        spec_file = SPEC_FILE_PATH + 'cmdctl.spec'
+        module_spec = isc.config.module_spec_from_file(spec_file)
+        config = isc.config.config_data.ConfigData(module_spec)
+        self._module_name = 'Cmdctl'
+        self._cmdctl_config_data = config.get_full_config()
+
+    def _handle_msg_from_msgq(self): 
+        pass
+
+class TestCommandControl(unittest.TestCase):
+
+    def setUp(self):
+        self.old_stdout = sys.stdout
+        sys.stdout = open(os.devnull, 'w')
+        self.cmdctl = MyCommandControl(None, True)
+   
+    def tearDown(self):
+        sys.stdout = self.old_stdout
+
+    def _check_config(self, cmdctl):
+        key, cert, account = cmdctl.get_cmdctl_config_data()
+        self.assertIsNotNone(key)
+        self.assertIsNotNone(cert)
+        self.assertIsNotNone(account)
+
+    def test_get_cmdctl_config_data(self):
+        old_env = os.environ
+        if 'B10_FROM_SOURCE' in os.environ:
+            del os.environ['B10_FROM_SOURCE']
+        self.cmdctl.get_cmdctl_config_data() 
+        self._check_config(self.cmdctl)
+        os.environ = old_env
+
+        old_env = os.environ
+        os.environ['B10_FROM_SOURCE'] = '../'
+        self._check_config(self.cmdctl)
+        os.environ = old_env
+    
+    def test_parse_command_result(self):
+        self.assertEqual({}, self.cmdctl._parse_command_result(1, {'error' : 1}))
+        self.assertEqual({'a': 1}, self.cmdctl._parse_command_result(0, {'a' : 1}))
+
+    def _check_answer(self, answer, rcode_, msg_):
+        rcode, msg = ccsession.parse_answer(answer)
+        self.assertEqual(rcode, rcode_)
+        self.assertEqual(msg, msg_)
+
+    def test_command_handler(self):
+        answer = self.cmdctl.command_handler('unknown-command', None)
+        self._check_answer(answer, 1, 'unknown command: unknown-command')
+
+        answer = self.cmdctl.command_handler('print_settings', None)
+        rcode, msg = ccsession.parse_answer(answer)
+        self.assertEqual(rcode, 0)
+        self.assertTrue(msg != None)
+
+    def test_check_config_handler(self):
+        answer = self.cmdctl.config_handler({'non-exist': 123})
+        self._check_answer(answer, 1, 'unknown config item: non-exist')
+
+        old_env = os.environ
+        os.environ['B10_FROM_SOURCE'] = '../'
+        self._check_config(self.cmdctl)
+        os.environ = old_env
+
+        answer = self.cmdctl.config_handler({'key_file': '/user/non-exist_folder'})
+        self._check_answer(answer, 1, "the file doesn't exist: /user/non-exist_folder")
+
+        answer = self.cmdctl.config_handler({'cert_file': '/user/non-exist_folder'})
+        self._check_answer(answer, 1, "the file doesn't exist: /user/non-exist_folder")
+
+        answer = self.cmdctl.config_handler({'accounts_file': '/user/non-exist_folder'})
+        self._check_answer(answer, 1, 
+                "Invalid accounts file: [Errno 2] No such file or directory: '/user/non-exist_folder'")
+
+        # Test with invalid accounts file
+        file_name = 'tmp.account.file'
+        temp_file = open(file_name, 'w')
+        writer = csv.writer(temp_file)
+        writer.writerow(['a', 'b'])
+        temp_file.close()
+        answer = self.cmdctl.config_handler({'accounts_file': file_name})
+        self._check_answer(answer, 1, "Invalid accounts file: list index out of range")
+        os.remove(file_name)
+    
+    def test_send_command(self):
+        rcode, value = self.cmdctl.send_command('Cmdctl', 'print_settings', None)
+        self.assertEqual(rcode, 0)
+
+class MySecureHTTPServer(SecureHTTPServer):
+    def server_bind(self):
+        pass
+
+class TestSecureHTTPServer(unittest.TestCase):
+    def setUp(self):
+        self.old_stdout = sys.stdout
+        sys.stdout = open(os.devnull, 'w')
+        self.server = MySecureHTTPServer(('localhost', 8080), 
+                                         MySecureHTTPRequestHandler,
+                                         MyCommandControl, verbose=True)
+
+    def tearDown(self):
+        sys.stdout = self.old_stdout
+
+    def test_create_user_info(self):
+        self.server._create_user_info('/local/not-exist')
+        self.assertEqual(0, len(self.server._user_infos))
+
+        self.server._create_user_info(SRC_FILE_PATH + 'cmdctl-accounts.csv')
+        self.assertEqual(1, len(self.server._user_infos))
+        self.assertTrue('root' in self.server._user_infos)
+
+    def test_check_key_and_cert(self):
+        self.assertRaises(CmdctlException, self.server._check_key_and_cert,
+                         '/local/not-exist', 'cmdctl-keyfile.pem')
+
+        self.server._check_key_and_cert(SRC_FILE_PATH + 'cmdctl-keyfile.pem',
+                                        SRC_FILE_PATH + 'cmdctl-certfile.pem')
+
+    def test_wrap_sock_in_ssl_context(self):
+        sock = socket.socket()
+        self.assertRaises(socket.error, 
+                          self.server._wrap_socket_in_ssl_context,
+                          sock, 
+                          '../cmdctl-keyfile',
+                          '../cmdctl-certfile')
+
+        sock1 = socket.socket()
+        self.server._wrap_socket_in_ssl_context(sock1, 
+                          SRC_FILE_PATH + 'cmdctl-keyfile.pem',
+                          SRC_FILE_PATH + 'cmdctl-certfile.pem')
+
+class TestFuncNotInClass(unittest.TestCase):
+    def test_check_port(self):
+        self.assertRaises(OptionValueError, check_port, None, 'port', -1, None)        
+        self.assertRaises(OptionValueError, check_port, None, 'port', 65536, None)        
+        self.assertRaises(OptionValueError, check_addr, None, 'ipstr', 'a.b.d', None)        
+        self.assertRaises(OptionValueError, check_addr, None, 'ipstr', '1::0:a.b', None)        
+
+
 if __name__== "__main__":
     unittest.main()
+
+

Modified: branches/trac191/src/bin/host/host.cc
==============================================================================
--- branches/trac191/src/bin/host/host.cc (original)
+++ branches/trac191/src/bin/host/host.cc Fri Jul  9 10:04:58 2010
@@ -19,6 +19,8 @@
 #include <sys/time.h>       // for gettimeofday
 #include <sys/socket.h>     // networking functions and definitions on FreeBSD
 
+#include <unistd.h>
+
 #include <string>
 #include <iostream>
 

Modified: branches/trac191/src/bin/loadzone/Makefile.am
==============================================================================
--- branches/trac191/src/bin/loadzone/Makefile.am (original)
+++ branches/trac191/src/bin/loadzone/Makefile.am Fri Jul  9 10:04:58 2010
@@ -1,3 +1,5 @@
+SUBDIRS = tests/correct
+SUBDIRS += tests/error
 bin_SCRIPTS = b10-loadzone
 
 CLEANFILES = b10-loadzone
@@ -22,23 +24,27 @@
 	$(mkinstalldirs) $(DESTDIR)/@localstatedir@/@PACKAGE@
 # TODO: permissions handled later
 
-EXTRA_DIST += testdata/README
-EXTRA_DIST += testdata/dsset-subzone.example.com.
-EXTRA_DIST += testdata/example.com
-EXTRA_DIST += testdata/example.com.signed
-EXTRA_DIST += testdata/Kexample.com.+005+04456.key
-EXTRA_DIST += testdata/Kexample.com.+005+04456.private
-EXTRA_DIST += testdata/Kexample.com.+005+33495.key
-EXTRA_DIST += testdata/Kexample.com.+005+33495.private
-EXTRA_DIST += testdata/Ksql1.example.com.+005+12447.key
-EXTRA_DIST += testdata/Ksql1.example.com.+005+12447.private
-EXTRA_DIST += testdata/Ksql1.example.com.+005+33313.key
-EXTRA_DIST += testdata/Ksql1.example.com.+005+33313.private
-EXTRA_DIST += testdata/Ksql2.example.com.+005+38482.key
-EXTRA_DIST += testdata/Ksql2.example.com.+005+38482.private
-EXTRA_DIST += testdata/Ksql2.example.com.+005+63192.key
-EXTRA_DIST += testdata/Ksql2.example.com.+005+63192.private
-EXTRA_DIST += testdata/sql1.example.com
-EXTRA_DIST += testdata/sql1.example.com.signed
-EXTRA_DIST += testdata/sql2.example.com
-EXTRA_DIST += testdata/sql2.example.com.signed
+EXTRA_DIST += tests/normal/README
+EXTRA_DIST += tests/normal/dsset-subzone.example.com
+EXTRA_DIST += tests/normal/example.com
+EXTRA_DIST += tests/normal/example.com.signed
+EXTRA_DIST += tests/normal/Kexample.com.+005+04456.key
+EXTRA_DIST += tests/normal/Kexample.com.+005+04456.private
+EXTRA_DIST += tests/normal/Kexample.com.+005+33495.key
+EXTRA_DIST += tests/normal/Kexample.com.+005+33495.private
+EXTRA_DIST += tests/normal/Ksql1.example.com.+005+12447.key
+EXTRA_DIST += tests/normal/Ksql1.example.com.+005+12447.private
+EXTRA_DIST += tests/normal/Ksql1.example.com.+005+33313.key
+EXTRA_DIST += tests/normal/Ksql1.example.com.+005+33313.private
+EXTRA_DIST += tests/normal/Ksql2.example.com.+005+38482.key
+EXTRA_DIST += tests/normal/Ksql2.example.com.+005+38482.private
+EXTRA_DIST += tests/normal/Ksql2.example.com.+005+63192.key
+EXTRA_DIST += tests/normal/Ksql2.example.com.+005+63192.private
+EXTRA_DIST += tests/normal/sql1.example.com
+EXTRA_DIST += tests/normal/sql1.example.com.signed
+EXTRA_DIST += tests/normal/sql2.example.com
+EXTRA_DIST += tests/normal/sql2.example.com.signed
+
+pytest:
+	$(SHELL) tests/correct/correct_test.sh
+	$(SHELL) tests/error/error_test.sh

Modified: branches/trac191/src/bin/loadzone/b10-loadzone.py.in
==============================================================================
--- branches/trac191/src/bin/loadzone/b10-loadzone.py.in (original)
+++ branches/trac191/src/bin/loadzone/b10-loadzone.py.in Fri Jul  9 10:04:58 2010
@@ -19,7 +19,8 @@
 import re, getopt
 import isc.datasrc
 from isc.datasrc.master import MasterFile
-
+import time
+import os
 #########################################################################
 # usage: print usage note and exit
 #########################################################################
@@ -57,23 +58,32 @@
     if len(args) != 1:
         usage()
     zonefile = args[0]
-
+    verbose = os.isatty(sys.stdout.fileno())
     try:
-        master = MasterFile(zonefile, initial_origin)
+        master = MasterFile(zonefile, initial_origin, verbose)
     except Exception as e:
-        print("Error reading zone file: " + str(e))
+        sys.stderr.write("Error reading zone file: %s\n" % str(e))
         exit(1)
 
     try:
         zone = master.zonename()
+        if verbose:
+            sys.stdout.write("Using SQLite3 database file %s\n" % dbfile)
+            sys.stdout.write("Zone name is %s\n" % zone)
+            sys.stdout.write("Loading file \"%s\"\n" % zonefile)
     except Exception as e:
-        print("Error reading zone file: " + str(e))
+        sys.stdout.write("\n")
+        sys.stderr.write("Error reading zone file: %s\n" % str(e))
         exit(1)
 
     try:
         isc.datasrc.sqlite3_ds.load(dbfile, zone, master.zonedata)
+        if verbose:
+            master.closeverbose()
+            sys.stdout.write("\nDone.\n")
     except Exception as e:
-        print("Error loading database: " + str(e))
+        sys.stdout.write("\n")
+        sys.stderr.write("Error loading database: %s\n"% str(e))
         exit(1)
 
 if __name__ == "__main__":

Modified: branches/trac191/src/bin/loadzone/run_loadzone.sh.in
==============================================================================
--- branches/trac191/src/bin/loadzone/run_loadzone.sh.in (original)
+++ branches/trac191/src/bin/loadzone/run_loadzone.sh.in Fri Jul  9 10:04:58 2010
@@ -21,5 +21,5 @@
 PYTHONPATH=@abs_top_builddir@/src/lib/python
 export PYTHONPATH
 
-LOADZONE_PATH=@abs_top_srcdir@/src/bin/loadzone
+LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone
 exec ${LOADZONE_PATH}/b10-loadzone $*

Modified: branches/trac191/src/bin/xfrin/tests/Makefile.am
==============================================================================
--- branches/trac191/src/bin/xfrin/tests/Makefile.am (original)
+++ branches/trac191/src/bin/xfrin/tests/Makefile.am Fri Jul  9 10:04:58 2010
@@ -1,7 +1,5 @@
 PYTESTS = xfrin_test.py
 EXTRA_DIST = $(PYTESTS)
-
-if HAVE_BOOST_PYTHON
 
 # later will have configure option to choose this, like: coverage run --branch
 PYCOVERAGE = $(PYTHON)
@@ -9,8 +7,6 @@
 check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+	env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
 	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
 	done
-
-endif

Modified: branches/trac191/src/bin/xfrin/tests/xfrin_test.py
==============================================================================
--- branches/trac191/src/bin/xfrin/tests/xfrin_test.py (original)
+++ branches/trac191/src/bin/xfrin/tests/xfrin_test.py Fri Jul  9 10:04:58 2010
@@ -23,7 +23,7 @@
 # Commonly used (mostly constant) test parameters
 #
 TEST_ZONE_NAME = "example.com"
-TEST_RRCLASS = rr_class.IN()
+TEST_RRCLASS = RRClass.IN()
 TEST_DB_FILE = 'db_file'
 TEST_MASTER_IPV4_ADDRESS = '127.0.0.1'
 TEST_MASTER_IPV4_ADDRINFO = (socket.AF_INET, socket.SOCK_STREAM,
@@ -37,16 +37,16 @@
 # If some other process uses this port test will fail.
 TEST_MASTER_PORT = '53535'
 
-soa_rdata = create_rdata(rr_type.SOA(), TEST_RRCLASS,
-                         'master.example.com. admin.example.com ' +
-                         '1234 3600 1800 2419200 7200')
-soa_rrset = rrset(name(TEST_ZONE_NAME), TEST_RRCLASS, rr_type.SOA(),
-                  rr_ttl(3600))
+soa_rdata = Rdata(RRType.SOA(), TEST_RRCLASS,
+                  'master.example.com. admin.example.com ' +
+                  '1234 3600 1800 2419200 7200')
+soa_rrset = RRset(Name(TEST_ZONE_NAME), TEST_RRCLASS, RRType.SOA(),
+                  RRTTL(3600))
 soa_rrset.add_rdata(soa_rdata)
-example_axfr_question = question(name(TEST_ZONE_NAME), TEST_RRCLASS,
-                                 rr_type.AXFR())
-example_soa_question = question(name(TEST_ZONE_NAME), TEST_RRCLASS,
-                                 rr_type.SOA())
+example_axfr_question = Question(Name(TEST_ZONE_NAME), TEST_RRCLASS,
+                                 RRType.AXFR())
+example_soa_question = Question(Name(TEST_ZONE_NAME), TEST_RRCLASS,
+                                 RRType.SOA())
 default_questions = [example_axfr_question]
 default_answers = [soa_rrset]
 
@@ -121,26 +121,25 @@
         return len(data)
 
     def create_response_data(self, response = True, bad_qid = False,
-                             rcode = rcode.NOERROR(),
+                             rcode = Rcode.NOERROR(),
                              questions = default_questions,
                              answers = default_answers):
-        resp = message(message_mode.RENDER)
+        resp = Message(Message.RENDER)
         qid = self.qid
         if bad_qid:
             qid += 1
         resp.set_qid(qid)
-        resp.set_opcode(op_code.QUERY())
+        resp.set_opcode(Opcode.QUERY())
         resp.set_rcode(rcode)
         if response:
-            resp.set_header_flag(message_flag.QR())
+            resp.set_header_flag(MessageFlag.QR())
         [resp.add_question(q) for q in questions]
-        [resp.add_rrset(section.ANSWER(), a) for a in answers]
-
-        obuf = output_buffer(0)
-        renderer = message_render(obuf)
+        [resp.add_rrset(Section.ANSWER(), a) for a in answers]
+
+        renderer = MessageRenderer()
         resp.to_wire(renderer)
-        reply_data = struct.pack('H', socket.htons(obuf.get_length()))
-        reply_data += obuf.get_data()
+        reply_data = struct.pack('H', socket.htons(renderer.get_length()))
+        reply_data += renderer.get_data()
 
         return reply_data
 
@@ -158,7 +157,7 @@
             'questions': [example_soa_question],
             'bad_qid': False,
             'response': True,
-            'rcode': rcode.NOERROR(),
+            'rcode': Rcode.NOERROR(),
             'axfr_after_soa': self._create_normal_response_data
             }
 
@@ -189,11 +188,11 @@
         c.close()
 
     def test_init_chclass(self):
-        c = XfrinConnection({}, 'example.com.', rr_class.CH(), TEST_DB_FILE,
+        c = XfrinConnection({}, 'example.com.', RRClass.CH(), TEST_DB_FILE,
                             threading.Event(), TEST_MASTER_IPV4_ADDRINFO)
-        axfrmsg = c._create_query(rr_type.AXFR())
-        self.assertEqual(question_iter(axfrmsg).get_question().get_class(),
-                         rr_class.CH())
+        axfrmsg = c._create_query(RRType.AXFR())
+        self.assertEqual(axfrmsg.get_question()[0].get_class(),
+                         RRClass.CH())
         c.close()
 
     def test_response_with_invalid_msg(self):
@@ -201,41 +200,41 @@
         self.assertRaises(XfrinTestException, self._handle_xfrin_response)
 
     def test_response_without_end_soa(self):
-        self.conn._send_query(rr_type.AXFR())
+        self.conn._send_query(RRType.AXFR())
         self.conn.reply_data = self.conn.create_response_data()
         self.assertRaises(XfrinTestException, self._handle_xfrin_response)
 
     def test_response_bad_qid(self):
-        self.conn._send_query(rr_type.AXFR())
+        self.conn._send_query(RRType.AXFR())
         self.conn.reply_data = self.conn.create_response_data(bad_qid = True)
         self.assertRaises(XfrinException, self._handle_xfrin_response)
 
     def test_response_non_response(self):
-        self.conn._send_query(rr_type.AXFR())
+        self.conn._send_query(RRType.AXFR())
         self.conn.reply_data = self.conn.create_response_data(response = False)
         self.assertRaises(XfrinException, self._handle_xfrin_response)
 
     def test_response_error_code(self):
-        self.conn._send_query(rr_type.AXFR())
+        self.conn._send_query(RRType.AXFR())
         self.conn.reply_data = self.conn.create_response_data(
-            rcode=rcode.SERVFAIL())
+            rcode=Rcode.SERVFAIL())
         self.assertRaises(XfrinException, self._handle_xfrin_response)
 
     def test_response_multi_question(self):
-        self.conn._send_query(rr_type.AXFR())
+        self.conn._send_query(RRType.AXFR())
         self.conn.reply_data = self.conn.create_response_data(
             questions=[example_axfr_question, example_axfr_question])
         self.assertRaises(XfrinException, self._handle_xfrin_response)
 
     def test_response_empty_answer(self):
-        self.conn._send_query(rr_type.AXFR())
+        self.conn._send_query(RRType.AXFR())
         self.conn.reply_data = self.conn.create_response_data(answers=[])
         # Should an empty answer trigger an exception?  Even though it's very
         # unusual it's not necessarily invalid.  Need to revisit.
         self.assertRaises(XfrinException, self._handle_xfrin_response)
 
     def test_response_non_response(self):
-        self.conn._send_query(rr_type.AXFR())
+        self.conn._send_query(RRType.AXFR())
         self.conn.reply_data = self.conn.create_response_data(response = False)
         self.assertRaises(XfrinException, self._handle_xfrin_response)
 
@@ -247,7 +246,7 @@
 
     def test_soacheck_with_bad_response(self):
         self.conn.response_generator = self._create_broken_response_data
-        self.assertRaises(UserWarning, self.conn._check_soa_serial)
+        self.assertRaises(MessageTooShort, self.conn._check_soa_serial)
 
     def test_soacheck_badqid(self):
         self.soa_response_params['bad_qid'] = True
@@ -260,14 +259,14 @@
         self.assertRaises(XfrinException, self.conn._check_soa_serial)
 
     def test_soacheck_error_code(self):
-        self.soa_response_params['rcode'] = rcode.SERVFAIL()
+        self.soa_response_params['rcode'] = Rcode.SERVFAIL()
         self.conn.response_generator = self._create_soa_response_data
         self.assertRaises(XfrinException, self.conn._check_soa_serial)
 
     def test_response_shutdown(self):
         self.conn.response_generator = self._create_normal_response_data
         self.conn._shutdown_event.set()
-        self.conn._send_query(rr_type.AXFR())
+        self.conn._send_query(RRType.AXFR())
         self.assertRaises(XfrinException, self._handle_xfrin_response)
 
     def test_response_timeout(self):
@@ -282,13 +281,13 @@
 
     def test_response_bad_message(self):
         self.conn.response_generator = self._create_broken_response_data
-        self.conn._send_query(rr_type.AXFR())
+        self.conn._send_query(RRType.AXFR())
         self.assertRaises(Exception, self._handle_xfrin_response)
 
     def test_response(self):
         # normal case.
         self.conn.response_generator = self._create_normal_response_data
-        self.conn._send_query(rr_type.AXFR())
+        self.conn._send_query(RRType.AXFR())
         # two SOAs, and only these have been transfered.  the 2nd SOA is just
         # a marker, so only 1 RR has been provided in the iteration.
         self.assertEqual(self._handle_xfrin_response(), 1)
@@ -317,7 +316,10 @@
 
     def test_do_soacheck_broken_response(self):
         self.conn.response_generator = self._create_broken_response_data
-        self.assertEqual(self.conn.do_xfrin(True), XFRIN_FAIL)
+        # XXX: TODO: this test failed here, should xfr not raise an
+        # exception but simply drop and return FAIL?
+        #self.assertEqual(self.conn.do_xfrin(True), XFRIN_FAIL)
+        self.assertRaises(MessageTooShort, self.conn.do_xfrin, True)
 
     def test_do_soacheck_badqid(self):
         # the QID mismatch would internally trigger a XfrinException exception,
@@ -488,12 +490,12 @@
                                                   self.args)['result'][0], 1)
 
     def test_command_handler_retransfer_nomodule(self):
-        dns_module = sys.modules['bind10_dns'] # this must exist
-        del sys.modules['bind10_dns']
+        dns_module = sys.modules['libdns_python'] # this must exist
+        del sys.modules['libdns_python']
         self.assertEqual(self.xfr.command_handler("retransfer",
                                                   self.args)['result'][0], 1)
         # sys.modules is global, so we must recover it
-        sys.modules['bind10_dns'] = dns_module
+        sys.modules['libdns_python'] = dns_module
 
     def test_command_handler_refresh(self):
         # at this level, refresh is no different than retransfer.

Modified: branches/trac191/src/bin/xfrin/xfrin.py.in
==============================================================================
--- branches/trac191/src/bin/xfrin/xfrin.py.in (original)
+++ branches/trac191/src/bin/xfrin/xfrin.py.in Fri Jul  9 10:04:58 2010
@@ -29,7 +29,7 @@
 from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
 try:
-    from bind10_dns import *
+    from libdns_python import *
 except ImportError as e:
     # C++ loadable module may not be installed; even so the xfrin process
     # must keep running, so we warn about it and move forward.
@@ -40,11 +40,14 @@
 # installed on the system
 if "B10_FROM_BUILD" in os.environ:
     SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrin"
+    AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
 else:
     PREFIX = "@prefix@"
     DATAROOTDIR = "@datarootdir@"
     SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
+    AUTH_SPECFILE_PATH = SPECFILE_PATH
 SPECFILE_LOCATION = SPECFILE_PATH + "/xfrin.spec"
+AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + "/auth.spec"
 
 
 __version__ = 'BIND10'
@@ -98,14 +101,13 @@
     def _create_query(self, query_type):
         '''Create dns query message. '''
 
-        msg = message(message_mode.RENDER)
+        msg = Message(Message.RENDER)
         query_id = random.randint(0, 0xFFFF)
         self._query_id = query_id
         msg.set_qid(query_id)
-        msg.set_opcode(op_code.QUERY())
-        msg.set_rcode(rcode.NOERROR())
-        query_question = question(name(self._zone_name), self._rrclass,
-                                  query_type)
+        msg.set_opcode(Opcode.QUERY())
+        msg.set_rcode(Rcode.NOERROR())
+        query_question = Question(Name(self._zone_name), self._rrclass, query_type)
         msg.add_question(query_question)
         return msg
 
@@ -120,13 +122,12 @@
         '''Send query message over TCP. '''
 
         msg = self._create_query(query_type)
-        obuf = output_buffer(0)
-        render = message_render(obuf)
+        render = MessageRenderer()
         msg.to_wire(render)
-        header_len = struct.pack('H', socket.htons(obuf.get_length()))
+        header_len = struct.pack('H', socket.htons(render.get_length()))
 
         self._send_data(header_len)
-        self._send_data(obuf.get_data())
+        self._send_data(render.get_data())
 
     def _asyncore_loop(self):
         '''
@@ -157,12 +158,12 @@
         True: soa serial in master is bigger
         '''
 
-        self._send_query(rr_type.SOA())
+        self._send_query(RRType("SOA"))
         data_len = self._get_request_response(2)
         msg_len = socket.htons(struct.unpack('H', data_len)[0])
         soa_response = self._get_request_response(msg_len)
-        msg = message(message_mode.PARSE)
-        msg.from_wire(input_buffer(soa_response))
+        msg = Message(Message.PARSE)
+        msg.from_wire(soa_response)
 
         # perform some minimal level validation.  It's an open issue how
         # strict we should be (see the comment in _check_response_header())
@@ -181,11 +182,12 @@
             if check_soa:
                 logstr = 'SOA check for \'%s\' ' % self._zone_name
                 ret =  self._check_soa_serial()
-            
+
             logstr = 'transfer of \'%s\': AXFR ' % self._zone_name
             if ret == XFRIN_OK:
                 self.log_msg(logstr + 'started')
-                self._send_query(rr_type.AXFR())
+                # TODO: .AXFR() RRType.AXFR()
+                self._send_query(RRType(252))
                 isc.datasrc.sqlite3_ds.load(self._db_file, self._zone_name,
                                             self._handle_xfrin_response)
 
@@ -226,10 +228,10 @@
         # cause interoperability trouble with stricter checks.
 
         msg_rcode = msg.get_rcode()
-        if msg_rcode != rcode.NOERROR():
+        if msg_rcode != Rcode.NOERROR():
             raise XfrinException('error response: %s' % msg_rcode.to_text())
 
-        if not msg.get_header_flag(message_flag.QR()):
+        if not msg.get_header_flag(MessageFlag.QR()):
             raise XfrinException('response is not a response ')
 
         if msg.get_qid() != self._query_id:
@@ -240,28 +242,24 @@
 
         self._check_response_header(msg)
 
-        if msg.get_rr_count(section.ANSWER()) == 0:
+        if msg.get_rr_count(Section.ANSWER()) == 0:
             raise XfrinException('answer section is empty')
 
-        if msg.get_rr_count(section.QUESTION()) > 1:
+        if msg.get_rr_count(Section.QUESTION()) > 1:
             raise XfrinException('query section count greater than 1')
 
-    def _handle_answer_section(self, rrset_iter):
+    def _handle_answer_section(self, answer_section):
         '''Return a generator for the reponse in one tcp package to a zone transfer.'''
 
-        while not rrset_iter.is_last():
-            rrset = rrset_iter.get_rrset()
-            rrset_iter.next()
+        for rrset in answer_section:
             rrset_name = rrset.get_name().to_text()
             rrset_ttl = int(rrset.get_ttl().to_text())
             rrset_class = rrset.get_class().to_text()
             rrset_type = rrset.get_type().to_text()
 
-            rdata_iter = rrset.get_rdata_iterator()
-            rdata_iter.first()
-            while not rdata_iter.is_last():
+            for rdata in rrset.get_rdata():
                 # Count the soa record count
-                if rrset.get_type() == rr_type.SOA():
+                if rrset.get_type() == RRType("SOA"):
                     self._soa_rr_count += 1
 
                     # XXX: the current DNS message parser can't preserve the
@@ -273,24 +271,22 @@
                         # Avoid inserting soa record twice
                         break
 
-                rdata_text = rdata_iter.get_current().to_text()
+                rdata_text = rdata.to_text()
                 yield (rrset_name, rrset_ttl, rrset_class, rrset_type,
                        rdata_text)
-                rdata_iter.next()
 
     def _handle_xfrin_response(self):
         '''Return a generator for the response to a zone transfer. '''
-
         while True:
             data_len = self._get_request_response(2)
             msg_len = socket.htons(struct.unpack('H', data_len)[0])
             recvdata = self._get_request_response(msg_len)
-            msg = message(message_mode.PARSE)
-            msg.from_wire(input_buffer(recvdata))
+            msg = Message(Message.PARSE)
+            msg.from_wire(recvdata)
             self._check_response_status(msg)
-
-            rrset_iter = section_iter(msg, section.ANSWER())
-            for rr in self._handle_answer_section(rrset_iter):
+            
+            answer_section = msg.get_section(Section.ANSWER())
+            for rr in self._handle_answer_section(answer_section):
                 yield rr
 
             if self._soa_rr_count == 2:
@@ -411,7 +407,7 @@
                 # The default RR class is IN.  We should fix this so that
                 # the class is passed in the command arg (where we specify
                 # the default)
-                rrclass = rr_class.IN()
+                rrclass = RRClass.IN()
                 zone_name, master_addr, db_file = self._parse_cmd_params(args)
                 ret = self.xfrin_start(zone_name, rrclass, db_file, master_addr,
                                    False if command == 'retransfer' else True)
@@ -441,7 +437,18 @@
         db_file = args.get('db_file')
         if not db_file:
             #TODO, the db file path should be got in auth server's configuration
-            db_file = '@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3'
+            # if we need access to this configuration more often, we
+            # should add it on start, and not remove it here
+            # (or, if we have writable ds, we might not need this in
+            # the first place)
+            self._cc.add_remote_config(AUTH_SPECFILE_LOCATION)
+            db_file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
+            if is_default and "B10_FROM_BUILD" in os.environ:
+                # this too should be unnecessary, but currently the
+                # 'from build' override isn't stored in the config
+                # (and we don't have writable datasources yet)
+                db_file = os.environ["B10_FROM_BUILD"] + os.sep + "bind10_zones.sqlite3"
+            self._cc.remove_remote_config(AUTH_SPECFILE_LOCATION)
 
         return (zone_name, master_addrinfo, db_file)
 
@@ -451,8 +458,8 @@
 
     def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo,
                     check_soa = True):
-        if "bind10_dns" not in sys.modules:
-            return (1, "xfrin failed, can't load dns message python library: 'bind10_dns'")
+        if "libdns_python" not in sys.modules:
+            return (1, "xfrin failed, can't load dns message python library: 'libdns_python'")
 
         # check max_transfer_in, else return quota error
         if self.recorder.count() >= self._max_transfers_in:

Modified: branches/trac191/src/bin/xfrout/run_b10-xfrout.sh.in
==============================================================================
--- branches/trac191/src/bin/xfrout/run_b10-xfrout.sh.in (original)
+++ branches/trac191/src/bin/xfrout/run_b10-xfrout.sh.in Fri Jul  9 10:04:58 2010
@@ -19,7 +19,7 @@
 export PYTHON_EXEC
 
 MYPATH_PATH=@abs_top_builddir@/src/bin/xfrout
-PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/xfr/.libs
+PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/dns/python/.libs
 export PYTHONPATH
 
 cd ${MYPATH_PATH}

Modified: branches/trac191/src/bin/xfrout/tests/Makefile.am
==============================================================================
--- branches/trac191/src/bin/xfrout/tests/Makefile.am (original)
+++ branches/trac191/src/bin/xfrout/tests/Makefile.am Fri Jul  9 10:04:58 2010
@@ -1,7 +1,5 @@
 PYTESTS = xfrout_test.py
 EXTRA_DIST = $(PYTESTS)
-
-if HAVE_BOOST_PYTHON
 
 # later will have configure option to choose this, like: coverage run --branch
 PYCOVERAGE = $(PYTHON)
@@ -9,8 +7,6 @@
 check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
+	env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
 	$(PYCOVERAGE) $(abs_srcdir)/$$pytest ; \
 	done
-
-endif

Modified: branches/trac191/src/bin/xfrout/tests/xfrout_test.py
==============================================================================
--- branches/trac191/src/bin/xfrout/tests/xfrout_test.py (original)
+++ branches/trac191/src/bin/xfrout/tests/xfrout_test.py Fri Jul  9 10:04:58 2010
@@ -19,7 +19,7 @@
 import unittest
 import os
 from isc.cc.session import *
-from bind10_dns import *
+from libdns_python import *
 from xfrout import *
 
 # our fake socket, where we can read and insert messages
@@ -46,8 +46,8 @@
     
     def read_msg(self):
         sent_data = self.readsent()
-        get_msg = message(message_mode.PARSE)
-        get_msg.from_wire(input_buffer(bytes(sent_data[2:])))
+        get_msg = Message(Message.PARSE)
+        get_msg.from_wire(bytes(sent_data[2:]))
         return get_msg
     
     def clear_send(self):
@@ -69,15 +69,16 @@
 
 class TestXfroutSession(unittest.TestCase):
     def getmsg(self):
-        msg = message(message_mode.PARSE)
-        msg.from_wire(input_buffer(self.mdata))
+        msg = Message(Message.PARSE)
+        msg.from_wire(self.mdata)
         return msg
 
     def setUp(self):
         request = MySocket(socket.AF_INET,socket.SOCK_STREAM)
-        self.xfrsess = MyXfroutSession(request, None, None)
+        self.log = isc.log.NSLogger('xfrout', '',  severity = 'critical', log_to_console = False )
+        self.xfrsess = MyXfroutSession(request, None, None, self.log)
         self.xfrsess.server = Dbserver()
-        self.mdata = b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01'
+        self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
         self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
         self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200')
 
@@ -96,7 +97,7 @@
 
     def test_reply_xfrout_query_with_error_rcode(self):
         msg = self.getmsg()
-        self.xfrsess._reply_query_with_error_rcode(msg, self.sock, rcode(3))
+        self.xfrsess._reply_query_with_error_rcode(msg, self.sock, Rcode(3))
         get_msg = self.sock.read_msg()
         self.assertEqual(get_msg.get_rcode().to_text(), "NXDOMAIN") 
      
@@ -110,7 +111,7 @@
         self.assertEqual(msg.get_qid(), qid)
         self.assertEqual(msg.get_opcode(), opcode)
         self.assertEqual(msg.get_rcode(), rcode)
-        self.assertTrue(msg.get_header_flag(message_flag.AA()))
+        self.assertTrue(msg.get_header_flag(MessageFlag.AA()))
 
     def test_reply_query_with_format_error(self):
          
@@ -122,11 +123,10 @@
     def test_create_rrset_from_db_record(self):
         rrset = self.xfrsess._create_rrset_from_db_record(self.soa_record)
         self.assertEqual(rrset.get_name().to_text(), "example.com.")
-        self.assertEqual(rrset.get_class(), rr_class.IN())
+        self.assertEqual(rrset.get_class(), RRClass("IN"))
         self.assertEqual(rrset.get_type().to_text(), "SOA")
-        rdata_iter = rrset.get_rdata_iterator()
-        rdata_iter.first()
-        self.assertEqual(rdata_iter.get_current().to_text(), self.soa_record[7])
+        rdata = rrset.get_rdata()
+        self.assertEqual(rdata[0].to_text(), self.soa_record[7])
 
     def test_send_message_with_last_soa(self):
         rrset_soa = self.xfrsess._create_rrset_from_db_record(self.soa_record)
@@ -136,18 +136,17 @@
         self.xfrsess._send_message_with_last_soa(msg, self.sock, rrset_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_rrset_iter = section_iter(get_msg, section.ANSWER())
-        answer = answer_rrset_iter.get_rrset()
+        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_rrset_iter = section_iter(get_msg, section.ANSWER())
+        answer = get_msg.get_section(Section.ANSWER())[0]#answer_rrset_iter.get_rrset()
         self.assertEqual(answer.get_name().to_text(), "example.com.")
-        self.assertEqual(answer.get_class(), rr_class.IN())
+        self.assertEqual(answer.get_class(), RRClass("IN"))
         self.assertEqual(answer.get_type().to_text(), "SOA")
-        rdata_iter = answer.get_rdata_iterator()
-        rdata_iter.first()
-        self.assertEqual(rdata_iter.get_current().to_text(), self.soa_record[7])
+        rdata = answer.get_rdata()
+        self.assertEqual(rdata[0].to_text(), self.soa_record[7])
 
     def test_get_message_len(self):
         msg = self.getmsg()
@@ -205,7 +204,7 @@
     def test_dns_xfrout_start_notauth(self):
         self.xfrsess._get_query_zone_name = self.default
         def notauth(formpara):
-            return rcode.NOTAUTH()
+            return Rcode.NOTAUTH()
         self.xfrsess._check_xfrout_available = notauth
         self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
         get_msg = self.sock.read_msg()
@@ -214,7 +213,7 @@
     def test_dns_xfrout_start_noerror(self):
         self.xfrsess._get_query_zone_name = self.default
         def noerror(form):
-            return rcode.NOERROR() 
+            return Rcode.NOERROR() 
         self.xfrsess._check_xfrout_available = noerror
         
         def myreply(msg, sock, zonename):
@@ -236,28 +235,35 @@
         sqlite3_ds.get_zone_datas = get_zone_datas
         self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock, "example.com.")
         reply_msg = self.sock.read_msg()
-        self.assertEqual(reply_msg.get_rr_count(section.ANSWER()), 2)
-
-        # set event
-        self.xfrsess.server._shutdown_event.set()
-        self.assertRaises(XfroutException, self.xfrsess._reply_xfrout_query, self.getmsg(), self.sock, "example.com.")
+        self.assertEqual(reply_msg.get_rr_count(Section.ANSWER()), 2)
+
+class MyCCSession():
+    def __init__(self):
+        pass
+
+    def get_remote_config_value(self, module_name, identifier):
+        if module_name == "Auth" and identifier == "database_file":
+            return "initdb.file", False
+        else:
+            return "unknown", False
+    
 
 class MyUnixSockServer(UnixSockServer):
     def __init__(self):
         self._lock = threading.Lock()
         self._transfers_counter = 0
         self._shutdown_event = threading.Event()
-        self._db_file = "initdb.file"
         self._max_transfers_out = 10
+        self._cc = MyCCSession()
+        self._log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
 
 class TestUnixSockServer(unittest.TestCase):
     def setUp(self):
         self.unix = MyUnixSockServer()
      
     def test_updata_config_data(self):
-        self.unix.update_config_data({'transfers_out':10, 'db_file':"db.file"})
+        self.unix.update_config_data({'transfers_out':10 })
         self.assertEqual(self.unix._max_transfers_out, 10)
-        self.assertEqual(self.unix._db_file, "db.file")
 
     def test_get_db_file(self):
         self.assertEqual(self.unix.get_db_file(), "initdb.file")

Modified: branches/trac191/src/bin/xfrout/xfrout.py.in
==============================================================================
--- branches/trac191/src/bin/xfrout/xfrout.py.in (original)
+++ branches/trac191/src/bin/xfrout/xfrout.py.in Fri Jul  9 10:04:58 2010
@@ -26,14 +26,15 @@
 from socketserver import *
 import os
 from isc.config.ccsession import *
+from isc.log.log import *
 from isc.cc import SessionError
 import socket
 import select
 import errno
 from optparse import OptionParser, OptionValueError
 try:
-    from bind10_xfr import *
-    from bind10_dns import *
+    from libxfr_python import *
+    from libdns_python import *
 except ImportError as e:
     # C++ loadable module may not be installed; even so the xfrout process
     # must keep running, so we warn about it and move forward.
@@ -41,30 +42,35 @@
 
 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"
     UNIX_SOCKET_FILE= os.environ["B10_FROM_BUILD"] + "/auth_xfrout_conn"
 else:
     PREFIX = "@prefix@"
     DATAROOTDIR = "@datarootdir@"
     SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
+    AUTH_SPECFILE_PATH = SPECFILE_PATH
     UNIX_SOCKET_FILE = "@@LOCALSTATEDIR@@/auth_xfrout_conn"
 
 SPECFILE_LOCATION = SPECFILE_PATH + "/xfrout.spec"
+AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
 MAX_TRANSFERS_OUT = 10
-verbose_mode = False
-
-
-class XfroutException(Exception): pass
-
+VERBOSE_MODE = False
 
 class XfroutSession(BaseRequestHandler):
+    def __init__(self, request, client_address, server, log):
+        # The initializer for the superclass may call functions
+        # that need _log to be set, so we set it first
+        self._log = log
+        BaseRequestHandler.__init__(self, request, client_address, server)
+
     def handle(self):
         fd = recv_fd(self.request.fileno())
+        
         if fd < 0:
             # This may happen when one xfrout process try to connect to
             # xfrout unix socket server, to check whether there is another
             # xfrout running. 
-            print("[b10-xfrout] Failed to receive the FD for XFR connection, "
-                  "maybe because another xfrout process was started.")
+            self._log.log_message("error", "Failed to receive the file descriptor for XFR connection")
             return
 
         data_len = self.request.recv(2)
@@ -73,28 +79,35 @@
         sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
         try:
             self.dns_xfrout_start(sock, msgdata)
+            #TODO, avoid catching all exceptions
         except Exception as e:
-            if verbose_mode:
-                self.log_msg(str(e))
+            self._log.log_message("error", str(e))
+
+        try:
+            sock.shutdown(socket.SHUT_RDWR)
+        except socket.error:
+            # Avoid socket error caused by shutting down 
+            # one non-connected socket.
+            pass
 
         sock.close()
+        os.close(fd)
+        pass
 
     def _parse_query_message(self, mdata):
         ''' parse query message to [socket,message]'''
         #TODO, need to add parseHeader() in case the message header is invalid 
         try:
-            msg = message(message_mode.PARSE)
-            msg.from_wire(input_buffer(mdata))
+            msg = Message(Message.PARSE)
+            Message.from_wire(msg, mdata)
         except Exception as err:
-            if verbose_mode:
-                self.log_msg(str(err))
-            return rcode.FORMERR(), None
-
-        return rcode.NOERROR(), msg
+            self._log.log_message("error", str(err))
+            return Rcode.FORMERR(), None
+
+        return Rcode.NOERROR(), msg
 
     def _get_query_zone_name(self, msg):
-        q_iter = question_iter(msg)
-        question = q_iter.get_question()
+        question = msg.get_question()[0]
         return question.get_name().to_text()
 
 
@@ -107,12 +120,14 @@
 
 
     def _send_message(self, sock, msg):
-        obuf = output_buffer(0)
-        render = message_render(obuf)
+        #obuf = output_buffer(0)
+        #render = message_render(obuf)
+        render = MessageRenderer()
+        render.set_length_limit(65535)
         msg.to_wire(render)
-        header_len = struct.pack('H', socket.htons(obuf.get_length()))
+        header_len = struct.pack('H', socket.htons(render.get_length()))
         self._send_data(sock, header_len)
-        self._send_data(sock, obuf.get_data())
+        self._send_data(sock, render.get_data())
 
 
     def _reply_query_with_error_rcode(self, msg, sock, rcode_):
@@ -127,7 +142,7 @@
             return # query message is invalid. send nothing back. 
 
         msg.make_response()
-        msg.set_rcode(rcode.FORMERR())
+        msg.set_rcode(Rcode.FORMERR())
         self._send_message(sock, msg)
 
 
@@ -152,40 +167,37 @@
            eg. check allow_transfer setting, 
         '''
         if not self._zone_exist(zone_name):
-            return rcode.NOTAUTH()
+            return Rcode.NOTAUTH()
 
         if self._zone_is_empty(zone_name):
-            return rcode.SERVFAIL() 
+            return Rcode.SERVFAIL() 
 
         #TODO, check allow_transfer
         if not self.server.increase_transfers_counter():
-            return rcode.REFUSED()
-
-        return rcode.NOERROR()
+            return Rcode.REFUSED()
+
+        return Rcode.NOERROR()
 
 
     def dns_xfrout_start(self, sock, msg_query):
         rcode_, msg = self._parse_query_message(msg_query)
         #TODO. create query message and parse header
-        if rcode_ != rcode.NOERROR():
+        if rcode_ != Rcode.NOERROR():
             return self._reply_query_with_format_error(msg, sock)
 
         zone_name = self._get_query_zone_name(msg)
         rcode_ = self._check_xfrout_available(zone_name)
-        if rcode_ != rcode.NOERROR():
+        if rcode_ != Rcode.NOERROR():
+            self._log.log_message("info", "transfer of '%s/IN' failed: %s",
+                                  zone_name, rcode_.to_text())
             return self. _reply_query_with_error_rcode(msg, sock, rcode_)
 
         try:
-            if verbose_mode:
-                self.log_msg("transfer of '%s/IN': AXFR started" % zone_name)
-
+            self._log.log_message("info", "transfer of '%s/IN': AXFR started" % zone_name)
             self._reply_xfrout_query(msg, sock, zone_name)
-
-            if verbose_mode:
-                self.log_msg("transfer of '%s/IN': AXFR end" % zone_name)
+            self._log.log_message("info", "transfer of '%s/IN': AXFR end" % zone_name)
         except Exception as err:
-            if verbose_mode:
-                sys.stderr.write("[b10-xfrout] %s\n" % str(err))
+            self._log.log_message("error", str(err))
 
         self.server.decrease_transfers_counter()
         return    
@@ -196,21 +208,21 @@
         opcode = msg.get_opcode()
         rcode = msg.get_rcode()
         
-        msg.clear(message_mode.RENDER)
+        msg.clear(Message.RENDER)
         msg.set_qid(qid)
         msg.set_opcode(opcode)
         msg.set_rcode(rcode)
-        msg.set_header_flag(message_flag.AA())
-        msg.set_header_flag(message_flag.QR())
+        msg.set_header_flag(MessageFlag.AA())
+        msg.set_header_flag(MessageFlag.QR())
         return msg
 
     def _create_rrset_from_db_record(self, record):
         '''Create one rrset from one record of datasource, if the schema of record is changed, 
         This function should be updated first.
         '''
-        rrtype_ = rr_type(record[5])
-        rdata_ = create_rdata(rrtype_, rr_class.IN(), " ".join(record[7:]))
-        rrset_ = rrset(name(record[2]), rr_class.IN(), rrtype_, rr_ttl( int(record[4])))
+        rrtype_ = RRType(record[5])
+        rdata_ = Rdata(rrtype_, RRClass("IN"), " ".join(record[7:]))
+        rrset_ = RRset(Name(record[2]), RRClass("IN"), rrtype_, RRTTL( int(record[4])))
         rrset_.add_rdata(rdata_)
         return rrset_
          
@@ -219,20 +231,19 @@
         added, a new message should be created to send out the last soa .
         '''
 
-        obuf = output_buffer(0)
-        render = message_render(obuf)
+        render = MessageRenderer()
         msg.to_wire(render)
-        old_message_len = obuf.get_length()
-        msg.add_rrset(section.ANSWER(), rrset_soa)
+        old_message_len = render.get_length()
+        msg.add_rrset(Section.ANSWER(), rrset_soa)
 
         msg.to_wire(render)
-        message_len = obuf.get_length()
+        message_len = render.get_length()
 
         if message_len != old_message_len:
             self._send_message(sock, msg)
         else:
             msg = self._clear_message(msg)
-            msg.add_rrset(section.ANSWER(), rrset_soa)
+            msg.add_rrset(Section.ANSWER(), rrset_soa)
             self._send_message(sock, msg)
 
     def _get_message_len(self, msg):
@@ -240,59 +251,62 @@
         a better way, I need check with jinmei later.
         '''
 
-        obuf = output_buffer(0)
-        render = message_render(obuf)
+        render = MessageRenderer()
         msg.to_wire(render)
-        return obuf.get_length()
+        return render.get_length()
 
 
     def _reply_xfrout_query(self, msg, sock, zone_name):
         #TODO, there should be a better way to insert rrset.
         msg.make_response()
-        msg.set_header_flag(message_flag.AA())
+        msg.set_header_flag(MessageFlag.AA())
         soa_record = sqlite3_ds.get_zone_soa(zone_name, self.server.get_db_file())
         rrset_soa = self._create_rrset_from_db_record(soa_record)
-        msg.add_rrset(section.ANSWER(), rrset_soa)
+        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.
         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
-                raise XfroutException("shutdown!")
-
-            if rr_type(rr_data[5]) == rr_type.SOA(): #ignore soa record
+                self._log.log_message("error", "shutdown!")
+
+            # TODO: RRType.SOA() ?
+            if RRType(rr_data[5]) == RRType("SOA"): #ignore soa record
                 continue
-           
+
             rrset_ = self._create_rrset_from_db_record(rr_data)
-            msg.add_rrset(section.ANSWER(), rrset_)
-            message_len = self._get_message_len(msg) 
+            msg.add_rrset(Section.ANSWER(), rrset_)
+            message_len = self._get_message_len(msg)
             if message_len != old_message_len:
                 old_message_len = message_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
+            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)
 
-    def log_msg(self, msg):
-        print('[b10-xfrout] ', msg)
-   
 
 class UnixSockServer(ThreadingUnixStreamServer):
     '''The unix domain socket server which accept xfr query sent from auth server.'''
 
-    def __init__(self, sock_file, handle_class, shutdown_event, config_data):
+    def __init__(self, sock_file, handle_class, shutdown_event, config_data, cc, log):
         self._remove_unused_sock_file(sock_file)
         self._sock_file = sock_file
         ThreadingUnixStreamServer.__init__(self, sock_file, handle_class)
         self._lock = threading.Lock()
         self._transfers_counter = 0
         self._shutdown_event = shutdown_event
+        self._log = log
         self.update_config_data(config_data)
+        self._cc = cc
+
+    def finish_request(self, request, client_address):
+        '''Finish one request by instantiating RequestHandlerClass.'''
+        self.RequestHandlerClass(request, client_address, self, self._log)
 
     def _remove_unused_sock_file(self, sock_file):
         '''Try to remove the socket file. If the file is being used 
@@ -329,22 +343,27 @@
         ThreadingUnixStreamServer.shutdown(self)
         try:
             os.unlink(self._sock_file)
-        except:
-            pass
+        except Exception as e:
+            self._log.log_message("error", str(e))
 
     def update_config_data(self, new_config):
         '''Apply the new config setting of xfrout module. '''
-
+        self._log.log_message('info', 'update config data start.')
         self._lock.acquire()
         self._max_transfers_out = new_config.get('transfers_out')
-        self._db_file = new_config.get('db_file')
+        self._log.log_message('info', 'max transfer out : %d', self._max_transfers_out)
         self._lock.release()
+        self._log.log_message('info', 'update config data complete.')
 
     def get_db_file(self):
-        self._lock.acquire()
-        file = self._db_file
-        self._lock.release()
+        file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
+        # this too should be unnecessary, but currently the
+        # 'from build' override isn't stored in the config
+        # (and we don't have indirect python access to datasources yet)
+        if is_default and "B10_FROM_BUILD" in os.environ:
+            file = os.environ["B10_FROM_BUILD"] + os.sep + "bind10_zones.sqlite3"
         return file
+
 
     def increase_transfers_counter(self):
         '''Return False, if counter + 1 > max_transfers_out, or else
@@ -385,19 +404,24 @@
 class XfroutServer:
     def __init__(self):
         self._unix_socket_server = None
+        self._log = None
         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._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)
         self._start_xfr_query_listener()
-
 
     def _start_xfr_query_listener(self):
         '''Start a new thread to accept xfr query. '''
     
         self._unix_socket_server = UnixSockServer(self._listen_sock_file, XfroutSession, 
-                                                  self._shutdown_event, self._config_data);
+                                                  self._shutdown_event, self._config_data,
+                                                  self._cc, self._log);
         listener = threading.Thread(target = listen_on_xfr_query, args = (self._unix_socket_server,))
         listener.start()
 
@@ -411,6 +435,9 @@
                 continue
             self._config_data[key] = new_config[key]
         
+        if self._log:
+            self._log.update_config(new_config)
+
         if self._unix_socket_server:
             self._unix_socket_server.update_config_data(self._config_data)
 
@@ -436,8 +463,7 @@
 
     def command_handler(self, cmd, args):
         if cmd == "shutdown":
-            if verbose_mode:
-                print("[b10-xfrout] Received shutdown command")
+            self._log.log_message("info", "Received shutdown command.")
             self.shutdown()
             answer = create_answer(0)
         else: 
@@ -472,18 +498,18 @@
         parser = OptionParser()
         set_cmd_options(parser)
         (options, args) = parser.parse_args()
-        verbose_mode = options.verbose
+        VERBOSE_MODE = options.verbose
 
         set_signal_handler()
         xfrout_server = XfroutServer()
         xfrout_server.run()
     except KeyboardInterrupt:
-        print("[b10-xfrout] exit xfrout process")
+        sys.stderr.write("[b10-xfrout] exit xfrout process")
     except SessionError as e:
-        print('[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?")
     except ModuleCCSessionError as e:
-        print('[b10-xfrout] exit xfrout process:', e)
+        sys.stderr.write("info", '[b10-xfrout] exit xfrout process:', e)
 
     if xfrout_server:
         xfrout_server.shutdown()

Modified: branches/trac191/src/bin/xfrout/xfrout.spec.pre.in
==============================================================================
--- branches/trac191/src/bin/xfrout/xfrout.spec.pre.in (original)
+++ branches/trac191/src/bin/xfrout/xfrout.spec.pre.in Fri Jul  9 10:04:58 2010
@@ -12,7 +12,37 @@
          "item_name": "db_file",
          "item_type": "string",
          "item_optional": False,
-         "item_default": '@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3'
+         "item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3"
+       },
+       {
+         "item_name": "log_name",
+         "item_type": "string",
+         "item_optional": False,
+         "item_default": "Xfrout"
+       },
+       {
+         "item_name": "log_file",
+    	 "item_type": "string",
+         "item_optional": False,
+         "item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/log/Xfrout.log"
+       },
+       {
+         "item_name": "log_severity",
+    	 "item_type": "string",
+         "item_optional": False,
+    	 "item_default": "debug"
+       },
+       {
+         "item_name": "log_versions",
+    	 "item_type": "integer",
+         "item_optional": False,
+    	 "item_default": 5
+       },
+       {
+         "item_name": "log_max_bytes",
+    	 "item_type": "integer",
+         "item_optional": False,
+    	 "item_default": 1048576
        }
       ],
       "commands": [

Modified: branches/trac191/src/lib/Makefile.am
==============================================================================
--- branches/trac191/src/lib/Makefile.am (original)
+++ branches/trac191/src/lib/Makefile.am Fri Jul  9 10:04:58 2010
@@ -1,4 +1,1 @@
-SUBDIRS = exceptions dns cc config datasrc python
-if HAVE_BOOST_PYTHON
-SUBDIRS += xfr
-endif
+SUBDIRS = exceptions dns cc config datasrc python xfr

Modified: branches/trac191/src/lib/cc/Makefile.am
==============================================================================
--- branches/trac191/src/lib/cc/Makefile.am (original)
+++ branches/trac191/src/lib/cc/Makefile.am Fri Jul  9 10:04:58 2010
@@ -6,10 +6,12 @@
 # error.  Unfortunately there doesn't seem to be an easy way to selectively
 # avoid the error.  As a short term workaround we suppress this warning
 # for the entire this module.  See also src/bin/auth/Makefile.am.
+if USE_GXX
 AM_CXXFLAGS += -Wno-unused-parameter
+endif
 
-lib_LIBRARIES = libcc.a
-libcc_a_SOURCES = data.cc data.h session.cc session.h
+lib_LTLIBRARIES = libcc.la
+libcc_la_SOURCES = data.cc data.h session.cc session.h
 
 CLEANFILES = *.gcno *.gcda session_config.h
 
@@ -27,7 +29,7 @@
 # TODO: remove PTHREAD_LDFLAGS (and from configure too)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(PTHREAD_LDFLAGS)
 
-run_unittests_LDADD = libcc.a $(GTEST_LDADD)
+run_unittests_LDADD = libcc.la $(GTEST_LDADD)
 run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/.libs/libdns++.a
 run_unittests_LDADD +=  $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
 

Modified: branches/trac191/src/lib/cc/data.cc
==============================================================================
--- branches/trac191/src/lib/cc/data.cc (original)
+++ branches/trac191/src/lib/cc/data.cc Fri Jul  9 10:04:58 2010
@@ -19,6 +19,7 @@
 #include "data.h"
 
 #include <cassert>
+#include <climits>
 #include <cstdio>
 #include <iostream>
 #include <string>
@@ -26,29 +27,34 @@
 
 #include <boost/algorithm/string.hpp> // for iequals
 
+#include <cmath>
+
 using namespace std;
-
-namespace {
-const unsigned char PROTOCOL_VERSION[4] = { 0x53, 0x6b, 0x61, 0x6e };
-
-const unsigned char ITEM_BLOB = 0x01;
-const unsigned char ITEM_HASH = 0x02;
-const unsigned char ITEM_LIST = 0x03;
-const unsigned char ITEM_NULL = 0x04;
-const unsigned char ITEM_BOOL = 0x05;
-const unsigned char ITEM_INT  = 0x06;
-const unsigned char ITEM_REAL = 0x07;
-const unsigned char ITEM_UTF8 = 0x08;
-const unsigned char ITEM_MASK = 0x0f;
-
-const unsigned char ITEM_LENGTH_32   = 0x00;
-const unsigned char ITEM_LENGTH_16   = 0x10;
-const unsigned char ITEM_LENGTH_8    = 0x20;
-const unsigned char ITEM_LENGTH_MASK = 0x30;
-}
 
 namespace isc {
 namespace data {
+
+std::string
+Element::str()
+{
+    std::stringstream ss;
+    toJSON(ss);
+    return ss.str();
+}
+
+std::string
+Element::toWire()
+{
+    std::stringstream ss;
+    toJSON(ss);
+    return ss.str();
+}
+
+void
+Element::toWire(std::ostream& ss)
+{
+    toJSON(ss);
+}
 
 //
 // The following methods are effectively empty, and their parameters are
@@ -58,7 +64,7 @@
 // installed files we define the methods here.
 //
 bool
-Element::getValue(int& t UNUSED_PARAM) {
+Element::getValue(long int& t UNUSED_PARAM) {
     return false;
 }
 
@@ -88,7 +94,7 @@
 }
 
 bool
-Element::setValue(const int v UNUSED_PARAM) {
+Element::setValue(const long int v UNUSED_PARAM) {
     return false;
 }
 
@@ -179,15 +185,11 @@
 
 namespace {
 inline void
-throwParseError(const std::string& error, const std::string& file, int line = 0, int pos = 0)
-{
-    if (line != 0 || pos != 0) {
-        std::stringstream ss;
-        ss << error << " in " + file + ":" << line << ":" << pos;
-        throw ParseError(ss.str());
-    } else {
-        throw ParseError(error);
-    }
+throwJSONError(const std::string& error, const std::string& file, int line, int pos)
+{
+    std::stringstream ss;
+    ss << error << " in " + file + ":" << line << ":" << pos;
+    isc_throw(JSONError, ss.str());
 }
 }
 
@@ -203,7 +205,12 @@
 // factory functions
 //
 ElementPtr
-Element::create(const int i) {
+Element::create() {
+    return ElementPtr(new NullElement());
+}
+
+ElementPtr
+Element::create(const long int i) {
     return ElementPtr(new IntElement(i));
 }
 
@@ -223,24 +230,18 @@
 }
 
 ElementPtr
-Element::create(const std::vector<ElementPtr>& v) {
-    return ElementPtr(new ListElement(v));
-}
-
-ElementPtr
-Element::create(const std::map<std::string, ElementPtr>& m) {
-    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
-         it != m.end(); ++it) {
-        if ((*it).first.length() > 255) {
-            isc_throw(TypeError, "Map tag is too long");
-        }
-    }
-    return ElementPtr(new MapElement(m));
+Element::createList() {
+    return ElementPtr(new ListElement());
+}
+
+ElementPtr
+Element::createMap() {
+    return ElementPtr(new MapElement());
 }
 
 
 //
-// helper functions for createFromString factory
+// helper functions for fromJSON factory
 //
 namespace {
 bool
@@ -300,15 +301,17 @@
             --pos;
             return;
         } else {
-            throwParseError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", file, line, pos);
-        }
-    }
-    throwParseError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
-}
-
+            throwJSONError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", file, line, pos);
+        }
+    }
+    throwJSONError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
+}
+
+// TODO: Should we check for all other official escapes here (and
+// error on the rest)?
 std::string
 str_from_stringstream(std::istream &in, const std::string& file, const int line,
-                      int& pos) throw (ParseError)
+                      int& pos) throw (JSONError)
 {
     char c = 0;
     std::stringstream ss;
@@ -318,7 +321,7 @@
         c = in.get();
         ++pos;
     } else {
-        throwParseError("String expected", file, line, pos);
+        throwJSONError("String expected", file, line, pos);
     }
     while (c != EOF && c != '"') {
         ss << c;
@@ -342,36 +345,47 @@
     return ss.str();
 }
 
-inline int
-count_chars_i(int i) {
-    int result = 1;
-    while (i > 10) {
-        ++result;
-        i = i / 10;
-    }
-    return result;
-}
-
-inline int
-count_chars_d(double d) {
-    int result = 1;
-    while (d < 1.0) {
-        ++result;
-        d = d * 10;
-    }
-    return result;
-}
-
-ElementPtr
-from_stringstream_int_or_double(std::istream &in, int &pos) {
-    int i;
-    in >> i;
-    pos += count_chars_i(i);
-    if (in.peek() == '.') {
-        double d;
-        in >> d;
-        pos += count_chars_d(i);
-        d += i;
+static std::string
+number_from_stringstream(std::istream &in, int& pos) {
+    std::stringstream ss;
+    while (isdigit(in.peek()) || in.peek() == '+' || in.peek() == '-' ||
+           in.peek() == '.' || in.peek() == 'e' || in.peek() == 'E') {
+        ss << (char) in.get();
+    }
+    pos += ss.str().size();
+    return ss.str();
+}
+
+// Should we change from IntElement and DoubleElement to NumberElement
+// that can also hold an e value? (and have specific getters if the
+// value is larger than an int can handle)
+ElementPtr
+from_stringstream_number(std::istream &in, int &pos) {
+    long int i = 0;
+    double d = 0.0;
+    bool is_double = false;
+    char *endptr;
+
+    std::string number = number_from_stringstream(in, pos);
+
+    i = strtol(number.c_str(), &endptr, 10);
+    if (*endptr != '\0') {
+        d = strtod(number.c_str(), &endptr);
+        is_double = true;
+        if (*endptr != '\0') {
+            isc_throw(JSONError, std::string("Bad number: ") + number);
+        } else {
+            if (d == HUGE_VAL || d == -HUGE_VAL) {
+                isc_throw(JSONError, std::string("Number overflow: ") + number);
+            }
+        }
+    } else {
+        if (i == LONG_MAX || i == LONG_MIN) {
+            isc_throw(JSONError, std::string("Number overflow: ") + number);
+        }
+    }
+    
+    if (is_double) {
         return Element::create(d);
     } else {
         return Element::create(i);
@@ -388,13 +402,26 @@
     } else if (boost::iequals(word, "False")) {
         return Element::create(false);
     } else {
-        throwParseError(std::string("Bad boolean value: ") + word, file, line, pos);
-        // above is a throw shortcur, return empty is never reached
+        throwJSONError(std::string("Bad boolean value: ") + word, file, line, pos);
+        // above is a throw shortcurt, return empty is never reached
         return ElementPtr();
     }
 }
 
 ElementPtr
+from_stringstream_null(std::istream &in, const std::string& file,
+                       const int line, int& pos)
+{
+    const std::string word = word_from_stringstream(in, pos);
+    if (boost::iequals(word, "null")) {
+        return Element::create();
+    } else {
+        throwJSONError(std::string("Bad null value: ") + word, file, line, pos);
+        return ElementPtr();
+    }
+}
+
+ElementPtr
 from_stringstream_string(std::istream& in, const std::string& file, int& line, int& pos)
 {
     return Element::create(str_from_stringstream(in, file, line, pos));
@@ -404,27 +431,27 @@
 from_stringstream_list(std::istream &in, const std::string& file, int& line, int& pos)
 {
     char c = 0;
-    std::vector<ElementPtr> v;
+    ElementPtr list = Element::createList();
     ElementPtr cur_list_element;
 
     skip_chars(in, " \t\n", line, pos);
     while (c != EOF && c != ']') {
         if (in.peek() != ']') {
-            cur_list_element = Element::createFromString(in, file, line, pos);
-            v.push_back(cur_list_element);
+            cur_list_element = Element::fromJSON(in, file, line, pos);
+            list->add(cur_list_element);
             skip_to(in, file, line, pos, ",]", " \t\n");
         }
         c = in.get();
         pos++;
     }
-    return Element::create(v);
+    return list;
 }
 
 ElementPtr
 from_stringstream_map(std::istream &in, const std::string& file, int& line,
                       int& pos)
 {
-    std::map<std::string, ElementPtr> m;
+    ElementPtr map = Element::createMap();
     skip_chars(in, " \t\n", line, pos);
     char c = in.peek();
     if (c == '}') {
@@ -432,45 +459,88 @@
         c = in.get();
     } else {
         while (c != EOF && c != '}') {
-            std::pair<std::string, ElementPtr> p;
-
-            p.first = str_from_stringstream(in, file, line, pos);
-            if (p.first.length() > 255) {
-                // Map tag has one-byte length field in wire format, so the
-                // length cannot exceed 255.
-                throwParseError("Map tag is too long", file, line, pos);
-            }
+            std::string key = str_from_stringstream(in, file, line, pos);
 
             skip_to(in, file, line, pos, ":", " \t\n");
             // skip the :
             in.get();
             pos++;
-            p.second = Element::createFromString(in, file, line, pos);
-            m.insert(p);
+
+            ElementPtr value = Element::fromJSON(in, file, line, pos);
+            map->set(key, value);
+            
             skip_to(in, file, line, pos, ",}", " \t\n");
             c = in.get();
             pos++;
         }
     }
-    return Element::create(m);
-}
-}
-
-ElementPtr
-Element::createFromString(std::istream& in) throw(ParseError) {
+    return map;
+}
+}
+
+std::string
+Element::typeToName(Element::types type)
+{
+    switch(type) {
+    case Element::integer:
+        return std::string("integer");
+    case Element::real:
+        return std::string("real");
+    case Element::boolean:
+        return std::string("boolean");
+    case Element::string:
+        return std::string("string");
+    case Element::list:
+        return std::string("list");
+    case Element::map:
+        return std::string("map");
+    case Element::null:
+        return std::string("null");
+    case Element::any:
+        return std::string("any");
+    default:
+        return std::string("unknown");
+    }
+}
+
+Element::types
+Element::nameToType(const std::string& type_name) {
+    if (type_name == "integer") {
+        return Element::integer;
+    } else if (type_name == "real") {
+        return Element::real;
+    } else if (type_name == "boolean") {
+        return Element::boolean;
+    } else if (type_name == "string") {
+        return Element::string;
+    } else if (type_name == "list") {
+        return Element::list;
+    } else if (type_name == "map") {
+        return Element::map;
+    } else if (type_name == "null") {
+        return Element::null;
+    } else if (type_name == "any") {
+        return Element::any;
+    } else {
+        isc_throw(TypeError, type_name + " is not a valid type name");
+    }
+}
+
+ElementPtr
+Element::fromJSON(std::istream& in) throw(JSONError) {
     int line = 1, pos = 1;
-    return createFromString(in, "<istream>", line, pos);
-}
-
-ElementPtr
-Element::createFromString(std::istream& in, const std::string& file_name) throw(ParseError)
+    return fromJSON(in, "<istream>", line, pos);
+}
+
+ElementPtr
+Element::fromJSON(std::istream& in, const std::string& file_name) throw(JSONError)
 {
     int line = 1, pos = 1;
-    return createFromString(in, file_name, line, pos);
-}
-
-ElementPtr
-Element::createFromString(std::istream &in, const std::string& file, int& line, int& pos) throw(ParseError)
+    return fromJSON(in, file_name, line, pos);
+}
+
+ElementPtr
+Element::fromJSON(std::istream &in, const std::string& file, int& line, int& pos) throw(JSONError)
 {
     char c = 0;
     ElementPtr element;
@@ -490,8 +560,11 @@
             case '8':
             case '9':
             case '0':
+            case '-':
+            case '+':
+            case '.':
                 in.putback(c);
-                element = from_stringstream_int_or_double(in, pos);
+                element = from_stringstream_number(in, pos);
                 el_read = true;
                 break;
             case 't':
@@ -500,6 +573,12 @@
             case 'F':
                 in.putback(c);
                 element = from_stringstream_bool(in, file, line, pos);
+                el_read = true;
+                break;
+            case 'n':
+            case 'N':
+                in.putback(c);
+                element = from_stringstream_null(in, file, line, pos);
                 el_read = true;
                 break;
             case '"':
@@ -518,62 +597,65 @@
             case EOF:
                 break;
             default:
-                throwParseError(std::string("error: unexpected character ") + c, file, line, pos);
+                throwJSONError(std::string("error: unexpected character ") + c, file, line, pos);
                 break;
         }
     }
     if (el_read) {
         return element;
     } else {
-        throw ParseError("nothing read");
-    }
-}
-
-ElementPtr
-Element::createFromString(const std::string &in) {
+        isc_throw(JSONError, "nothing read");
+    }
+}
+
+ElementPtr
+Element::fromJSON(const std::string &in) {
     std::stringstream ss;
     ss << in;
-    return createFromString(ss, "<string>");
-}
-
-//
-// a general to_str() function
-//
-std::string
-IntElement::str() {
-    std::stringstream ss;
+    return fromJSON(ss, "<string>");
+}
+
+// to JSON format
+
+void
+IntElement::toJSON(std::ostream& ss)
+{
     ss << intValue();
-    return ss.str();
-}
-
-std::string
-DoubleElement::str() {
-    std::stringstream ss;
+}
+
+void
+DoubleElement::toJSON(std::ostream& ss)
+{
     ss << doubleValue();
-    return ss.str();
-}
-
-std::string
-BoolElement::str() {
-    if (b) {
-        return "True";
-    } else {
-        return "False";
-    }
-}
-
-std::string
-StringElement::str() {
-    std::stringstream ss;
+}
+
+void
+BoolElement::toJSON(std::ostream& ss)
+{
+    if (boolValue()) {
+        ss << "true";
+    } else {
+        ss << "false";
+    }
+}
+
+void
+NullElement::toJSON(std::ostream& ss)
+{
+    ss << "null";
+}
+
+void
+StringElement::toJSON(std::ostream& ss)
+{
     ss << "\"";
     ss << stringValue();
     ss << "\"";
-    return ss.str();
-}
-
-std::string
-ListElement::str() {
-    std::stringstream ss;
+}
+
+void
+ListElement::toJSON(std::ostream& ss)
+{
     ss << "[ ";
 
     const std::vector<ElementPtr>& v = listValue();
@@ -582,16 +664,15 @@
         if (it != v.begin()) {
             ss << ", ";
         }
-        ss << (*it)->str();
+        (*it)->toJSON(ss);
     }
     ss << " ]";
-    return ss.str();
-}
-
-std::string
-MapElement::str() {
-    std::stringstream ss;
-    ss << "{";
+}
+
+void
+MapElement::toJSON(std::ostream& ss)
+{
+    ss << "{ ";
 
     const std::map<std::string, ElementPtr>& m = mapValue();
     for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
@@ -601,13 +682,12 @@
         }
         ss << "\"" << (*it).first << "\": ";
         if ((*it).second) {
-            ss << (*it).second->str();
+            (*it).second->toJSON(ss);
         } else {
             ss << "None";
         }
     }
-    ss << "}";
-    return ss.str();
+    ss << " }";
 }
 
 // throws when one of the types in the path (except the one
@@ -634,169 +714,12 @@
     }
 }
 
-//
-// Decode from wire format.
-//
-namespace {
-ElementPtr decode_element(std::stringstream& in, int& in_length);
-
-unsigned char
-get_byte(std::stringstream& in) {
-    const int c = in.get();
-    if (c == EOF) {
-        throw DecodeError("End of data while decoding wire format message");
-    }
-
-    return c;
-}
-
-std::string
-decode_tag(std::stringstream& in, int& item_length) {
-    char buf[256];
-
-    const int len = get_byte(in);
-    item_length--;
-
-    in.read(buf, len);
-    if (in.fail()) {
-        throw DecodeError();
-    }
-    buf[len] = 0;
-    item_length -= len;
-
-    return std::string(buf, len);
-}
-
-ElementPtr
-decode_bool(std::stringstream& in) {
-    const char c = in.get();
-    
-    if (c == '1') {
-        return Element::create(true);
-    } else {
-        return Element::create(false);
-    }
-}
-
-ElementPtr
-decode_int(std::stringstream& in) {
-    int me;
-    return from_stringstream_int_or_double(in, me);
-}
-
-ElementPtr
-decode_real(std::stringstream& in) {
-    int me;
-    return from_stringstream_int_or_double(in, me);
-}
-
-ElementPtr
-decode_blob(std::stringstream& in, const int item_length) {
-    vector<char> buf(item_length + 1);
-
-    in.read(&buf[0], item_length);
-    if (in.fail()) {
-        throw DecodeError();
-    }
-    buf[item_length] = 0;
-
-    return Element::create(std::string(&buf[0], item_length));
-}
-
-ElementPtr
-decode_hash(std::stringstream& in, int item_length) {
-    std::map<std::string, ElementPtr> m;
-    std::pair<std::string, ElementPtr> p;
-
-    while (item_length > 0) {
-        p.first = decode_tag(in, item_length);
-        p.second = decode_element(in, item_length);
-        m.insert(p);
-    }
-
-    return Element::create(m);
-}
-
-ElementPtr
-decode_list(std::stringstream& in, int item_length) {
-    std::vector<ElementPtr> v;
-
-    while (item_length > 0) {
-        v.push_back(decode_element(in, item_length));
-    }
-    return Element::create(v);
-}
-
-ElementPtr
-decode_null() {
-    return Element::create("NULL");
-}
-
-ElementPtr
-decode_element(std::stringstream& in, int& in_length) {
-    ElementPtr element;
-
-    const unsigned char type_and_length = get_byte(in);
-    const unsigned char type = type_and_length & ITEM_MASK;
-    const unsigned char lenbytes = type_and_length & ITEM_LENGTH_MASK;
-    in_length--;
-
-    int item_length = 0;
-    switch (lenbytes) {
-    case ITEM_LENGTH_32:
-        item_length |= get_byte(in);
-        item_length <<= 8;
-        item_length |= get_byte(in);
-        item_length <<= 8;
-        in_length -= 2;  // only 2 here, we will get more later
-    case ITEM_LENGTH_16:
-        item_length |= get_byte(in);
-        item_length <<= 8;
-        in_length--;  // only 1 here
-    case ITEM_LENGTH_8:
-        item_length |= get_byte(in);
-        in_length--;
-    }
-
-    in_length -= item_length;
-
-    switch (type) {
-    case ITEM_BOOL:
-        element = decode_bool(in);
-        break;
-    case ITEM_INT:
-        element = decode_int(in);
-        break;
-    case ITEM_REAL:
-        element = decode_real(in);
-        break;
-    case ITEM_BLOB:
-        element = decode_blob(in, item_length);
-        break;
-    case ITEM_UTF8:
-        // XXXMLG currently identical to decode_blob
-        element = decode_blob(in, item_length);
-        break;
-    case ITEM_HASH:
-        element = decode_hash(in, item_length);
-        break;
-    case ITEM_LIST:
-        element = decode_list(in, item_length);
-        break;
-    case ITEM_NULL:
-        element = decode_null();
-        break;
-    }
-
-    return (element);
-}
-}
-
 ElementPtr
 Element::fromWire(const std::string& s) {
     std::stringstream ss;
     ss << s;
-    return fromWire(ss, s.length());
+    int line = 0, pos = 0;
+    return fromJSON(ss, "<wire>", line, pos);
 }
 
 ElementPtr
@@ -804,159 +727,20 @@
     //
     // Check protocol version
     //
-    for (int i = 0 ; i < 4 ; ++i) {
-        const unsigned char version_byte = get_byte(in);
-        if (PROTOCOL_VERSION[i] != version_byte) {
-            throw DecodeError("Protocol version incorrect");
-        }
-    }
-    length -= 4;
-
-    return (decode_hash(in, length));
-}
-
-//
-// Encode into wire format.
-//
-
-std::string
-encode_length(const unsigned int length, unsigned char type) {
-    std::stringstream ss;
-
-    if (length <= 0x000000ff) {
-        const unsigned char val = (length & 0x000000ff);
-        type |= ITEM_LENGTH_8;
-        ss << type << val;
-    } else if (length <= 0x0000ffff) {
-        unsigned char val[2];
-        val[0] = (length & 0x0000ff00) >> 8;
-        val[1] = (length & 0x000000ff);
-        type |= ITEM_LENGTH_16;
-        ss << type << val[0] << val[1];
-    } else {
-        unsigned char val[4];
-        val[0] = (length & 0xff000000) >> 24;
-        val[1] = (length & 0x00ff0000) >> 16;
-        val[2] = (length & 0x0000ff00) >> 8;
-        val[3] = (length & 0x000000ff);
-        type |= ITEM_LENGTH_32;
-        ss << type << val[0] << val[1] << val[2] << val[3];
-    }
-    return ss.str();
-}
-
-std::string
-Element::toWire(const int omit_length) {
-    std::stringstream ss;
-    toWire(ss, omit_length);
-    return ss.str();
-}
-
-void
-StringElement::toWire(std::stringstream& ss,
-                      const int omit_length UNUSED_PARAM)
-{
-    unsigned int length = stringValue().length();
-    ss << encode_length(length, ITEM_UTF8) << stringValue();
-}
-
-void
-IntElement::toWire(std::stringstream& ss,
-                   const int omit_length UNUSED_PARAM)
-{
-    const std::string& s = str();
-    ss << encode_length(s.length(), ITEM_INT) << s;
-}
-
-void
-BoolElement::toWire(std::stringstream& ss,
-                    const int omit_length UNUSED_PARAM)
-{
-    ss << encode_length(1, ITEM_BOOL);
-    if (boolValue()) {
-        ss << '1';
-    } else {
-        ss << '0';
-    }
-}
-
-void
-DoubleElement::toWire(std::stringstream& ss,
-                      const int omit_length UNUSED_PARAM)
-{
-    std::stringstream text;
-
-    text << str();
-    const int length = text.str().length();
-    ss << encode_length(length, ITEM_REAL) << text.str();
-}
-
-void
-ListElement::toWire(std::stringstream& ss, const int omit_length) {
-    std::stringstream ss2;
-    const std::vector<ElementPtr>& v = listValue();
-    for (std::vector<ElementPtr>::const_iterator it = v.begin() ;
-         it != v.end() ; ++it) {
-        (*it)->toWire(ss2, 0);
-    }
-
-
-    if (omit_length) {
-        stringbuf *ss2_buf = ss2.rdbuf();
-        ss2_buf->pubseekpos(0);
-        if (ss2_buf->in_avail() > 0) {
-            ss << ss2_buf;
-        }
-    } else {
-        stringbuf *ss2_buf = ss2.rdbuf();
-        ss2_buf->pubseekpos(0);
-        ss << encode_length(ss2_buf->in_avail(), ITEM_LIST);
-        if (ss2_buf->in_avail() > 0) {
-            ss << ss2_buf;
-        }
-    }
-}
-
-void
-MapElement::toWire(std::stringstream& ss, int omit_length) {
-    std::stringstream ss2;
-
-    //
-    // If we don't want the length, we will want the protocol header
-    //
-    if (omit_length) {
-        ss2 << PROTOCOL_VERSION[0] << PROTOCOL_VERSION[1];
-        ss2 << PROTOCOL_VERSION[2] << PROTOCOL_VERSION[3];
-    }
-
-    const std::map<std::string, ElementPtr>& m = mapValue();
-    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
-         it != m.end(); ++it) {
-        const size_t taglen = (*it).first.length();
-        assert(taglen <= 0xff);
-        const unsigned char val = (taglen & 0x000000ff);
-        ss2 << val << (*it).first;
-
-        (*it).second->toWire(ss2, 0);
-    }
-
-    //
-    // add length if needed
-    //
-    if (omit_length) {
-        stringbuf *ss2_buf = ss2.rdbuf();
-        ss2_buf->pubseekpos(0);
-        if (ss2_buf->in_avail()) {
-            ss << ss2_buf;
-        }
-    } else {
-        stringbuf *ss2_buf = ss2.rdbuf();
-        ss2_buf->pubseekpos(0);
-        ss << encode_length(ss2_buf->in_avail(), ITEM_HASH);
-        if (ss2_buf->in_avail()) {
-            ss << ss2_buf;
-        }
-    }
+    //for (int i = 0 ; i < 4 ; ++i) {
+    //    const unsigned char version_byte = get_byte(in);
+    //    if (PROTOCOL_VERSION[i] != version_byte) {
+    //        throw DecodeError("Protocol version incorrect");
+    //    }
+    //}
+    //length -= 4;
+    int line = 0, pos = 0;
+    return fromJSON(in, "<wire>", line, pos);
+}
+
+void
+MapElement::set(const std::string& key, ElementPtr value) {
+    m[key] = value;
 }
 
 bool
@@ -989,6 +773,11 @@
 BoolElement::equals(ElementPtr other) {
     return (other->getType() == Element::boolean) &&
            (b == other->boolValue());
+}
+
+bool
+NullElement::equals(ElementPtr other) {
+    return other->getType() == Element::null;
 }
 
 bool
@@ -1082,7 +871,7 @@
     std::map<std::string, ElementPtr> m = other->mapValue();
     for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
          it != m.end() ; ++it) {
-        if ((*it).second) {
+        if ((*it).second && (*it).second->getType() != Element::null) {
             element->set((*it).first, (*it).second);
         } else if (element->contains((*it).first)) {
             element->remove((*it).first);

Modified: branches/trac191/src/lib/cc/data.h
==============================================================================
--- branches/trac191/src/lib/cc/data.h (original)
+++ branches/trac191/src/lib/cc/data.h Fri Jul  9 10:04:58 2010
@@ -48,22 +48,10 @@
 // i'd like to use Exception here but we need one that is derived from
 // runtime_error (as this one is directly based on external data, and
 // i want to add some values to any static data string that is provided)
-class ParseError : public std::runtime_error {
-public:
-    ParseError(const std::string &err) : std::runtime_error(err) {};
-};
-
-///
-/// \brief A standard Data module exception that is thrown if an error
-/// is found when decoding an Element from wire format
-///
-class DecodeError : public std::exception {
-public:
-    DecodeError(std::string m = "Wire-format data is invalid") : msg(m) {}
-    ~DecodeError() throw() {}
-    const char* what() const throw() { return msg.c_str(); }
-private:
-    std::string msg;
+class JSONError : public isc::Exception {
+public:
+    JSONError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
 };
 
 ///
@@ -75,7 +63,7 @@
 ///
 /// Elements should in calling functions usually be referenced through
 /// an \c ElementPtr, which can be created using the factory functions
-/// \c Element::create() and \c Element::createFromString()
+/// \c Element::create() and \c Element::fromJSON()
 ///
 /// Notes to developers: Element is a base class, implemented by a
 /// specific subclass for each type (IntElement, BoolElement, etc).
@@ -97,40 +85,40 @@
 public:
     // any is a special type used in list specifications, specifying
     // that the elements can be of any type
-    enum types { integer, real, boolean, string, list, map, any };
+    enum types { integer, real, boolean, null, string, list, map, any };
     // base class; make dtor virtual
     virtual ~Element() {};
 
     /// \return the type of this element
     int getType() { return type; };
+
+    /// Returns a string representing the Element and all its
+    /// child elements; note that this is different from stringValue(),
+    /// which only returns the single value of a StringElement
+    ///
+    /// The resulting string will contain the Element in JSON format.
+    ///
+    /// \return std::string containing the string representation
+    std::string str();
+
+    /// Returns the wireformat for the Element and all its child
+    /// elements.
+    ///
+    /// \return std::string containing the element in wire format
+    std::string toWire();
+    void toWire(std::ostream& out);
+
+    /// \name pure virtuals, every derived class must implement these
 
     /// \returns true if the other ElementPtr has the same type and
     ///          value
     virtual bool equals(ElementPtr other) = 0;
     
-    // pure virtuals, every derived class must implement these
-
-    /// Returns a string representing the Element and all its
-    /// child elements; note that this is different from stringValue(),
-    /// which only returns the single value of a StringElement
-    /// A MapElement will be represented as { "name1": \<value1\>, "name2", \<value2\>, etc }
-    /// A ListElement will be represented as [ \<item1\>, \<item2\>, etc ]
-    /// All other elements will be represented directly
-    ///
-    /// \return std::string containing the string representation
-    virtual std::string str() = 0;
-
-    /// Returns the wireformat for the Element and all its child
-    /// elements.
-    ///
-    /// \param omit_length If this is non-zero, the item length will
-    ///        be omitted from the wire format
-    /// \return std::string containing the element in wire format
-    std::string toWire(int omit_length = 1);
-    virtual void toWire(std::stringstream& out, int omit_length = 1) = 0;
+    /// Converts the Element to JSON format and appends it to
+    /// the given stringstream.
+    virtual void toJSON(std::ostream& ss) = 0;
 
     /// \name Type-specific getters
-    ///
     ///
     /// \brief These functions only
     /// work on their corresponding Element type. For all other
@@ -138,7 +126,7 @@
     /// If you want an exception-safe getter method, use
     /// getValue() below
     //@{
-    virtual int intValue() { isc_throw(TypeError, "intValue() called on non-integer Element"); };
+    virtual long int intValue() { isc_throw(TypeError, "intValue() called on non-integer Element"); };
     virtual double doubleValue() { isc_throw(TypeError, "doubleValue() called on non-double Element"); };
     virtual bool boolValue() { isc_throw(TypeError, "boolValue() called on non-Bool Element"); };
     virtual std::string stringValue() { isc_throw(TypeError, "stringValue() called on non-string Element"); };
@@ -155,13 +143,14 @@
     /// data to the given reference and returning true
     ///
     //@{
-    virtual bool getValue(int& t);
+    virtual bool getValue(long int& t);
     virtual bool getValue(double& t);
     virtual bool getValue(bool& t);
     virtual bool getValue(std::string& t);
     virtual bool getValue(std::vector<ElementPtr>& t);
     virtual bool getValue(std::map<std::string, ElementPtr>& t);
     //@}
+
     ///
     /// \name Exception-safe setters.
     ///
@@ -170,7 +159,7 @@
     /// is of the correct type
     ///
     //@{
-    virtual bool setValue(const int v);
+    virtual bool setValue(const long int v);
     virtual bool setValue(const double v);
     virtual bool setValue(const bool t);
     virtual bool setValue(const std::string& v);
@@ -191,21 +180,26 @@
     /// of bounds, this function throws an std::out_of_range exception.
     /// \param i The position of the ElementPtr to return
     virtual ElementPtr get(const int i);
+
     /// Sets the ElementPtr at the given index. If the index is out
     /// of bounds, this function throws an std::out_of_range exception.
     /// \param i The position of the ElementPtr to set
     /// \param element The ElementPtr to set at the position
     virtual void set(const size_t i, ElementPtr element);
+
     /// Adds an ElementPtr to the list
     /// \param element The ElementPtr to add
     virtual void add(ElementPtr element);
+
     /// Removes the element at the given position. If the index is out
     /// of nothing happens.
     /// \param i The index of the element to remove.
     virtual void remove(const int i);
+
     /// Returns the number of elements in the list.
     virtual size_t size();
     //@}
+
     
     /// \name MapElement functions
     ///
@@ -216,16 +210,20 @@
     /// \param name The key of the Element to return
     /// \return The ElementPtr at the given key
     virtual ElementPtr get(const std::string& name);
+
     /// Sets the ElementPtr at the given key
     /// \param name The key of the Element to set
     virtual void set(const std::string& name, ElementPtr element);
+
     /// Remove the ElementPtr at the given key
     /// \param name The key of the Element to remove
     virtual void remove(const std::string& name);
+
     /// Checks if there is data at the given key
     /// \param name The key of the Element to remove
     /// \return true if there is data at the key, false if not.
     virtual bool contains(const std::string& name);
+
     /// Recursively finds any data at the given identifier. The
     /// identifier is a /-separated list of names of nested maps, with
     /// the last name being the leaf that is returned.
@@ -240,6 +238,7 @@
     /// null ElementPtr if it is not found, which can be checked with
     /// Element::is_null(ElementPtr e).
     virtual ElementPtr find(const std::string& identifier);
+
     /// See \c Element::find()
     /// \param identifier The identifier of the element to find
     /// \param t Reference to store the resulting ElementPtr, if found.
@@ -247,6 +246,7 @@
     virtual bool find(const std::string& identifier, ElementPtr& t);
     //@}
 
+
     /// \name Factory functions
     
     // TODO: should we move all factory functions to a different class
@@ -257,39 +257,52 @@
     /// \brief These functions simply wrap the given data directly
     /// in an Element object, and return a reference to it, in the form
     /// of an \c ElementPtr.
-    /// If there is a memory allocation problem, these functions will
-    /// return a NULL ElementPtr, which can be checked with
-    /// Element::is_null(ElementPtr ep).
-    //@{
-    static ElementPtr create(const int i);
+    /// These factory functions are exception-free (unless there is
+    /// no memory available, in which case bad_alloc is raised by the
+    /// underlying system).
+    /// (Note that that is different from an NullElement, which
+    /// represents an empty value, and is created with Element::create())
+    //@{
+    static ElementPtr create();
+    static ElementPtr create(const long int i);
+    static ElementPtr create(const int i) { return create(static_cast<long int>(i)); };
     static ElementPtr create(const double d);
     static ElementPtr create(const bool b);
     static ElementPtr create(const std::string& s);
     // need both std:string and char *, since c++ will match
     // bool before std::string when you pass it a char *
-    static ElementPtr create(const char *s) { return create(std::string(s)); }; 
-    static ElementPtr create(const std::vector<ElementPtr>& v);
-    static ElementPtr create(const std::map<std::string, ElementPtr>& m);
-    //@}
+    static ElementPtr create(const char *s) { return create(std::string(s)); };
+
+    /// \brief Creates an empty ListElement type ElementPtr.
+    static ElementPtr createList();
+
+    /// \brief Creates an empty MapElement type ElementPtr.
+    static ElementPtr createMap();
+    //@}
+
 
     /// \name Compound factory functions
 
-    /// \brief These functions will parse the given string representation
-    /// of a compound element. If there is a parse error, an exception
-    /// of the type isc::data::ParseError is thrown.
-
-    //@{
-    /// Creates an Element from the given string
+    /// \brief These functions will parse the given string (JSON)
+    /// representation  of a compound element. If there is a parse
+    /// error, an exception of the type isc::data::JSONError is thrown.
+
+    //@{
+    /// Creates an Element from the given JSON string
     /// \param in The string to parse the element from
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given string.
-    static ElementPtr createFromString(const std::string& in);
-    /// Creates an Element from the given input stream
+    static ElementPtr fromJSON(const std::string& in);
+
+    /// Creates an Element from the given input stream containing JSON
+    /// formatted data.
+    ///
     /// \param in The string to parse the element from
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
-    static ElementPtr createFromString(std::istream& in) throw(ParseError);
-    static ElementPtr createFromString(std::istream& in, const std::string& file_name) throw(ParseError);
+    static ElementPtr fromJSON(std::istream& in) throw(JSONError);
+    static ElementPtr fromJSON(std::istream& in, const std::string& file_name) throw(JSONError);
+
     /// Creates an Element from the given input stream, where we keep
     /// track of the location in the stream for error reporting.
     ///
@@ -301,8 +314,24 @@
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
     // make this one private?
-    static ElementPtr createFromString(std::istream& in, const std::string& file, int& line, int &pos) throw(ParseError);
-    //@}
+    static ElementPtr fromJSON(std::istream& in, const std::string& file, int& line, int &pos) throw(JSONError);
+    //@}
+
+    /// \name Type name conversion functions
+
+    /// Returns the name of the given type as a string
+    ///
+    /// \param type The type to return the name of
+    /// \return The name of the type, or "unknown" if the type
+    ///         is not known.
+    static std::string typeToName(Element::types type);
+
+    /// Converts the string to the corresponding type
+    /// Throws a TypeError if the name is unknown.
+    ///
+    /// \param type_name The name to get the type of
+    /// \return the corresponding type value
+    static Element::types nameToType(const std::string& type_name);
 
     /// \name Wire format factory functions
 
@@ -313,11 +342,18 @@
     //@{
     /// Creates an Element from the wire format in the given
     /// stringstream of the given length.
+    /// Since the wire format is JSON, thise is the same as
+    /// fromJSON, and could be removed.
+    ///
     /// \param in The input stringstream.
     /// \param length The length of the wireformat data in the stream
     /// \return ElementPtr with the data that is parsed.
     static ElementPtr fromWire(std::stringstream& in, int length);
+
     /// Creates an Element from the wire format in the given string
+    /// Since the wire format is JSON, thise is the same as
+    /// fromJSON, and could be removed.
+    ///
     /// \param s The input string
     /// \return ElementPtr with the data that is parsed.
     static ElementPtr fromWire(const std::string& s);
@@ -325,17 +361,16 @@
 };
 
 class IntElement : public Element {
-    int i;
-
-public:
-    IntElement(int v) : Element(integer), i(v) { };
-    int intValue() { return i; }
+    long int i;
+
+public:
+    IntElement(long int v) : Element(integer), i(v) { };
+    long int intValue() { return i; }
     using Element::getValue;
-    bool getValue(int& t) { t = i; return true; };
+    bool getValue(long int& t) { t = i; return true; };
     using Element::setValue;
-    bool setValue(const int v) { i = v; return true; };
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    bool setValue(const long int v) { i = v; return true; };
+    void toJSON(std::ostream& ss);
     bool equals(ElementPtr other);
 };
 
@@ -349,8 +384,7 @@
     bool getValue(double& t) { t = d; return true; };
     using Element::setValue;
     bool setValue(const double v) { d = v; return true; };
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    void toJSON(std::ostream& ss);
     bool equals(ElementPtr other);
 };
 
@@ -364,8 +398,14 @@
     bool getValue(bool& t) { t = b; return true; };
     using Element::setValue;
     bool setValue(const bool v) { b = v; return true; };
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    void toJSON(std::ostream& ss);
+    bool equals(ElementPtr other);
+};
+
+class NullElement : public Element {
+public:
+    NullElement() : Element(null) {};
+    void toJSON(std::ostream& ss);
     bool equals(ElementPtr other);
 };
 
@@ -379,8 +419,7 @@
     bool getValue(std::string& t) { t = s; return true; };
     using Element::setValue;
     bool setValue(const std::string& v) { s = v; return true; };
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    void toJSON(std::ostream& ss);
     bool equals(ElementPtr other);
 };
 
@@ -388,7 +427,7 @@
     std::vector<ElementPtr> l;
 
 public:
-    ListElement(std::vector<ElementPtr> v) : Element(list), l(v) {};
+    ListElement() : Element(list), l(std::vector<ElementPtr>()) {};
     const std::vector<ElementPtr>& listValue() { return l; }
     using Element::getValue;
     bool getValue(std::vector<ElementPtr>& t) { t = l; return true; };
@@ -401,8 +440,7 @@
     void add(ElementPtr e) { l.push_back(e); };
     using Element::remove;
     void remove(int i) { l.erase(l.begin() + i); };
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    void toJSON(std::ostream& ss);
     size_t size() { return l.size(); }
     bool equals(ElementPtr other);
 };
@@ -411,7 +449,8 @@
     std::map<std::string, ElementPtr> m;
 
 public:
-    MapElement(const std::map<std::string, ElementPtr>& v) : Element(map), m(v) {};
+    MapElement() : Element(map), m(std::map<std::string, ElementPtr>()) {};
+    // TODO: should we have direct iterators instead of exposing the std::map here?
     const std::map<std::string, ElementPtr>& mapValue() { return m; }
     using Element::getValue;
     bool getValue(std::map<std::string, ElementPtr>& t) { t = m; return true; };
@@ -420,18 +459,12 @@
     using Element::get;
     ElementPtr get(const std::string& s) { if (contains(s)) { return m[s]; } else { return ElementPtr();} };
     using Element::set;
-    void set(const std::string& s, ElementPtr p) { m[s] = p; };
+    void set(const std::string& key, ElementPtr value);
     using Element::remove;
     void remove(const std::string& s) { m.erase(s); }
     bool contains(const std::string& s) { return m.find(s) != m.end(); }
-    std::string str();
-    void toWire(std::stringstream& ss, int omit_length = 1);
+    void toJSON(std::ostream& ss);
     
-    //
-    // Encode into the CC wire format.
-    //
-    void toWire(std::ostream& ss);
-
     // we should name the two finds better...
     // find the element at id; raises TypeError if one of the
     // elements at path except the one we're looking for is not a
@@ -467,8 +500,12 @@
 /// MapElements.
 /// Every string,value pair in other is copied into element
 /// (the ElementPtr of value is copied, this is not a new object)
-/// Unless the value is an empty ElementPtr, in which case the
-/// whole key is removed from element.
+/// Unless the value is a NullElement, in which case the
+/// key is removed from element, rather than setting the value to
+/// the given NullElement.
+/// This way, we can remove values from for instance maps with
+/// configuration data (which would then result in reverting back
+/// to the default).
 /// Raises a TypeError if either ElementPtr is not a MapElement
 void merge(ElementPtr element, const ElementPtr other);
 

Modified: branches/trac191/src/lib/cc/data_unittests.cc
==============================================================================
--- branches/trac191/src/lib/cc/data_unittests.cc (original)
+++ branches/trac191/src/lib/cc/data_unittests.cc Fri Jul  9 10:04:58 2010
@@ -40,58 +40,124 @@
     EXPECT_EQ(bool_el.getType(), Element::boolean);
     StringElement str_el = StringElement("foo");
     EXPECT_EQ(str_el.getType(), Element::string);
-    std::vector<ElementPtr> v;
-    ListElement list_el = ListElement(v);
+    ListElement list_el = ListElement();
     EXPECT_EQ(list_el.getType(), Element::list);
-    std::map<std::string, ElementPtr> m;
-    MapElement map_el = MapElement(m);
+    MapElement map_el = MapElement();
     EXPECT_EQ(map_el.getType(), Element::map);
-}
-
-TEST(Element, from_and_to_str) {
-    // this test checks whether the str() method returns the same
-    // string that was used for creation
+
+}
+
+TEST(Element, TypeNameConversion) {
+    EXPECT_EQ(Element::integer, Element::nameToType("integer"));
+    EXPECT_EQ(Element::real, Element::nameToType("real"));
+    EXPECT_EQ(Element::boolean, Element::nameToType("boolean"));
+    EXPECT_EQ(Element::string, Element::nameToType("string"));
+    EXPECT_EQ(Element::list, Element::nameToType("list"));
+    EXPECT_EQ(Element::map, Element::nameToType("map"));
+    EXPECT_EQ(Element::null, Element::nameToType("null"));
+    EXPECT_EQ(Element::any, Element::nameToType("any"));
+    EXPECT_THROW(Element::nameToType("somethingunknown"), TypeError);
+
+    EXPECT_EQ("integer", Element::typeToName(Element::integer));
+    EXPECT_EQ("real", Element::typeToName(Element::real));
+    EXPECT_EQ("boolean", Element::typeToName(Element::boolean));
+    EXPECT_EQ("string", Element::typeToName(Element::string));
+    EXPECT_EQ("list", Element::typeToName(Element::list));
+    EXPECT_EQ("map", Element::typeToName(Element::map));
+    EXPECT_EQ("null", Element::typeToName(Element::null));
+    EXPECT_EQ("any", Element::typeToName(Element::any));
+    EXPECT_EQ("unknown", Element::typeToName((Element::types)123));
+}
+
+TEST(Element, from_and_to_json) {
+    // a set of inputs that are the same when converted to json and
+    // back to a string (tests for inputs that have equivalent, but
+    // different string representations when converted back are below)
     ElementPtr el;
     std::vector<std::string> sv;
 
     sv.push_back("12");
     sv.push_back("1.1");
-    sv.push_back("True");
-    sv.push_back("False");
+    sv.push_back("true");
+    sv.push_back("false");
     sv.push_back("\"asdf\"");
+    sv.push_back("null");
     sv.push_back("[ 1, 2, 3, 4 ]");
-    sv.push_back("{\"name\": \"foo\", \"value\": 47806}");
-    sv.push_back("[ {\"a\": 1, \"b\": \"c\"}, {\"a\": 2, \"b\": \"d\"} ]");
-
+    sv.push_back("{ \"name\": \"foo\", \"value\": 47806 }");
+    sv.push_back("[ { \"a\": 1, \"b\": \"c\" }, { \"a\": 2, \"b\": \"d\" } ]");
+    sv.push_back("8.23");
+    sv.push_back("123.456");
+    sv.push_back("null");
+    sv.push_back("-1");
+    sv.push_back("-1.234");
+    sv.push_back("-123.456");
+    
     BOOST_FOREACH(std::string s, sv) {
-        // also test << operator, which uses Element::str()
+        // test << operator, which uses Element::str()
         std::ostringstream stream;
-        el = Element::createFromString(s);
+        el = Element::fromJSON(s);
         stream << el;
         EXPECT_EQ(stream.str(), s);
+
+        // test toWire(ostream), which should also be the same now
+        std::ostringstream wire_stream;
+        el->toWire(wire_stream);
+        EXPECT_EQ(wire_stream.str(), s);
     }
 
     // some parse errors
     try {
-        Element::createFromString("{1}");
-    } catch (isc::data::ParseError pe) {
+        Element::fromJSON("{1}");
+    } catch (isc::data::JSONError pe) {
         std::string s = std::string(pe.what());
         EXPECT_EQ(s, "String expected in <string>:1:3");
     }
     
     sv.clear();
     sv.push_back("{1}");
-    //ElementPtr ep = Element::createFromString("\"aaa\nbbb\"err");
+    //ElementPtr ep = Element::fromJSON("\"aaa\nbbb\"err");
     //std::cout << ep << std::endl;
     sv.push_back("\n\nTru");
     sv.push_back("{ \n \"aaa\nbbb\"err:");
     sv.push_back("{ \t\n \"aaa\nbbb\"\t\n\n:\n True, \"\\\"");
     sv.push_back("{ \"a\": None}");
     sv.push_back("");
+    sv.push_back("nul");
     BOOST_FOREACH(std::string s, sv) {
         
-        EXPECT_THROW(el = Element::createFromString(s), isc::data::ParseError);
+        EXPECT_THROW(el = Element::fromJSON(s), isc::data::JSONError);
     }
+
+    // some json specific format tests, here the str() output is
+    // different from the string input
+    EXPECT_EQ("100", Element::fromJSON("+100")->str());
+    EXPECT_EQ("100", Element::fromJSON("1e2")->str());
+    EXPECT_EQ("100", Element::fromJSON("+1e2")->str());
+    EXPECT_EQ("-100", Element::fromJSON("-1e2")->str());
+    EXPECT_EQ("0.01", Element::fromJSON("1e-2")->str());
+    EXPECT_EQ("0.01", Element::fromJSON(".01")->str());
+    EXPECT_EQ("-0.01", Element::fromJSON("-1e-2")->str());
+    EXPECT_EQ("1.2", Element::fromJSON("1.2")->str());
+    EXPECT_EQ("1", Element::fromJSON("1.0")->str());
+    EXPECT_EQ("120", Element::fromJSON("1.2e2")->str());
+    EXPECT_EQ("100", Element::fromJSON("1.0e2")->str());
+    EXPECT_EQ("100", Element::fromJSON("1.0E2")->str());
+    EXPECT_EQ("0.01", Element::fromJSON("1.0e-2")->str());
+    EXPECT_EQ("0.012", Element::fromJSON("1.2e-2")->str());
+    EXPECT_EQ("0.012", Element::fromJSON("1.2E-2")->str());
+    EXPECT_EQ("null", Element::fromJSON("Null")->str());
+    EXPECT_EQ("null", Element::fromJSON("NULL")->str());
+    EXPECT_EQ("false", Element::fromJSON("False")->str());
+    EXPECT_EQ("false", Element::fromJSON("FALSE")->str());
+    EXPECT_EQ("true", Element::fromJSON("True")->str());
+    EXPECT_EQ("true", Element::fromJSON("TRUE")->str());
+
+    // number overflows
+    EXPECT_THROW(Element::fromJSON("12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("1.1e12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("-1.1e12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("1e12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("1e50000")->str(), JSONError);
 
 }
 
@@ -99,61 +165,150 @@
     // this test checks whether elements throw exceptions if the
     // incorrect type is requested
     ElementPtr el;
+    long int i;
+    double d;
+    bool b;
+    std::string s("asdf");
+    std::vector<ElementPtr> v;
+    std::map<std::string, ElementPtr> m;
+    
 
     el = Element::create(1);
+    EXPECT_NO_THROW(el->intValue());
     EXPECT_THROW(el->doubleValue(), TypeError);
     EXPECT_THROW(el->boolValue(), TypeError);
     EXPECT_THROW(el->stringValue(), TypeError);
     EXPECT_THROW(el->listValue(), TypeError);
     EXPECT_THROW(el->mapValue(), TypeError);
+    EXPECT_TRUE(el->getValue(i));
+    EXPECT_FALSE(el->getValue(d));
+    EXPECT_FALSE(el->getValue(b));
+    EXPECT_FALSE(el->getValue(s));
+    EXPECT_FALSE(el->getValue(v));
+    EXPECT_FALSE(el->getValue(m));
+    EXPECT_EQ(i, 1);
+    i = 2;
+    EXPECT_TRUE(el->setValue(i));
+    EXPECT_EQ(2, el->intValue());
+    EXPECT_FALSE(el->setValue(d));
+    EXPECT_FALSE(el->setValue(b));
+    EXPECT_FALSE(el->setValue(s));
+    EXPECT_FALSE(el->setValue(v));
+    EXPECT_FALSE(el->setValue(m));
+    EXPECT_THROW(el->get(1), TypeError);
+    EXPECT_THROW(el->set(1, el), TypeError);
+    EXPECT_THROW(el->add(el), TypeError);
+    EXPECT_THROW(el->remove(1), TypeError);
+    EXPECT_THROW(el->size(), TypeError);
+    EXPECT_THROW(el->get("foo"), TypeError);
+    EXPECT_THROW(el->set("foo", el), TypeError);
+    EXPECT_THROW(el->remove("foo"), TypeError);
+    EXPECT_THROW(el->contains("foo"), TypeError);
+    ElementPtr tmp;
+    EXPECT_FALSE(el->find("foo", tmp));
+    
 
     el = Element::create(1.1);
     EXPECT_THROW(el->intValue(), TypeError);
+    EXPECT_NO_THROW(el->doubleValue());
     EXPECT_THROW(el->boolValue(), TypeError);
     EXPECT_THROW(el->stringValue(), TypeError);
     EXPECT_THROW(el->listValue(), TypeError);
     EXPECT_THROW(el->mapValue(), TypeError);
+    EXPECT_FALSE(el->getValue(i));
+    EXPECT_TRUE(el->getValue(d));
+    EXPECT_FALSE(el->getValue(b));
+    EXPECT_FALSE(el->getValue(s));
+    EXPECT_FALSE(el->getValue(v));
+    EXPECT_FALSE(el->getValue(m));
+    EXPECT_EQ(d, 1.1);
+    d = 2.2;
+    EXPECT_TRUE(el->setValue(d));
+    EXPECT_EQ(2.2, el->doubleValue());
+    EXPECT_FALSE(el->setValue(i));
+    EXPECT_FALSE(el->setValue(b));
+    EXPECT_FALSE(el->setValue(s));
+    EXPECT_FALSE(el->setValue(v));
+    EXPECT_FALSE(el->setValue(m));
 
     el = Element::create(true);
     EXPECT_THROW(el->intValue(), TypeError);
     EXPECT_THROW(el->doubleValue(), TypeError);
+    EXPECT_NO_THROW(el->boolValue());
     EXPECT_THROW(el->stringValue(), TypeError);
     EXPECT_THROW(el->listValue(), TypeError);
     EXPECT_THROW(el->mapValue(), TypeError);
+    EXPECT_FALSE(el->getValue(i));
+    EXPECT_FALSE(el->getValue(d));
+    EXPECT_TRUE(el->getValue(b));
+    EXPECT_FALSE(el->getValue(s));
+    EXPECT_FALSE(el->getValue(v));
+    EXPECT_FALSE(el->getValue(m));
+    EXPECT_EQ(b, true);
+    b = false;
+    EXPECT_TRUE(el->setValue(b));
+    EXPECT_EQ(false, el->boolValue());
 
     el = Element::create("foo");
     EXPECT_THROW(el->intValue(), TypeError);
     EXPECT_THROW(el->doubleValue(), TypeError);
     EXPECT_THROW(el->boolValue(), TypeError);
+    EXPECT_NO_THROW(el->stringValue());
     EXPECT_THROW(el->listValue(), TypeError);
     EXPECT_THROW(el->mapValue(), TypeError);
-
-    std::vector<ElementPtr> v;
-    el = Element::create(v);
+    EXPECT_FALSE(el->getValue(i));
+    EXPECT_FALSE(el->getValue(d));
+    EXPECT_FALSE(el->getValue(b));
+    EXPECT_TRUE(el->getValue(s));
+    EXPECT_FALSE(el->getValue(v));
+    EXPECT_FALSE(el->getValue(m));
+    EXPECT_EQ(s, "foo");
+    s = "bar";
+    EXPECT_TRUE(el->setValue(s));
+    EXPECT_EQ("bar", el->stringValue());
+
+    el = Element::createList();
     EXPECT_THROW(el->intValue(), TypeError);
     EXPECT_THROW(el->doubleValue(), TypeError);
     EXPECT_THROW(el->boolValue(), TypeError);
     EXPECT_THROW(el->stringValue(), TypeError);
+    EXPECT_NO_THROW(el->listValue());
     EXPECT_THROW(el->mapValue(), TypeError);
-
-    std::map<std::string, ElementPtr> m;
-    el = Element::create(m);
+    EXPECT_FALSE(el->getValue(i));
+    EXPECT_FALSE(el->getValue(d));
+    EXPECT_FALSE(el->getValue(b));
+    EXPECT_FALSE(el->getValue(s));
+    EXPECT_TRUE(el->getValue(v));
+    EXPECT_FALSE(el->getValue(m));
+    EXPECT_EQ("[  ]", el->str());
+    v.push_back(Element::create(1));
+    EXPECT_TRUE(el->setValue(v));
+    EXPECT_EQ("[ 1 ]", el->str());
+
+    el = Element::createMap();
     EXPECT_THROW(el->intValue(), TypeError);
     EXPECT_THROW(el->doubleValue(), TypeError);
     EXPECT_THROW(el->boolValue(), TypeError);
     EXPECT_THROW(el->stringValue(), TypeError);
     EXPECT_THROW(el->listValue(), TypeError);
+    EXPECT_NO_THROW(el->mapValue());
+    EXPECT_FALSE(el->getValue(i));
+    EXPECT_FALSE(el->getValue(d));
+    EXPECT_FALSE(el->getValue(b));
+    EXPECT_FALSE(el->getValue(s));
+    EXPECT_FALSE(el->getValue(v));
+    EXPECT_TRUE(el->getValue(m));
 
 }
 
 TEST(Element, ListElement) {
     // this function checks the specific functions for ListElements
-    ElementPtr el = Element::createFromString("[ 1, \"bar\", 3 ]");
+    ElementPtr el = Element::fromJSON("[ 1, \"bar\", 3 ]");
     EXPECT_EQ(el->get(0)->intValue(), 1);
     EXPECT_EQ(el->get(1)->stringValue(), "bar");
     EXPECT_EQ(el->get(2)->intValue(), 3);
 
-    el->set(0, Element::createFromString("\"foo\""));
+    el->set(0, Element::fromJSON("\"foo\""));
     EXPECT_EQ(el->get(0)->stringValue(), "foo");
 
     el->add(Element::create(47806));
@@ -170,18 +325,9 @@
     EXPECT_EQ(el->get(2)->intValue(), 32);
 }
 
-namespace {
-const string long_maptag("0123456789abcdef1123456789abcdef2123456789abcdef"
-                         "3123456789abcdef4123456789abcdef5123456789abcdef"
-                         "6123456789abcdef7123456789abcdef8123456789abcdef"
-                         "9123456789abcdefa123456789abcdefb123456789abcdef"
-                         "c123456789abcdefd123456789abcdefe123456789abcdef"
-                         "f123456789abcdef");
-}
-
 TEST(Element, MapElement) {
     // this function checks the specific functions for ListElements
-    ElementPtr el = Element::createFromString("{ \"name\": \"foo\", \"value1\": \"bar\", \"value2\": { \"number\": 42 } }");
+    ElementPtr el = Element::fromJSON("{ \"name\": \"foo\", \"value1\": \"bar\", \"value2\": { \"number\": 42 } }");
     ElementPtr el2;
     
     EXPECT_EQ(el->get("name")->stringValue(), "foo");
@@ -211,240 +357,241 @@
                        "9123456789abcdefa123456789abcdefb123456789abcdef"
                        "c123456789abcdefd123456789abcdefe123456789abcdef"
                        "f123456789abcde");
-    std::map<std::string, ElementPtr> long_maptag_map;
     
     EXPECT_EQ(255, long_maptag.length()); // check prerequisite
-    el = Element::createFromString("{ \"" + long_maptag + "\": \"bar\"}");
+    el = Element::fromJSON("{ \"" + long_maptag + "\": \"bar\"}");
     EXPECT_EQ("bar", el->find(long_maptag)->stringValue());
 
-    long_maptag_map[long_maptag] = Element::create("bar");
-    el = Element::create(long_maptag_map);
+    el = Element::createMap();
+    el->set(long_maptag, Element::create("bar"));
     EXPECT_EQ("bar", el->find(long_maptag)->stringValue());
 
-    // A one-byte longer tag should trigger an exception.
+    // A one-byte longer tag should still be allowed
     long_maptag.push_back('f');
-    EXPECT_THROW(Element::createFromString("{ \"" + long_maptag +
-                                           "\": \"bar\"}"),
-                 ParseError);
-
-    long_maptag_map[long_maptag] = Element::create("bar");
-    EXPECT_THROW(Element::create(long_maptag_map), TypeError);
+    el = Element::fromJSON("{ \"" + long_maptag + "\": \"bar\"}");
+    el->set(long_maptag, Element::create("bar"));
+    EXPECT_EQ("bar", el->find(long_maptag)->stringValue());
 
 }
 
 TEST(Element, to_and_from_wire) {
-    ElementPtr el, decoded_el;
-    std::string wire;
-    std::vector<std::string> sv;
-    std::vector<std::string> sw;
-    std::stringstream bigstring, bigstring2;
-    std::stringstream bigwire, bigwire2;
-
-    sv.push_back("{\"name\": \"foo\"}");
-    sw.push_back("Skan\004name\050\003foo");
-    sv.push_back("{\"value2\": {\"number\": 42}}");
-    sw.push_back("Skan\006value2\042\013\006number\046\00242");
-    sv.push_back("{\"bool\": False, \"bool2\": True, \"real\": 2.34, \"string\": \"foo\"}");
-    sw.push_back("Skan\004bool\045\0010\005bool2\045\0011\004real\047\0042\05634\006string\050\003foo");
-    sv.push_back("{\"list\": [ 1, 2, 3, 4 ]}");
-    sw.push_back("Skan\004list\043\014\046\0011\046\0012\046\0013\046\0014");
-
-    // some big ones
-
-    bigstring << "{\"bigstring\": \"";
-    bigwire << "Skan\011bigstring\030\001\001";
-    for (size_t i = 0; i < 257; i++) {
-        bigstring << "x";
-        bigwire << "x";
-    }
-    bigstring << "\"}";
-    sv.push_back(bigstring.str());
-    sw.push_back(bigwire.str());
-
-
-    bigstring2 << "{\"bigstring2\": \"";
-    bigwire2 << "Skan\012bigstring2\010";
-    bigwire2 << '\000' << '\001' << '\000' << '\001';
-    for (size_t i = 0; i < 65537; i++) {
-        bigstring2 << "x";
-        bigwire2 << "x";
-    }
-    bigstring2 << "\"}";
-    sv.push_back(bigstring2.str());
-    sw.push_back(bigwire2.str());
-
-
-    BOOST_FOREACH(std::string s, sv) {
-        // also test << operator, which uses Element::str()
-        el = Element::createFromString(s);
-        EXPECT_EQ(s, el->str());
-        wire = el->toWire();
-        /*
-        std::cout << "Encoded wire format:" << std::endl;
-        my_print(wire);
-        std::cout << "Expecting:" << std::endl;
-        my_print(sw.at(0));
-        */
-        EXPECT_EQ(sw.at(0), wire);
-        sw.erase(sw.begin());
-        decoded_el = Element::fromWire(wire);
-        EXPECT_EQ(s, decoded_el->str());
-    }
-    
-    //EXPECT_THROW(Element::fromWire("Skan\004name\050\003foo"), DecodeError);
-    EXPECT_THROW(Element::fromWire("Skan\004name\050"), DecodeError);
-    EXPECT_THROW(Element::fromWire("Skan\004na"), DecodeError);
-    EXPECT_THROW(Element::fromWire("Skan\004name\050\003fo"), DecodeError);
-    EXPECT_NO_THROW(Element::fromWire("Skan\004name\041\003foo"));
-    EXPECT_THROW(Element::fromWire("Skan\004name\041\003fo"), DecodeError);
-    EXPECT_NO_THROW(Element::fromWire("Skan\004name\044\001a"));
-    EXPECT_THROW(Element::fromWire("Skab\004name\050\003foo"), DecodeError);
-
-    //EXPECT_EQ("\047\0031.2", Element::create(1.2)->toWire(0));
-    EXPECT_EQ("\046\0011", Element::createFromString("[ 1 ]")->toWire(1));
-
-    std::string ddef = "{\"data_specification\": {\"config_data\": [ {\"item_default\": \"Hello, world!\", \"item_name\": \"default_name\", \"item_optional\": False, \"item_type\": \"string\"}, {\"item_default\": [  ], \"item_name\": \"zone_list\", \"item_optional\": False, \"item_type\": \"list\", \"list_item_spec\": {\"item_name\": \"zone_name\", \"item_optional\": True, \"item_type\": \"string\"}} ], \"module_name\": \"Auth\"}}";
-    //std::string ddef = "{\"aaa\": 123, \"test\": [  ], \"zzz\": 123}";
-    ElementPtr ddef_el = Element::createFromString(ddef);
-    std::string ddef_wire = ddef_el->toWire();
-    ElementPtr ddef_el2 = Element::fromWire(ddef_wire);
-    std::string ddef2 = ddef_el2->str();
-    EXPECT_EQ(ddef, ddef2);
-}
-
-ElementPtr efs(const std::string& str) {
-    return Element::createFromString(str);
+    // Wire format is now plain JSON.
+    ElementPtr el;
+    EXPECT_EQ("1", Element::create(1)->toWire());
+    EXPECT_EQ("1.1", Element::create(1.1)->toWire());
+    EXPECT_EQ("true", Element::create(true)->toWire());
+    EXPECT_EQ("false", Element::create(false)->toWire());
+    EXPECT_EQ("null", Element::create()->toWire());
+    EXPECT_EQ("\"a string\"", Element::create("a string")->toWire());
+    EXPECT_EQ("[ \"a\", \"list\" ]", Element::fromJSON("[ \"a\", \"list\" ]")->toWire());
+    EXPECT_EQ("{ \"a\": \"map\" }", Element::fromJSON("{ \"a\": \"map\" }")->toWire());
+
+    EXPECT_EQ("1", Element::fromWire("1")->str());
+
+    std::stringstream ss;
+    ss << "1";
+    EXPECT_EQ("1", Element::fromWire(ss, 1)->str());
+
+    // Some malformed JSON input
+    EXPECT_THROW(Element::fromJSON("{\":"), isc::data::JSONError);
+    EXPECT_THROW(Element::fromJSON("]"), isc::data::JSONError);
+    EXPECT_THROW(Element::fromJSON("[ 1, 2, }"), isc::data::JSONError);
+}
+
+static ElementPtr
+efs(const std::string& str) {
+    return Element::fromJSON(str);
 }
 
 TEST(Element, equals) {
     // why does EXPECT_EQ not work?
-    EXPECT_TRUE(efs("1") == efs("1"));
-    EXPECT_FALSE(efs("1") == efs("2"));
-    EXPECT_FALSE(efs("1") == efs("\"1\""));
-    EXPECT_FALSE(efs("1") == efs("[]"));
-    EXPECT_FALSE(efs("1") == efs("True"));
-    EXPECT_FALSE(efs("1") == efs("{}"));
-
-    EXPECT_TRUE(efs("1.1") == efs("1.1"));
-    EXPECT_FALSE(efs("1.0") == efs("1"));
-    EXPECT_FALSE(efs("1.1") == efs("\"1\""));
-    EXPECT_FALSE(efs("1.1") == efs("[]"));
-    EXPECT_FALSE(efs("1.1") == efs("True"));
-    EXPECT_FALSE(efs("1.1") == efs("{}"));
-
-    EXPECT_TRUE(efs("True") == efs("True"));
-    EXPECT_FALSE(efs("True") == efs("False"));
-    EXPECT_FALSE(efs("True") == efs("1"));
-    EXPECT_FALSE(efs("True") == efs("\"1\""));
-    EXPECT_FALSE(efs("True") == efs("[]"));
-    EXPECT_FALSE(efs("True") == efs("{}"));
-
-    EXPECT_TRUE(efs("\"foo\"") == efs("\"foo\""));
-    EXPECT_FALSE(efs("\"foo\"") == efs("\"bar\""));
-    EXPECT_FALSE(efs("\"foo\"") == efs("1"));
-    EXPECT_FALSE(efs("\"foo\"") == efs("\"1\""));
-    EXPECT_FALSE(efs("\"foo\"") == efs("True"));
-    EXPECT_FALSE(efs("\"foo\"") == efs("[]"));
-    EXPECT_FALSE(efs("\"foo\"") == efs("{}"));
-
-    EXPECT_TRUE(efs("[]") == efs("[]"));
-    EXPECT_TRUE(efs("[ 1, 2, 3 ]") == efs("[ 1, 2, 3 ]"));
-    EXPECT_TRUE(efs("[ \"a\", [ True, 1], 2.2 ]") == efs("[ \"a\", [ True, 1], 2.2 ]"));
-    EXPECT_FALSE(efs("[ \"a\", [ True, 1], 2.2 ]") == efs("[ \"a\", [ True, 2], 2.2 ]"));
-    EXPECT_FALSE(efs("[]") == efs("[1]"));
-    EXPECT_FALSE(efs("[]") == efs("1"));
-    EXPECT_FALSE(efs("[]") == efs("\"1\""));
-    EXPECT_FALSE(efs("[]") == efs("{}"));
-
-    EXPECT_TRUE(efs("{}") == efs("{}"));
-    EXPECT_TRUE(efs("{ \"foo\": \"bar\" }") == efs("{ \"foo\": \"bar\" }"));
-    EXPECT_TRUE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }") == efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"));
-    EXPECT_FALSE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }") == efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar2\" } }"));
-    EXPECT_FALSE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }") == efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\", 1 ], \"item3\": { \"foo\": \"bar\" } }"));
-    EXPECT_FALSE(efs("{ \"foo\": \"bar\" }") == efs("1"));
-    EXPECT_FALSE(efs("{ \"foo\": \"bar\" }") == efs("\"1\""));
-    EXPECT_FALSE(efs("{ \"foo\": \"bar\" }") == efs("[]"));
-    EXPECT_FALSE(efs("{ \"foo\": \"bar\" }") == efs("{}"));
+    EXPECT_EQ(efs("1"), efs("1"));
+    EXPECT_NE(efs("1"), efs("2"));
+    EXPECT_NE(efs("1"), efs("\"1\""));
+    EXPECT_NE(efs("1"), efs("[]"));
+    EXPECT_NE(efs("1"), efs("True"));
+    EXPECT_NE(efs("1"), efs("{}"));
+
+    EXPECT_EQ(efs("1.1"), efs("1.1"));
+    EXPECT_NE(efs("1.0"), efs("1"));
+    EXPECT_NE(efs("1.1"), efs("\"1\""));
+    EXPECT_NE(efs("1.1"), efs("[]"));
+    EXPECT_NE(efs("1.1"), efs("True"));
+    EXPECT_NE(efs("1.1"), efs("{}"));
+
+    EXPECT_EQ(efs("True"), efs("True"));
+    EXPECT_NE(efs("True"), efs("False"));
+    EXPECT_NE(efs("True"), efs("1"));
+    EXPECT_NE(efs("True"), efs("\"1\""));
+    EXPECT_NE(efs("True"), efs("[]"));
+    EXPECT_NE(efs("True"), efs("{}"));
+
+    EXPECT_EQ(efs("\"foo\""), efs("\"foo\""));
+    EXPECT_NE(efs("\"foo\""), efs("\"bar\""));
+    EXPECT_NE(efs("\"foo\""), efs("1"));
+    EXPECT_NE(efs("\"foo\""), efs("\"1\""));
+    EXPECT_NE(efs("\"foo\""), efs("True"));
+    EXPECT_NE(efs("\"foo\""), efs("[]"));
+    EXPECT_NE(efs("\"foo\""), efs("{}"));
+
+    EXPECT_EQ(efs("[]"), efs("[]"));
+    EXPECT_EQ(efs("[ 1, 2, 3 ]"), efs("[ 1, 2, 3 ]"));
+    EXPECT_EQ(efs("[ \"a\", [ True, 1], 2.2 ]"), efs("[ \"a\", [ True, 1], 2.2 ]"));
+    EXPECT_NE(efs("[ \"a\", [ True, 1], 2.2 ]"), efs("[ \"a\", [ True, 2], 2.2 ]"));
+    EXPECT_NE(efs("[]"), efs("[1]"));
+    EXPECT_NE(efs("[]"), efs("1"));
+    EXPECT_NE(efs("[]"), efs("\"1\""));
+    EXPECT_NE(efs("[]"), efs("{}"));
+
+    EXPECT_EQ(efs("{}"), efs("{}"));
+    EXPECT_EQ(efs("{ \"foo\": \"bar\" }"), efs("{ \"foo\": \"bar\" }"));
+    EXPECT_EQ(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"));
+    EXPECT_NE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar2\" } }"));
+    EXPECT_NE(efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\" ], \"item3\": { \"foo\": \"bar\" } }"), efs("{ \"item1\": 1, \"item2\": [ \"a\", \"list\", 1 ], \"item3\": { \"foo\": \"bar\" } }"));
+    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("1"));
+    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("\"1\""));
+    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("[]"));
+    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("{}"));
+    EXPECT_NE(efs("{ \"foo\": \"bar\" }"), efs("{ \"something\": \"different\" }"));
+
+    EXPECT_EQ(efs("null"), Element::create());
 }
 
 TEST(Element, removeIdentical) {
-    ElementPtr a = Element::createFromString("{}");
-    ElementPtr b = Element::createFromString("{}");
-    ElementPtr c = Element::createFromString("{}");
-    removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
-
-    a = Element::createFromString("{ \"a\": 1 }");
-    b = Element::createFromString("{ \"a\": 1 }");
-    c = Element::createFromString("{}");
-    removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
-
-    a = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 2 ] }");
-    b = Element::createFromString("{}");
-    c = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 2 ] }");
-    removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
-
-    a = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 2 ] }");
-    b = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 2 ] }");
-    c = Element::createFromString("{}");
-    removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
-
-    a = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 2 ] }");
-    b = Element::createFromString("{ \"a\": 1, \"b\": [ 1, 3 ] }");
-    c = Element::createFromString("{ \"b\": [ 1, 2 ] }");
-    removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
-
-    a = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    b = Element::createFromString("{}");
-    c = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
-
-    a = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    b = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    c = Element::createFromString("{}");
-    removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
-
-    a = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    b = Element::createFromString("{ \"a\": { \"b\": \"d\" } }");
-    c = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    removeIdentical(a, b);
-    EXPECT_TRUE(a == c);
+    ElementPtr a = Element::createMap();
+    ElementPtr b = Element::createMap();
+    ElementPtr c = Element::createMap();
+    removeIdentical(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": 1 }");
+    b = Element::fromJSON("{ \"a\": 1 }");
+    c = Element::createMap();
+    removeIdentical(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    b = Element::createMap();
+    c = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    removeIdentical(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    b = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    c = Element::createMap();
+    removeIdentical(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 2 ] }");
+    b = Element::fromJSON("{ \"a\": 1, \"b\": [ 1, 3 ] }");
+    c = Element::fromJSON("{ \"b\": [ 1, 2 ] }");
+    removeIdentical(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::createMap();
+    c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    removeIdentical(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    c = Element::createMap();
+    removeIdentical(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
+    c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    removeIdentical(a, b);
+    EXPECT_EQ(a, c);
+
+    EXPECT_THROW(removeIdentical(Element::create(1), Element::create(2)), TypeError);
 }
 
 TEST(Element, merge)
 {
-    ElementPtr a = Element::createFromString("{}");
-    ElementPtr b = Element::createFromString("{}");
-    ElementPtr c = Element::createFromString("{}");
-    merge(a, b);
-    EXPECT_TRUE(a == c);
-
-    a = Element::createFromString("1");
-    b = Element::createFromString("{}");
+    ElementPtr a = Element::createMap();
+    ElementPtr b = Element::createMap();
+    ElementPtr c = Element::createMap();
+    merge(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("1");
+    b = Element::createMap();
     EXPECT_THROW(merge(a, b), TypeError);
 
-    a = Element::createFromString("{}");
-    b = Element::createFromString("{ \"a\": 1 }");
-    c = Element::createFromString("{ \"a\": 1 }");
-    merge(a, b);
-    EXPECT_TRUE(a == c);
-
-    a = Element::createFromString("{ \"a\": 1 }");
-    b = Element::createFromString("{ \"a\": 2 }");
-    c = Element::createFromString("{ \"a\": 2 }");
-    merge(a, b);
-    EXPECT_TRUE(a == c);
-
-    a = Element::createFromString("{ \"a\": { \"b\": \"c\" } }");
-    b = Element::createFromString("{ \"a\": { \"b\": \"d\" } }");
-    c = Element::createFromString("{ \"a\": { \"b\": \"d\" } }");
-    merge(a, b);
-    EXPECT_TRUE(a == c);
-
-}
+    a = Element::createMap();
+    b = Element::fromJSON("{ \"a\": 1 }");
+    c = Element::fromJSON("{ \"a\": 1 }");
+    merge(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::createMap();
+    b = Element::fromJSON("{ \"a\": 1 }");
+    c = Element::fromJSON("{ \"a\": 1 }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
+
+    a = Element::fromJSON("{ \"a\": 1 }");
+    b = Element::fromJSON("{ \"a\": 2 }");
+    c = Element::fromJSON("{ \"a\": 2 }");
+    merge(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": 1 }");
+    b = Element::fromJSON("{ \"a\": 2 }");
+    c = Element::fromJSON("{ \"a\": 1 }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
+    c = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
+    merge(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": { \"b\": \"d\" } }");
+    c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": null }");
+    c = Element::fromJSON("{  }");
+    merge(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    b = Element::fromJSON("{ \"a\": null }");
+    c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
+    
+    // And some tests with multiple values
+    a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
+    b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
+    c = Element::fromJSON("{ \"a\": 1, \"c\": \"a string\" }");
+    merge(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
+    b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
+    c = Element::fromJSON("{ \"a\": 1, \"b\": true }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
+    b = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
+    c = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
+    merge(a, b);
+    EXPECT_EQ(a, c);
+
+    a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
+    b = Element::fromJSON("{ \"a\": 3, \"b\": 2, \"c\": 1 }");
+    c = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
+    merge(b, a);
+    EXPECT_EQ(b, c);
+
+}

Modified: branches/trac191/src/lib/cc/session.cc
==============================================================================
--- branches/trac191/src/lib/cc/session.cc (original)
+++ branches/trac191/src/lib/cc/session.cc Fri Jul  9 10:04:58 2010
@@ -25,6 +25,7 @@
 // A middle term solution is to generalize our local wrapper interface
 // (currently only available for the auth server), where all such portability
 // issues are hidden, and to have other modules use the wrapper.
+#include <unistd.h>             // for some IPC/network system calls
 #include <asio.hpp>
 #include <asio/error_code.hpp>
 #include <asio/system_error.hpp>
@@ -62,7 +63,7 @@
 
 class SessionImpl {
 public:
-    SessionImpl() : sequence_(-1) { queue_ = Element::createFromString("[]"); }
+    SessionImpl() : sequence_(-1) { queue_ = Element::createList(); }
     virtual ~SessionImpl() {}
     virtual void establish(const char& socket_file) = 0;
     virtual int getSocket() = 0;
@@ -72,7 +73,7 @@
     virtual void readData(void* data, size_t datalen) = 0;
     virtual void startRead(boost::function<void()> user_handler) = 0;
     
-    int sequence_; // the next sequence number to use
+    long int sequence_; // the next sequence number to use
     std::string lname_;
     ElementPtr queue_;
 };
@@ -320,7 +321,7 @@
     // send a request for our local name, and wait for a response
     //
     ElementPtr get_lname_msg =
-        Element::createFromString("{ \"type\": \"getlname\" }");
+        Element::fromJSON("{ \"type\": \"getlname\" }");
     sendmsg(get_lname_msg);
 
     ElementPtr routing, msg;
@@ -428,7 +429,7 @@
         msg = l_msg;
         return true;
     } else {
-        ElementPtr q_el = Element::createFromString("[]");
+        ElementPtr q_el = Element::createList();
         q_el->add(l_env);
         q_el->add(l_msg);
         impl_->queue_->add(q_el);
@@ -439,7 +440,7 @@
 
 void
 Session::subscribe(std::string group, std::string instance) {
-    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
+    ElementPtr env = Element::createMap();
 
     env->set("type", Element::create("subscribe"));
     env->set("group", Element::create(group));
@@ -450,7 +451,7 @@
 
 void
 Session::unsubscribe(std::string group, std::string instance) {
-    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
+    ElementPtr env = Element::createMap();
 
     env->set("type", Element::create("unsubscribe"));
     env->set("group", Element::create(group));
@@ -463,8 +464,8 @@
 Session::group_sendmsg(ElementPtr msg, std::string group,
                        std::string instance, std::string to)
 {
-    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
-    int nseq = ++impl_->sequence_;
+    ElementPtr env = Element::createMap();
+    long int nseq = ++impl_->sequence_;
     
     env->set("type", Element::create("send"));
     env->set("from", Element::create(impl_->lname_));
@@ -487,8 +488,8 @@
 
 int
 Session::reply(ElementPtr& envelope, ElementPtr& newmsg) {
-    ElementPtr env = Element::create(std::map<std::string, ElementPtr>());
-    int nseq = ++impl_->sequence_;
+    ElementPtr env = Element::createMap();
+    long int nseq = ++impl_->sequence_;
     
     env->set("type", Element::create("send"));
     env->set("from", Element::create(impl_->lname_));

Modified: branches/trac191/src/lib/cc/session_unittests.cc
==============================================================================
--- branches/trac191/src/lib/cc/session_unittests.cc (original)
+++ branches/trac191/src/lib/cc/session_unittests.cc Fri Jul  9 10:04:58 2010
@@ -16,6 +16,8 @@
 
 #include "config.h"
 
+// for some IPC/network system calls in asio/detail/pipe_select_interrupter.hpp 
+#include <unistd.h>
 // XXX: the ASIO header must be included before others.  See session.cc.
 #include <asio.hpp>
 

Modified: branches/trac191/src/lib/config/ccsession.cc
==============================================================================
--- branches/trac191/src/lib/config/ccsession.cc (original)
+++ branches/trac191/src/lib/config/ccsession.cc Fri Jul  9 10:04:58 2010
@@ -47,7 +47,7 @@
 
 using isc::data::Element;
 using isc::data::ElementPtr;
-using isc::data::ParseError;
+using isc::data::JSONError;
 
 namespace isc {
 namespace config {
@@ -56,7 +56,7 @@
 ElementPtr
 createAnswer()
 {
-    ElementPtr answer = Element::createFromString("{\"result\": [] }");
+    ElementPtr answer = Element::fromJSON("{\"result\": [] }");
     ElementPtr answer_content = answer->get("result");
     answer_content->add(Element::create(0));
     return answer;
@@ -68,7 +68,7 @@
     if (rcode != 0 && (!arg || arg->getType() != Element::string)) {
         isc_throw(CCSessionError, "Bad or no argument for rcode != 0");
     }
-    ElementPtr answer = Element::createFromString("{\"result\": [] }");
+    ElementPtr answer = Element::fromJSON("{\"result\": [] }");
     ElementPtr answer_content = answer->get("result");
     answer_content->add(Element::create(rcode));
     answer_content->add(arg);
@@ -78,7 +78,7 @@
 ElementPtr
 createAnswer(const int rcode, const std::string& arg)
 {
-    ElementPtr answer = Element::createFromString("{\"result\": [] }");
+    ElementPtr answer = Element::fromJSON("{\"result\": [] }");
     ElementPtr answer_content = answer->get("result");
     answer_content->add(Element::create(rcode));
     answer_content->add(Element::create(arg));
@@ -125,8 +125,8 @@
 ElementPtr
 createCommand(const std::string& command, ElementPtr arg)
 {
-    ElementPtr cmd = Element::createFromString("{}");
-    ElementPtr cmd_parts = Element::createFromString("[]");
+    ElementPtr cmd = Element::createMap();
+    ElementPtr cmd_parts = Element::createList();
     cmd_parts->add(Element::create(command));
     if (arg) {
         cmd_parts->add(arg);
@@ -175,7 +175,7 @@
 
     try {
         module_spec = moduleSpecFromFile(file, true);
-    } catch (ParseError pe) {
+    } catch (JSONError pe) {
         cout << "Error parsing module specification file: " << pe.what() << endl;
         exit(1);
     } catch (ModuleSpecError dde) {
@@ -252,10 +252,10 @@
         std::cerr << "[" << module_name_ << "] Error in specification: " << answer << std::endl;
     }
     
-    setLocalConfig(Element::createFromString("{}"));
+    setLocalConfig(Element::fromJSON("{}"));
     // get any stored configuration from the manager
     if (config_handler_) {
-        ElementPtr cmd = Element::createFromString("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name_ + "\"} ] }");
+        ElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name_ + "\"} ] }");
         seq = session_.group_sendmsg(cmd, "ConfigManager");
         session_.group_recvmsg(env, answer, false, seq);
         ElementPtr new_config = parseAnswer(rcode, answer);
@@ -274,7 +274,7 @@
 ModuleCCSession::handleConfigUpdate(ElementPtr new_config)
 {
     ElementPtr answer;
-    ElementPtr errors = Element::createFromString("[]");
+    ElementPtr errors = Element::createList();
     if (!config_handler_) {
         answer = createAnswer(1, module_name_ + " does not have a config handler");
     } else if (!module_specification_.validate_config(new_config, false, errors)) {
@@ -363,7 +363,7 @@
     session_.subscribe(module_name);
 
     // Get the current configuration values for that module
-    ElementPtr cmd = Element::createFromString("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
+    ElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
     ElementPtr env, answer;
     int rcode;
     

Modified: branches/trac191/src/lib/config/config_data.cc
==============================================================================
--- branches/trac191/src/lib/config/config_data.cc (original)
+++ branches/trac191/src/lib/config/config_data.cc Fri Jul  9 10:04:58 2010
@@ -26,6 +26,16 @@
 namespace isc {
 namespace config {
 
+//
+// Return a part of a specification, as identified by the
+// '/'-separated identifier.
+// If it cannot be found, a DataNotFound error is thrown.
+//
+// Recursively goes through the Element. If it is a List,
+// we search it contents to have 'items' (i.e. contain item_name)
+// If it is a map, we search through the list contained in its
+// 'map_item_spec' value. This code assumes the data has been
+// validated and conforms to the specification.
 static ElementPtr
 find_spec_part(ElementPtr spec, const std::string& identifier)
 {
@@ -97,6 +107,11 @@
     return spec_part;
 }
 
+//
+// Adds the names of the items in the given specification part.
+// If recurse is true, maps will also have their children added.
+// Result must be a ListElement
+//
 static void
 spec_name_list(ElementPtr result, ElementPtr spec_part, std::string prefix, bool recurse = false)
 {
@@ -129,6 +144,8 @@
 ElementPtr
 ConfigData::getValue(const std::string& identifier)
 {
+    // 'fake' is set, but dropped by this function and
+    // serves no further purpose.
     bool fake;
     return getValue(fake, identifier);
 }
@@ -158,7 +175,7 @@
 ElementPtr
 ConfigData::getItemList(const std::string& identifier, bool recurse)
 {
-    ElementPtr result = Element::createFromString("[]");
+    ElementPtr result = Element::createList();
     ElementPtr spec_part = getModuleSpec().getConfigSpec();
     if (identifier != "" && identifier != "/") {
         spec_part = find_spec_part(spec_part, identifier);
@@ -172,7 +189,7 @@
 ElementPtr
 ConfigData::getFullConfig()
 {
-    ElementPtr result = Element::createFromString("{}");
+    ElementPtr result = Element::createMap();
     ElementPtr items = getItemList("", true);
     BOOST_FOREACH(ElementPtr item, items->listValue()) {
         result->set(item->stringValue(), getValue(item->stringValue()));

Modified: branches/trac191/src/lib/config/config_data.h
==============================================================================
--- branches/trac191/src/lib/config/config_data.h (original)
+++ branches/trac191/src/lib/config/config_data.h Fri Jul  9 10:04:58 2010
@@ -39,12 +39,12 @@
 public:
     /// Constructs a ConfigData option with no specification and an
     /// empty configuration.
-    ConfigData() { _config = Element::createFromString("{}"); };
+    ConfigData() { _config = Element::createMap(); };
     
     /// Constructs a ConfigData option with the given specification
     /// and an empty configuration.
     /// \param module_spec A ModuleSpec for the relevant module
-    ConfigData(const ModuleSpec& module_spec) : _module_spec(module_spec) { _config = Element::createFromString("{}"); }
+    ConfigData(const ModuleSpec& module_spec) : _module_spec(module_spec) { _config = Element::createMap(); }
 
     virtual ~ConfigData() {};
 

Modified: branches/trac191/src/lib/config/module_spec.cc
==============================================================================
--- branches/trac191/src/lib/config/module_spec.cc (original)
+++ branches/trac191/src/lib/config/module_spec.cc Fri Jul  9 10:04:58 2010
@@ -31,49 +31,6 @@
 // static functions
 //
 
-// todo: is there a direct way to get a std::string from an enum label?
-static std::string
-getType_string(Element::types type)
-{
-    switch(type) {
-    case Element::integer:
-        return std::string("integer");
-    case Element::real:
-        return std::string("real");
-    case Element::boolean:
-        return std::string("boolean");
-    case Element::string:
-        return std::string("string");
-    case Element::list:
-        return std::string("list");
-    case Element::map:
-        return std::string("map");
-    default:
-        return std::string("unknown");
-    }
-}
-
-static Element::types
-getType_value(const std::string& type_name) {
-    if (type_name == "integer") {
-        return Element::integer;
-    } else if (type_name == "real") {
-        return Element::real;
-    } else if (type_name == "boolean") {
-        return Element::boolean;
-    } else if (type_name == "string") {
-        return Element::string;
-    } else if (type_name == "list") {
-        return Element::list;
-    } else if (type_name == "map") {
-        return Element::map;
-    } else if (type_name == "any") {
-        return Element::any;
-    } else {
-        throw ModuleSpecError(type_name + " is not a valid type name");
-    }
-}
-
 static void
 check_leaf_item(const ElementPtr& spec, const std::string& name, Element::types type, bool mandatory)
 {
@@ -81,7 +38,7 @@
         if (spec->get(name)->getType() == type) {
             return;
         } else {
-            throw ModuleSpecError(name + " not of type " + getType_string(type));
+            throw ModuleSpecError(name + " not of type " + Element::typeToName(type));
         }
     } else if (mandatory) {
         // todo: want parent item name, and perhaps some info about location
@@ -99,17 +56,17 @@
     check_leaf_item(spec, "item_type", Element::string, true);
     check_leaf_item(spec, "item_optional", Element::boolean, true);
     check_leaf_item(spec, "item_default",
-                    getType_value(spec->get("item_type")->stringValue()),
+                    Element::nameToType(spec->get("item_type")->stringValue()),
                     !spec->get("item_optional")->boolValue()
                    );
 
     // if list, check the list specification
-    if (getType_value(spec->get("item_type")->stringValue()) == Element::list) {
+    if (Element::nameToType(spec->get("item_type")->stringValue()) == Element::list) {
         check_leaf_item(spec, "list_item_spec", Element::map, true);
         check_config_item(spec->get("list_item_spec"));
     }
     // todo: add stuff for type map
-    if (getType_value(spec->get("item_type")->stringValue()) == Element::map) {
+    if (Element::nameToType(spec->get("item_type")->stringValue()) == Element::map) {
         check_leaf_item(spec, "map_item_spec", Element::list, true);
         check_config_item_list(spec->get("map_item_spec"));
     }
@@ -161,7 +118,11 @@
 static void
 check_module_specification(const ElementPtr& def)
 {
-    check_data_specification(def);
+    try {
+        check_data_specification(def);
+    } catch (TypeError te) {
+        throw ModuleSpecError(te.what());
+    }
 }
 
 //
@@ -231,7 +192,7 @@
 
 ModuleSpec
 moduleSpecFromFile(const std::string& file_name, const bool check)
-                   throw(ParseError, ModuleSpecError)
+                   throw(JSONError, ModuleSpecError)
 {
     std::ifstream file;
 
@@ -242,7 +203,7 @@
         throw ModuleSpecError(errs.str());
     }
 
-    ElementPtr module_spec_element = Element::createFromString(file, file_name);
+    ElementPtr module_spec_element = Element::fromJSON(file, file_name);
     if (module_spec_element->contains("module_spec")) {
         return ModuleSpec(module_spec_element->get("module_spec"), check);
     } else {
@@ -252,8 +213,8 @@
 
 ModuleSpec
 moduleSpecFromFile(std::ifstream& in, const bool check)
-                   throw(ParseError, ModuleSpecError) {
-    ElementPtr module_spec_element = Element::createFromString(in);
+                   throw(JSONError, ModuleSpecError) {
+    ElementPtr module_spec_element = Element::fromJSON(in);
     if (module_spec_element->contains("module_spec")) {
         return ModuleSpec(module_spec_element->get("module_spec"), check);
     } else {

Modified: branches/trac191/src/lib/config/module_spec.h
==============================================================================
--- branches/trac191/src/lib/config/module_spec.h (original)
+++ branches/trac191/src/lib/config/module_spec.h Fri Jul  9 10:04:58 2010
@@ -110,7 +110,7 @@
     /// is checked to be of the correct form
     ModuleSpec
     moduleSpecFromFile(const std::string& file_name, const bool check = true)
-                       throw(ParseError, ModuleSpecError);
+                       throw(JSONError, ModuleSpecError);
 
     /// Creates a \c ModuleSpec instance from the given input
     /// stream that contains the contents of a .spec file.
@@ -122,7 +122,7 @@
     /// to be of the correct form
     ModuleSpec
     moduleSpecFromFile(std::ifstream& in, const bool check = true)
-                       throw(ParseError, ModuleSpecError);
+                       throw(JSONError, ModuleSpecError);
 } }
 
 #endif // _DATA_DEF_H

Modified: branches/trac191/src/lib/config/tests/Makefile.am
==============================================================================
--- branches/trac191/src/lib/config/tests/Makefile.am (original)
+++ branches/trac191/src/lib/config/tests/Makefile.am Fri Jul  9 10:04:58 2010
@@ -2,7 +2,9 @@
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 # see src/lib/cc/Makefile.am for -Wno-unused-parameter
+if USE_GXX
 AM_CXXFLAGS += -Wno-unused-parameter
+endif
 
 CLEANFILES = *.gcno *.gcda
 
@@ -20,6 +22,9 @@
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.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

Modified: branches/trac191/src/lib/config/tests/ccsession_unittests.cc
==============================================================================
--- branches/trac191/src/lib/config/tests/ccsession_unittests.cc (original)
+++ branches/trac191/src/lib/config/tests/ccsession_unittests.cc Fri Jul  9 10:04:58 2010
@@ -37,7 +37,7 @@
 static ElementPtr
 el(const std::string& str)
 {
-    return Element::createFromString(str);
+    return Element::fromJSON(str);
 }
 
 // upon creation of a ModuleCCSession, the class
@@ -66,16 +66,16 @@
 {
     ElementPtr answer;
     answer = createAnswer();
-    EXPECT_EQ("{\"result\": [ 0 ]}", answer->str());
+    EXPECT_EQ("{ \"result\": [ 0 ] }", answer->str());
     answer = createAnswer(1, "error");
-    EXPECT_EQ("{\"result\": [ 1, \"error\" ]}", answer->str());
+    EXPECT_EQ("{ \"result\": [ 1, \"error\" ] }", answer->str());
 
     EXPECT_THROW(createAnswer(1, ElementPtr()), CCSessionError);
     EXPECT_THROW(createAnswer(1, Element::create(1)), CCSessionError);
 
     ElementPtr arg = el("[ \"just\", \"some\", \"data\" ]");
     answer = createAnswer(0, arg);
-    EXPECT_EQ("{\"result\": [ 0, [ \"just\", \"some\", \"data\" ] ]}", answer->str());
+    EXPECT_EQ("{ \"result\": [ 0, [ \"just\", \"some\", \"data\" ] ] }", answer->str());
 }
 
 TEST(CCSession, parseAnswer)
@@ -87,24 +87,24 @@
     EXPECT_THROW(parseAnswer(rcode, ElementPtr()), CCSessionError);
     EXPECT_THROW(parseAnswer(rcode, el("1")), CCSessionError);
     EXPECT_THROW(parseAnswer(rcode, el("[]")), CCSessionError);
-    EXPECT_THROW(parseAnswer(rcode, el("{}")), CCSessionError);
+    EXPECT_THROW(parseAnswer(rcode, el("{  }")), CCSessionError);
     EXPECT_THROW(parseAnswer(rcode, el("{ \"something\": 1 }")), CCSessionError);
     EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": 0 }")), CCSessionError);
     EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": 1 }")), CCSessionError);
-    EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1 ]}")), CCSessionError);
-    EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1, 1 ]}")), CCSessionError);
-    
-    answer = el("{\"result\": [ 0 ]}");
+    EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1 ] }")), CCSessionError);
+    EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1, 1 ] }")), CCSessionError);
+    
+    answer = el("{ \"result\": [ 0 ] }");
     arg = parseAnswer(rcode, answer);
     EXPECT_EQ(0, rcode);
     EXPECT_TRUE(isNull(arg));
 
-    answer = el("{\"result\": [ 1, \"error\"]}");
+    answer = el("{ \"result\": [ 1, \"error\"] }");
     arg = parseAnswer(rcode, answer);
     EXPECT_EQ(1, rcode);
     EXPECT_EQ("error", arg->stringValue());
 
-    answer = el("{\"result\": [ 0, [ \"just\", \"some\", \"data\" ] ] }");
+    answer = el("{ \"result\": [ 0, [ \"just\", \"some\", \"data\" ] ] }");
     arg = parseAnswer(rcode, answer);
     EXPECT_EQ(0, rcode);
     EXPECT_EQ("[ \"just\", \"some\", \"data\" ]", arg->str());
@@ -116,19 +116,19 @@
     ElementPtr arg;
 
     command = createCommand("my_command");
-    ASSERT_EQ("{\"command\": [ \"my_command\" ]}", command->str());
+    ASSERT_EQ("{ \"command\": [ \"my_command\" ] }", command->str());
 
     arg = el("1");
     command = createCommand("my_command", arg);
-    ASSERT_EQ("{\"command\": [ \"my_command\", 1 ]}", command->str());
+    ASSERT_EQ("{ \"command\": [ \"my_command\", 1 ] }", command->str());
 
     arg = el("[ \"a\", \"b\" ]");
     command = createCommand("my_cmd", arg);
-    ASSERT_EQ("{\"command\": [ \"my_cmd\", [ \"a\", \"b\" ] ]}", command->str());
+    ASSERT_EQ("{ \"command\": [ \"my_cmd\", [ \"a\", \"b\" ] ] }", command->str());
 
     arg = el("{ \"a\": \"map\" }");
     command = createCommand("foo", arg);
-    ASSERT_EQ("{\"command\": [ \"foo\", {\"a\": \"map\"} ]}", command->str());
+    ASSERT_EQ("{ \"command\": [ \"foo\", { \"a\": \"map\" } ] }", command->str());
 }
 
 TEST(CCSession, parseCommand)
@@ -139,21 +139,21 @@
     // should throw
     EXPECT_THROW(parseCommand(arg, ElementPtr()), CCSessionError);
     EXPECT_THROW(parseCommand(arg, el("1")), CCSessionError);
-    EXPECT_THROW(parseCommand(arg, el("{}")), CCSessionError);
-    EXPECT_THROW(parseCommand(arg, el("{\"not a command\": 1 }")), CCSessionError);
-    EXPECT_THROW(parseCommand(arg, el("{\"command\": 1 }")), CCSessionError);
-    EXPECT_THROW(parseCommand(arg, el("{\"command\": [] }")), CCSessionError);
-    EXPECT_THROW(parseCommand(arg, el("{\"command\": [ 1 ] }")), CCSessionError);
-
-    cmd = parseCommand(arg, el("{\"command\": [ \"my_command\" ] }"));
+    EXPECT_THROW(parseCommand(arg, el("{  }")), CCSessionError);
+    EXPECT_THROW(parseCommand(arg, el("{ \"not a command\": 1 }")), CCSessionError);
+    EXPECT_THROW(parseCommand(arg, el("{ \"command\": 1 }")), CCSessionError);
+    EXPECT_THROW(parseCommand(arg, el("{ \"command\": [] }")), CCSessionError);
+    EXPECT_THROW(parseCommand(arg, el("{ \"command\": [ 1 ] }")), CCSessionError);
+
+    cmd = parseCommand(arg, el("{ \"command\": [ \"my_command\" ] }"));
     EXPECT_EQ("my_command", cmd);
     EXPECT_TRUE(isNull(arg));
 
-    cmd = parseCommand(arg, el("{\"command\": [ \"my_command\", 1 ] }"));
+    cmd = parseCommand(arg, el("{ \"command\": [ \"my_command\", 1 ] }"));
     EXPECT_EQ("my_command", cmd);
     EXPECT_EQ("1", arg->str());
 
-    parseCommand(arg, el("{\"command\": [ \"my_command\", [ \"some\", \"argument\", \"list\" ] ] }"));
+    parseCommand(arg, el("{ \"command\": [ \"my_command\", [ \"some\", \"argument\", \"list\" ] ] }"));
     EXPECT_EQ("my_command", cmd);
     EXPECT_EQ("[ \"some\", \"argument\", \"list\" ]", arg->str());
 
@@ -170,7 +170,7 @@
     ElementPtr msg;
     std::string group, to;
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"command\": [ \"module_spec\", {\"module_name\": \"Spec1\"} ]}", msg->str());
+    EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"module_name\": \"Spec1\" } ] }", msg->str());
     EXPECT_EQ("ConfigManager", group);
     EXPECT_EQ("*", to);
     EXPECT_EQ(0, msg_queue->size());
@@ -188,7 +188,7 @@
     ElementPtr msg;
     std::string group, to;
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"command\": [ \"module_spec\", {\"commands\": [ {\"command_args\": [ {\"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": False, \"item_type\": \"string\"} ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\"}, {\"command_args\": [  ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\"} ], \"config_data\": [ {\"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": False, \"item_type\": \"integer\"}, {\"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": False, \"item_type\": \"real\"}, {\"item_default\": True, \"item_name\": \"item3\", \"item_optional\": False, \"item_type\": \"boolean\"}, {\"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": False, \"item_type\": \"string\"}, {\"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": False, \"item_type\": \"list\", \"list_item_spec\": {\"item_def
 ault\": \"\", \"item_name\": \"list_element\", \"item_optional\": False, \"item_type\": \"string\"}}, {\"item_default\": {}, \"item_name\": \"item6\", \"item_optional\": False, \"item_type\": \"map\", \"map_item_spec\": [ {\"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": True, \"item_type\": \"string\"}, {\"item_name\": \"value2\", \"item_optional\": True, \"item_type\": \"integer\"} ]} ], \"module_name\": \"Spec2\"} ]}", msg->str());
+    EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"commands\": [ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [  ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ], \"config_data\": [ { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }, { \"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": false, \"item_type\": \"real\" }, { \"item_default\": true, \"item_name\": \"item3\", \"item_optional\": false, \"item_type\": \"boolean\" }, { \"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": false, \"item_type\": \"string\" }, { \"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": false, \"item_type\": \"list\", \"list_item_sp
 ec\": { \"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": false, \"item_type\": \"string\" } }, { \"item_default\": {  }, \"item_name\": \"item6\", \"item_optional\": false, \"item_type\": \"map\", \"map_item_spec\": [ { \"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": true, \"item_type\": \"string\" }, { \"item_name\": \"value2\", \"item_optional\": true, \"item_type\": \"integer\" } ] } ], \"module_name\": \"Spec2\" } ] }", msg->str());
     EXPECT_EQ("ConfigManager", group);
     EXPECT_EQ("*", to);
     EXPECT_EQ(0, msg_queue->size());
@@ -227,7 +227,7 @@
 {
     initFakeSession();
     // client will ask for config
-    initial_messages->add(createAnswer(0, el("{}")));
+    initial_messages->add(createAnswer(0, el("{  }")));
 
     EXPECT_EQ(false, haveSubscription("Spec2", "*"));
     ModuleCCSession mccs(ccspecfile("spec2.spec"), my_config_handler, my_command_handler);
@@ -237,12 +237,12 @@
     ElementPtr msg;
     std::string group, to;
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"command\": [ \"module_spec\", {\"commands\": [ {\"command_args\": [ {\"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": False, \"item_type\": \"string\"} ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\"}, {\"command_args\": [  ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\"} ], \"config_data\": [ {\"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": False, \"item_type\": \"integer\"}, {\"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": False, \"item_type\": \"real\"}, {\"item_default\": True, \"item_name\": \"item3\", \"item_optional\": False, \"item_type\": \"boolean\"}, {\"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": False, \"item_type\": \"string\"}, {\"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": False, \"item_type\": \"list\", \"list_item_spec\": {\"item_def
 ault\": \"\", \"item_name\": \"list_element\", \"item_optional\": False, \"item_type\": \"string\"}}, {\"item_default\": {}, \"item_name\": \"item6\", \"item_optional\": False, \"item_type\": \"map\", \"map_item_spec\": [ {\"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": True, \"item_type\": \"string\"}, {\"item_name\": \"value2\", \"item_optional\": True, \"item_type\": \"integer\"} ]} ], \"module_name\": \"Spec2\"} ]}", msg->str());
+    EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"commands\": [ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [  ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ], \"config_data\": [ { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }, { \"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": false, \"item_type\": \"real\" }, { \"item_default\": true, \"item_name\": \"item3\", \"item_optional\": false, \"item_type\": \"boolean\" }, { \"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": false, \"item_type\": \"string\" }, { \"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": false, \"item_type\": \"list\", \"list_item_sp
 ec\": { \"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": false, \"item_type\": \"string\" } }, { \"item_default\": {  }, \"item_name\": \"item6\", \"item_optional\": false, \"item_type\": \"map\", \"map_item_spec\": [ { \"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": true, \"item_type\": \"string\" }, { \"item_name\": \"value2\", \"item_optional\": true, \"item_type\": \"integer\" } ] } ], \"module_name\": \"Spec2\" } ] }", msg->str());
     EXPECT_EQ("ConfigManager", group);
     EXPECT_EQ("*", to);
     EXPECT_EQ(1, msg_queue->size());
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"command\": [ \"get_config\", {\"module_name\": \"Spec2\"} ]}", msg->str());
+    EXPECT_EQ("{ \"command\": [ \"get_config\", { \"module_name\": \"Spec2\" } ] }", msg->str());
     EXPECT_EQ("ConfigManager", group);
     EXPECT_EQ("*", to);
     EXPECT_EQ(0, msg_queue->size());
@@ -253,7 +253,7 @@
 {
     initFakeSession();
     // client will ask for config
-    initial_messages->add(createAnswer(0, el("{}")));
+    initial_messages->add(createAnswer(0, el("{  }")));
 
     EXPECT_EQ(false, haveSubscription("Spec2", "*"));
     ModuleCCSession mccs(ccspecfile("spec2.spec"), my_config_handler, my_command_handler);
@@ -279,42 +279,42 @@
     result = mccs.checkCommand();
     EXPECT_EQ(1, msg_queue->size());
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"result\": [ 0 ]}", msg->str());
+    EXPECT_EQ("{ \"result\": [ 0 ] }", msg->str());
     EXPECT_EQ(0, result);
 
     addMessage(el("{ \"command\": \"bad_command\" }"), "Spec2", "*");
     result = mccs.checkCommand();
     EXPECT_EQ(1, msg_queue->size());
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"result\": [ 1, \"Command part in command message missing, empty, or not a list\" ]}", msg->str());
+    EXPECT_EQ("{ \"result\": [ 1, \"Command part in command message missing, empty, or not a list\" ] }", msg->str());
     EXPECT_EQ(0, result);
 
     addMessage(el("{ \"command\": [ \"bad_command\" ] }"), "Spec2", "*");
     result = mccs.checkCommand();
     EXPECT_EQ(1, msg_queue->size());
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"result\": [ 1, \"bad command\" ]}", msg->str());
+    EXPECT_EQ("{ \"result\": [ 1, \"bad command\" ] }", msg->str());
     EXPECT_EQ(0, result);
 
     addMessage(el("{ \"command\": [ \"command_with_arg\", 1 ] }"), "Spec2", "*");
     result = mccs.checkCommand();
     EXPECT_EQ(1, msg_queue->size());
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"result\": [ 0, 2 ]}", msg->str());
+    EXPECT_EQ("{ \"result\": [ 0, 2 ] }", msg->str());
     EXPECT_EQ(0, result);
 
     addMessage(el("{ \"command\": [ \"command_with_arg\" ] }"), "Spec2", "*");
     result = mccs.checkCommand();
     EXPECT_EQ(1, msg_queue->size());
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"result\": [ 1, \"arg missing\" ]}", msg->str());
+    EXPECT_EQ("{ \"result\": [ 1, \"arg missing\" ] }", msg->str());
     EXPECT_EQ(0, result);
 
     addMessage(el("{ \"command\": [ \"command_with_arg\", \"asdf\" ] }"), "Spec2", "*");
     result = mccs.checkCommand();
     EXPECT_EQ(1, msg_queue->size());
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"result\": [ 1, \"arg bad type\" ]}", msg->str());
+    EXPECT_EQ("{ \"result\": [ 1, \"arg bad type\" ] }", msg->str());
     EXPECT_EQ(0, result);
 
     mccs.setCommandHandler(NULL);
@@ -322,7 +322,7 @@
     result = mccs.checkCommand();
     EXPECT_EQ(1, msg_queue->size());
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"result\": [ 1, \"Command given but no command handler for module\" ]}", msg->str());
+    EXPECT_EQ("{ \"result\": [ 1, \"Command given but no command handler for module\" ] }", msg->str());
     EXPECT_EQ(0, result);
 
     EXPECT_EQ(1, mccs.getValue("item1")->intValue());
@@ -330,7 +330,7 @@
     result = mccs.checkCommand();
     EXPECT_EQ(1, msg_queue->size());
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"result\": [ 0 ]}", msg->str());
+    EXPECT_EQ("{ \"result\": [ 0 ] }", msg->str());
     EXPECT_EQ(0, result);
     EXPECT_EQ(2, mccs.getValue("item1")->intValue());
 
@@ -338,7 +338,7 @@
     result = mccs.checkCommand();
     EXPECT_EQ(1, msg_queue->size());
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"result\": [ 2, \"Error in config validation: Type mismatch\" ]}", msg->str());
+    EXPECT_EQ("{ \"result\": [ 2, \"Error in config validation: Type mismatch\" ] }", msg->str());
     EXPECT_EQ(0, result);
     EXPECT_EQ(2, mccs.getValue("item1")->intValue());
 
@@ -346,7 +346,7 @@
     result = mccs.checkCommand();
     EXPECT_EQ(1, msg_queue->size());
     msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"result\": [ 6, \"I do not like the number 5\" ]}", msg->str());
+    EXPECT_EQ("{ \"result\": [ 6, \"I do not like the number 5\" ] }", msg->str());
     EXPECT_EQ(0, result);
     EXPECT_EQ(2, mccs.getValue("item1")->intValue());
 
@@ -364,7 +364,7 @@
     
     // first simply connect, with no config values, and see we get
     // the default
-    initial_messages->add(createAnswer(0, el("{}")));
+    initial_messages->add(createAnswer(0, el("{  }")));
 
     EXPECT_EQ(false, haveSubscription("Spec2", "*"));
     module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"));
@@ -393,7 +393,7 @@
 
     // remove, re-add, now with a *bad* config request answer
     mccs.removeRemoteConfig(module_name);
-    initial_messages->add(el("{}"));
+    initial_messages->add(el("{  }"));
     EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
     
     initial_messages->add(createAnswer(1, "my_error"));

Modified: branches/trac191/src/lib/config/tests/config_data_unittests.cc
==============================================================================
--- branches/trac191/src/lib/config/tests/config_data_unittests.cc (original)
+++ branches/trac191/src/lib/config/tests/config_data_unittests.cc Fri Jul  9 10:04:58 2010
@@ -62,9 +62,9 @@
     EXPECT_EQ("b", cd.getValue(is_default, "value5")->get(1)->stringValue());
     EXPECT_EQ("b", cd.getValue(is_default, "value5/")->get(1)->stringValue());
     EXPECT_TRUE(is_default);
-    EXPECT_EQ("{}", cd.getValue("value6")->str());
-    EXPECT_EQ("{}", cd.getValue(is_default, "value6")->str());
-    EXPECT_EQ("{}", cd.getValue(is_default, "value6/")->str());
+    EXPECT_EQ("{  }", cd.getValue("value6")->str());
+    EXPECT_EQ("{  }", cd.getValue(is_default, "value6")->str());
+    EXPECT_EQ("{  }", cd.getValue(is_default, "value6/")->str());
     EXPECT_TRUE(is_default);
     EXPECT_EQ("[  ]", cd.getValue("value8")->str());
 
@@ -87,34 +87,34 @@
     ConfigData cd = ConfigData(spec2);
     bool is_default;
 
-    ElementPtr my_config = Element::createFromString("{\"item1\": 2}");
-    ElementPtr my_config2 = Element::createFromString("{\"item6\": { \"value1\": \"a\" } }");
+    ElementPtr my_config = Element::fromJSON("{ \"item1\": 2 }");
+    ElementPtr my_config2 = Element::fromJSON("{ \"item6\": { \"value1\": \"a\" } }");
 
-    EXPECT_EQ("{}", cd.getValue("item6")->str());
+    EXPECT_EQ("{  }", cd.getValue("item6")->str());
 
     cd.setLocalConfig(my_config);
     EXPECT_EQ(2, cd.getValue(is_default, "item1")->intValue());
     EXPECT_FALSE(is_default);
-    EXPECT_EQ("{}", cd.getValue("item6")->str());
+    EXPECT_EQ("{  }", cd.getValue("item6")->str());
     EXPECT_EQ(1.1, cd.getValue(is_default, "item2")->doubleValue());
     EXPECT_TRUE(is_default);
 
     cd.setLocalConfig(my_config2);
-    EXPECT_EQ("{\"value1\": \"a\"}", cd.getValue("item6")->str());
+    EXPECT_EQ("{ \"value1\": \"a\" }", cd.getValue("item6")->str());
 }
 
 TEST(ConfigData, getLocalConfig) {
     ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
     ConfigData cd = ConfigData(spec2);
-    EXPECT_EQ("{}", cd.getLocalConfig()->str());
+    EXPECT_EQ("{  }", cd.getLocalConfig()->str());
     
-    ElementPtr my_config = Element::createFromString("{\"item1\": 2}");
+    ElementPtr my_config = Element::fromJSON("{ \"item1\": 2 }");
     cd.setLocalConfig(my_config);
-    EXPECT_EQ("{\"item1\": 2}", cd.getLocalConfig()->str());
+    EXPECT_EQ("{ \"item1\": 2 }", cd.getLocalConfig()->str());
 
-    ElementPtr my_config2 = Element::createFromString("{\"item6\": { \"value1\": \"a\" } }");
+    ElementPtr my_config2 = Element::fromJSON("{ \"item6\": { \"value1\": \"a\" } }");
     cd.setLocalConfig(my_config2);
-    EXPECT_EQ("{\"item6\": {\"value1\": \"a\"}}", cd.getLocalConfig()->str());
+    EXPECT_EQ("{ \"item6\": { \"value1\": \"a\" } }", cd.getLocalConfig()->str());
 }
 
 TEST(ConfigData, getItemList) {
@@ -130,12 +130,12 @@
     ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
     ConfigData cd = ConfigData(spec2);
 
-    EXPECT_EQ("{\"item1\": 1, \"item2\": 1.1, \"item3\": True, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None}", cd.getFullConfig()->str());
-    ElementPtr my_config = Element::createFromString("{\"item1\": 2}");
+    EXPECT_EQ("{ \"item1\": 1, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None }", cd.getFullConfig()->str());
+    ElementPtr my_config = Element::fromJSON("{ \"item1\": 2 }");
     cd.setLocalConfig(my_config);
-    EXPECT_EQ("{\"item1\": 2, \"item2\": 1.1, \"item3\": True, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None}", cd.getFullConfig()->str());
-    ElementPtr my_config2 = Element::createFromString("{\"item6\": { \"value1\": \"a\" } }");
+    EXPECT_EQ("{ \"item1\": 2, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"default\", \"item6/value2\": None }", cd.getFullConfig()->str());
+    ElementPtr my_config2 = Element::fromJSON("{ \"item6\": { \"value1\": \"a\" } }");
     cd.setLocalConfig(my_config2);
-    EXPECT_EQ("{\"item1\": 1, \"item2\": 1.1, \"item3\": True, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"a\", \"item6/value2\": None}", cd.getFullConfig()->str());
+    EXPECT_EQ("{ \"item1\": 1, \"item2\": 1.1, \"item3\": true, \"item4\": \"test\", \"item5/\": [ \"a\", \"b\" ], \"item6/value1\": \"a\", \"item6/value2\": None }", cd.getFullConfig()->str());
 }
 

Modified: branches/trac191/src/lib/config/tests/fake_session.cc
==============================================================================
--- branches/trac191/src/lib/config/tests/fake_session.cc (original)
+++ branches/trac191/src/lib/config/tests/fake_session.cc Fri Jul  9 10:04:58 2010
@@ -90,14 +90,13 @@
 }
 
 void
-addMessage(ElementPtr msg, const std::string& group, const std::string& to)
-{
-    ElementPtr m_el = Element::createFromString("[]");
+addMessage(ElementPtr msg, const std::string& group, const std::string& to) {
+    ElementPtr m_el = Element::createList();
     m_el->add(Element::create(group));
     m_el->add(Element::create(to));
     m_el->add(msg);
     if (!msg_queue) {
-        msg_queue = Element::createFromString("[]");
+        msg_queue = Element::createList();
     }
     msg_queue->add(m_el);
 }
@@ -108,8 +107,8 @@
     if (!subscriptions) {
         return false;
     }
-    ElementPtr s1 = Element::createFromString("[]");
-    ElementPtr s2 = Element::createFromString("[]");
+    ElementPtr s1 = Element::createList();
+    ElementPtr s2 = Element::createList();
     s1->add(Element::create(group));
     s1->add(Element::create(instance));
     s2->add(Element::create(group));
@@ -206,7 +205,7 @@
         BOOST_FOREACH(ElementPtr c_m, msg_queue->listValue()) {
             ElementPtr to_remove = ElementPtr();
             if (haveSubscription(c_m->get(0), c_m->get(1))) {
-                env = Element::createFromString("{}");
+                env = Element::createMap();
                 env->set("group", c_m->get(0));
                 env->set("to", c_m->get(1));
                 msg = c_m->get(2);
@@ -226,11 +225,11 @@
 void
 Session::subscribe(std::string group, std::string instance) {
     //cout << "[XX] client subscribes to " << group << " . " << instance << endl;
-    ElementPtr s_el = Element::createFromString("[]");
+    ElementPtr s_el = Element::createList();
     s_el->add(Element::create(group));
     s_el->add(Element::create(instance));
     if (!subscriptions) {
-        subscriptions = Element::createFromString("[]");
+        subscriptions = Element::createList();
     }
     subscriptions->add(s_el);
 }
@@ -238,7 +237,7 @@
 void
 Session::unsubscribe(std::string group, std::string instance) {
     //cout << "[XX] client unsubscribes from " << group << " . " << instance << endl;
-    ElementPtr s_el = Element::createFromString("[]");
+    ElementPtr s_el = Element::createList();
     s_el->add(Element::create(group));
     s_el->add(Element::create(instance));
     if (!subscriptions) {
@@ -247,7 +246,7 @@
     listRemove(subscriptions, s_el);
 }
 
-unsigned int
+int
 Session::group_sendmsg(ElementPtr msg, std::string group,
                        std::string to, std::string instance UNUSED_PARAM)
 {
@@ -264,7 +263,7 @@
     return (recvmsg(envelope, msg, nonblock, seq));
 }
 
-unsigned int
+int
 Session::reply(ElementPtr& envelope, ElementPtr& newmsg) {
     //cout << "[XX] client sends reply: " << newmsg << endl;
     //cout << "[XX] env: " << envelope << endl;

Modified: branches/trac191/src/lib/config/tests/fake_session.h
==============================================================================
--- branches/trac191/src/lib/config/tests/fake_session.h (original)
+++ branches/trac191/src/lib/config/tests/fake_session.h Fri Jul  9 10:04:58 2010
@@ -86,16 +86,16 @@
                            std::string instance = "*");
             void unsubscribe(std::string group,
                              std::string instance = "*");
-            unsigned int group_sendmsg(isc::data::ElementPtr msg,
-                                       std::string group,
-                                       std::string instance = "*",
-                                       std::string to = "*");
+            int group_sendmsg(isc::data::ElementPtr msg,
+                              std::string group,
+                              std::string instance = "*",
+                              std::string to = "*");
             bool group_recvmsg(isc::data::ElementPtr& envelope,
                                isc::data::ElementPtr& msg,
                                bool nonblock = true,
                                int seq = -1);
-            unsigned int reply(isc::data::ElementPtr& envelope,
-                               isc::data::ElementPtr& newmsg);
+            int reply(isc::data::ElementPtr& envelope,
+                      isc::data::ElementPtr& newmsg);
             bool hasQueuedMsgs();
 
         };

Modified: branches/trac191/src/lib/config/tests/module_spec_unittests.cc
==============================================================================
--- branches/trac191/src/lib/config/tests/module_spec_unittests.cc (original)
+++ branches/trac191/src/lib/config/tests/module_spec_unittests.cc Fri Jul  9 10:04:58 2010
@@ -58,7 +58,7 @@
                    ": No such file or directory");
 
     dd = moduleSpecFromFile(specfile("spec2.spec"));
-    EXPECT_EQ("[ {\"command_args\": [ {\"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": False, \"item_type\": \"string\"} ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\"}, {\"command_args\": [  ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\"} ]", dd.getCommandsSpec()->str());
+    EXPECT_EQ("[ { \"command_args\": [ { \"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": false, \"item_type\": \"string\" } ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }, { \"command_args\": [  ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\" } ]", dd.getCommandsSpec()->str());
     EXPECT_EQ("Spec2", dd.getModuleName());
     EXPECT_EQ("", dd.getModuleDescription());
 
@@ -82,13 +82,13 @@
 
 TEST(ModuleSpec, SpecfileItems) {
     module_spec_error("spec3.spec",
-                   "item_name missing in {\"item_default\": 1, \"item_optional\": False, \"item_type\": \"integer\"}");
+                   "item_name missing in { \"item_default\": 1, \"item_optional\": false, \"item_type\": \"integer\" }");
     module_spec_error("spec4.spec",
-                   "item_type missing in {\"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": False}");
+                   "item_type missing in { \"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": false }");
     module_spec_error("spec5.spec",
-                   "item_optional missing in {\"item_default\": 1, \"item_name\": \"item1\", \"item_type\": \"integer\"}");
+                   "item_optional missing in { \"item_default\": 1, \"item_name\": \"item1\", \"item_type\": \"integer\" }");
     module_spec_error("spec6.spec",
-                   "item_default missing in {\"item_name\": \"item1\", \"item_optional\": False, \"item_type\": \"integer\"}");
+                   "item_default missing in { \"item_name\": \"item1\", \"item_optional\": false, \"item_type\": \"integer\" }");
     module_spec_error("spec9.spec",
                    "item_default not of type integer");
     module_spec_error("spec10.spec",
@@ -108,7 +108,7 @@
 TEST(ModuleSpec, SpecfileConfigData)
 {
     module_spec_error("spec7.spec",
-                   "module_name missing in {}");
+                   "module_name missing in {  }");
     module_spec_error("spec8.spec",
                    "No module_spec in specification");
     module_spec_error("spec16.spec",
@@ -120,9 +120,9 @@
 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\"}");
+                   "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",
-                   "command_args missing in {\"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\"}");
+                   "command_args missing in { \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\" }");
     module_spec_error("spec19.spec",
                    "command_args not of type list");
     module_spec_error("spec20.spec",
@@ -139,7 +139,7 @@
     std::ifstream data_file;
 
     data_file.open(specfile(data_file_name).c_str());
-    ElementPtr data = Element::createFromString(data_file, data_file_name);
+    ElementPtr data = Element::fromJSON(data_file, data_file_name);
     data_file.close();
 
     return dd.validate_config(data);
@@ -151,7 +151,7 @@
     std::ifstream data_file;
 
     data_file.open(specfile(data_file_name).c_str());
-    ElementPtr data = Element::createFromString(data_file, data_file_name);
+    ElementPtr data = Element::fromJSON(data_file, data_file_name);
     data_file.close();
 
     return dd.validate_config(data, true, errors);
@@ -169,7 +169,7 @@
     EXPECT_TRUE(data_test(dd, "data22_7.data"));
     EXPECT_FALSE(data_test(dd, "data22_8.data"));
 
-    ElementPtr errors = Element::createFromString("[]");
+    ElementPtr errors = Element::createList();
     EXPECT_FALSE(data_test_with_errors(dd, "data22_8.data", errors));
     EXPECT_EQ("[ \"Type mismatch\" ]", errors->str());
 }

Modified: branches/trac191/src/lib/datasrc/Makefile.am
==============================================================================
--- branches/trac191/src/lib/datasrc/Makefile.am (original)
+++ branches/trac191/src/lib/datasrc/Makefile.am Fri Jul  9 10:04:58 2010
@@ -13,3 +13,4 @@
 libdatasrc_la_SOURCES += static_datasrc.h static_datasrc.cc
 libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc
 libdatasrc_la_SOURCES += query.h query.cc
+libdatasrc_la_SOURCES += cache.h cache.cc

Modified: branches/trac191/src/lib/datasrc/data_source.cc
==============================================================================
--- branches/trac191/src/lib/datasrc/data_source.cc (original)
+++ branches/trac191/src/lib/datasrc/data_source.cc Fri Jul  9 10:04:58 2010
@@ -24,6 +24,10 @@
 #include <boost/shared_ptr.hpp>
 #include <boost/foreach.hpp>
 
+#include <datasrc/cache.h>
+#include <datasrc/data_source.h>
+#include <datasrc/query.h>
+
 #include <dns/base32.h>
 #include <dns/buffer.h>
 #include <dns/message.h>
@@ -35,9 +39,6 @@
 
 #include <cc/data.h>
 
-#include "data_source.h"
-#include "query.h"
-
 #define RETERR(x) do { \
                       DataSrc::Result r = (x); \
                       if (r != DataSrc::SUCCESS) \
@@ -53,7 +54,37 @@
 
 typedef boost::shared_ptr<const Nsec3Param> ConstNsec3ParamPtr;
 
-namespace {
+class ZoneInfo {
+public:
+    ZoneInfo(DataSrc* ts,
+             const isc::dns::Name& n,
+             const isc::dns::RRClass& c, 
+             const isc::dns::RRType& t = isc::dns::RRType::ANY()) :
+        top_source_(ts),
+        dsm_(((t == RRType::DS() && n.getLabelCount() != 1)
+              ? n.split(1, n.getLabelCount() - 1) : n),
+             c)
+    {}
+
+    const Name* getEnclosingZone() {
+        if (dsm_.getEnclosingZone() == NULL) {
+            top_source_->findClosestEnclosure(dsm_);
+        }
+        return (dsm_.getEnclosingZone());
+    }
+
+    const DataSrc* getDataSource() {
+        if (dsm_.getDataSource() == NULL) {
+            top_source_->findClosestEnclosure(dsm_);
+        }
+        return (dsm_.getDataSource());
+    }
+
+private:
+    const DataSrc* top_source_;
+    DataSrcMatch dsm_;
+};
+
 // Add a task to the query task queue to look up additional data
 // (i.e., address records for the names included in NS or MX records)
 void
@@ -68,14 +99,14 @@
         if (rrset->getType() == RRType::NS()) {
             const generic::NS& ns = dynamic_cast<const generic::NS&>(rd);
             q.tasks().push(QueryTaskPtr(
-                               new QueryTask(ns.getNSName(), q.qclass(),
+                               new QueryTask(q, ns.getNSName(),
                                              Section::ADDITIONAL(),
                                              QueryTask::GLUE_QUERY,
                                              QueryTask::GETADDITIONAL))); 
         } else if (rrset->getType() == RRType::MX()) {
             const generic::MX& mx = dynamic_cast<const generic::MX&>(rd);
             q.tasks().push(QueryTaskPtr(
-                               new QueryTask(mx.getMXName(), q.qclass(),
+                               new QueryTask(q, mx.getMXName(),
                                              Section::ADDITIONAL(),
                                              QueryTask::NOGLUE_QUERY,
                                              QueryTask::GETADDITIONAL))); 
@@ -125,45 +156,330 @@
         return;
     }
 
+    // Stop chasing CNAMES after 16 lookups, to prevent loops
     if (q.tooMany()) {
         return;
     }
 
     q.tasks().push(QueryTaskPtr(
-                       new QueryTask(dynamic_cast<const generic::CNAME&>
+                       new QueryTask(q, dynamic_cast<const generic::CNAME&>
                                      (it->getCurrent()).getCname(),
-                                     task->qclass,
-                                     task->qtype,
-                                     Section::ANSWER(),
+                                     task->qtype, Section::ANSWER(),
                                      QueryTask::FOLLOWCNAME)));
 }
 
-// Perform the query specified in a QueryTask object
+// Check the cache for data which can answer the current query task.
+bool
+checkCache(QueryTask& task, RRsetList& target) {
+    HotCache& cache = task.q.getCache();
+    RRsetList rrsets;
+    RRsetPtr rrset;
+    int count = 0;
+    uint32_t flags = 0, cflags = 0;
+    bool hit = false, found = false;
+
+    switch (task.op) {
+    case QueryTask::SIMPLE_QUERY:       // Find exact RRset
+        // ANY queries must be handled by the low-level data source,
+        // or the results won't be guaranteed to be complete
+        if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) {
+            break;
+        }
+
+        hit = cache.retrieve(task.qname, task.qclass, task.qtype, rrset, flags);
+        if (hit) {
+            if (rrset) {
+                rrsets.addRRset(rrset);
+                target.append(rrsets);
+            }
+            task.flags = flags;
+            return (true);
+        }
+        break;
+
+    case QueryTask::AUTH_QUERY:         // Find exact RRset or CNAME
+        if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) {
+            break;
+        }
+
+        hit = cache.retrieve(task.qname, task.qclass, task.qtype, rrset, flags);
+        if (!hit || !rrset || (flags & DataSrc::CNAME_FOUND) != 0) {
+            hit = cache.retrieve(task.qname, task.qclass, RRType::CNAME(),
+                                 rrset, flags);
+        }
+
+        if (hit) {
+            if (rrset) {
+                rrsets.addRRset(rrset);
+                target.append(rrsets);
+            }
+            task.flags = flags;
+            return (true);
+        }
+        break;
+
+    case QueryTask::GLUE_QUERY:         // Find addresses
+    case QueryTask::NOGLUE_QUERY:
+        // (XXX: need to figure out how to deal with noglue case)
+        flags = 0;
+
+        hit = cache.retrieve(task.qname, task.qclass, RRType::A(),
+                             rrset, cflags);
+        if (hit) {
+            flags |= cflags;
+            ++count;
+            if (rrset) {
+                rrsets.addRRset(rrset);
+                found = true;
+            }
+        }
+
+        hit = cache.retrieve(task.qname, task.qclass, RRType::AAAA(),
+                             rrset, flags);
+        if (hit) {
+            flags |= cflags;
+            ++count;
+            if (rrset) {
+                rrsets.addRRset(rrset);
+                found = true;
+            }
+        }
+
+        if (count == 2) {
+            if (found) {
+                flags &= ~DataSrc::TYPE_NOT_FOUND;
+                target.append(rrsets);
+            }
+            task.flags = flags;
+            return (true);
+        } 
+        break;
+
+
+    case QueryTask::REF_QUERY:          // Find NS, DS and/or DNAME
+        flags = count = 0;
+
+        hit = cache.retrieve(task.qname, task.qclass, RRType::NS(),
+                             rrset, cflags);
+        if (hit) {
+            flags |= cflags;
+            ++count;
+            if (rrset) {
+                rrsets.addRRset(rrset);
+                found = true;
+            }
+        }
+
+        hit = cache.retrieve(task.qname, task.qclass, RRType::DS(),
+                             rrset, flags);
+        if (hit) {
+            flags |= cflags;
+            ++count;
+            if (rrset) {
+                rrsets.addRRset(rrset);
+                found = true;
+            }
+        }
+
+        hit = cache.retrieve(task.qname, task.qclass, RRType::DNAME(),
+                             rrset, flags);
+        if (hit) {
+            flags |= cflags;
+            ++count;
+            if (rrset) {
+                rrsets.addRRset(rrset);
+                found = true;
+            }
+        }
+
+        if (count == 3) {
+            if (found) {
+                flags &= ~DataSrc::TYPE_NOT_FOUND;
+                flags &= DataSrc::REFERRAL;
+                target.append(rrsets);
+            }
+            task.flags = flags;
+            return (true);
+        } 
+        break;
+    }
+
+    return (false);
+}
+
+// Carry out the query specified in a QueryTask object
 DataSrc::Result
-doQueryTask(const DataSrc* ds, const Name* zonename, QueryTask& task,
-            RRsetList& target)
-{
+doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) {
+    HotCache& cache = task.q.getCache();
+    RRsetPtr rrset;
+
+    // First, check the cache for matching data
+    if (checkCache(task, target)) {
+        return (DataSrc::SUCCESS);
+    }
+
+    // Requested data weren't in the cache (or were, but had expired),
+    // so now we proceed with the low-level data source lookup, and cache
+    // whatever we find.
+    const DataSrc* ds = zoneinfo.getDataSource();
+    const Name* const zonename = zoneinfo.getEnclosingZone();
+
+    if (ds == NULL) {
+        task.flags |= DataSrc::NO_SUCH_ZONE;
+        return (DataSrc::SUCCESS);
+    }
+
+    DataSrc::Result result;
     switch (task.op) {
+    case QueryTask::SIMPLE_QUERY:
+        result = ds->findExactRRset(task.qname, task.qclass, task.qtype,
+                                    target, task.flags, zonename);
+
+        if (result != DataSrc::SUCCESS) {
+            return (result);
+        }
+
+        if (task.qclass == RRClass::ANY()) {
+            // XXX: Currently, RRsetList::findRRset() doesn't handle
+            // ANY queries, and without that we can't cache the results,
+            // so we just return in that case.
+            return (result);
+        }
+
+        if (task.flags == 0) {
+            rrset = target.findRRset(task.qtype, task.qclass);
+            assert(rrset);
+            cache.addPositive(rrset, task.flags);
+        } else {
+            cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
+        }
+
+        return (result);
+
     case QueryTask::AUTH_QUERY:
-        return (ds->findRRset(task.qname, task.qclass, task.qtype,
-                              target, task.flags, zonename));
-
-    case QueryTask::SIMPLE_QUERY:
-        return (ds->findExactRRset(task.qname, task.qclass, task.qtype,
-                                   target, task.flags, zonename));
+        result = ds->findRRset(task.qname, task.qclass, task.qtype,
+                               target, task.flags, zonename);
+
+        if (result != DataSrc::SUCCESS) {
+            return (result);
+        }
+
+        if (task.qclass == RRClass::ANY()) {
+            return (result);
+        }
+
+        if (task.qtype == RRType::ANY()) {
+            BOOST_FOREACH(RRsetPtr rr, target) {
+                cache.addPositive(rr, task.flags);
+            }
+        } else if ((task.flags & DataSrc::CNAME_FOUND) != 0) {
+            cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
+            rrset = target.findRRset(RRType::CNAME(), task.qclass);
+            assert(rrset);
+            cache.addPositive(rrset, task.flags);
+        } else if ((task.flags & DataSrc::DATA_NOT_FOUND) == 0) {
+            if (task.qtype != RRType::CNAME()) {
+                cache.addNegative(task.qname, task.qclass, RRType::CNAME(),
+                                  task.flags);
+            }
+            rrset = target.findRRset(task.qtype, task.qclass);
+            assert(rrset);
+            cache.addPositive(rrset, task.flags);
+        } else {
+            cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
+        }
+
+        return (result);
 
     case QueryTask::GLUE_QUERY:
     case QueryTask::NOGLUE_QUERY:
-        return (ds->findAddrs(task.qname, task.qclass, target,
-                              task.flags, zonename));
+        result = ds->findAddrs(task.qname, task.qclass, target,
+                               task.flags, zonename);
+
+        if (result != DataSrc::SUCCESS) {
+            return (result);
+        }
+
+        if (task.qclass == RRClass::ANY()) {
+            return (result);
+        }
+
+        rrset = target.findRRset(RRType::A(), task.qclass);
+        if (rrset) {
+            cache.addPositive(rrset, task.flags);
+        } else {
+            cache.addNegative(task.qname, task.qclass, RRType::A(), task.flags);
+        }
+
+        rrset = target.findRRset(RRType::AAAA(), task.qclass);
+        if (rrset) {
+            cache.addPositive(rrset, task.flags);
+        } else {
+            cache.addNegative(task.qname, task.qclass, RRType::AAAA(),
+                              task.flags);
+        }
+
+        return (result);
 
     case QueryTask::REF_QUERY:
-        return (ds->findReferral(task.qname, task.qclass, target,
-                                 task.flags, zonename));
+        result = ds->findReferral(task.qname, task.qclass, target,
+                                 task.flags, zonename);
+
+        if (result != DataSrc::SUCCESS) {
+            return (result);
+        }
+
+        if (task.qclass == RRClass::ANY()) {
+            return (result);
+        }
+
+        rrset = target.findRRset(RRType::NS(), task.qclass);
+        if (rrset) {
+            cache.addPositive(rrset, task.flags);
+        } else {
+            cache.addNegative(task.qname, task.qclass, RRType::NS(),
+                              task.flags);
+        }
+        rrset = target.findRRset(RRType::DS(), task.qclass);
+        if (rrset) {
+            cache.addPositive(rrset, task.flags);
+        } else {
+            cache.addNegative(task.qname, task.qclass, RRType::DS(),
+                              task.flags);
+        }
+        rrset = target.findRRset(RRType::DNAME(), task.qclass);
+        if (rrset) {
+            cache.addPositive(rrset, task.flags);
+        } else {
+            cache.addNegative(task.qname, task.qclass, RRType::DNAME(),
+                              task.flags);
+        }
+
+        return (result);
     }
 
     // Not reached
     return (DataSrc::ERROR);
+}
+
+
+// Add an RRset (and its associated RRSIG) to a message section,
+// checking first to ensure that there isn't already an RRset with
+// the same name and type.
+inline void
+addToMessage(Query& q, const Section& sect, RRsetPtr rrset,
+             bool no_dnssec = false)
+{
+    Message& m = q.message();
+    if (no_dnssec) {
+        if (rrset->getType() == RRType::RRSIG() || !m.hasRRset(sect, rrset)) {
+            m.addRRset(sect, rrset, false);
+        }
+    } else {
+        if (!m.hasRRset(sect, rrset)) {
+            m.addRRset(sect, rrset, q.wantDnssec());
+        }
+    }
 }
 
 // Copy referral information into the authority section of a message
@@ -176,23 +492,23 @@
         if (rrset->getType() == RRType::DS() && !q.wantDnssec()) {
             continue;
         }
-        q.message().addRRset(Section::AUTHORITY(), rrset, q.wantDnssec());
+        addToMessage(q, Section::AUTHORITY(), rrset);
         getAdditional(q, rrset);
     }
 }
 
 // Query for referrals (i.e., NS/DS or DNAME) at a given name
 inline bool
-refQuery(const Name& name, const RRClass& qclass, const DataSrc* ds,
-         const Name* zonename, RRsetList& target)
+refQuery(const Query& q, const Name& name, ZoneInfo& zoneinfo,
+         RRsetList& target)
 {
-    QueryTask newtask(name, qclass, QueryTask::REF_QUERY);
-
-    if (doQueryTask(ds, zonename, newtask, target) != DataSrc::SUCCESS) {
+    QueryTask newtask(q, name, QueryTask::REF_QUERY);
+
+    if (doQueryTask(newtask, zoneinfo, target) != DataSrc::SUCCESS) {
         // Lookup failed
         return (false);
     }
-    
+
     // Referral bit is expected, so clear it when checking flags
     if ((newtask.flags & ~DataSrc::REFERRAL) != 0) {
         return (false);
@@ -205,16 +521,22 @@
 // referrals.  Note that we exclude the apex name and query name themselves;
 // they'll be handled in a normal lookup in the zone.
 inline bool
-hasDelegation(const DataSrc* ds, const Name* zonename, Query& q,
-              QueryTaskPtr task)
-{
+hasDelegation(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo) {
+    const Name* const zonename = zoneinfo.getEnclosingZone();
+    if (zonename == NULL) {
+        if (task->state == QueryTask::GETANSWER) {
+            q.message().setRcode(Rcode::REFUSED());
+        }
+        return (false);
+    }
+
     const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
     if (diff > 1) {
         bool found = false;
         RRsetList ref;
         for (int i = diff - 1; i > 0; --i) {
             const Name sub(task->qname.split(i));
-            if (refQuery(sub, q.qclass(), ds, zonename, ref)) {
+            if (refQuery(q, sub, zoneinfo, ref)) {
                 found = true;
                 break;
             }
@@ -232,14 +554,12 @@
             RRsetPtr r = ref.findRRset(RRType::DNAME(), q.qclass());
             if (r != NULL) {
                 RRsetList syn;
-                q.message().addRRset(Section::ANSWER(), r, q.wantDnssec());
+                addToMessage(q, Section::ANSWER(), r);
                 q.message().setHeaderFlag(MessageFlag::AA());
                 synthesizeCname(task, r, syn);
                 if (syn.size() == 1) {
-                    q.message().addRRset(Section::ANSWER(),
-                                         syn.findRRset(RRType::CNAME(),
-                                                       q.qclass()),
-                                         q.wantDnssec());
+                    addToMessage(q, Section::ANSWER(),
+                                 syn.findRRset(RRType::CNAME(), q.qclass()));
                     chaseCname(q, task, syn.findRRset(RRType::CNAME(),
                                                       q.qclass()));
                     return (true);
@@ -263,58 +583,61 @@
 }
 
 inline DataSrc::Result
-addSOA(Query& q, const Name* zonename, const DataSrc* ds) {
-    Message& m = q.message();
+addSOA(Query& q, ZoneInfo& zoneinfo) {
     RRsetList soa;
 
-    QueryTask newtask(*zonename, q.qclass(), RRType::SOA(),
-                      QueryTask::SIMPLE_QUERY);
-    RETERR(doQueryTask(ds, zonename, newtask, soa));
+    const Name* const zonename = zoneinfo.getEnclosingZone();
+    QueryTask newtask(q, *zonename, RRType::SOA(), QueryTask::SIMPLE_QUERY);
+    RETERR(doQueryTask(newtask, zoneinfo, soa));
     if (newtask.flags != 0) {
         return (DataSrc::ERROR);
     }
 
-    m.addRRset(Section::AUTHORITY(), soa.findRRset(RRType::SOA(), q.qclass()),
-               q.wantDnssec());
+    addToMessage(q, Section::AUTHORITY(),
+                 soa.findRRset(RRType::SOA(), q.qclass()));
     return (DataSrc::SUCCESS);
 }
 
 inline DataSrc::Result
-addNSEC(Query& q, const QueryTaskPtr task, const Name& name,
-        const Name& zonename, const DataSrc* ds)
-{
+addNSEC(Query& q, const Name& name, ZoneInfo& zoneinfo) {
     RRsetList nsec;
-    Message& m = q.message();
-
-    QueryTask newtask(name, task->qclass, RRType::NSEC(),
-                      QueryTask::SIMPLE_QUERY); 
-    RETERR(doQueryTask(ds, &zonename, newtask, nsec));
+
+    QueryTask newtask(q, name, RRType::NSEC(), QueryTask::SIMPLE_QUERY); 
+    RETERR(doQueryTask(newtask, zoneinfo, nsec));
     if (newtask.flags == 0) {
-        m.addRRset(Section::AUTHORITY(), nsec.findRRset(RRType::NSEC(),
-                                                        q.qclass()), true);
+        addToMessage(q, Section::AUTHORITY(),
+                     nsec.findRRset(RRType::NSEC(), q.qclass()));
     }
 
     return (DataSrc::SUCCESS);
 }
 
 inline DataSrc::Result
-getNsec3(const DataSrc* ds, const Name& zonename, const RRClass& qclass,
-         string& hash, RRsetPtr& target)
-{
+getNsec3(Query& q, ZoneInfo& zoneinfo, string& hash, RRsetPtr& target) {
+    const DataSrc* ds = zoneinfo.getDataSource();
+    const Name* const zonename = zoneinfo.getEnclosingZone();
+
+    if (ds == NULL) {
+        q.message().setRcode(Rcode::SERVFAIL());
+        return (DataSrc::ERROR);
+    }
+
     RRsetList rl;
-    RETERR(ds->findCoveringNSEC3(zonename, hash, rl));
-    target = rl.findRRset(RRType::NSEC3(), qclass);
+    RETERR(ds->findCoveringNSEC3(*zonename, hash, rl));
+    target = rl.findRRset(RRType::NSEC3(), q.qclass());
+
     return (DataSrc::SUCCESS);
 }
 
 ConstNsec3ParamPtr
-getNsec3Param(Query& q, const DataSrc* ds, const Name& zonename) {
+getNsec3Param(Query& q, ZoneInfo& zoneinfo) {
     DataSrc::Result result;
     RRsetList nsec3param;
 
-    QueryTask newtask(zonename, q.qclass(), RRType::NSEC3PARAM(),
+    const Name* const zonename = zoneinfo.getEnclosingZone();
+    QueryTask newtask(q, *zonename, RRType::NSEC3PARAM(),
                       QueryTask::SIMPLE_QUERY); 
-    result = doQueryTask(ds, &zonename, newtask, nsec3param);
+    result = doQueryTask(newtask, zoneinfo, nsec3param);
     newtask.flags &= ~DataSrc::REFERRAL;
     if (result != DataSrc::SUCCESS || newtask.flags != 0) {
         return (ConstNsec3ParamPtr());
@@ -341,17 +664,17 @@
 }
 
 inline DataSrc::Result
-proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
-        const Name& zonename, const bool wildcard)
-{
+proveNX(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, const bool wildcard) {
     Message& m = q.message();
-    ConstNsec3ParamPtr nsec3 = getNsec3Param(q, ds, zonename);
+    const Name* const zonename = zoneinfo.getEnclosingZone();
+    ConstNsec3ParamPtr nsec3 = getNsec3Param(q, zoneinfo);
+
     if (nsec3 != NULL) {
         // Attach the NSEC3 record covering the QNAME
         RRsetPtr rrset;
         string hash1(nsec3->getHash(task->qname));
-        RETERR(getNsec3(ds, zonename, q.qclass(), hash1, rrset));
-        m.addRRset(Section::AUTHORITY(), rrset, true);
+        RETERR(getNsec3(q, zoneinfo, hash1, rrset));
+        addToMessage(q, Section::AUTHORITY(), rrset);
 
         // If this is an NXRRSET or NOERROR/NODATA, we're done
         if ((task->flags & DataSrc::TYPE_NOT_FOUND) != 0) {
@@ -359,7 +682,7 @@
         }
 
         // Find the closest provable enclosing name for QNAME
-        Name enclosure(zonename);
+        Name enclosure(*zonename);
         const int diff = task->qname.getLabelCount() -
             enclosure.getLabelCount();
         string hash2;
@@ -374,9 +697,9 @@
 
             // hash2 will be overwritten with the actual hash found;
             // we don't want to use one until we find an exact match
-            RETERR(getNsec3(ds, zonename, q.qclass(), hash2, rrset));
+            RETERR(getNsec3(q, zoneinfo, hash2, rrset));
             if (hash2 == nodehash) {
-                m.addRRset(Section::AUTHORITY(), rrset, true);
+                addToMessage(q, Section::AUTHORITY(), rrset);
                 break;
             }
         }
@@ -389,19 +712,24 @@
         // Otherwise, there is no wildcard record, so we must add a
         // covering NSEC3 to prove that it doesn't exist.
         string hash3(nsec3->getHash(Name("*").concatenate(enclosure)));
-        RETERR(getNsec3(ds, zonename, q.qclass(), hash3, rrset));
+        RETERR(getNsec3(q, zoneinfo, hash3, rrset));
         if (hash3 != hash1 && hash3 != hash2) {
-            m.addRRset(Section::AUTHORITY(), rrset, true);
+            addToMessage(q, Section::AUTHORITY(), rrset);
         }
     } else {
         Name nsecname(task->qname);
         if ((task->flags & DataSrc::NAME_NOT_FOUND) != 0 || wildcard) {
-            ds->findPreviousName(task->qname, nsecname, &zonename);
-        }
-
-        RETERR(addNSEC(q, task, nsecname, zonename, ds));
+            const DataSrc* ds = zoneinfo.getDataSource();
+            if (ds == NULL) {
+                m.setRcode(Rcode::SERVFAIL());
+                return (DataSrc::ERROR);
+            }
+            ds->findPreviousName(task->qname, nsecname, zonename);
+        }
+
+        RETERR(addNSEC(q, nsecname, zoneinfo));
         if ((task->flags & DataSrc::TYPE_NOT_FOUND) != 0 ||
-            nsecname == zonename)
+            nsecname == *zonename)
         {
             return (DataSrc::SUCCESS);
         }
@@ -413,7 +741,7 @@
 
         // Otherwise, there is no wildcard record, so we must add an
         // NSEC for the zone to prove the wildcard doesn't exist.
-        RETERR(addNSEC(q, task, zonename, zonename, ds));
+        RETERR(addNSEC(q, *zonename, zoneinfo));
     }
 
     return (DataSrc::SUCCESS);
@@ -421,9 +749,7 @@
 
 // Attempt a wildcard lookup
 inline DataSrc::Result
-tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
-            const Name* zonename, bool& found)
-{
+tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
     Message& m = q.message();
     DataSrc::Result result;
     found = false;
@@ -434,6 +760,7 @@
         return (DataSrc::SUCCESS);
     }
 
+    const Name* const zonename = zoneinfo.getEnclosingZone();
     const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
     if (diff < 1) {
         return (DataSrc::SUCCESS);
@@ -445,9 +772,9 @@
 
     for (int i = 1; i <= diff; ++i) {
         const Name& wname(star.concatenate(task->qname.split(i)));
-        QueryTask newtask(wname, task->qclass, task->qtype, Section::ANSWER(),
+        QueryTask newtask(q, wname, task->qtype, Section::ANSWER(),
                           QueryTask::AUTH_QUERY); 
-        result = doQueryTask(ds, zonename, newtask, wild);
+        result = doQueryTask(newtask, zoneinfo, wild);
         if (result == DataSrc::SUCCESS) {
             if (newtask.flags == 0) {
                 task->flags &= ~DataSrc::NAME_NOT_FOUND;
@@ -473,7 +800,7 @@
     if (found) {
         // Prove the nonexistence of the name we were looking for
         if (q.wantDnssec()) {
-            result = proveNX(q, task, ds, *zonename, true);
+            result = proveNX(q, task, zoneinfo, true);
             if (result != DataSrc::SUCCESS) {
                 m.setRcode(Rcode::SERVFAIL());
                 return (DataSrc::ERROR);
@@ -487,17 +814,17 @@
             RRsetPtr rrset = wild.findRRset(RRType::CNAME(), q.qclass());
             if (rrset != NULL) {
                 rrset->setName(task->qname);
-                m.addRRset(Section::ANSWER(), rrset, q.wantDnssec());
+                addToMessage(q, Section::ANSWER(), rrset);
                 chaseCname(q, task, rrset);
             }
         } else {
             BOOST_FOREACH (RRsetPtr rrset, wild) {
                 rrset->setName(task->qname);
-                m.addRRset(Section::ANSWER(), rrset, q.wantDnssec());
+                addToMessage(q, Section::ANSWER(), rrset);
             }
 
             RRsetList auth;
-            if (!refQuery(*zonename, q.qclass(), ds, zonename, auth)) {
+            if (!refQuery(q, *zonename, zoneinfo, auth)) {
                 return (DataSrc::ERROR);
             }
 
@@ -507,7 +834,6 @@
 
     return (DataSrc::SUCCESS);
 }
-} // end of anonymous namespace
 
 //
 // doQuery: Processes a query.
@@ -517,6 +843,12 @@
     Message& m = q.message();
     vector<RRsetPtr> additional;
 
+    // Record the fact that the query is being processed by the
+    // current data source.
+    q.setDatasrc(this);
+
+    // Process the query task queue.  (The queue is initialized
+    // and the first task placed on it by the Query constructor.)
     m.clearHeaderFlag(MessageFlag::AA());
     while (!q.tasks().empty()) {
         QueryTaskPtr task = q.tasks().front();
@@ -535,60 +867,47 @@
             return;
         }
 
-        // Find the closest enclosing zone for which we are authoritative,
-        // and the concrete data source which is authoritative for it.
-        // (Note that RRtype DS queries need to go to the parent.)
-        const int nlabels = task->qname.getLabelCount() - 1;
-        NameMatch match(nlabels != 0 && task->qtype == RRType::DS() ?
-                        task->qname.split(1) : task->qname);
-        findClosestEnclosure(match, task->qclass);
-        const DataSrc* datasource = match.bestDataSrc();
-        const Name* zonename = match.closestName();
-
-        assert((datasource == NULL && zonename == NULL) ||
-               (datasource != NULL && zonename != NULL));
-
+        ZoneInfo zoneinfo(this, task->qname, task->qclass, task->qtype);
         RRsetList data;
         Result result = SUCCESS;
 
-        if (datasource) {
-            // For these query task types, if there is more than
-            // one level between the zone name and qname, we need to
-            // check the intermediate nodes for referrals.
-            if ((task->op == QueryTask::AUTH_QUERY ||
-                 task->op == QueryTask::NOGLUE_QUERY) &&
-                hasDelegation(datasource, zonename, q, task)) {
-                continue;
-            }
-
-            result = doQueryTask(datasource, zonename, *task, data);
-            if (result != SUCCESS) {
-                m.setRcode(Rcode::SERVFAIL());
-                return;
-            }
-
-            // Query found a referral; let's find out if that was expected--
-            // i.e., if an NS was at the zone apex, or if we were querying
-            // specifically for, and found, a DS, NSEC, or DNAME record.
-            if ((task->flags & REFERRAL) != 0 &&
-                (zonename->getLabelCount() == task->qname.getLabelCount() ||
-                 ((task->qtype == RRType::NSEC() ||
-                   task->qtype == RRType::DS() ||
-                   task->qtype == RRType::DNAME()) &&
-                  data.findRRset(task->qtype, task->qclass)))) {
-                task->flags &= ~REFERRAL;
-            }
-        } else {
-            task->flags = NO_SUCH_ZONE;
-
-            // No such zone.  If we're chasing cnames or adding additional
-            // data, that's okay, but if doing an original query, return
-            // REFUSED.
+        // For these query task types, if there is more than
+        // one level between the zone name and qname, we need to
+        // check the intermediate nodes for referrals.
+        if ((task->op == QueryTask::AUTH_QUERY ||
+             task->op == QueryTask::NOGLUE_QUERY) &&
+            hasDelegation(q, task, zoneinfo)) {
+            continue;
+        }
+
+        result = doQueryTask(*task, zoneinfo, data);
+        if (result != SUCCESS) {
+            m.setRcode(Rcode::SERVFAIL());
+            return;
+        }
+
+        // No such zone.  If we're chasing cnames or adding additional
+        // data, that's okay, but if doing an original query, return
+        // REFUSED.
+        if (task->flags == NO_SUCH_ZONE) {
             if (task->state == QueryTask::GETANSWER) {
                 m.setRcode(Rcode::REFUSED());
                 return;
             }
             continue;
+        }
+
+        // Query found a referral; let's find out if that was expected--
+        // i.e., if an NS was at the zone apex, or if we were querying
+        // specifically for, and found, a DS, NSEC, or DNAME record.
+        const Name* const zonename = zoneinfo.getEnclosingZone();
+        if ((task->flags & REFERRAL) != 0 &&
+            (zonename->getLabelCount() == task->qname.getLabelCount() ||
+             ((task->qtype == RRType::NSEC() ||
+               task->qtype == RRType::DS() ||
+               task->qtype == RRType::DNAME()) &&
+              data.findRRset(task->qtype, task->qclass)))) {
+            task->flags &= ~REFERRAL;
         }
 
         if (result == SUCCESS && task->flags == 0) {
@@ -597,7 +916,7 @@
             case QueryTask::GETANSWER:
             case QueryTask::FOLLOWCNAME:
                 BOOST_FOREACH(RRsetPtr rrset, data) {
-                    m.addRRset(task->section, rrset, q.wantDnssec());
+                    addToMessage(q, task->section, rrset);
                     if (q.tasks().empty()) {
                         need_auth = true;
                     }
@@ -612,10 +931,12 @@
                     // Add the NS records for the enclosing zone to
                     // the authority section.
                     RRsetList auth;
-                    if (!refQuery(*zonename, q.qclass(), datasource, zonename,
-                                  auth)) {
-                        m.setRcode(Rcode::SERVFAIL());
-                        return;
+                    const DataSrc* ds = zoneinfo.getDataSource();
+                    if (!refQuery(q, Name(*zonename), zoneinfo, auth)  ||
+                        !auth.findRRset(RRType::NS(), ds->getClass())) {
+                        isc_throw(DataSourceError,
+                                  "NS RR not found in " << *zonename << "/" <<
+                                  q.qclass());
                     }
 
                     copyAuth(q, auth);
@@ -647,7 +968,7 @@
             // queue to look up its target.
             RRsetPtr rrset = data.findRRset(RRType::CNAME(), q.qclass());
             if (rrset != NULL) {
-                m.addRRset(task->section, rrset, q.wantDnssec());
+                addToMessage(q, task->section, rrset);
                 chaseCname(q, task, rrset);
             }
             continue;
@@ -656,19 +977,18 @@
             if (task->state == QueryTask::GETANSWER) {
                 RRsetList auth;
                 m.clearHeaderFlag(MessageFlag::AA());
-                if (!refQuery(task->qname, q.qclass(), datasource, zonename,
-                              auth)) {
+                if (!refQuery(q, task->qname, zoneinfo, auth)) {
                     m.setRcode(Rcode::SERVFAIL());
                     return;
                 }
                 BOOST_FOREACH (RRsetPtr rrset, auth) {
                     if (rrset->getType() == RRType::NS()) {
-                        m.addRRset(Section::AUTHORITY(), rrset, q.wantDnssec());
+                        addToMessage(q, Section::AUTHORITY(), rrset);
                     } else if (rrset->getType() == task->qtype) {
-                        m.addRRset(Section::ANSWER(), rrset, q.wantDnssec());
+                        addToMessage(q, Section::ANSWER(), rrset);
                     } else if (rrset->getType() == RRType::DS() &&
                                q.wantDnssec()) {
-                        m.addRRset(Section::AUTHORITY(), rrset, true);
+                        addToMessage(q, Section::AUTHORITY(), rrset);
                     }
                     getAdditional(q, rrset);
                 }
@@ -680,7 +1000,7 @@
             // and the name was not found, we need to find out whether
             // there are any relevant wildcards.
             bool wildcard_found = false;
-            result = tryWildcard(q, task, datasource, zonename, wildcard_found);
+            result = tryWildcard(q, task, zoneinfo, wildcard_found);
             if (result != SUCCESS) {
                 m.setRcode(Rcode::SERVFAIL());
                 return;
@@ -702,20 +1022,22 @@
                     m.setRcode(Rcode::NXDOMAIN());
                 }
 
-                result = addSOA(q, zonename, datasource);
+                result = addSOA(q, zoneinfo);
                 if (result != SUCCESS) {
-                    m.setRcode(Rcode::SERVFAIL());
-                    return;
+                    isc_throw(DataSourceError,
+                              "SOA RR not found in " << *zonename <<
+                              "/" << q.qclass());
                 }
             }
 
             Name nsecname(task->qname);
             if ((task->flags & NAME_NOT_FOUND) != 0) {
-                datasource->findPreviousName(task->qname, nsecname, zonename);
+                const DataSrc* ds = zoneinfo.getDataSource();
+                ds->findPreviousName(task->qname, nsecname, zonename);
             }
 
             if (q.wantDnssec()) {
-                result = proveNX(q, task, datasource, *zonename, false);
+                result = proveNX(q, task, zoneinfo, false);
                 if (result != DataSrc::SUCCESS) {
                     m.setRcode(Rcode::SERVFAIL());
                     return;
@@ -735,13 +1057,13 @@
     // space, signatures in additional section are
     // optional.)
     BOOST_FOREACH(RRsetPtr rrset, additional) {
-        m.addRRset(Section::ADDITIONAL(), rrset, false);
+        addToMessage(q, Section::ADDITIONAL(), rrset, true);
     }
 
     if (q.wantDnssec()) {
         BOOST_FOREACH(RRsetPtr rrset, additional) {
             if (rrset->getRRsig()) {
-                m.addRRset(Section::ADDITIONAL(), rrset->getRRsig(), false);
+                addToMessage(q, Section::ADDITIONAL(), rrset->getRRsig(), true);
             }
         }
     }
@@ -840,25 +1162,38 @@
 }
 
 void
-MetaDataSrc::findClosestEnclosure(NameMatch& match, const RRClass& qclass) const
-{
-    if (getClass() != qclass &&
-        getClass() != RRClass::ANY() && qclass != RRClass::ANY()) {
+MetaDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
+    if (getClass() != match.getClass() &&
+        getClass() != RRClass::ANY() && match.getClass() != RRClass::ANY()) {
         return;
     }
 
     BOOST_FOREACH (ConstDataSrcPtr data_src, data_sources) {
-        data_src->findClosestEnclosure(match, qclass);
-    }
-}
-
-NameMatch::~NameMatch() {
+        data_src->findClosestEnclosure(match);
+    }
+}
+
+DataSrcMatch::~DataSrcMatch() {
     delete closest_name_;
 }
 
 void
-NameMatch::update(const DataSrc& new_source, const Name& container) {
+DataSrcMatch::update(const DataSrc& new_source, const Name& container) {
+    if (getClass() != new_source.getClass() && getClass() != RRClass::ANY() &&
+        new_source.getClass() != RRClass::ANY())
+    {
+        return;
+    }
+
     if (closest_name_ == NULL) {
+        const NameComparisonResult::NameRelation cmp =
+            getName().compare(container).getRelation();
+        if (cmp != NameComparisonResult::EQUAL &&
+            cmp != NameComparisonResult::SUBDOMAIN)
+        {
+            return;
+        }
+
         closest_name_ = new Name(container);
         best_source_ = &new_source;
         return;
@@ -866,7 +1201,7 @@
 
     if (container.compare(*closest_name_).getRelation() ==
         NameComparisonResult::SUBDOMAIN) {
-        const Name* newname = new Name(container);
+        Name* newname = new Name(container);
         delete closest_name_;
         closest_name_ = newname;
         best_source_ = &new_source;

Modified: branches/trac191/src/lib/datasrc/data_source.h
==============================================================================
--- branches/trac191/src/lib/datasrc/data_source.h (original)
+++ branches/trac191/src/lib/datasrc/data_source.h Fri Jul  9 10:04:58 2010
@@ -40,7 +40,7 @@
 
 namespace datasrc {
 
-class NameMatch;
+class DataSrcMatch;
 class Query;
 
 class DataSrc;
@@ -89,12 +89,15 @@
     // NAME_NOT_FOUND: The node does not exist in the data source.
     // TYPE_NOT_FOUND: The node does not contain the requested RRType
     // NO_SUCH_ZONE:   The zone does not exist in this data source.
+    //
+    // DATA_NOT_FOUND: A combination of the last three, for coding convenience
     enum QueryResponseFlags {
         REFERRAL = 0x01,
         CNAME_FOUND = 0x02,
         NAME_NOT_FOUND = 0x04,
         TYPE_NOT_FOUND = 0x08,
-        NO_SUCH_ZONE = 0x10
+        NO_SUCH_ZONE = 0x10,
+        DATA_NOT_FOUND = (NAME_NOT_FOUND|TYPE_NOT_FOUND|NO_SUCH_ZONE)
     };
 
     // 'High-level' methods.  These will be implemented by the
@@ -107,9 +110,7 @@
 
     // 'Medium-level' methods.  This will be implemented by the general
     // DataSrc class but MAY be overwritten by subclasses.
-    virtual void findClosestEnclosure(NameMatch& match,
-                                      const isc::dns::RRClass& qclasss)
-                                      const = 0;
+    virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
 
     // Optional 'low-level' methods.  These will have stub implementations
     // in the general DataSrc class but MAY be overwritten by subclasses
@@ -179,9 +180,7 @@
 
     virtual void doQuery(Query& q);
 
-    virtual void findClosestEnclosure(NameMatch& match,
-                                      const isc::dns::RRClass& qclass)
-                                      const = 0;
+    virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
 
     const isc::dns::RRClass& getClass() const { return rrclass; }
     void setClass(isc::dns::RRClass& c) { rrclass = c; }
@@ -250,8 +249,7 @@
     void removeDataSrc(ConstDataSrcPtr data_src);
     size_t dataSrcCount() { return data_sources.size(); };
     
-    void findClosestEnclosure(NameMatch& match,
-                              const isc::dns::RRClass& qclass) const;
+    void findClosestEnclosure(DataSrcMatch& match) const;
 
     // Actual queries for data should not be sent to a MetaDataSrc object,
     // so we return NOT_IMPLEMENTED if we receive any.
@@ -298,31 +296,107 @@
     std::vector<ConstDataSrcPtr> data_sources;
 };
 
-class NameMatch {
+/// \brief Information about the zone along with the %data source that best
+/// matches a give name and RR class.
+///
+/// A \c DataSrcMatch object is created with a domain name and RR class to
+/// hold the search state of looking for the zone and the %data source that
+/// stores the zone that best match the given name and RR class.
+/// The application of this class passes an object of \c DataSrcMatch to
+/// one or more ^data sources via their \c findClosestEnclosure() method.
+/// The %data source searches its content for the given key, and update
+/// the state if it finds a better zone than the currently recorded one.
+///
+/// The state of a \c DataSrcMatch object should be updated if and only if:
+///  - The specified RR class and the RR class of the %data source are the
+//     same, or the specified RR class is ANY; and
+///  - There is no matching %data source and name found (which is probably
+///    wrong, see below), or the given enclosing name gives a longer match
+///    than the currently stored enclosing name against the specified name.
+class DataSrcMatch {
     ///
     /// \name Constructors, Assignment Operator and Destructor.
     ///
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private.
-private:
-    NameMatch(const NameMatch& source);
-    NameMatch& operator=(const NameMatch& source);
-public:
-    NameMatch(const isc::dns::Name& qname) :
-        closest_name_(NULL), best_source_(NULL), qname_(qname) {}
-    ~NameMatch();
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private.
+    //@{
+private:
+    DataSrcMatch(const DataSrcMatch& source);
+    DataSrcMatch& operator=(const DataSrcMatch& source);
+public:
+    /// \brief The constructor.
+    ///
+    /// This constructor normally doesn't throw an exception.  However,
+    /// it creates a copy of the given name object, which may require memory
+    /// allocation, and if it fails the corresponding standard exception will
+    /// be thrown.
+    ///
+    /// \param name The domain name to be matched.
+    /// \param rrclass The RR class to be matched
+    DataSrcMatch(const isc::dns::Name& name,
+                 const isc::dns::RRClass& rrclass) :
+        closest_name_(NULL), best_source_(NULL),
+        name_(name), rrclass_(rrclass)
+    {}
+    ~DataSrcMatch();
     //@}
 
+    /// \name Getter and Setter Methods
+    //@{
+    /// \brief Returns the name to be matched.
+    const isc::dns::Name& getName() const { return (name_); }
+
+    /// \brief Returns the RR class to be matched.
+    ///
+    /// This method never throws an exception.
+    const isc::dns::RRClass& getClass() const { return (rrclass_); }
+
+    /// \brief Returns the best enclosing zone name found for the given
+    // name and RR class so far.
+    /// 
+    /// \return A pointer to the zone apex \c Name, NULL if none found yet.
+    ///
+    /// This method never throws an exception.
+    const isc::dns::Name* getEnclosingZone() const { return (closest_name_); }
+
+    /// \brief Returns the best %data source found for the given name and
+    /// RR class so far.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A pointer to a concrete %data source, NULL if none found yet.
+    const DataSrc* getDataSource() const { return (best_source_); }
+    //@}
+
+    /// \brief Update the object state with better information if possible.
+    ///
+    /// This method is intended to be called by a concrete %data source's
+    /// \c findClosestEnclosure() method to store the best match for
+    /// the given name and class that has been found so far.
+    ///
+    /// It compares the best name (if found) and \c container, and if the
+    /// latter gives a longer match, it will install the given %data source
+    /// and the enclosing name as the best match;
+    /// if there is no known pair of %data source and enclosing name,
+    /// this method will install the given pair unconditionally.
+    /// (which is probably BAD);
+    /// otherwise this method does nothing.
+    ///
+    /// In any case, if a new pair of %data source and enclosing name are
+    /// installed, a new name object will be internally allocated.
+    /// And, if memory allocation fails the corresponding standard exception
+    /// will be thrown.
+    ///
+    /// \param new_source A candidate %data source that gives a better match.
+    /// \param container The enclosing name of the matching zone in
+    /// \c new_source.
     void update(const DataSrc& new_source, const isc::dns::Name& container);
 
-    const isc::dns::Name& qname() const { return (qname_); }
-    const isc::dns::Name* closestName() const { return (closest_name_); }
-    const DataSrc* bestDataSrc() const { return (best_source_); }
-
-private:
-    const isc::dns::Name* closest_name_;
+private:
+    isc::dns::Name* closest_name_;
     const DataSrc* best_source_;
-    const isc::dns::Name qname_;
+    const isc::dns::Name name_;
+    const isc::dns::RRClass& rrclass_;
 };
 
 class Nsec3Param {

Modified: branches/trac191/src/lib/datasrc/query.cc
==============================================================================
--- branches/trac191/src/lib/datasrc/query.cc (original)
+++ branches/trac191/src/lib/datasrc/query.cc Fri Jul  9 10:04:58 2010
@@ -28,36 +28,37 @@
 namespace isc {
 namespace datasrc {
 
-QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
+QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
                      const isc::dns::RRType& t, const isc::dns::Section& sect) :
-    qname(n), qclass(c), qtype(t), section(sect), op(AUTH_QUERY),
+    q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
+    op(AUTH_QUERY), state(GETANSWER), flags(0)
+{}
+
+QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n, 
+                     const isc::dns::RRType& t, const isc::dns::Section& sect,
+                     const Op o) :
+    q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect), op(o),
     state(GETANSWER), flags(0)
 {}
 
-QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
+QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
                      const isc::dns::RRType& t, const isc::dns::Section& sect,
-                     const Op o) :
-    qname(n), qclass(c), qtype(t), section(sect), op(o), state(GETANSWER),
-    flags(0)
+                     const State st) :
+    q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
+    op(AUTH_QUERY), state(st), flags(0)
 {}
 
-QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
+QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
                      const isc::dns::RRType& t, const isc::dns::Section& sect,
-                     const State st) :
-    qname(n), qclass(c), qtype(t), section(sect), op(AUTH_QUERY), state(st),
-    flags(0)
+                     const Op o, const State st) :
+    q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect), op(o),
+    state(st), flags(0) 
 {}
 
-QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
-                     const isc::dns::RRType& t, const isc::dns::Section& sect,
-                     const Op o, const State st) :
-    qname(n), qclass(c), qtype(t), section(sect), op(o), state(st), flags(0) 
-{}
-
-QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
+QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n, 
                      const isc::dns::RRType& t, const Op o) :
-    qname(n), qclass(c), qtype(t), section(Section::ANSWER()), op(o),
-    state(GETANSWER), flags(0)
+    q(qry), qname(n), qclass(qry.qclass()), qtype(t),
+    section(Section::ANSWER()), op(o), state(GETANSWER), flags(0)
 {
     if (op != SIMPLE_QUERY) {
         isc_throw(Unexpected, "invalid constructor for this task operation");
@@ -65,21 +66,20 @@
 }
 
 // A referral query doesn't need to specify section, state, or type.
-QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
-                     const Op o) :
-    qname(n), qclass(c), qtype(RRType::ANY()), section(Section::ANSWER()),
-    op(o), state(GETANSWER), flags(0)
+QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n, const Op o) :
+    q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
+    section(Section::ANSWER()), op(o), state(GETANSWER), flags(0)
 {
     if (op != REF_QUERY) {
         isc_throw(Unexpected, "invalid constructor for this task operation");
     }
 }
 
-QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
+QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
                      const isc::dns::Section& sect, const Op o,
                      const State st) :
-        qname(n), qclass(c), qtype(RRType::ANY()), section(sect), op(o),
-        state(st), flags(0)
+        q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
+        section(sect), op(o), state(st), flags(0)
 {
     if (op != GLUE_QUERY && op != NOGLUE_QUERY) {
         isc_throw(Unexpected, "invalid constructor for this task operation");
@@ -88,9 +88,9 @@
 
 QueryTask::~QueryTask() {}
 
-Query::Query(Message& m, bool dnssec) :
+Query::Query(Message& m, HotCache& c, bool dnssec) :
     status_(PENDING), qname_(NULL), qclass_(NULL), qtype_(NULL),
-    message_(&m), want_additional_(true), want_dnssec_(dnssec)
+    cache_(&c), message_(&m), want_additional_(true), want_dnssec_(dnssec)
 {
     // Check message formatting
     if (message_->getRRCount(Section::QUESTION()) != 1) {
@@ -104,7 +104,7 @@
     qtype_ = &question->getType();
     restarts_ = 0;
 
-    querytasks_.push(QueryTaskPtr(new QueryTask(*qname_, *qclass_, *qtype_,
+    querytasks_.push(QueryTaskPtr(new QueryTask(*this, *qname_, *qtype_,
                                                 Section::ANSWER())));
 }
 

Modified: branches/trac191/src/lib/datasrc/query.h
==============================================================================
--- branches/trac191/src/lib/datasrc/query.h (original)
+++ branches/trac191/src/lib/datasrc/query.h Fri Jul  9 10:04:58 2010
@@ -19,6 +19,9 @@
 
 #include <boost/shared_ptr.hpp>
 
+#include <datasrc/cache.h>
+#include <datasrc/data_source.h>
+
 #include <dns/name.h>
 #include <dns/message.h>
 #include <dns/rrtype.h>
@@ -27,8 +30,10 @@
 #include <queue>
 
 namespace isc {
-
 namespace datasrc {
+
+class Query;
+typedef boost::shared_ptr<Query> QueryPtr;
 
 // An individual task to be carried out by the query logic
 class QueryTask {
@@ -41,6 +46,9 @@
     // XXX: Members are currently public, but should probably be
     // moved to private and wrapped in get() functions later.
 
+    // The \c Query that this \c QueryTask was created to service.
+    const Query& q;
+
     // The standard query tuple: qname/qclass/qtype.
     // Note that qtype is ignored in the GLUE_QUERY/NOGLUE_QUERY case.
     const isc::dns::Name qname;
@@ -118,15 +126,14 @@
     uint32_t flags;
 
     // Constructors
-    QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
+    QueryTask(const Query& q, const isc::dns::Name& n,
               const isc::dns::RRType& t, const isc::dns::Section& sect);
-    QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
-              const isc::dns::RRType& t, const isc::dns::Section& sect,
-              Op o);
-    QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
+    QueryTask(const Query& q, const isc::dns::Name& n,
+              const isc::dns::RRType& t, const isc::dns::Section& sect, Op o);
+    QueryTask(const Query& q, const isc::dns::Name& n,
               const isc::dns::RRType& t, const isc::dns::Section& sect,
               const State st);
-    QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
+    QueryTask(const Query& q, const isc::dns::Name& n,
               const isc::dns::RRType& t, const isc::dns::Section& sect,
               Op o, State st);
 
@@ -134,12 +141,12 @@
     // to simplify the code.
     //
     // A simple query doesn't need to specify section or state.
-    QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
+    QueryTask(const Query& q, const isc::dns::Name& n,
               const isc::dns::RRType& t, Op o);
     // A referral query doesn't need to specify section, state, or type.
-    QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, Op o);
+    QueryTask(const Query& q, const isc::dns::Name& n, Op o);
     // A glue (or noglue) query doesn't need to specify type.
-    QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
+    QueryTask(const Query& q, const isc::dns::Name& n,
               const isc::dns::Section& sect, Op o, State st);
 
     ~QueryTask();
@@ -147,9 +154,6 @@
 
 typedef boost::shared_ptr<QueryTask> QueryTaskPtr;
 typedef std::queue<QueryTaskPtr> QueryTaskQueue;
-
-class Query;
-typedef boost::shared_ptr<Query> QueryPtr;
 
 // Data Source query
 class Query {
@@ -171,7 +175,7 @@
     Query& operator=(const Query& source);
 public:
     // Query constructor
-    Query(isc::dns::Message& m, bool dnssec);
+    Query(isc::dns::Message& m, HotCache& c, bool dnssec);
     /// \brief The destructor.
     virtual ~Query();
     //@}
@@ -179,17 +183,17 @@
     // wantAdditional() == true indicates that additional-section data
     // should be looked up while processing this query.  false indicates
     // that we're only interested in answer-section data
-    bool wantAdditional() { return want_additional_; }
+    bool wantAdditional() { return (want_additional_); }
     void setWantAdditional(bool d) { want_additional_ = d; }
 
     // wantDnssec() == true indicates that DNSSEC data should be retrieved
     // from the data source when this query is being processed
-    bool wantDnssec() const { return want_dnssec_; }
+    bool wantDnssec() const { return (want_dnssec_); }
     void setWantDnssec(bool d) { want_dnssec_ = d; }
 
-    const isc::dns::Name& qname() const { return *qname_; }
-    const isc::dns::RRClass& qclass() const { return *qclass_; }
-    const isc::dns::RRType& qtype() const { return *qtype_; }
+    const isc::dns::Name& qname() const { return (*qname_); }
+    const isc::dns::RRClass& qclass() const { return (*qclass_); }
+    const isc::dns::RRType& qtype() const { return (*qtype_); }
 
     // Note: these can't be constant member functions because they expose
     // writable 'handles' of internal member variables.  It's questionable
@@ -197,10 +201,10 @@
     // corresponding members are public (which itself is not a good practice
     // but it's a different topic), but at the moment we keep them.
     // We should definitely revisit the design later.
-    isc::dns::Message& message() { return *message_; }
-    QueryTaskQueue& tasks() { return querytasks_; }
-
-    Status status() const { return status_; }
+    isc::dns::Message& message() { return (*message_); }
+    QueryTaskQueue& tasks() { return (querytasks_); }
+
+    Status status() const { return (status_); }
     void setStatus(Status s) { status_ = s; }
 
     // Limit CNAME chains to 16 per query, to avoid loops
@@ -211,6 +215,13 @@
         return (false);
     }
 
+    void setDatasrc(DataSrc* ds) { datasrc_ = ds; }
+    DataSrc* datasrc() const { return (datasrc_); }
+
+    // \brief The query cache.  This is a static member of class \c Query;
+    // the same cache will be used by all instances.
+    HotCache& getCache() const { return (*cache_); }
+
 private:
     Status status_;
 
@@ -218,6 +229,9 @@
     const isc::dns::RRClass* qclass_;
     const isc::dns::RRType* qtype_;
 
+    HotCache* cache_;
+    DataSrc* datasrc_;
+
     isc::dns::Message* message_;
     QueryTaskQueue querytasks_;
 

Modified: branches/trac191/src/lib/datasrc/sqlite3_datasrc.cc
==============================================================================
--- branches/trac191/src/lib/datasrc/sqlite3_datasrc.cc (original)
+++ branches/trac191/src/lib/datasrc/sqlite3_datasrc.cc Fri Jul  9 10:04:58 2010
@@ -344,19 +344,17 @@
 }
 
 void
-Sqlite3DataSrc::findClosestEnclosure(NameMatch& match,
-                                     const RRClass& qclass) const
-{
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
+Sqlite3DataSrc::findClosestEnclosure(DataSrcMatch& match) const {
+    if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
         return;
     }
 
     unsigned int position;
-    if (findClosest(match.qname(), &position) == -1) {
+    if (findClosest(match.getName(), &position) == -1) {
         return;
     }
 
-    match.update(*this, match.qname().split(position));
+    match.update(*this, match.getName().split(position));
 }
 
 DataSrc::Result

Modified: branches/trac191/src/lib/datasrc/sqlite3_datasrc.h
==============================================================================
--- branches/trac191/src/lib/datasrc/sqlite3_datasrc.h (original)
+++ branches/trac191/src/lib/datasrc/sqlite3_datasrc.h Fri Jul  9 10:04:58 2010
@@ -58,8 +58,7 @@
     ~Sqlite3DataSrc();
     //@}
 
-    void findClosestEnclosure(NameMatch& match,
-                              const isc::dns::RRClass& qclass) const;
+    void findClosestEnclosure(DataSrcMatch& match) const;
 
     Result findRRset(const isc::dns::Name& qname,
                      const isc::dns::RRClass& qclass,

Modified: branches/trac191/src/lib/datasrc/static_datasrc.cc
==============================================================================
--- branches/trac191/src/lib/datasrc/static_datasrc.cc (original)
+++ branches/trac191/src/lib/datasrc/static_datasrc.cc Fri Jul  9 10:04:58 2010
@@ -129,11 +129,10 @@
 }
 
 void
-StaticDataSrc::findClosestEnclosure(NameMatch& match,
-                                    const RRClass& qclass) const {
-    const Name& qname = match.qname();
-
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
+StaticDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
+    const Name& qname = match.getName();
+
+    if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
         return;
     }
 

Modified: branches/trac191/src/lib/datasrc/static_datasrc.h
==============================================================================
--- branches/trac191/src/lib/datasrc/static_datasrc.h (original)
+++ branches/trac191/src/lib/datasrc/static_datasrc.h Fri Jul  9 10:04:58 2010
@@ -39,8 +39,6 @@
 
 namespace datasrc {
 
-class Query;
-class NameMatch;
 struct StaticDataSrcImpl;
 
 class StaticDataSrc : public DataSrc {
@@ -58,8 +56,7 @@
     ~StaticDataSrc();
     //@}
 
-    void findClosestEnclosure(NameMatch& match,
-                              const isc::dns::RRClass& qclass) const;
+    void findClosestEnclosure(DataSrcMatch& match) const;
 
     Result findRRset(const isc::dns::Name& qname,
                      const isc::dns::RRClass& qclass,

Modified: branches/trac191/src/lib/datasrc/tests/Makefile.am
==============================================================================
--- branches/trac191/src/lib/datasrc/tests/Makefile.am (original)
+++ branches/trac191/src/lib/datasrc/tests/Makefile.am Fri Jul  9 10:04:58 2010
@@ -16,6 +16,7 @@
 run_unittests_SOURCES += sqlite3_unittest.cc
 run_unittests_SOURCES += static_unittest.cc
 run_unittests_SOURCES += query_unittest.cc
+run_unittests_SOURCES += cache_unittest.cc
 run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
@@ -23,7 +24,7 @@
 run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a 
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns++.a 
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.a 
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/.libs/libcc.a
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
 endif
 
@@ -40,6 +41,7 @@
 EXTRA_DIST += testdata/q_cname_ext
 EXTRA_DIST += testdata/q_cname_int
 EXTRA_DIST += testdata/q_dname
+EXTRA_DIST += testdata/q_example_dnskey
 EXTRA_DIST += testdata/q_example_ns
 EXTRA_DIST += testdata/q_example_ptr
 EXTRA_DIST += testdata/q_glork

Modified: branches/trac191/src/lib/datasrc/tests/datasrc_unittest.cc
==============================================================================
--- branches/trac191/src/lib/datasrc/tests/datasrc_unittest.cc (original)
+++ branches/trac191/src/lib/datasrc/tests/datasrc_unittest.cc Fri Jul  9 10:04:58 2010
@@ -49,7 +49,7 @@
 using namespace isc::data;
 
 namespace {
-const ElementPtr SQLITE_DBFILE_EXAMPLE = Element::createFromString(
+const ElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/example.org.sqlite3\"}");
 
 class DataSrcTest : public ::testing::Test {
@@ -69,6 +69,7 @@
     void createAndProcessQuery(const Name& qname, const RRClass& qclass,
                                const RRType& qtype);
 
+    HotCache cache;
     MetaDataSrc meta_source;
     OutputBuffer obuffer;
     MessageRenderer renderer;
@@ -76,10 +77,10 @@
 };
 
 void
-performQuery(DataSrc& data_source, Message& message) {
+performQuery(DataSrc& data_source, HotCache& cache, Message& message) {
     message.setHeaderFlag(MessageFlag::AA());
     message.setRcode(Rcode::NOERROR());
-    Query q(message, true);
+    Query q(message, cache, true);
     data_source.doQuery(q);
 }
 
@@ -92,7 +93,7 @@
     msg.fromWire(buffer);
 
     msg.makeResponse();
-    performQuery(meta_source, msg);
+    performQuery(meta_source, cache, msg);
 }
 
 void
@@ -103,7 +104,7 @@
     msg.setOpcode(Opcode::QUERY());
     msg.addQuestion(Question(qname, qclass, qtype));
     msg.setHeaderFlag(MessageFlag::RD());
-    performQuery(meta_source, msg);
+    performQuery(meta_source, cache, msg);
 }
 
 void
@@ -213,6 +214,83 @@
     EXPECT_TRUE(it->isLast());
 }
 
+// Make sure two successive queries have the same result
+TEST_F(DataSrcTest, DuplicateQuery) {
+    readAndProcessQuery("q_example_ns");
+    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 0, 6);
+
+    RRsetIterator rit = msg.beginSection(Section::ANSWER());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("example.com"), rrset->getName());
+    EXPECT_EQ(RRType::NS(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    RdataIteratorPtr it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
+    it->next();
+    EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
+    it->next();
+    EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+
+    msg.clear(Message::PARSE);
+    readAndProcessQuery("q_example_ns");
+    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 0, 6);
+
+    rit = msg.beginSection(Section::ANSWER());
+    rrset = *rit;
+    EXPECT_EQ(Name("example.com"), rrset->getName());
+    EXPECT_EQ(RRType::NS(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
+    it->next();
+    EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
+    it->next();
+    EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+}
+
+TEST_F(DataSrcTest, DNSKEYQuery) {
+    readAndProcessQuery("q_example_dnskey");
+    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
+
+    RRsetIterator rit = msg.beginSection(Section::ANSWER());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("example.com"), rrset->getName());
+    EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+}
+
+// Repeat the previous query to check that cache is working correctly.
+// We query for a record at a zone cut to ensure the REFERRAL flag doesn't
+// cause incorrect behavior.
+TEST_F(DataSrcTest, DNSKEYDuplicateQuery) {
+    readAndProcessQuery("q_example_dnskey");
+    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
+
+    RRsetIterator rit = msg.beginSection(Section::ANSWER());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("example.com"), rrset->getName());
+    EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    msg.clear(Message::PARSE);
+    readAndProcessQuery("q_example_dnskey");
+    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
+
+    rit = msg.beginSection(Section::ANSWER());
+    rrset = *rit;
+    EXPECT_EQ(Name("example.com"), rrset->getName());
+    EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+}
+
 TEST_F(DataSrcTest, NxRRset) {
     readAndProcessQuery("q_example_ptr");
 
@@ -770,8 +848,14 @@
 }
 
 TEST_F(DataSrcTest, CNAMELoop) {
-    createAndProcessQuery(Name("loop1.example.com"), RRClass::IN(),
+    createAndProcessQuery(Name("one.loop.example"), RRClass::IN(),
                           RRType::A());
+    EXPECT_EQ(Rcode::NOERROR(), msg.getRcode());
+
+    // one.loop.example points to two.loop.example, which points back
+    // to one.loop.example, so there should be exactly two CNAME records
+    // in the answer.
+    EXPECT_EQ(2, msg.getRRCount(Section::ANSWER()));
 }
 
 // NSEC query for the name of a zone cut for non-secure delegation.
@@ -852,8 +936,26 @@
     EXPECT_EQ(0, ds.dataSrcCount());
 }
 
-#if 0                           // currently fails
-TEST_F(DataSrcTest, synthesizedCnameTooLong) {
+TEST_F(DataSrcTest, noNSZone) {
+    EXPECT_THROW(createAndProcessQuery(Name("www.nons.example"),
+                                       RRClass::IN(), RRType::A()),
+                 DataSourceError);
+}
+
+TEST_F(DataSrcTest, noNSButDnameZone) {
+    EXPECT_THROW(createAndProcessQuery(Name("www.nons-dname.example"),
+                                       RRClass::IN(), RRType::A()),
+                 DataSourceError);
+}
+
+TEST_F(DataSrcTest, noSOAZone) {
+    EXPECT_THROW(createAndProcessQuery(Name("notexist.nosoa.example"),
+                                       RRClass::IN(), RRType::A()),
+                 DataSourceError);
+}
+
+// currently fails
+TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
     // qname has the possible max length (255 octets).  it matches a DNAME,
     // and the synthesized CNAME would exceed the valid length.
     createAndProcessQuery(
@@ -863,6 +965,105 @@
              "0123456789abcdef0123456789abcdef0123456789a.dname.example.org."),
         RRClass::IN(), RRType::A());
 }
-#endif
-
-}
+
+// Tests of the DataSrcMatch class start here
+class DataSrcMatchTest : public ::testing::Test {
+protected:
+    DataSrcMatchTest() {
+        datasrc1.init();
+    }
+    // test data source serves example.com/IN.
+    TestDataSrc datasrc1;
+    // this data source is dummy.  Its content doesn't matter in the tests.
+    TestDataSrc datasrc2;
+};
+
+TEST_F(DataSrcMatchTest, match) {
+    DataSrcMatch match(Name("very.very.long.example.com"), RRClass::IN());
+    datasrc1.findClosestEnclosure(match);
+    EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
+    EXPECT_EQ(&datasrc1, match.getDataSource());
+}
+
+TEST_F(DataSrcMatchTest, matchWithWrongClass) {
+    DataSrcMatch match(Name("very.very.long.example.com"), RRClass::CH());
+    datasrc1.findClosestEnclosure(match);
+    // XXX: some deviant compilers seem to fail to recognize a NULL as a
+    // pointer type.  This explicit cast works around such compilers.
+    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
+    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
+}
+
+TEST_F(DataSrcMatchTest, matchWithAnyClass) {
+    DataSrcMatch match(Name("very.very.long.example.com"), RRClass::ANY());
+    datasrc1.findClosestEnclosure(match);
+    EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
+    EXPECT_EQ(&datasrc1, match.getDataSource());
+}
+
+TEST_F(DataSrcMatchTest, updateWithWrongClass) {
+    DataSrcMatch match(Name("www.example.com"), RRClass::CH());
+
+    EXPECT_EQ(RRClass::IN(), datasrc2.getClass());
+    match.update(datasrc2, Name("com"));
+    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
+    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
+
+    EXPECT_EQ(RRClass::IN(), datasrc1.getClass());
+    match.update(datasrc1, Name("example.com"));
+    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
+    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
+}
+
+TEST_F(DataSrcMatchTest, updateAgainstAnyClass) {
+    DataSrcMatch match(Name("www.example.com"), RRClass::ANY());
+    match.update(datasrc2, Name("com"));
+    EXPECT_EQ(Name("com"), *match.getEnclosingZone());
+    EXPECT_EQ(&datasrc2, match.getDataSource());
+
+    // the given class for search is ANY, so update should be okay.
+    EXPECT_EQ(RRClass::IN(), datasrc1.getClass());
+    match.update(datasrc1, Name("example.com"));
+    EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
+    EXPECT_EQ(&datasrc1, match.getDataSource());
+}
+
+TEST_F(DataSrcMatchTest, updateWithNoMatch) {
+    DataSrcMatch match(Name("www.example.com"), RRClass::IN());
+    match.update(datasrc1, Name("com"));
+    EXPECT_EQ(Name("com"), *match.getEnclosingZone());
+    EXPECT_EQ(&datasrc1, match.getDataSource());
+
+    // An attempt of update with a name that doesn't match.  This attempt
+    // should be ignored.
+    match.update(datasrc2, Name("example.org"));
+    EXPECT_EQ(Name("com"), *match.getEnclosingZone());
+    EXPECT_EQ(&datasrc1, match.getDataSource());
+}
+
+// This test currently fails.
+TEST_F(DataSrcMatchTest, initialUpdateWithNoMatch) {
+    DataSrcMatch match(Name("www.example.com"), RRClass::IN());
+
+    // An initial attempt of update with a name that doesn't match.
+    // Should be ignored.
+    match.update(datasrc1, Name("example.org"));
+    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
+    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
+}
+
+TEST_F(DataSrcMatchTest, updateWithShorterMatch) {
+    DataSrcMatch match(Name("www.example.com"), RRClass::IN());
+
+    match.update(datasrc1, Name("example.com"));
+    EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
+    EXPECT_EQ(&datasrc1, match.getDataSource());
+
+    // An attempt of update with a name that gives a shorter match.
+    // This attempt should be ignored.
+    match.update(datasrc2, Name("com"));
+    EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
+    EXPECT_EQ(&datasrc1, match.getDataSource());
+}
+
+}

Modified: branches/trac191/src/lib/datasrc/tests/query_unittest.cc
==============================================================================
--- branches/trac191/src/lib/datasrc/tests/query_unittest.cc (original)
+++ branches/trac191/src/lib/datasrc/tests/query_unittest.cc Fri Jul  9 10:04:58 2010
@@ -16,43 +16,61 @@
 
 #include <gtest/gtest.h>
 
+#include <dns/buffer.h>
+#include <dns/message.h>
 #include <dns/name.h>
 #include <dns/rrtype.h>
 #include <dns/rrclass.h>
 
 #include <datasrc/query.h>
 
-namespace {
+#include <dns/tests/unittest_util.h>
 
+using isc::UnitTestUtil;
 using namespace isc::dns;
 using namespace isc::datasrc;
 
+namespace {
 
 class QueryTest : public ::testing::Test {
 protected:
-    QueryTest() :
-        name(Name("www.example.com")),
-        rrtype(RRType::A()),
-        rrclass(RRClass::IN())
-    {}
-    const Name name;
-    const RRType rrtype;
-    const RRClass rrclass;
+    void readQuery(Message& m, const char* datafile);
+
+    HotCache cache;
 };
 
+void
+QueryTest::readQuery(Message& m, const char* datafile) {
+    std::vector<unsigned char> data;
+    UnitTestUtil::readWireData(datafile, data);
+
+    InputBuffer buffer(&data[0], data.size());
+    m.fromWire(buffer);
+}
+
 QueryTaskPtr
-createTask(const Name& name, const RRClass& rrclass0, const RRType& rrtype0) {
+createTask(Message& m, const Name& name, const RRType& rrtype0, HotCache& c) {
     RRType rrtype(rrtype0);
-    return (QueryTaskPtr(new QueryTask(name, rrclass0, rrtype,
+    Query q(m, c, true);
+    return (QueryTaskPtr(new QueryTask(q, name, rrtype,
                                        QueryTask::SIMPLE_QUERY)));
 }
 
 // Check the QueryTask created using a temporary RRType object will remain
 // valid.
 TEST_F(QueryTest, constructWithTemporary) {
-    QueryTaskPtr task_a = createTask(name, rrclass, RRType::A());
-    QueryTaskPtr task_aaaa = createTask(name, rrclass, RRType::AAAA());
-    EXPECT_EQ(rrtype, task_a->qtype);
+    Message m1(Message::PARSE);
+    readQuery(m1, "q_wild_a");
+    QueryTaskPtr task_a = createTask(m1, Name("www.wild.example.com"),
+                                        RRType::A(), cache);
+    EXPECT_EQ(RRType::A(), task_a->qtype);
+
+    Message m2(Message::PARSE);
+    readQuery(m2, "q_wild_aaaa");
+    QueryTaskPtr task_aaaa = createTask(m2, Name("www.wild.example.com"),
+                                        RRType::AAAA(), cache);
+    EXPECT_EQ(RRType::AAAA(), task_aaaa->qtype);
+
 }
 
 }

Modified: branches/trac191/src/lib/datasrc/tests/sqlite3_unittest.cc
==============================================================================
--- branches/trac191/src/lib/datasrc/tests/sqlite3_unittest.cc (original)
+++ branches/trac191/src/lib/datasrc/tests/sqlite3_unittest.cc Fri Jul  9 10:04:58 2010
@@ -43,22 +43,22 @@
 using namespace isc::data;
 
 namespace {
-ElementPtr SQLITE_DBFILE_EXAMPLE = Element::createFromString(
+ElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/test.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::createFromString(
+ElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/example2.com.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::createFromString(
+ElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/test-root.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_BROKENDB = Element::createFromString(
+ElementPtr SQLITE_DBFILE_BROKENDB = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/brokendb.sqlite3\"}");
-ElementPtr SQLITE_DBFILE_MEMORY = Element::createFromString(
+ElementPtr SQLITE_DBFILE_MEMORY = Element::fromJSON(
     "{ \"database_file\": \":memory:\"}");
 
 // The following file must be non existent and must be non"creatable";
 // the sqlite3 library will try to create a new DB file if it doesn't exist,
 // so to test a failure case the create operation should also fail.
 // The "nodir", a non existent directory, is inserted for this purpose.
-ElementPtr SQLITE_DBFILE_NOTEXIST = Element::createFromString(
+ElementPtr SQLITE_DBFILE_NOTEXIST = Element::fromJSON(
     "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}");
 
 const string sigdata_common(" 20100322084538 20100220084538 "
@@ -374,12 +374,10 @@
     EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
     EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE2));
 
-    NameMatch name_match(www_name);
-    data_source.findClosestEnclosure(name_match, rrclass);
-    // XXX: some deviant compilers seem to fail to recognize a NULL as a
-    // pointer type.  This explicit cast works around such compilers.
-    EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName());
-    EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc());
+    DataSrcMatch match(www_name, rrclass);
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
+    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
 }
 
 TEST_F(Sqlite3DataSourceTest, openFail) {
@@ -415,52 +413,52 @@
 }
 
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosure) {
-    NameMatch name_match(www_name);
-    data_source.findClosestEnclosure(name_match, rrclass);
-    EXPECT_EQ(zone_name, *name_match.closestName());
-    EXPECT_EQ(&data_source, name_match.bestDataSrc());
+    DataSrcMatch match(www_name, rrclass);
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(zone_name, *match.getEnclosingZone());
+    EXPECT_EQ(&data_source, match.getDataSource());
 }
 
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosureMatchRoot) {
     EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
     EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE_ROOT));
 
-    NameMatch name_match(Name("org."));
-    data_source.findClosestEnclosure(name_match, rrclass);
-    EXPECT_EQ(Name("."), *name_match.closestName());
-    EXPECT_EQ(&data_source, name_match.bestDataSrc());
+    DataSrcMatch match(Name("org."), rrclass);
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(Name("."), *match.getEnclosingZone());
+    EXPECT_EQ(&data_source, match.getDataSource());
 }
 
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosureAtDelegation) {
     // The search name exists both in the parent and child zones, but
     // child has a better match.
-    NameMatch name_match(child_name);
-    data_source.findClosestEnclosure(name_match, rrclass);
-    EXPECT_EQ(child_name, *name_match.closestName());
-    EXPECT_EQ(&data_source, name_match.bestDataSrc());
+    DataSrcMatch match(child_name, rrclass);
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(child_name, *match.getEnclosingZone());
+    EXPECT_EQ(&data_source, match.getDataSource());
 }
 
 TEST_F(Sqlite3DataSourceTest, findClosestEnclosureNoMatch) {
-    NameMatch name_match(nomatch_name);
-    data_source.findClosestEnclosure(name_match, rrclass);
-    EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName());
-    EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc());
+    DataSrcMatch match(nomatch_name, rrclass);
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
+    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
 }
 
 TEST_F(Sqlite3DataSourceTest, findClosestClassMismatch) {
-    NameMatch name_match(www_name);
-    data_source.findClosestEnclosure(name_match, rrclass_notmatch);
-    EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName());
-    EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc());
+    DataSrcMatch match(nomatch_name, rrclass);
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
+    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
 }
 
 // If the query class is ANY, the result should be the same as the case where
 // the class exactly matches.
 TEST_F(Sqlite3DataSourceTest, findClosestClassAny) {
-    NameMatch name_match(www_name);
-    data_source.findClosestEnclosure(name_match, RRClass::ANY());
-    EXPECT_EQ(zone_name, *name_match.closestName());
-    EXPECT_EQ(&data_source, name_match.bestDataSrc());
+    DataSrcMatch match(www_name, RRClass::ANY());
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(zone_name, *match.getEnclosingZone());
+    EXPECT_EQ(&data_source, match.getDataSource());
 }
 
 TEST_F(Sqlite3DataSourceTest, findRRsetNormal) {

Modified: branches/trac191/src/lib/datasrc/tests/static_unittest.cc
==============================================================================
--- branches/trac191/src/lib/datasrc/tests/static_unittest.cc (original)
+++ branches/trac191/src/lib/datasrc/tests/static_unittest.cc Fri Jul  9 10:04:58 2010
@@ -196,55 +196,54 @@
 }
 
 TEST_F(StaticDataSourceTest, findClosestEnclosureForVersion) {
-    NameMatch name_match(version_name);
-    data_source.findClosestEnclosure(name_match, rrclass);
-    EXPECT_EQ(version_name, *name_match.closestName());
-    EXPECT_EQ(&data_source, name_match.bestDataSrc());
+    DataSrcMatch match(version_name, rrclass);
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(version_name, *match.getEnclosingZone());
+    EXPECT_EQ(&data_source, match.getDataSource());
 }
 
 // Class Any query should result in the same answer.
 TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionClassAny) {
-    NameMatch name_match(version_name);
-    data_source.findClosestEnclosure(name_match, RRClass::ANY());
-    EXPECT_EQ(version_name, *name_match.closestName());
-    EXPECT_EQ(&data_source, name_match.bestDataSrc());
+    DataSrcMatch match(version_name, RRClass::ANY());
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(version_name, *match.getEnclosingZone());
+    EXPECT_EQ(&data_source, match.getDataSource());
 }
 
 // If class doesn't match the lookup should fail.
 TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionClassMismatch) {
-    NameMatch name_match(version_name);
-    data_source.findClosestEnclosure(name_match, RRClass::IN());
-    // XXX: see sqlite3_unittest.cc about the cast.
-    EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName());
-    EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc());
+    DataSrcMatch match(version_name, RRClass::IN());
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
+    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
 }
 
 TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionPartial) {
-    NameMatch name_match(Name("foo").concatenate(version_name));
-    data_source.findClosestEnclosure(name_match, rrclass);
-    EXPECT_EQ(version_name, *name_match.closestName());
-    EXPECT_EQ(&data_source, name_match.bestDataSrc());
+    DataSrcMatch match(Name("foo").concatenate(version_name), rrclass);
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(version_name, *match.getEnclosingZone());
+    EXPECT_EQ(&data_source, match.getDataSource());
 }
 
 TEST_F(StaticDataSourceTest, findClosestEnclosureForAuthors) {
-    NameMatch name_match(authors_name);
-    data_source.findClosestEnclosure(name_match, rrclass);
-    EXPECT_EQ(authors_name, *name_match.closestName());
-    EXPECT_EQ(&data_source, name_match.bestDataSrc());
+    DataSrcMatch match(authors_name, rrclass);
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(authors_name, *match.getEnclosingZone());
+    EXPECT_EQ(&data_source, match.getDataSource());
 }
 
 TEST_F(StaticDataSourceTest, findClosestEnclosureForAuthorsPartial) {
-    NameMatch name_match(Name("foo").concatenate(authors_name));
-    data_source.findClosestEnclosure(name_match, rrclass);
-    EXPECT_EQ(authors_name, *name_match.closestName());
-    EXPECT_EQ(&data_source, name_match.bestDataSrc());
+    DataSrcMatch match(Name("foo").concatenate(authors_name), rrclass);
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(authors_name, *match.getEnclosingZone());
+    EXPECT_EQ(&data_source, match.getDataSource());
 }
 
 TEST_F(StaticDataSourceTest, findClosestEnclosureNoMatch) {
-    NameMatch name_match(nomatch_name);
-    data_source.findClosestEnclosure(name_match, rrclass);
-    EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName());
-    EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc());
+    DataSrcMatch match(nomatch_name, rrclass);
+    data_source.findClosestEnclosure(match);
+    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
+    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
 }
 
 TEST_F(StaticDataSourceTest, findRRsetVersionTXT) {

Modified: branches/trac191/src/lib/datasrc/tests/test_datasrc.cc
==============================================================================
--- branches/trac191/src/lib/datasrc/tests/test_datasrc.cc (original)
+++ branches/trac191/src/lib/datasrc/tests/test_datasrc.cc Fri Jul  9 10:04:58 2010
@@ -17,6 +17,8 @@
 #include "config.h"
 
 #include <cassert>
+
+#include <algorithm>
 
 #include <dns/tests/unittest_util.h>
 #include "test_datasrc.h"
@@ -45,69 +47,303 @@
 namespace datasrc {
 
 namespace {
-const Name example("example.com");
-const Name sql1("sql1.example.com");
-const Name www_sql1("www.sql1.example.com");
-const Name www("www.example.com");
-const Name foo("foo.example.com");
-const Name dns01("dns01.example.com");
-const Name dns02("dns02.example.com");
-const Name dns03("dns03.example.com");
-const Name cnameint("cname-int.example.com");
-const Name cnameext("cname-ext.example.com");
-const Name dname("dname.example.com");
-const Name wild("*.wild.example.com");
-const Name wild2("*.wild2.example.com");
-const Name wild3("*.wild3.example.com");
-const Name subzone("subzone.example.com");
-const Name loop1("loop1.example.com");
-const Name loop2("loop2.example.com");
-
-RRsetPtr example_ns;
-RRsetPtr example_soa;
-RRsetPtr example_nsec;
-RRsetPtr www_a;
-RRsetPtr www_nsec;
-RRsetPtr foo_cname;
-RRsetPtr foo_nsec;
-RRsetPtr cnameint_cname;
-RRsetPtr cnameint_nsec;
-RRsetPtr cnameext_cname;
-RRsetPtr cnameext_nsec;
-RRsetPtr dns01_a;
-RRsetPtr dns01_nsec;
-RRsetPtr dns02_a;
-RRsetPtr dns02_nsec;
-RRsetPtr dns03_a;
-RRsetPtr dns03_nsec;
-RRsetPtr wild_a;
-RRsetPtr wild_nsec;
-RRsetPtr wild2_cname;
-RRsetPtr wild2_nsec;
-RRsetPtr wild3_cname;
-RRsetPtr wild3_nsec;
-RRsetPtr dname_dname;
-RRsetPtr dname_nsec;
-RRsetPtr sql1_ns;
-RRsetPtr sql1_soa;
-RRsetPtr sql1_nsec;
-RRsetPtr sql1_ds;
-RRsetPtr sql1_ds_nsec;
-RRsetPtr www_sql1_a;
-RRsetPtr www_sql1_nsec;
-RRsetPtr subzone_ns;
-RRsetPtr subzone_nsec;
-RRsetPtr subzone_glue1;
-RRsetPtr subzone_glue2;
-RRsetPtr subzone_ds;
-RRsetPtr loop1_cname;
-RRsetPtr loop2_cname;
+
+// This is a mock data source for testing.  It can contain multiple zones.
+// The content of each zone should be configured in the form of RRData{}.
+// Each RRData element is a tuple of char strings, representing
+// "name, RRtype, RDATA".  For simplicity we use the same single TTL for
+// RRs (TEST_TTL) defined below.
+// Multiple RRs of the same pair of (name, RRtype) can be defined, but
+// they must not be interleaved with other types of pair.  For example,
+// This is okay:
+// {"example.com", "AAAA", "2001:db8::1"},
+// {"example.com", "AAAA", "2001:db8::2"},
+// ...
+// but this is invalid:
+// {"example.com", "AAAA", "2001:db8::1"},
+// {"example.com", "A", "192.0.2.1"},
+// {"example.com", "AAAA", "2001:db8::2"},
+// ...
+// If an RRset is associated with an RRSIG, the RRSIG must immediately follow
+// the RRset to be signed.  Multiple RRSIGs can follow the RRset.  RRSIG
+// records will always be attached to the most recent non-RRSIG RRset;
+// consequently, the first RR listed must not be an RRSIG record.
+//
+// Names are sorted internally, and don't have to be sorted in the data.
+//
+// A zone is defined in the form of ZoneData{}, which contains:
+// zone name (character string)
+// RRclass (character string)
+// A pointer to in-zone RRs in the form of RRData{}
+// A pointer to glue RRs in the form of RRData{}
+// Glues can be omitted, in which case a convenient constant "empty_records"
+// can be specified.
+
+// For simplicity we use the same single TTL for all test RRs.
+const uint32_t TEST_TTL = 3600;
+
+struct RRData {
+    const char* const name;
+    const char* const rrtype;
+    const char* const rdata;
+};
+
+struct ZoneData {
+    const char* const zone_name;
+    const char* const rrclass;
+    const struct RRData* records;
+    const struct RRData* glue_records;
+};
+
+//
+// zone data for example.com
+//
+const struct RRData example_com_records[] = {
+    // example.com
+    {"example.com", "NS", "dns01.example.com"},
+    {"example.com", "NS", "dns02.example.com"},
+    {"example.com", "NS", "dns03.example.com"},
+    {"example.com", "RRSIG", "NS 5 2 3600 20100322084538 20100220084538 33495 example.com. ClcrfjkQZUY5L6ZlCkU3cJHzcrEGrofKSVeeoeZ+w6yeEowFNVXs2YBo3tom53DiCrdD9rs3feVSLGW5rjsz/O6lDuomgQG+EVSnWa7GTIPBXj1BmDXXp3XxeldYmhf4UzaN5BA+RUA5E8NChNKuNNof76j2S9tilfN/kvpy4fw="},
+    {"example.com", "SOA", "master.example.com. admin.example.com. 1234 3600 1800 2419200 7200"},
+    {"example.com", "RRSIG", "SOA 5 2 3600 20100322084538 20100220084538 33495 example.com.  KUun66Qaw36osk2BJS6U1fAy3PPDkNo2QK4meGNbDBY8q8b+f2o+IXJ14YCvssGl1ORW0CcLnDRxssnk8V/Svmj5iFhO+8HC2hnVBdi2zewvdVtwRb+lWwKN7pkXXwuy6g1t9WCd/j5FCc/wgxqtZUTPb6XgZcnHrORDMOTqLs4="},
+    {"example.com", "NSEC", "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY"},
+    {"example.com", "RRSIG", "NSEC 5 2 7200 20100322084538 20100220084538 33495 example.com. KxuVaPPKNPJzr/q+cJPiNlkHVTQK0LVsgTbSqruXQc25lAd0wn5oKUtxL1bEAchHkfA8eLzcYCj2ZqqAv9OJubw53mfskTad7UHs4Uj2RTrIsNGMCiZGgOpvNb9JcWpQtoyXVT1uNse+Qsbeir0eyeYIufUynFU041jtNrlJMio="},
+    {"example.com", "DNSKEY", "257 3 5 AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJtbvzg62tRx0gkoCDoBI9DPjlOQG0UAbj+xUV4HQZJStJaZ+fHU5AwVNT+bBZdtV+NujSikhdTHb4FYLg2b3Cx9NyJvAVukHp/91HnWuG4T36CzAFrfPwsHIrBz9BsaIQ21VRkcmj7DswfI/iDGd8j6bqiODyNZYQ+ZrLmF0KIJ2yPN3iO6Zq23TaOrVTjB7d1a/h31ODfiHAxFHrkY3t3D5JR9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86AcvRyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGfoIK/aKwENrsjcKZZj660b1M="},
+    {"example.com", "DNSKEY", "256 3 5 AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4qNGV7WcTD0WEiuV7IjXgHE36fCmS9QsUxSSOVo1I/FMxI2PJVqTYHkXFBS7AzLGsQYMU7UjBZSotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g0qcbWYF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx"},
+    {"example.com", "RRSIG", "DNSKEY 5 2 3600 20100416210049 20100317210049 4456 example.com. 37FC0rcwOZVarTMjft0BMbvv8hbJU7OHNsvO7R1q6OgsLTj7QGMX3sC42JGbwUrYI/OwnZblNcv1eim0g0jX5k+sVr2OJsEubngRjVqLo54qV8rBC14tLk9PGKxxjQG0IBJU866uHxzXYBO2a1r2g93/qyTtrT7iPLu/2Ce1WRKMBPK0yf4nW2usFU/PXesXFWpZ7HLGZL73/NWv8wcezBDuU0B2PlHLjSu7k6poq6JWDC02o5SYnEBwsJ5Chi+3/NZmzKTiNP7g0H4t6QhunkEXxL3z0617mwwQt00ypXsNunnPy4Ub5Kllk1SKJl8ZkEDKkJtSvuXJhcAZsLyMQw=="},
+    {"example.com", "RRSIG", "DNSKEY 5 2 3600 20100416210049 20100317210049 33495 example.com. h3OM5r3roBsgnEQk9fcjTg5L7p3yDptDpVzDN/lgjqpaWxtlz5LsulBH3YzwYyXzT7pG7L0/qT6dcuRECc/rniECviWvmJMJZzEAMry0Of/pk/8ekuGTxABpqwAoCwM5as30sc0cfMJTS7umpJVDA4lRB2zoKGefWnJ3+pREDiY="},
+
+    // dns01.example.com
+    {"dns01.example.com", "A", "192.0.2.1"},
+    {"dns01.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. NIawlZLk8WZAjNux7oQM2mslfW52OZFFkWt++7FHu2SU98XqEeKfCMnpgtWe5T8Nr9cS8df901iEOJoWQzGTEaHYUBtEhsSjBVn7mKp3fz6473a2xxy75SUKZ0rxjNXSZ8Q5rnFmkX0HTH2Sg51mtjH6aC2pfheQnA2t193BnSg="},
+    {"dns01.example.com", "NSEC", "dns02.example.com. A RRSIG NSEC"},
+    {"dns01.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. EkyeshmMNP9xiAz6mDFDIwksTdmkF9zsFzLuVKAgK6eUk7St6tp5PSvjA8nWol0vdvvz4LK85a4ffTFEiNRyvWeYP2vOhEkyDcrwuCd8Vc3jh/8Sm1Js+nX7hJStrZGFvp2TWPpt9nKH5p3MxXvTb/YVurnue0xSeFAE17O3+I0="},
+
+    // dns02.example.com
+    {"dns02.example.com", "A", "192.0.2.2"},
+    {"dns02.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. XJtVMbUIRE0mk6Hn/Nx6k36jaxaBDPK2/IYB6vCQjJETz6gW4T6q/H/eY9/Lsw5iYPFhoBRDxT4XFj575t98kELXnJe1WhuMbRPlOhyOjxkLECaUne/sbFPOtbGFx9ohuojI0RgxxZiCFaO8wJuv6nfPuzmlLajWS6z9NZeOMIk="},
+    {"dns02.example.com", "NSEC", "dns03.example.com. A RRSIG NSEC"},
+    {"dns02.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. imBNTMB3sPU4kblcaAH6V7lCVt5xgtAybi3DA/SbLEulLaV2NE6vcoEn/AieaM4mOJicQnUDj/H+1hSEhzxU2tRM8zfVlvztxQWn6eh7ZR4mKfNDSvRUGU9ykhpwMyC7wjOt1j5bcSA/OTnLRAilslnJyOM4bSaxVEFo8YPjncY="},
+
+    // dns03.example.com
+    {"dns03.example.com", "A", "192.0.2.3"},
+    {"dns03.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. Ubrcm1H+F6m8khle7P9zU8eO+Jtuj+1Vx1MM5KAkmZPJwQe9uTcoCpQa6DXOGG9kajDTnNN1Be1gkZuJDTZJG4SmJLXLbNY3RDnxpGmWta3qs/VgDq78/YM8ropt1/s7YKyrCfGE2ff+FUB0mLObiG01ZV2gu5HJzgE7SEWLEiI="},
+    {"dns03.example.com", "NSEC", "foo.example.com. A RRSIG NSEC"},
+    {"dns03.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com.  nn829Xw5CJFnPHwI9WHeT5epQv+odtCkHnjlPFGoPTLOyiks+041UmMqtq3uiSp4d2meMSe9UuDvoROT0L6NTtQQvVqiDhTn0irTFw1uw7fO8ZTG7eyu6Ypfz0+HvfbNvd4kMoD2OTgADRXPVsCTwK+PBOIIG9YTEQfl8pCqW5g="},
+
+    // www.example.com
+    {"www.example.com", "A", "192.0.2.1"},
+    {"www.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. qyFyyV/mE8x4pdhudr5iycwhDsva31MzwO1kBR+bDKvzJg8mN8KxlPZrOlNNUhd3YRXQVwieMyxOTWRPXoxrNEDkNwimXkfe3rrHY7ibV9eNS4OIBUjb44VjCNr9CmQSzfuQ2yxO2r+YIuPYHRCjieD4xh6t9ay4IaCN/tDAJ+Q="},
+    {"www.example.com", "NSEC", "example.com. A RRSIG NSEC"},
+    {"www.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. ZLZlSVBa2oe4U+7SZASnypP2VkI5gg1/1cVGqYUvfYNIUkcVMWDgn7DZCfpmo+2vdlV/4VhAc+sjDd+X+e57XGnW8+lqZHvG6NMMhmSGmeATD3D+8lEJJGo0dxoN4rHJQyp/eT2S4nChz+D/ze+YRagYxGF7pXm9zcrw3kKZGTs="},
+
+    // *.wild.example.com
+    {"*.wild.example.com", "A", "192.0.2.2"},
+    {"*.wild.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. FdO+UWONgtLKFxUzzygGunw67F9y8SzsP7yOLEYVJclRR8X3Ii62L0gtQHq2y0TcKsXttRsD6XY+tM5P/pgXlTNi7Bk4Fgb0PIDPjOsfT4DrS80kWn0YbinM/4/FA1j5ru5sTTboOY5UGhvDnoA9ogNuQQYb2/3wkoH0PrA2Q/0="},
+    {"*.wild.example.com", "NSEC", "*.wild2.example.com. A RRSIG NSEC"},
+    {"*.wild.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. OoGYslRj4xjZnBuzgOqsrvkDAHWycmQzbUxCRmgWnCbXiobJK7/ynONH3jm8G3vGlU0lwpHkhNs6cUK+6Nu8W49X3MT0Xksl/brroLcXYLi3vfxnYUNMMpXdeFl6WNNfoJRo90F/f/TWXAClRrDS29qiG3G1PEJZikIxZsZ0tyM="},
+
+    // *.wild2.example.com
+    {"*.wild2.example.com", "CNAME", "www.example.com"},
+    {"*.wild2.example.com", "RRSIG", "CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI="},
+    {"*.wild2.example.com", "NSEC", "*.wild3.example.com. CNAME RRSIG NSEC"},
+    {"*.wild2.example.com", "RRSIG", "NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="},
+
+    // *.wild3.example.com -- a wildcard record with a lame CNAME
+    {"*.wild3.example.com", "CNAME", "spork.example.com"},
+    {"*.wild3.example.com", "RRSIG", "CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI="},
+    {"*.wild3.example.com", "NSEC", "www.example.com. CNAME RRSIG NSEC"},
+    {"*.wild3.example.com", "RRSIG", "NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="},
+
+    // foo.example.com
+    {"foo.example.com", "CNAME", "cnametest.flame.org"},
+    {"foo.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. DSqkLnsh0gCeCPVW/Q8viy9GNP+KHmFGfWqyVG1S6koBtGN/VQQ16M4PHZ9Zssmf/JcDVJNIhAChHPE2WJiaPCNGTprsaUshf1Q2vMPVnkrJKgDY8SVRYMptmT8eaT0gGri4KhqRoFpMT5OYfesybwDgfhFSQQAh6ps3bIUsy4o="},
+    {"foo.example.com", "NSEC", "mail.example.com. CNAME RRSIG NSEC"},
+    {"foo.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. RTQwlSqui6StUYye1KCSOEr1d3irndWFqHBpwP7g7n+w8EDXJ8I7lYgwzHvlQt6BLAxe5fUDi7ct8M5hXvsm7FoWPZ5wXH+2/eJUCYxIw4vezKMkMwBP6M/YkJ2CMqY8DppYf60QaLDONQAr7AcK/naSyioeI5h6eaoVitUDMso="},
+
+    // cname-int.example.com
+    {"cname-int.example.com", "CNAME", "www.example.com."},
+    {"cname-int.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. U1wjt0XY9xjTwvUmWSUcfLGMhCjfX2ylWfHrycy50x2oxcK9z94E1ejen9wDTIEBSGYgi6wpZ8RK0+02N1DWTGpDqNXd7aFRfDrWQJ/q/XJHDx0vlcmhkWhrT82LBfKxkrptOzchuSo/c0mpK+mpiIMc1VOwY+yuQ2ALfcD6EHw="},
+    {"cname-int.example.com", "NSEC", "dname.example.com. CNAME RRSIG NSEC"},
+    {"cname-int.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. rbV+gaxfrsoha59NOLF4EFyWQ+GuFCVK/8D77x1atan3HNlXBlZ1smgudKTaJ3CtlobIDt0MEdPxY1yn2Tskw/5mlP1PWf8oaP3BwGSQdn4gLI8+sMpNOPFEdXpxqxngm2F6/7fqniL1QuSAQBEdO+5UiCAgnncPmAsSJg3u1zg="},
+
+    // cname-ext.example.com
+    {"cname-ext.example.com", "CNAME", "www.sql1.example.com"},
+    {"cname-ext.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. bGPIuZilyygvTThK4BrdECuaBcnZUgW/0d09iN2CrNjckchQl3dtbnMNirFsVs9hShDSldRNlQpiAVMpnPgXHhReNum7jmX6yqIH6s8GKIo91zr3VL/ramlezie5w4MilDHrxXLK2pb8IHmP+ZHivQ2EtdYQZgETWBWxr5FDfwk="},
+    {"cname-ext.example.com", "NSEC", "cname-int.example.com. CNAME RRSIG NSEC"},
+    {"cname-ext.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. inWsFwSDWG7TakjwbUTzTRpXz0WifelA5Kn3ABk6BVirIPmd+yQoNj2QZBDFAQwhnLPlNws2Oo4vgMsBMyx1Fv5eHgMUuCN3DUDaLlzlPtUb42CjOUa+jZBeTV/Hd7WZrirluASE1QFDprLdSSqoPPfAKvN3pORtW7y580dMOIM="},
+
+    // dname.example.com
+    {"dname.example.com", "DNAME", "sql1.example.com."},
+    {"dname.example.com", "RRSIG", "DNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. ae8U47oaiwWdurkSyzcsCAF6DxBqjukizwF7K7U6lQVMtfoUE14oiAqfj1fjH8YLDOO/Hd1twrd/u0vgjnI1Gg32YTi7cYOzwE912SV1u2B/y0awaQKWPBwOW0aI7vxelt1vMUF81xosiQD04gOIdDBTqbHKcDxum87iWbhk4Ug="},
+    {"dname.example.com", "NSEC", "dns01.example.com. DNAME RRSIG NSEC"},
+    {"dname.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. c21Fff2D8vBrLzohBnUeflkaRdUAnUxAFGp+UQ0miACDCMOFBlCS9v9g/2+orOnKfd3l4vyz55C310t8JXgXb119ofaZWj2zkdUe+X8Bax+sMS0Y5K/sUhSNvbJbozr9UYPdvjSVBiWgh3s9fsb+etKq9uFukAzGU/FuGYpO0r0="},
+
+    // subzone.example.com
+    {"subzone.example.com", "NS", "ns1.subzone.example.com"},
+    {"subzone.example.com", "NS", "ns2.subzone.example.com"},
+    {"subzone.example.com", "NSEC", "*.wild.example.com. NS DS RRSIG NSEC"},
+    {"subzone.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. Oe2kgIhsLtPJ4+lDZDxznV8/vEVoXKOBFN9lwWyebaKa19BaSXlQ+YVejmulmKDDjEucMvEfuItfn6w7bnU+DzOLk5D1lJCjwDlKz8u3xOAx16TiuQn4bgQAOiFtBQygmGGqO3BVpX+jxsmw7eH3emofy8uUqr/C4aopnwuf28g="},
+    {"subzone.example.com", "DS", "33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"},
+    {"subzone.example.com", "DS", "33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"},
+    {"subzone.example.com", "RRSIG", "DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="},
+
+    // subset of child zone: sql1
+    {"sql1.example.com", "NS", "dns01.example.com"},
+    {"sql1.example.com", "NS", "dns02.example.com"},
+    {"sql1.example.com", "NS", "dns03.example.com"},
+
+    {"sql1.example.com", "DS", "33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"},
+    {"sql1.example.com", "DS", "33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"},
+    {"sql1.example.com", "RRSIG", "DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="},
+    {"sql1.example.com", "NSEC", "subzone.example.com. NS DS RRSIG NSEC"},
+    {"sql1.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. k9FRdFyk/cPdkmmaoZbGZPpzIzfbFWQ3QCHd2qhJa0xAXaEOT/GBL6aFqx9SlunDu2wgES+To5fWPZGi4NzWpp6c5t27rnATN/oCEQ/UYIJKmWbqrXdst0Ps5boznk7suK2Y+km31KxaIf3fDd/T3kZCVsR0aWKRRRatPb7GfLw="},
+
+    {NULL, NULL, NULL}
+};
+const struct RRData example_com_glue_records[] = {
+    {"ns1.subzone.example.com", "A", "192.0.2.1"},
+    {"ns2.subzone.example.com", "A", "192.0.2.2"},
+    {NULL, NULL, NULL}
+};
+
+//
+// zone data for sql1.example.com
+//
+const struct RRData sql1_example_com_records[] = {
+    {"sql1.example.com", "NS", "dns01.example.com"},
+    {"sql1.example.com", "NS", "dns02.example.com"},
+    {"sql1.example.com", "NS", "dns03.example.com"},
+    {"sql1.example.com", "RRSIG", "NS 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. 0CL8noy0NSgoWwuKd+Dc6vyIIw2BrAEBx0IJzcSB6GlB25x/zjEd6AJG0be13HN6jOaTX8iWTuCVrEYuXg76V+M4EvTZHjEScj0az74TrDv4Vdo459paGKCX9B8NLJW1mW4fzZrrXQ8jmBEZeS91Q5rJrO+UKJEuUz3LYdTPvao="},
+    {"sql1.example.com", "SOA", "master.example.com. admin.example.com. 678 3600 1800 2419200 7200"},
+    {"sql1.example.com", "RRSIG", "SOA 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. oakulfyljL/RAKgCKXEZ3KsG8BJj5WG4JK4moWFB6c9OKem6jIk8hKP2XlUVXFuOYJlRdIM4KicmR2GAK+5jJp6z5ShssstYTXo3QosVm6oCKumuFeLFHzcjfqP1D+F9NsvHldJIBnS/4ebPkmR5OENyCZXQF5HmN2awIj4CLjE="},
+    {"sql1.example.com", "NSEC", "www.sql1.example.com. NS SOA RRSIG NSEC DNSKEY"},
+    {"sql1.example.com", "RRSIG", "NSEC 5 3 7200 20100322084536 20100220084536 12447 sql1.example.com. v71CgdTYccCiTqfRcn6HsvISQa8ruvUfCKtpwym0RW/G27xlZn8otj2IMtWwkLxti8Rqqu+PTViLaOIbeVfHBcqzAd7U59cAOYoq3ODZx6auiE3C23HAKqUavKcP7Esaajm1cbcWy6Kyie4CAZc8M7EeKxgkXMKJGqBQzF+/FOo="},
+
+    // www.sql1.example.com
+    {"www.sql1.example.com", "A", "192.0.2.2"},
+    {"www.sql1.example.com", "RRSIG", "A 5 4 3600 20100322084536 20100220084536 12447 sql1.example.com. DNdVKxB3oBsB14NPoV9WG14Y/g4zMcIXLYnFjj9vRZRZJpAvbTEipiXlayuhOxnqU827OipETQyeULZmLsqIQ1wK4Fgf+9b5aJ8D85/o4wBka00X4hZ3MwDPRb4mjuogwBTBg5NRpNSzUfbkPGiav08BFwgg+Efm9veSB05arS0="},
+    {"www.sql1.example.com", "NSEC", "sql1.example.com. A RRSIG NSEC"},
+    {"www.sql1.example.com", "RRSIG", "NSEC 5 4 7200 20100322084536 20100220084536 12447 sql1.example.com. cJMJhDx/ND7/9j3zhyXe+6eaSsU7ByYpXhJzbe+OhjFgH0VasQXq7o1QB3I293UZ+yhkjgXap+9QtPlraaNaYyTyOMQ42OoxSefJpYz9CME/FI2tsUfyrCnLFxYRNet7sMS0q+hLqxRayuEHDFDp72hHPGLJQ8a7jq4SrIonT50="},
+
+    {NULL, NULL, NULL}
+};
+
+//
+// zone data for loop.example
+//
+const struct RRData loop_example_records[] = {
+    {"loop.example", "SOA", "master.loop.example admin.loop.example. "
+     "1234 3600 1800 2419200 7200"},
+    {"loop.example", "NS", "ns.loop.example"},
+    {"one.loop.example", "CNAME", "two.loop.example"},
+    {"two.loop.example", "CNAME", "one.loop.example"},
+    {NULL, NULL, NULL}
+};
+
+//
+// zone data for nons.example
+//
+const struct RRData nons_example_records[] = {
+    {"nons.example", "SOA", "master.nons.example admin.nons.example. "
+     "1234 3600 1800 2419200 7200"},
+    {"www.nons.example", "A", "192.0.2.1"},
+    {"ns.nons.example", "A", "192.0.2.2"},
+    {NULL, NULL, NULL}
+};
+
+//
+// zone data for nons-dname.example
+//
+const struct RRData nonsdname_example_records[] = {
+    {"nons-dname.example", "SOA", "master.nons-dname.example "
+     "admin.nons-dname.example. 1234 3600 1800 2419200 7200"},
+    {"nons-dname.example", "DNAME", "example.org"},
+    {"www.nons-dname.example", "A", "192.0.2.1"},
+    {"ns.nons-dname.example", "A", "192.0.2.2"},
+    {NULL, NULL, NULL}
+};
+
+//
+// zone data for nosoa.example
+//
+const struct RRData nosoa_example_records[] = {
+    {"nosoa.example", "NS", "ns.nosoa.example"},
+    {"www.nosoa.example", "A", "192.0.2.1"},
+    {"ns.nosoa.example", "A", "192.0.2.2"},
+    {NULL, NULL, NULL}
+};
+
+//
+// empty data set, for convenience.
+//
+const struct RRData empty_records[] = {
+    {NULL, NULL, NULL}
+};
+
+//
+// test zones
+//
+const struct ZoneData zone_data[] = {
+    { "example.com", "IN", example_com_records, example_com_glue_records },
+    { "sql1.example.com", "IN", sql1_example_com_records, empty_records },
+    { "loop.example", "IN", loop_example_records, empty_records },
+    { "nons.example", "IN", nons_example_records, empty_records },
+    { "nons-dname.example", "IN", nonsdname_example_records, empty_records },
+    { "nosoa.example", "IN", nosoa_example_records, empty_records }
+};
+const size_t NUM_ZONES = sizeof(zone_data) / sizeof(zone_data[0]);
+
+struct Zone {
+    Zone(const char* const name, const char* const class_txt) :
+        zone_name(Name(name)), rrclass(class_txt)
+    {}
+    Name zone_name;
+    RRClass rrclass;
+    vector<Name> names;
+    vector<RRsetPtr> rrsets;
+};
+vector<Zone> zones;
 }
 
 DataSrc::Result
 TestDataSrc::init(const isc::data::ElementPtr config UNUSED_PARAM)
 {
     return init();
+}
+
+void
+buildZone(Zone& zone, const RRData* records, const bool is_glue) {
+    RRsetPtr prev_rrset;
+    for (int i = 0; records[i].name != NULL; ++i) {
+        Name name(records[i].name);
+        RRType rrtype(records[i].rrtype);
+        RRsetPtr rrset;
+        bool new_name = false;
+
+        if (!prev_rrset || prev_rrset->getName() != name) {
+            if (!is_glue) {
+                zone.names.push_back(name);
+            }
+            new_name = true;
+        }
+
+        if (new_name || prev_rrset->getType() != rrtype) {
+            rrset = RRsetPtr(new RRset(name, zone.rrclass, rrtype,
+                                       RRTTL(TEST_TTL)));
+            if (rrtype != RRType::RRSIG()) {
+                zone.rrsets.push_back(rrset);
+            }
+        } else {
+            rrset = prev_rrset;
+        }
+        rrset->addRdata(createRdata(rrtype, zone.rrclass, records[i].rdata));
+        if (rrtype == RRType::RRSIG()) {
+            prev_rrset->addRRsig(rrset);
+        } else {
+            prev_rrset = rrset;
+        }
+    }
 }
 
 DataSrc::Result
@@ -116,630 +352,174 @@
         return (SUCCESS);
     }
 
-    RRset* rp;
-    RRsetPtr rrsig;
-
-    // example.com
-    example_ns = RRsetPtr(new RRset(example, RRClass::IN(),
-                                    RRType::NS(), RRTTL(3600)));
-    example_ns->addRdata(generic::NS(dns01));
-    example_ns->addRdata(generic::NS(dns02));
-    example_ns->addRdata(generic::NS(dns03));
-
-    rp = new RRset(example, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
-    rrsig = RRsetPtr(rp);
-    rrsig->addRdata(generic::RRSIG("NS 5 2 3600 20100322084538 20100220084538 33495 example.com. ClcrfjkQZUY5L6ZlCkU3cJHzcrEGrofKSVeeoeZ+w6yeEowFNVXs2YBo3tom53DiCrdD9rs3feVSLGW5rjsz/O6lDuomgQG+EVSnWa7GTIPBXj1BmDXXp3XxeldYmhf4UzaN5BA+RUA5E8NChNKuNNof76j2S9tilfN/kvpy4fw="));
-    example_ns->addRRsig(rrsig);
-
-    example_soa = RRsetPtr(new RRset(example, RRClass::IN(),
-                                    RRType::SOA(), RRTTL(3600)));
-    example_soa->addRdata(generic::SOA("master.example.com. admin.example.com. 1234 3600 1800 2419200 7200"));
-
-    rrsig = RRsetPtr(new RRset(example, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("SOA 5 2 3600 20100322084538 20100220084538 33495 example.com.  KUun66Qaw36osk2BJS6U1fAy3PPDkNo2QK4meGNbDBY8q8b+f2o+IXJ14YCvssGl1ORW0CcLnDRxssnk8V/Svmj5iFhO+8HC2hnVBdi2zewvdVtwRb+lWwKN7pkXXwuy6g1t9WCd/j5FCc/wgxqtZUTPb6XgZcnHrORDMOTqLs4="));
-    example_soa->addRRsig(rrsig);
-
-    example_nsec = RRsetPtr(new RRset(example, RRClass::IN(),
-                                      RRType::NSEC(), RRTTL(3600)));
-    example_nsec->addRdata(generic::NSEC("cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY"));
-    rrsig = RRsetPtr(new RRset(example, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NSEC 5 2 7200 20100322084538 20100220084538 33495 example.com. KxuVaPPKNPJzr/q+cJPiNlkHVTQK0LVsgTbSqruXQc25lAd0wn5oKUtxL1bEAchHkfA8eLzcYCj2ZqqAv9OJubw53mfskTad7UHs4Uj2RTrIsNGMCiZGgOpvNb9JcWpQtoyXVT1uNse+Qsbeir0eyeYIufUynFU041jtNrlJMio="));
-    example_nsec->addRRsig(rrsig);
-
-    // sql1.example.com
-    sql1_ns = RRsetPtr(new RRset(sql1, RRClass::IN(),
-                                 RRType::NS(), RRTTL(3600)));
-    sql1_ns->addRdata(generic::NS(dns01));
-    sql1_ns->addRdata(generic::NS(dns02));
-    sql1_ns->addRdata(generic::NS(dns03));
-
-    rrsig = RRsetPtr(new RRset(sql1, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. 0CL8noy0NSgoWwuKd+Dc6vyIIw2BrAEBx0IJzcSB6GlB25x/zjEd6AJG0be13HN6jOaTX8iWTuCVrEYuXg76V+M4EvTZHjEScj0az74TrDv4Vdo459paGKCX9B8NLJW1mW4fzZrrXQ8jmBEZeS91Q5rJrO+UKJEuUz3LYdTPvao="));
-    sql1_ns->addRRsig(rrsig);
-
-    sql1_soa = RRsetPtr(new RRset(sql1, RRClass::IN(),
-                                 RRType::SOA(), RRTTL(3600)));
-    sql1_soa->addRdata(generic::SOA("master.example.com. admin.example.com. 678 3600 1800 2419200 7200"));
-
-    rrsig = RRsetPtr(new RRset(sql1, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("SOA 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. oakulfyljL/RAKgCKXEZ3KsG8BJj5WG4JK4moWFB6c9OKem6jIk8hKP2XlUVXFuOYJlRdIM4KicmR2GAK+5jJp6z5ShssstYTXo3QosVm6oCKumuFeLFHzcjfqP1D+F9NsvHldJIBnS/4ebPkmR5OENyCZXQF5HmN2awIj4CLjE="));
-    sql1_soa->addRRsig(rrsig);
-
-    sql1_nsec = RRsetPtr(new RRset(sql1, RRClass::IN(),
-                                      RRType::NSEC(), RRTTL(3600)));
-    sql1_nsec->addRdata(generic::NSEC("www.sql1.example.com. NS SOA RRSIG NSEC DNSKEY"));
-    rrsig = RRsetPtr(new RRset(sql1, RRClass::IN(), RRType::RRSIG(), RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084536 20100220084536 12447 sql1.example.com. v71CgdTYccCiTqfRcn6HsvISQa8ruvUfCKtpwym0RW/G27xlZn8otj2IMtWwkLxti8Rqqu+PTViLaOIbeVfHBcqzAd7U59cAOYoq3ODZx6auiE3C23HAKqUavKcP7Esaajm1cbcWy6Kyie4CAZc8M7EeKxgkXMKJGqBQzF+/FOo="));
-    sql1_nsec->addRRsig(rrsig);
-    sql1_ds = RRsetPtr(new RRset(sql1, RRClass::IN(),
-                                 RRType::DS(), RRTTL(3600)));
-    sql1_ds->addRdata(generic::DS("33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"));
-    sql1_ds->addRdata(generic::DS("33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"));
-
-    rrsig = RRsetPtr(new RRset(sql1, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="));
-    sql1_ds->addRRsig(rrsig);
-
-
-    sql1_ds_nsec = RRsetPtr(new RRset(sql1, RRClass::IN(),
-                                   RRType::NSEC(), RRTTL(3600)));
-    sql1_ds_nsec->addRdata(generic::NSEC("subzone.example.com. NS DS RRSIG NSEC"));
-    rrsig = RRsetPtr(new RRset(sql1, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. k9FRdFyk/cPdkmmaoZbGZPpzIzfbFWQ3QCHd2qhJa0xAXaEOT/GBL6aFqx9SlunDu2wgES+To5fWPZGi4NzWpp6c5t27rnATN/oCEQ/UYIJKmWbqrXdst0Ps5boznk7suK2Y+km31KxaIf3fDd/T3kZCVsR0aWKRRRatPb7GfLw="));
-    sql1_ds_nsec->addRRsig(rrsig);
-
-    // www.sql1.example.com
-    www_sql1_a = RRsetPtr(new RRset(www_sql1,
-                                    RRClass::IN(), RRType::A(),
-                                    RRTTL(3600)));
-    www_sql1_a->addRdata(in::A("192.0.2.2"));
-
-    rrsig = RRsetPtr(new RRset(www_sql1, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("A 5 4 3600 20100322084536 20100220084536 12447 sql1.example.com. DNdVKxB3oBsB14NPoV9WG14Y/g4zMcIXLYnFjj9vRZRZJpAvbTEipiXlayuhOxnqU827OipETQyeULZmLsqIQ1wK4Fgf+9b5aJ8D85/o4wBka00X4hZ3MwDPRb4mjuogwBTBg5NRpNSzUfbkPGiav08BFwgg+Efm9veSB05arS0="));
-    www_sql1_a->addRRsig(rrsig);
-
-    www_sql1_nsec = RRsetPtr(new RRset(www_sql1,
-                                       RRClass::IN(), RRType::NSEC(),
-                                       RRTTL(3600)));
-    www_sql1_nsec->addRdata(generic::NSEC("sql1.example.com. A RRSIG NSEC"));
-    rrsig = RRsetPtr(new RRset(www_sql1, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NSEC 5 4 7200 20100322084536 20100220084536 12447 sql1.example.com. cJMJhDx/ND7/9j3zhyXe+6eaSsU7ByYpXhJzbe+OhjFgH0VasQXq7o1QB3I293UZ+yhkjgXap+9QtPlraaNaYyTyOMQ42OoxSefJpYz9CME/FI2tsUfyrCnLFxYRNet7sMS0q+hLqxRayuEHDFDp72hHPGLJQ8a7jq4SrIonT50="));
-    www_sql1_nsec->addRRsig(rrsig);
-
-    // dns01.example.com
-    dns01_a = RRsetPtr(new RRset(dns01,
-                                         RRClass::IN(), RRType::A(),
-                                         RRTTL(3600)));
-    dns01_a->addRdata(in::A("192.0.2.1"));
-
-    rrsig = RRsetPtr(new RRset(dns01, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. NIawlZLk8WZAjNux7oQM2mslfW52OZFFkWt++7FHu2SU98XqEeKfCMnpgtWe5T8Nr9cS8df901iEOJoWQzGTEaHYUBtEhsSjBVn7mKp3fz6473a2xxy75SUKZ0rxjNXSZ8Q5rnFmkX0HTH2Sg51mtjH6aC2pfheQnA2t193BnSg="));
-    dns01_a->addRRsig(rrsig);
-
-    dns01_nsec = RRsetPtr(new RRset(dns01, RRClass::IN(), RRType::NSEC(), RRTTL(3600)));
-    dns01_nsec->addRdata(generic::NSEC("dns02.example.com. A RRSIG NSEC"));
-
-    rrsig = RRsetPtr(new RRset(dns01, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. EkyeshmMNP9xiAz6mDFDIwksTdmkF9zsFzLuVKAgK6eUk7St6tp5PSvjA8nWol0vdvvz4LK85a4ffTFEiNRyvWeYP2vOhEkyDcrwuCd8Vc3jh/8Sm1Js+nX7hJStrZGFvp2TWPpt9nKH5p3MxXvTb/YVurnue0xSeFAE17O3+I0="));
-    dns01_nsec->addRRsig(rrsig);
-
-    // dns02.example.com
-    dns02_a = RRsetPtr(new RRset(dns02, RRClass::IN(), RRType::A(), RRTTL(3600)));
-    dns02_a->addRdata(in::A("192.0.2.2"));
-
-    rrsig = RRsetPtr(new RRset(dns02, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. XJtVMbUIRE0mk6Hn/Nx6k36jaxaBDPK2/IYB6vCQjJETz6gW4T6q/H/eY9/Lsw5iYPFhoBRDxT4XFj575t98kELXnJe1WhuMbRPlOhyOjxkLECaUne/sbFPOtbGFx9ohuojI0RgxxZiCFaO8wJuv6nfPuzmlLajWS6z9NZeOMIk="));
-    dns02_a->addRRsig(rrsig);
-
-    dns02_nsec = RRsetPtr(new RRset(dns02, RRClass::IN(),
-                                      RRType::NSEC(), RRTTL(3600)));
-    dns02_nsec->addRdata(generic::NSEC("dns03.example.com. A RRSIG NSEC"));
-    rrsig = RRsetPtr(new RRset(dns02, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. imBNTMB3sPU4kblcaAH6V7lCVt5xgtAybi3DA/SbLEulLaV2NE6vcoEn/AieaM4mOJicQnUDj/H+1hSEhzxU2tRM8zfVlvztxQWn6eh7ZR4mKfNDSvRUGU9ykhpwMyC7wjOt1j5bcSA/OTnLRAilslnJyOM4bSaxVEFo8YPjncY="));
-    dns02_nsec->addRRsig(rrsig);
-
-    // dns03.example.com
-    dns03_a = RRsetPtr(new RRset(dns03,
-                                         RRClass::IN(), RRType::A(),
-                                         RRTTL(3600)));
-    dns03_a->addRdata(in::A("192.0.2.3"));
-
-    rrsig = RRsetPtr(new RRset(dns03, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. Ubrcm1H+F6m8khle7P9zU8eO+Jtuj+1Vx1MM5KAkmZPJwQe9uTcoCpQa6DXOGG9kajDTnNN1Be1gkZuJDTZJG4SmJLXLbNY3RDnxpGmWta3qs/VgDq78/YM8ropt1/s7YKyrCfGE2ff+FUB0mLObiG01ZV2gu5HJzgE7SEWLEiI="));
-    dns03_a->addRRsig(rrsig);
-
-    dns03_nsec = RRsetPtr(new RRset(dns03, RRClass::IN(),
-                                      RRType::NSEC(), RRTTL(3600)));
-    dns03_nsec->addRdata(generic::NSEC("foo.example.com. A RRSIG NSEC"));
-    rrsig = RRsetPtr(new RRset(dns03, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com.  nn829Xw5CJFnPHwI9WHeT5epQv+odtCkHnjlPFGoPTLOyiks+041UmMqtq3uiSp4d2meMSe9UuDvoROT0L6NTtQQvVqiDhTn0irTFw1uw7fO8ZTG7eyu6Ypfz0+HvfbNvd4kMoD2OTgADRXPVsCTwK+PBOIIG9YTEQfl8pCqW5g="));
-    dns03_nsec->addRRsig(rrsig);
-
-    // www.example.com
-    www_a = RRsetPtr(new RRset(www, RRClass::IN(), RRType::A(),
-                                       RRTTL(3600)));
-    www_a->addRdata(in::A("192.0.2.1"));
-
-    rrsig = RRsetPtr(new RRset(www, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. qyFyyV/mE8x4pdhudr5iycwhDsva31MzwO1kBR+bDKvzJg8mN8KxlPZrOlNNUhd3YRXQVwieMyxOTWRPXoxrNEDkNwimXkfe3rrHY7ibV9eNS4OIBUjb44VjCNr9CmQSzfuQ2yxO2r+YIuPYHRCjieD4xh6t9ay4IaCN/tDAJ+Q="));
-    www_a->addRRsig(rrsig);
-
-    www_nsec = RRsetPtr(new RRset(www, RRClass::IN(),
-                                  RRType::NSEC(), RRTTL(3600)));
-    www_nsec->addRdata(generic::NSEC("example.com. A RRSIG NSEC"));
-    rrsig = RRsetPtr(new RRset(www, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. ZLZlSVBa2oe4U+7SZASnypP2VkI5gg1/1cVGqYUvfYNIUkcVMWDgn7DZCfpmo+2vdlV/4VhAc+sjDd+X+e57XGnW8+lqZHvG6NMMhmSGmeATD3D+8lEJJGo0dxoN4rHJQyp/eT2S4nChz+D/ze+YRagYxGF7pXm9zcrw3kKZGTs="));
-    www_nsec->addRRsig(rrsig);
-
-    // *.wild.example.com
-    wild_a = RRsetPtr(new RRset(wild, RRClass::IN(), RRType::A(),
-                                        RRTTL(3600)));
-    wild_a->addRdata(in::A("192.0.2.2"));
-
-    rrsig = RRsetPtr(new RRset(wild, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. FdO+UWONgtLKFxUzzygGunw67F9y8SzsP7yOLEYVJclRR8X3Ii62L0gtQHq2y0TcKsXttRsD6XY+tM5P/pgXlTNi7Bk4Fgb0PIDPjOsfT4DrS80kWn0YbinM/4/FA1j5ru5sTTboOY5UGhvDnoA9ogNuQQYb2/3wkoH0PrA2Q/0="));
-    wild_a->addRRsig(rrsig);
-
-    wild_nsec = RRsetPtr(new RRset(wild, RRClass::IN(),
-                                   RRType::NSEC(), RRTTL(3600)));
-    wild_nsec->addRdata(generic::NSEC("*.wild2.example.com. A RRSIG NSEC"));
-
-    rrsig = RRsetPtr(new RRset(wild, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. OoGYslRj4xjZnBuzgOqsrvkDAHWycmQzbUxCRmgWnCbXiobJK7/ynONH3jm8G3vGlU0lwpHkhNs6cUK+6Nu8W49X3MT0Xksl/brroLcXYLi3vfxnYUNMMpXdeFl6WNNfoJRo90F/f/TWXAClRrDS29qiG3G1PEJZikIxZsZ0tyM="));
-    wild_nsec->addRRsig(rrsig);
-
-    // *.wild2.example.com
-    wild2_cname = RRsetPtr(new RRset(wild2, RRClass::IN(), RRType::CNAME(),
-                                     RRTTL(3600)));
-    wild2_cname->addRdata(generic::CNAME("www.example.com"));
-
-    rrsig = RRsetPtr(new RRset(wild2, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI="));
-    wild2_cname->addRRsig(rrsig);
-
-    wild2_nsec = RRsetPtr(new RRset(wild2, RRClass::IN(),
-                                    RRType::NSEC(), RRTTL(3600)));
-    wild2_nsec->addRdata(generic::NSEC("*.wild3.example.com. CNAME RRSIG NSEC"));
-
-    rrsig = RRsetPtr(new RRset(wild2, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="));
-    wild2_nsec->addRRsig(rrsig);
-
-    // *.wild3.example.com -- a wildcard record with a lame CNAME
-    wild3_cname = RRsetPtr(new RRset(wild3, RRClass::IN(), RRType::CNAME(),
-                                     RRTTL(3600)));
-    wild3_cname->addRdata(generic::CNAME("spork.example.com"));
-
-    rrsig = RRsetPtr(new RRset(wild3, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI="));
-    wild3_cname->addRRsig(rrsig);
-
-    wild3_nsec = RRsetPtr(new RRset(wild3, RRClass::IN(),
-                                    RRType::NSEC(), RRTTL(3600)));
-    wild3_nsec->addRdata(generic::NSEC("www.example.com. CNAME RRSIG NSEC"));
-
-    rrsig = RRsetPtr(new RRset(wild3, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="));
-    wild3_nsec->addRRsig(rrsig);
-
-    // foo.example.com
-    foo_cname = RRsetPtr(new RRset(foo, RRClass::IN(), RRType::CNAME(),
-                                           RRTTL(3600)));
-    foo_cname->addRdata(generic::CNAME("cnametest.flame.org"));
-
-    rrsig = RRsetPtr(new RRset(foo, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. DSqkLnsh0gCeCPVW/Q8viy9GNP+KHmFGfWqyVG1S6koBtGN/VQQ16M4PHZ9Zssmf/JcDVJNIhAChHPE2WJiaPCNGTprsaUshf1Q2vMPVnkrJKgDY8SVRYMptmT8eaT0gGri4KhqRoFpMT5OYfesybwDgfhFSQQAh6ps3bIUsy4o="));
-    foo_cname->addRRsig(rrsig);
-
-    foo_nsec = RRsetPtr(new RRset(foo, RRClass::IN(),
-                                  RRType::NSEC(), RRTTL(3600)));
-    foo_nsec->addRdata(generic::NSEC("mail.example.com. CNAME RRSIG NSEC"));
-    rrsig = RRsetPtr(new RRset(foo, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. RTQwlSqui6StUYye1KCSOEr1d3irndWFqHBpwP7g7n+w8EDXJ8I7lYgwzHvlQt6BLAxe5fUDi7ct8M5hXvsm7FoWPZ5wXH+2/eJUCYxIw4vezKMkMwBP6M/YkJ2CMqY8DppYf60QaLDONQAr7AcK/naSyioeI5h6eaoVitUDMso="));
-    foo_nsec->addRRsig(rrsig);
-
-    // cname-int.example.com
-    cnameint_cname = RRsetPtr(new RRset(cnameint, RRClass::IN(),
-                                        RRType::CNAME(), RRTTL(3600)));
-    cnameint_cname->addRdata(generic::CNAME("www.example.com."));
-
-    rrsig = RRsetPtr(new RRset(cnameint, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. U1wjt0XY9xjTwvUmWSUcfLGMhCjfX2ylWfHrycy50x2oxcK9z94E1ejen9wDTIEBSGYgi6wpZ8RK0+02N1DWTGpDqNXd7aFRfDrWQJ/q/XJHDx0vlcmhkWhrT82LBfKxkrptOzchuSo/c0mpK+mpiIMc1VOwY+yuQ2ALfcD6EHw="));
-    cnameint_cname->addRRsig(rrsig);
-
-    cnameint_nsec = RRsetPtr(new RRset(cnameint, RRClass::IN(),
-                                      RRType::NSEC(), RRTTL(3600)));
-    cnameint_nsec->addRdata(generic::NSEC("dname.example.com. CNAME RRSIG NSEC"));
-    rrsig = RRsetPtr(new RRset(cnameint, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. rbV+gaxfrsoha59NOLF4EFyWQ+GuFCVK/8D77x1atan3HNlXBlZ1smgudKTaJ3CtlobIDt0MEdPxY1yn2Tskw/5mlP1PWf8oaP3BwGSQdn4gLI8+sMpNOPFEdXpxqxngm2F6/7fqniL1QuSAQBEdO+5UiCAgnncPmAsSJg3u1zg="));
-    cnameint_nsec->addRRsig(rrsig);
-
-    // cname-ext.example.com
-    cnameext_cname = RRsetPtr(new RRset(cnameext, RRClass::IN(),
-                                        RRType::CNAME(), RRTTL(3600)));
-    cnameext_cname->addRdata(generic::CNAME("www.sql1.example.com"));
-
-    rrsig = RRsetPtr(new RRset(cnameext, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. bGPIuZilyygvTThK4BrdECuaBcnZUgW/0d09iN2CrNjckchQl3dtbnMNirFsVs9hShDSldRNlQpiAVMpnPgXHhReNum7jmX6yqIH6s8GKIo91zr3VL/ramlezie5w4MilDHrxXLK2pb8IHmP+ZHivQ2EtdYQZgETWBWxr5FDfwk="));
-    cnameext_cname->addRRsig(rrsig);
-
-    cnameext_nsec = RRsetPtr(new RRset(cnameext, RRClass::IN(),
-                                      RRType::NSEC(), RRTTL(3600)));
-    cnameext_nsec->addRdata(generic::NSEC("cname-int.example.com. CNAME RRSIG NSEC"));
-    rrsig = RRsetPtr(new RRset(cnameext, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. inWsFwSDWG7TakjwbUTzTRpXz0WifelA5Kn3ABk6BVirIPmd+yQoNj2QZBDFAQwhnLPlNws2Oo4vgMsBMyx1Fv5eHgMUuCN3DUDaLlzlPtUb42CjOUa+jZBeTV/Hd7WZrirluASE1QFDprLdSSqoPPfAKvN3pORtW7y580dMOIM="));
-    cnameext_nsec->addRRsig(rrsig);
-
-    // dname.example.com
-    dname_dname = RRsetPtr(new RRset(dname, RRClass::IN(), RRType::DNAME(),
-                               RRTTL(3600)));
-    dname_dname->addRdata(generic::DNAME("sql1.example.com."));
-
-    rrsig = RRsetPtr(new RRset(dname, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("DNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. ae8U47oaiwWdurkSyzcsCAF6DxBqjukizwF7K7U6lQVMtfoUE14oiAqfj1fjH8YLDOO/Hd1twrd/u0vgjnI1Gg32YTi7cYOzwE912SV1u2B/y0awaQKWPBwOW0aI7vxelt1vMUF81xosiQD04gOIdDBTqbHKcDxum87iWbhk4Ug="));
-    dname_dname->addRRsig(rrsig);
-
-    dname_nsec = RRsetPtr(new RRset(dname, RRClass::IN(),
-                                      RRType::NSEC(), RRTTL(3600)));
-    dname_nsec->addRdata(generic::NSEC("dns01.example.com. DNAME RRSIG NSEC"));
-    rrsig = RRsetPtr(new RRset(dname, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. c21Fff2D8vBrLzohBnUeflkaRdUAnUxAFGp+UQ0miACDCMOFBlCS9v9g/2+orOnKfd3l4vyz55C310t8JXgXb119ofaZWj2zkdUe+X8Bax+sMS0Y5K/sUhSNvbJbozr9UYPdvjSVBiWgh3s9fsb+etKq9uFukAzGU/FuGYpO0r0="));
-    dname_nsec->addRRsig(rrsig);
-
-    // subzone.example.com
-    subzone_ns = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::NS(),
-                                    RRTTL(3600)));
-    subzone_ns->addRdata(generic::NS(Name("ns1.subzone.example.com")));
-    subzone_ns->addRdata(generic::NS(Name("ns2.subzone.example.com")));
-
-    subzone_ds = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::DS(),
-                                    RRTTL(3600)));
-
-    subzone_glue1 = RRsetPtr(new RRset(Name("ns1.subzone.example.com"),
-                                    RRClass::IN(), RRType::A(),
-                                    RRTTL(3600)));
-    subzone_glue1->addRdata(in::A("192.0.2.1"));
-    subzone_glue2 = RRsetPtr(new RRset(Name("ns2.subzone.example.com"),
-                                    RRClass::IN(), RRType::A(),
-                                    RRTTL(3600)));
-    subzone_glue2->addRdata(in::A("192.0.2.2"));
-
-    subzone_ds = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::DS(),
-                                    RRTTL(3600)));
-
-    subzone_ds->addRdata(generic::DS("33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"));
-    subzone_ds->addRdata(generic::DS("33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"));
-
-    rrsig = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::RRSIG(),
-                               RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="));
-    subzone_ds->addRRsig(rrsig);
-
-    subzone_nsec = RRsetPtr(new RRset(subzone, RRClass::IN(),
-                                      RRType::NSEC(), RRTTL(3600)));
-    subzone_nsec->addRdata(generic::NSEC("*.wild.example.com. NS DS RRSIG NSEC"));
-    rrsig = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::RRSIG(), RRTTL(3600)));
-    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. Oe2kgIhsLtPJ4+lDZDxznV8/vEVoXKOBFN9lwWyebaKa19BaSXlQ+YVejmulmKDDjEucMvEfuItfn6w7bnU+DzOLk5D1lJCjwDlKz8u3xOAx16TiuQn4bgQAOiFtBQygmGGqO3BVpX+jxsmw7eH3emofy8uUqr/C4aopnwuf28g="));
-    subzone_nsec->addRRsig(rrsig);
-
-    loop1_cname = RRsetPtr(new RRset(loop1, RRClass::IN(), RRType::CNAME(),
-                                     RRTTL(3600)));
-    loop1_cname->addRdata(generic::CNAME(loop2));
-    loop2_cname = RRsetPtr(new RRset(loop2, RRClass::IN(), RRType::CNAME(),
-                                     RRTTL(3600)));
-    loop2_cname->addRdata(generic::CNAME(loop1));
+    if (zones.empty()) {
+        for (int i = 0; i < NUM_ZONES; ++i) {
+            Zone zone(zone_data[i].zone_name, zone_data[i].rrclass);
+            buildZone(zone, zone_data[i].records, false);
+            buildZone(zone, zone_data[i].glue_records, true);
+            sort(zone.names.begin(), zone.names.end());
+            zones.push_back(zone);
+        }
+    }
 
     initialized = true;
     return (SUCCESS);
 }
 
 void
-TestDataSrc::findClosestEnclosure(NameMatch& match,
-                                  const RRClass& qclass) const {
-    const Name& qname = match.qname();
-    NameComparisonResult::NameRelation cmp;
-
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
+TestDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
+    const Name& qname = match.getName();
+
+    if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
         return;
     }
 
-    cmp = qname.compare(sql1).getRelation();
-    if (cmp == NameComparisonResult::EQUAL ||
-          cmp == NameComparisonResult::SUBDOMAIN) {
-        match.update(*this, sql1);
-        return;
-    }
-
-    cmp = qname.compare(example).getRelation();
-    if (cmp == NameComparisonResult::EQUAL ||
-          cmp == NameComparisonResult::SUBDOMAIN) {
-        match.update(*this, example);
-        return;
-    }
-
-}
+    vector<Zone>::const_iterator it;
+    vector<Zone>::const_iterator best_it = zones.end();
+    unsigned int best_common_labels = 0;
+    for (it = zones.begin(); it != zones.end(); ++it) {
+        const NameComparisonResult cmp = qname.compare(it->zone_name);
+        const NameComparisonResult::NameRelation reln = cmp.getRelation();
+
+        if ((reln == NameComparisonResult::EQUAL ||
+             reln == NameComparisonResult::SUBDOMAIN) &&
+            cmp.getCommonLabels() > best_common_labels) {
+            best_it = it;
+            best_common_labels = cmp.getCommonLabels();
+        }
+    }
+
+    if (best_it != zones.end()) {
+        match.update(*this, best_it->zone_name);
+    }
+}
+
+struct ZoneNameMatch : public unary_function<Name, bool> {
+    ZoneNameMatch(const Name& name) : name_(name) {}
+    bool operator()(const Zone& zone) const {
+        return (zone.zone_name == name_);
+    }
+    const Name& name_;
+};
+
+// XXX: the main data source module can override the returned RRset.
+// That's bad and should be fixed (Trac #254), but for now we work around it.
+RRsetPtr
+copyRRset(RRsetPtr const source) {
+    RRsetPtr rrset = RRsetPtr(new RRset(source->getName(), source->getClass(),
+                                        source->getType(), source->getTTL()));
+    RdataIteratorPtr it = source->getRdataIterator();
+    for (it->first(); !it->isLast(); it->next()) {
+        rrset->addRdata(it->getCurrent());
+    }
+    if (source->getRRsig()) {
+        rrset->addRRsig(copyRRset(source->getRRsig()));
+    }
+
+    return (rrset);
+}
+
+class TestDataSrc::RRsetMatch {
+public:
+    struct MatchResult {
+        MatchResult(const bool name_found, const bool has_delegation) :
+            name_found_(name_found), has_delegation_(has_delegation)
+        {}
+        bool name_found_;
+        bool has_delegation_;
+    };
+    RRsetMatch(const Name& name, const RRType& rrtype, const Mode mode,
+               RRsetList& target, uint32_t& flags) :
+        name_(name), rrtype_(rrtype), mode_(mode), target_(target),
+        flags_(flags), name_found_(false), has_delegation_(false)
+    {}
+    void operator()(const RRsetPtr& rrset) {
+        if (rrset->getName() != name_) {
+            return;
+        }
+        name_found_ = true;
+
+        if (rrset->getType() == RRType::NS() ||
+            rrset->getType() == RRType::DNAME()) {
+            has_delegation_ = true;
+        }
+
+        if (mode_ == DELEGATION) {
+            if (rrset->getType() == RRType::NS() ||
+                rrset->getType() == RRType::DNAME() ||
+                rrset->getType() == RRType::DS()) {
+                target_.addRRset(copyRRset(rrset));
+            }
+        } else if (mode_ == ADDRESS) {
+            if (rrset->getType() == RRType::A() ||
+                rrset->getType() == RRType::AAAA()) {
+                target_.addRRset(copyRRset(rrset));
+            }
+        } else {
+            if (rrtype_ == RRType::NSEC() &&
+                rrset->getType() == RRType::CNAME()) {
+                // XXX: ignore CNAME if the qtype is NSEC.
+                // tricky, but necessary.
+                return;
+            }
+            if (rrtype_ == RRType::ANY() || rrtype_ == rrset->getType() ||
+                rrset->getType() == RRType::CNAME() ||
+                rrset->getType() == RRType::DNAME()) {
+                target_.addRRset(copyRRset(rrset));
+                if (rrset->getType() == RRType::CNAME()) {
+                    flags_ |= CNAME_FOUND;
+                }
+                if (rrset->getType() == RRType::DNAME()) {
+                    flags_ |= REFERRAL;
+                }
+            }
+        }
+        
+    }
+    MatchResult getResult() { return (MatchResult(name_found_,
+                                                  has_delegation_)); }
+    const Name& name_;
+    const RRType& rrtype_;
+    const Mode mode_;
+    RRsetList& target_;
+    uint32_t& flags_;
+    bool name_found_;
+    bool has_delegation_;
+};
 
 void
 TestDataSrc::findRecords(const Name& name, const RRType& rdtype,
-                         RRsetList& target, const Name* zonename, const Mode mode,
-                         uint32_t& flags) const
-{
-    const bool any = (rdtype == RRType::ANY());
+                         RRsetList& target, const Name* zonename,
+                         const Mode mode, uint32_t& flags) const
+{
     flags = 0;
 
     assert(zonename != NULL);
-    if (*zonename == sql1) {
-        if (name == sql1 && mode == DELEGATION) {
-            target.addRRset(sql1_ns);
-            flags |= REFERRAL;
-        } else if (name == sql1) {
-            flags |= REFERRAL;
-            if (any) {
-                target.addRRset(sql1_ns);
-                target.addRRset(sql1_nsec);
-            } else if (rdtype == RRType::NS()) {
-                target.addRRset(sql1_ns);
-            } else if (rdtype == RRType::SOA()) {
-                target.addRRset(sql1_soa);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(sql1_nsec);
-            } else {
-                flags |= TYPE_NOT_FOUND;
-            }
-        } else if (name == www_sql1) {
-            if (any) {
-                target.addRRset(www_sql1_a);
-                target.addRRset(www_sql1_nsec);
-            } else if (rdtype == RRType::A()) {
-                target.addRRset(www_sql1_a);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(www_sql1_nsec);
-            } else {
-                flags |= TYPE_NOT_FOUND;
-            }
+
+    vector<Zone>::const_iterator zone = find_if(zones.begin(), zones.end(),
+                                                ZoneNameMatch(*zonename));
+    if (zone == zones.end()) {
+        return;
+    }
+
+    const RRsetMatch::MatchResult match_result =
+        for_each(zone->rrsets.begin(), zone->rrsets.end(),
+                 RRsetMatch(name, rdtype, mode, target, flags)).getResult();
+    if (match_result.has_delegation_) {
+        flags |= REFERRAL;
+    }
+    if (target.size() == 0) {
+        if (match_result.name_found_) {
+            flags |= TYPE_NOT_FOUND;
         } else {
             flags |= NAME_NOT_FOUND;
         }
-    } else {
-        if (name == example && mode == DELEGATION) {
-            target.addRRset(example_ns);
-            flags |= REFERRAL;
-        } else if (name == example) {
-            flags |= REFERRAL;
-            if (any) {
-                target.addRRset(example_ns);
-                target.addRRset(example_soa);
-                target.addRRset(example_nsec);
-            } else if (rdtype == RRType::NS()) {
-                target.addRRset(example_ns);
-            } else if (rdtype == RRType::SOA()) {
-                target.addRRset(example_soa);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(example_nsec);
-            } else {
-                flags |= TYPE_NOT_FOUND;
-            }
-        } else if (name == sql1 && mode == DELEGATION) {
-            target.addRRset(sql1_ns);
-            target.addRRset(sql1_ds);
-            target.addRRset(sql1_ds_nsec);
-            flags |= REFERRAL;
-        } else if (name == sql1) {
-            flags |= REFERRAL;
-            if (any) {
-                target.addRRset(sql1_ns);
-                target.addRRset(sql1_ds);
-                target.addRRset(sql1_ds_nsec);
-            } else if (rdtype == RRType::DS()) {
-                target.addRRset(sql1_ds);
-            } else if (rdtype == RRType::NS()) {
-                target.addRRset(sql1_ns);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(sql1_ds_nsec);
-            } else {
-                flags |= TYPE_NOT_FOUND;
-            }
-        } else if (name == subzone && mode == DELEGATION) {
-            target.addRRset(subzone_ns);
-            target.addRRset(subzone_ds);
-            flags |= REFERRAL;
-        } else if (name == subzone) {
-            flags |= REFERRAL;
-            if (any) {
-                target.addRRset(subzone_ns);
-                target.addRRset(subzone_nsec);
-            } else if (rdtype == RRType::NS()) {
-                target.addRRset(subzone_ns);
-            } else if (rdtype == RRType::DS()) {
-                target.addRRset(subzone_ds);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(subzone_nsec);
-            } else {
-                flags |= TYPE_NOT_FOUND;
-            }
-        } else if (name == dns01 && mode == ADDRESS) {
-            target.addRRset(dns01_a);
-        } else if (name == dns01) {
-            if (any) {
-                target.addRRset(dns01_a);
-                target.addRRset(dns01_nsec);
-            } else if (rdtype == RRType::A()) {
-                target.addRRset(dns01_a);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(dns01_nsec);
-            } else {
-                flags |= TYPE_NOT_FOUND;
-            }
-        } else if (name == dns02 && mode == ADDRESS) {
-            target.addRRset(dns02_a);
-        } else if (name == dns02) {
-            if (any) {
-                target.addRRset(dns02_a);
-                target.addRRset(dns02_nsec);
-            } else if (rdtype == RRType::A()) {
-                target.addRRset(dns02_a);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(dns02_nsec);
-            } else {
-                flags |= TYPE_NOT_FOUND;
-            }
-        } else if (name == dns03 && mode == ADDRESS) {
-            target.addRRset(dns03_a);
-        } else if (name == dns03) {
-            if (any) {
-                target.addRRset(dns03_a);
-                target.addRRset(dns03_nsec);
-            } else if (rdtype == RRType::A()) {
-                target.addRRset(dns03_a);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(dns03_nsec);
-            } else {
-                flags |= TYPE_NOT_FOUND;
-            }
-        } else if (name == wild) {
-            if (any) {
-                target.addRRset(wild_a);
-                target.addRRset(wild_nsec);
-            } else if (rdtype == RRType::A()) {
-                target.addRRset(wild_a);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(wild_nsec);
-            } else {
-                flags |= TYPE_NOT_FOUND;
-            }
-        } else if (name == wild2) {
-            if (any) {
-                target.addRRset(wild2_cname);
-                target.addRRset(wild2_nsec);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(wild2_nsec);
-            } else {
-                target.addRRset(wild2_cname);
-                if (rdtype != RRType::CNAME()) {
-                    flags |= CNAME_FOUND;
-                }
-            }
-        } else if (name == wild3) {
-            if (any) {
-                target.addRRset(wild3_cname);
-                target.addRRset(wild3_nsec);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(wild3_nsec);
-            } else {
-                target.addRRset(wild3_cname);
-                if (rdtype != RRType::CNAME()) {
-                    flags |= CNAME_FOUND;
-                }
-            }
-        } else if (name == www) {
-            if (any) {
-                target.addRRset(www_a);
-                target.addRRset(www_nsec);
-            } else if (rdtype == RRType::A()) {
-                target.addRRset(www_a);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(www_nsec);
-            } else {
-                flags |= TYPE_NOT_FOUND;
-            }
-        } else if (name == foo) {
-            if (rdtype == RRType::NSEC()) {
-                target.addRRset(foo_nsec);
-            } else {
-                target.addRRset(foo_cname);
-                if (rdtype != RRType::CNAME()) {
-                    flags |= CNAME_FOUND;
-                }
-            }
-        } else if (name == cnameint) {
-            if (rdtype == RRType::NSEC()) {
-                target.addRRset(cnameint_nsec);
-            } else {
-                target.addRRset(cnameint_cname);
-                if (rdtype != RRType::CNAME()) {
-                    flags |= CNAME_FOUND;
-                }
-            }
-        } else if (name == cnameext) {
-            if (rdtype == RRType::NSEC()) {
-                target.addRRset(cnameext_nsec);
-            } else {
-                target.addRRset(cnameext_cname);
-                if (rdtype != RRType::CNAME()) {
-                    flags |= CNAME_FOUND;
-                }
-            }
-        } else if (name == dname) {
-            flags |= REFERRAL;
-            if (any) {
-                target.addRRset(dname_dname);
-                target.addRRset(dname_nsec);
-            } else if (rdtype == RRType::DNAME()) {
-                target.addRRset(dname_dname);
-            } else if (rdtype == RRType::NSEC()) {
-                target.addRRset(dns01_nsec);
-            } else {
-                flags |= TYPE_NOT_FOUND;
-            }
-        } else if (name == Name("ns1.subzone.example.com") && mode == ADDRESS) {
-            target.addRRset(subzone_glue1);
-        } else if (name == Name("ns2.subzone.example.com") && mode == ADDRESS) {
-            target.addRRset(subzone_glue2);
-        } else if (name == loop1) {
-            target.addRRset(loop1_cname);
-            if (rdtype != RRType::CNAME()) {
-                flags |= CNAME_FOUND;
-            }
-        } else if (name == loop2) {
-            target.addRRset(loop1_cname);
-            if (rdtype != RRType::CNAME()) {
-                flags |= CNAME_FOUND;
-            }
-        } else {
-            flags |= NAME_NOT_FOUND;
-        }
-    }
-    return;
+    }
 }
 
 DataSrc::Result
 TestDataSrc::findRRset(const Name& qname,
-                       const RRClass& qclass UNUSED_PARAM,
+                       const RRClass& qclass,
                        const RRType& qtype,
                        RRsetList& target,
                        uint32_t& flags,
@@ -755,7 +535,7 @@
 
 DataSrc::Result
 TestDataSrc::findExactRRset(const Name& qname,
-                            const RRClass& qclass UNUSED_PARAM,
+                            const RRClass& qclass,
                             const RRType& qtype,
                             RRsetList& target,
                             uint32_t& flags,
@@ -770,7 +550,7 @@
     flags &= ~REFERRAL;
 
     // CNAMEs don't count in this case
-    if (flags & CNAME_FOUND) {
+    if ((flags & CNAME_FOUND) != 0) {
         flags &= ~CNAME_FOUND;
         flags |= TYPE_NOT_FOUND;
     }
@@ -780,7 +560,7 @@
 
 DataSrc::Result
 TestDataSrc::findAddrs(const Name& qname,
-                       const RRClass& qclass UNUSED_PARAM,
+                       const RRClass& qclass,
                        RRsetList& target,
                        uint32_t& flags,
                        const Name* zonename) const
@@ -795,7 +575,7 @@
 
 DataSrc::Result
 TestDataSrc::findReferral(const Name& qname,
-                          const RRClass& qclass UNUSED_PARAM,
+                          const RRClass& qclass,
                           RRsetList& target,
                           uint32_t& flags,
                           const Name* zonename) const
@@ -815,42 +595,30 @@
 {
     assert(zonename != NULL);
 
-    if (*zonename == example) {
-        if (qname >= example && qname < cnameext) {
-            target = example;
-        } else if (qname < cnameint) {
-            target = cnameext;
-        } else if (qname < dname) {
-            target = cnameint;
-        } else if (qname < dns01) {
-            target = dname;
-        } else if (qname < dns02) {
-            target = dns01;
-        } else if (qname < dns03) {
-            target = dns02;
-        } else if (qname < foo) {
-            target = dns03;
-        } else if (qname < sql1) {
-            target = foo;
-        } else if (qname < subzone) {
-            target = sql1;
-        } else if (qname < wild) {
-            target = subzone;
-        } else if (qname < wild2) {
-            target = wild;
-        } else if (qname < wild3) {
-            target = wild2;
-        } else if (qname < www) {
-            target = wild3;
-        } else {
-            target = www;
-        }
+    vector<Zone>::const_iterator zone = find_if(zones.begin(), zones.end(),
+                                                ZoneNameMatch(*zonename));
+    if (zone == zones.end()) {
+        return (ERROR);
+    }
+
+    if (zone->names.empty()) {
+        return (ERROR);
+    }
+
+    // if found, next_name >= qname.
+    vector<Name>::const_iterator next_name =
+        lower_bound(zone->names.begin(), zone->names.end(), qname);
+    if (next_name == zone->names.end()) {
+        // if no such name was found, the previous name is the last name.
+        target = zone->names.back();
+    } else if (*next_name == qname) {
+        target = *next_name;
+    } else if (next_name == zone->names.begin()) {
+        // if qname < first_name, the "previous name" is the last name.
+        target = zone->names.back();
     } else {
-        if (qname >= sql1 || qname < www_sql1) {
-            target = sql1;
-        } else {
-            target = www_sql1;
-        }
+        // otherwise, qname and next_name share the same previous name.
+        target = *(next_name - 1);
     }
     return (SUCCESS);
 }

Modified: branches/trac191/src/lib/datasrc/tests/test_datasrc.h
==============================================================================
--- branches/trac191/src/lib/datasrc/tests/test_datasrc.h (original)
+++ branches/trac191/src/lib/datasrc/tests/test_datasrc.h Fri Jul  9 10:04:58 2010
@@ -48,8 +48,7 @@
     ~TestDataSrc() {}
     //@}
 
-    void findClosestEnclosure(NameMatch& match,
-                              const isc::dns::RRClass& qclass) const;
+    void findClosestEnclosure(DataSrcMatch& match) const;
 
     Result findRRset(const isc::dns::Name& qname,
                      const isc::dns::RRClass& qclass,
@@ -96,6 +95,7 @@
         ADDRESS,
         DELEGATION
     };
+    class RRsetMatch;
 
     void findRecords(const isc::dns::Name& name, const isc::dns::RRType& rdtype,
                      isc::dns::RRsetList& target,

Modified: branches/trac191/src/lib/dns/Makefile.am
==============================================================================
--- branches/trac191/src/lib/dns/Makefile.am (original)
+++ branches/trac191/src/lib/dns/Makefile.am Fri Jul  9 10:04:58 2010
@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = . tests python
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -77,28 +77,6 @@
 libdns___la_SOURCES += sha1.h sha1.cc
 libdns___la_SOURCES += tsig.h tsig.cc
 
-if HAVE_BOOST_PYTHON
-# This is a loadable module for python scripts, so we use the prefix "pyexec"
-# to make sure the object files will be installed in the appropriate place
-# for this purpose.
-pyexec_LTLIBRARIES = bind10_dns.la
-bind10_dns_la_SOURCES = python_dns.cc
-bind10_dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
-bind10_dns_la_CXXFLAGS = $(AM_CXXFLAGS) $(B10_CXXFLAGS)
-if GCC_WERROR_OK
-# XXX: Boost.Python triggers strict aliasing violation, so if we use -Werror
-# we need to suppress the warnings.
-bind10_dns_la_CXXFLAGS += -fno-strict-aliasing
-endif
-bind10_dns_la_LDFLAGS = $(BOOST_LDFLAGS) $(PYTHON_LDFLAGS)
-# Python prefers .so, while some OSes (specifically MacOS) use a different
-# suffix for dynamic objects.  -module is necessary to work this around.
-bind10_dns_la_LDFLAGS += -module
-bind10_dns_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
-bind10_dns_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-bind10_dns_la_LIBADD += $(BOOST_PYTHON_LIB) $(PYTHON_LIB)
-endif
-
 nodist_libdns___la_SOURCES = rdataclass.cc rrclass.h rrtype.h
 nodist_libdns___la_SOURCES += rrparamregistry.cc
 

Modified: branches/trac191/src/lib/dns/message.cc
==============================================================================
--- branches/trac191/src/lib/dns/message.cc (original)
+++ branches/trac191/src/lib/dns/message.cc Fri Jul  9 10:04:58 2010
@@ -21,6 +21,7 @@
 #include <sstream>
 #include <vector>
 
+#include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/shared_ptr.hpp>
 
@@ -370,7 +371,6 @@
                   "addRRset performed in non-render mode");
     }
 
-    // Note: should check duplicate (TBD)
     impl_->rrsets_[sectionCodeToId(section)].push_back(rrset);
     impl_->counts_[section.getCode()] += rrset->getRdataCount();
 
@@ -379,6 +379,20 @@
         impl_->rrsets_[sectionCodeToId(section)].push_back(sp);
         impl_->counts_[section.getCode()] += sp->getRdataCount();
     }
+}
+
+bool
+Message::hasRRset(const Section& section, RRsetPtr rrset) {
+    BOOST_FOREACH(RRsetPtr r, impl_->rrsets_[sectionCodeToId(section)]) {
+        if (r->getType() == rrset->getType() &&
+            r->getName() == rrset->getName())
+        {
+            return (true);
+
+        }
+    }
+
+    return (false);
 }
 
 void
@@ -923,7 +937,7 @@
 template <typename T>
 const T*
 SectionIterator<T>::operator->() const {
-    return (impl_->it_.operator->());
+    return (&(operator*()));
 }
 
 template <typename T>

Modified: branches/trac191/src/lib/dns/message.h
==============================================================================
--- branches/trac191/src/lib/dns/message.h (original)
+++ branches/trac191/src/lib/dns/message.h Fri Jul  9 10:04:58 2010
@@ -739,7 +739,15 @@
     /// \c rrset.  This interface design needs to be revisited later.
     ///
     /// Only allowed in the \c RENDER mode.
+    ///
+    /// Note that addRRset() does not currently check for duplicate
+    /// data before inserting RRsets.  The caller is responsible for
+    /// checking for these (see hasRRset() below).
     void addRRset(const Section& section, RRsetPtr rrset, bool sign = false);
+
+    /// \brief Determine whether the given section already has an RRset
+    /// matching the name and type of this one
+    bool hasRRset(const Section& section, RRsetPtr rrset);
 
     // The following methods are not currently implemented.
     //void removeQuestion(QuestionPtr question);

Modified: branches/trac191/src/lib/dns/question.h
==============================================================================
--- branches/trac191/src/lib/dns/question.h (original)
+++ branches/trac191/src/lib/dns/question.h Fri Jul  9 10:04:58 2010
@@ -229,6 +229,20 @@
     unsigned int toWire(OutputBuffer& buffer) const;
     //@}
 
+    ///
+    /// \name Comparison Operator
+    ///
+    //@{
+    /// A comparison operator is needed for this class so it can
+    /// function as an index to std::map.
+    bool operator <(const Question& rhs) const {
+        return (rrclass_ < rhs.rrclass_ ||
+                (rrclass_ == rhs.rrclass_ &&
+                 (rrtype_ < rhs.rrtype_ ||
+                  (rrtype_ == rhs.rrtype_ && (name_ < rhs.name_)))));
+    }
+    //@}
+
 private:
     Name name_;
     RRType rrtype_;

Modified: branches/trac191/src/lib/dns/rrsetlist.cc
==============================================================================
--- branches/trac191/src/lib/dns/rrsetlist.cc (original)
+++ branches/trac191/src/lib/dns/rrsetlist.cc Fri Jul  9 10:04:58 2010
@@ -29,8 +29,7 @@
 namespace dns {
 
 void
-RRsetList::addRRset(RRsetPtr rrsetptr)
-{
+RRsetList::addRRset(RRsetPtr rrsetptr) {
     ConstRRsetPtr rrset_found = findRRset(rrsetptr->getType(),
                                           rrsetptr->getClass());
     if (rrset_found != NULL) {
@@ -41,9 +40,16 @@
     rrsets_.push_back(rrsetptr);
 }
 
+void
+RRsetList::append(RRsetList& source)
+{
+    BOOST_FOREACH(RRsetPtr rrset, source) {
+        addRRset(rrset);
+    }
+}
+
 RRsetPtr
-RRsetList::findRRset(const RRType& rrtype, const RRClass& rrclass)
-{
+RRsetList::findRRset(const RRType& rrtype, const RRClass& rrclass) {
     BOOST_FOREACH(RRsetPtr rrsetptr, rrsets_) {
         if ((rrsetptr->getClass() == rrclass) &&
             (rrsetptr->getType() == rrtype)) {

Modified: branches/trac191/src/lib/dns/rrsetlist.h
==============================================================================
--- branches/trac191/src/lib/dns/rrsetlist.h (original)
+++ branches/trac191/src/lib/dns/rrsetlist.h Fri Jul  9 10:04:58 2010
@@ -60,7 +60,7 @@
     }
     P operator->() const
     {
-        return (it_.operator->());
+        return (&(operator*()));
     }
     bool operator==(const RRsetListIterator& other)
     {
@@ -82,6 +82,7 @@
 public:
     RRsetList() {}
     void addRRset(RRsetPtr new_rrsetptr);
+    void append(RRsetList& source);
     RRsetPtr findRRset(const RRType& rrtype, const RRClass& rrclass);
 
     typedef RRsetListIterator<std::vector<RRsetPtr>::iterator,

Modified: branches/trac191/src/lib/dns/tests/base64_unittest.cc
==============================================================================
--- branches/trac191/src/lib/dns/tests/base64_unittest.cc (original)
+++ branches/trac191/src/lib/dns/tests/base64_unittest.cc Fri Jul  9 10:04:58 2010
@@ -54,8 +54,7 @@
     EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
 }
 
-TEST_F(Base64Test, decode)
-{
+TEST_F(Base64Test, decode) {
     for (vector<StringPair>::const_iterator it = test_sequence.begin();
          it != test_sequence.end();
          ++it) {
@@ -82,8 +81,7 @@
     EXPECT_THROW(decodeBase64("Zm==", decoded_data), BadBase64String);
 }
 
-TEST_F(Base64Test, encode)
-{
+TEST_F(Base64Test, encode) {
     for (vector<StringPair>::const_iterator it = test_sequence.begin();
          it != test_sequence.end();
          ++it) {

Modified: branches/trac191/src/lib/dns/tests/question_unittest.cc
==============================================================================
--- branches/trac191/src/lib/dns/tests/question_unittest.cc (original)
+++ branches/trac191/src/lib/dns/tests/question_unittest.cc Fri Jul  9 10:04:58 2010
@@ -119,4 +119,35 @@
     oss << test_question1;
     EXPECT_EQ(test_question1.toText(), oss.str());
 }
+
+TEST_F(QuestionTest, comparison)
+{
+    const Name a("a"), b("b");
+    const RRClass in(RRClass::IN()), ch(RRClass::CH());
+    const RRType ns(RRType::NS()), aaaa(RRType::AAAA());
+
+    EXPECT_TRUE(Question(a, in, ns) < Question(a, in, aaaa));
+    EXPECT_FALSE(Question(a, in, aaaa) < Question(a, in, ns));
+
+    EXPECT_TRUE(Question(a, in, ns) < Question(a, ch, ns));
+    EXPECT_FALSE(Question(a, ch, ns) < Question(a, in, ns));
+
+    EXPECT_TRUE(Question(a, in, ns) < Question(a, ch, aaaa));
+    EXPECT_FALSE(Question(a, ch, aaaa) < Question(a, in, ns));
+
+    EXPECT_TRUE(Question(a, in, ns) < Question(b, in, ns));
+    EXPECT_FALSE(Question(a, in, ns) < Question(a, in, ns));
+
+    EXPECT_TRUE(Question(a, in, ns) < Question(b, ch, ns));
+    EXPECT_FALSE(Question(b, ch, ns) < Question(a, in, ns));
+
+    EXPECT_TRUE(Question(a, in, ns) < Question(b, ch, aaaa));
+    EXPECT_FALSE(Question(b, ch, aaaa) < Question(a, in, ns));
+
+    EXPECT_FALSE(Question(a, in, ns) < Question(a, in, ns));
+    EXPECT_FALSE(Question(a, ch, ns) < Question(a, ch, ns));
+    EXPECT_FALSE(Question(b, in, ns) < Question(b, in, ns));
+    EXPECT_FALSE(Question(b, in, aaaa) < Question(b, in, aaaa));
 }
+
+}

Modified: branches/trac191/src/lib/dns/tests/rrsetlist_unittest.cc
==============================================================================
--- branches/trac191/src/lib/dns/tests/rrsetlist_unittest.cc (original)
+++ branches/trac191/src/lib/dns/tests/rrsetlist_unittest.cc Fri Jul  9 10:04:58 2010
@@ -48,6 +48,7 @@
 const generic::SOA rdata_soa(Name("ns.example.com"), Name("root.example.com"),
                              2010012601, 3600, 300, 3600000, 1200);
 const generic::CNAME rdata_cname("target.example.com");
+const generic::DNAME rdata_dname("dtarget.example.com");
 
 void
 RRsetListTest::setupList(RRsetList& list) {
@@ -84,6 +85,24 @@
     RRsetList list;
     setupList(list);
     EXPECT_EQ(list.size(), 5);
+}
+
+TEST_F(RRsetListTest, append) {
+    RRsetList list1;
+    setupList(list1);
+    RRsetList list2;
+    RRsetPtr dname(new RRset(Name("example.com"), RRClass::IN(),
+                             RRType::DNAME(), example_ttl));
+    dname->addRdata(rdata_dname);
+    list2.addRRset(dname);
+    list1.append(list2);
+    EXPECT_EQ(list2.size(), 1);
+    EXPECT_EQ(list1.size(), 6);
+
+    RRsetPtr rrset = list1.findRRset(RRType::DNAME(), RRClass::IN());
+    EXPECT_EQ(RRType::DNAME(), rrset->getType());
+
+    EXPECT_THROW(list1.append(list2), DuplicateRRset);
 }
 
 TEST_F(RRsetListTest, extraRRset) {

Modified: branches/trac191/src/lib/python/isc/Makefile.am
==============================================================================
--- branches/trac191/src/lib/python/isc/Makefile.am (original)
+++ branches/trac191/src/lib/python/isc/Makefile.am Fri Jul  9 10:04:58 2010
@@ -1,4 +1,4 @@
-SUBDIRS = datasrc cc config # Util
+SUBDIRS = datasrc cc config log # Util
 
 python_PYTHON = __init__.py
 

Modified: branches/trac191/src/lib/python/isc/__init__.py
==============================================================================
--- branches/trac191/src/lib/python/isc/__init__.py (original)
+++ branches/trac191/src/lib/python/isc/__init__.py Fri Jul  9 10:04:58 2010
@@ -1,3 +1,5 @@
 import isc.datasrc
 import isc.cc
 import isc.config
+#import isc.dns
+import isc.log

Modified: branches/trac191/src/lib/python/isc/cc/message.py
==============================================================================
--- branches/trac191/src/lib/python/isc/cc/message.py (original)
+++ branches/trac191/src/lib/python/isc/cc/message.py Fri Jul  9 10:04:58 2010
@@ -13,224 +13,29 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+#
+# Functions for reading and parsing cc messages
+# Currently these are only abstraction functions for JSON conversion.
+#
+
 import sys
 import struct
 
-class DecodeError(Exception): pass
-
-PROTOCOL_VERSION = 0x536b616e
-
-_ITEM_BLOB = 0x01
-_ITEM_HASH = 0x02
-_ITEM_LIST = 0x03
-_ITEM_NULL = 0x04
-_ITEM_BOOL = 0x05
-_ITEM_INT  = 0x06
-_ITEM_REAL = 0x07
-_ITEM_UTF8 = 0x08
-_ITEM_MASK = 0x0f
-
-_ITEM_LENGTH_32   = 0x00
-_ITEM_LENGTH_16   = 0x10
-_ITEM_LENGTH_8    = 0x20
-_ITEM_LENGTH_MASK = 0x30
+import json
 
 def to_wire(items):
-    """Encode a dict into wire format.
-    >>> wire_format = Message.to_wire({"a": "b"})
-    """
-    return struct.pack(">I", PROTOCOL_VERSION) + _encode_hash(items)
-
-def _encode_tag(tag):
-    """Encode a single UTF-8 tag.
-    ... wire_partial = Message._encode_tag('this')
-    """
-    binary = bytes(tag, 'utf-8')
-    if len(binary) > 255:
-        raise ArgumentError("tag is too long (max 255 encoded bytes)")
-    return(struct.pack(">B", len(binary))) + binary
-
-def _encode_length_and_type(data, datatype):
-    """Helper method to handle the length encoding in one place."""
-    if data == None:
-        return(struct.pack(">B", _ITEM_NULL))
-    length = len(data)
-    if length < 0x0000100:
-        return(struct.pack(">B B", datatype | _ITEM_LENGTH_8, length) + data)
-    elif length < 0x00010000:
-        return(struct.pack(">B H", datatype | _ITEM_LENGTH_16, length) + data)
-    else:
-        return(struct.pack(">B I", datatype | _ITEM_LENGTH_32, length) + data)
-
-def _pack_utf8(item):
-    """Pack a string (utf-8) and its type/length prefix."""
-    return (_encode_length_and_type(bytes(item, 'utf-8'), _ITEM_UTF8))
-
-def _pack_blob(item):
-    """Pack a blob (binary data) and its type/length prefix."""
-    return (_encode_length_and_type(item, _ITEM_BLOB))
-
-def _pack_bool(item):
-    """Pack a bool and its type/length prefix."""
-    return (_encode_length_and_type(_encode_bool(item), _ITEM_BOOL))
-
-def _pack_int(item):
-    """Pack an integer and its type/length prefix."""
-    return (_encode_length_and_type(bytes(str(item), 'utf-8'), _ITEM_INT))
-
-def _pack_real(item):
-    """Pack an integer and its type/length prefix."""
-    return (_encode_length_and_type(bytes(str(item), 'utf-8'), _ITEM_REAL))
-
-def _pack_array(item):
-    """Pack a list (array) and its type/length prefix."""
-    return (_encode_length_and_type(_encode_array(item), _ITEM_LIST))
-
-def _pack_hash(item):
-    """Pack a dict (hash) and its type/length prefix."""
-    data = _encode_hash(item)
-    return (_encode_length_and_type(data, _ITEM_HASH))
-
-def _pack_nil():
-    """Encode a nil (NULL, None) item."""
-    return _encode_length_and_type(None, None)
-
-def _encode_item(item):
-    """Encode each item depending on its type"""
-    if item == None:
-        return (_pack_nil())
-    elif type(item) == bool:
-        return (_pack_bool(item))
-    elif type(item) == int:
-        return (_pack_int(item))
-    elif type(item) == float:
-        return (_pack_real(item))
-    elif type(item) == dict:
-        return (_pack_hash(item))
-    elif type(item) == list:
-        return (_pack_array(item))
-    elif type(item) in (bytes, bytearray):
-        return (_pack_blob(item))
-    else:
-        return (_pack_utf8(str(item)))
-
-def _encode_bool(item):
-    """Encode a boolean value into a bytearray of one byte (0=false)"""
-    if item:
-        return b'1'
-    else:
-        return b'0'
-
-def _encode_array(item):
-    """Encode an array, where each value is encoded recursively"""
-    ret = bytes()
-    for i in item:
-        ret += _encode_item(i)
-    return ret
-
-def _encode_hash(item):
-    """Encode a hash, where each value is encoded recursively"""
-    ret = bytes()
-    for key, value in item.items():
-        ret += _encode_tag(key)
-        ret += _encode_item(value)
-    return ret
-
-#
-# decode methods
-#
+    '''Encodes the given python structure in JSON, and converts the
+       result to bytes. Raises a TypeError if the given structure is
+       not serializable with JSON.'''
+    return json.dumps(items).encode('utf8')
 
 def from_wire(data):
-    if len(data) < 5:
-        raise DecodeError("Data is too short to decode")
-    wire_version, data = data[0:4], data[4:]
-    wire_version = struct.unpack(">I", wire_version)[0]
-    if wire_version != PROTOCOL_VERSION:
-        raise DecodeError("Incorrect protocol version")
-    return _decode_hash(data)
-
-def _decode_tag(data):
-    if len(data) < 1:
-        raise DecodeError("Data underrun while decoding")
-    length = data[0]
-    if len(data) - 1 < length:
-        raise DecodeError("Data underrun while decoding")
-    return [data[1:length + 1].decode(), data[length + 1:]]
-
-def _decode_item(data):
-    if len(data) < 1:
-        raise DecodeError("Data underrun while decoding")
-    type_and_length_format = data[0]
-    item_type = type_and_length_format & _ITEM_MASK
-    length_format = type_and_length_format & _ITEM_LENGTH_MASK
-
-    if item_type == _ITEM_NULL:
-        data = data[1:]
-    else:
-        if length_format == _ITEM_LENGTH_8:
-            if len(data) - 1 < 1:
-                raise DecodeError("Data underrun while decoding")
-            length = data[1]
-            data = data[2:]
-        elif length_format == _ITEM_LENGTH_16:
-            if len(data) - 1 < 2:
-                raise DecodeError("Data underrun while decoding")
-            length = struct.unpack(">H", data[1:3])[0]
-            data = data[3:]
-        elif length_format == _ITEM_LENGTH_32:
-            if len(data) - 1 < 4:
-                raise DecodeError("Data underrun while decoding")
-            length = struct.unpack(">I", data[1:5])[0]
-            data = data[5:]
-        if len(data) < length:
-            raise DecodeError("Data underrun while decoding")
-        item = data[0:length]
-        data = data[length:]
-
-    if item_type == _ITEM_BLOB:
-        value = item
-    elif item_type == _ITEM_BOOL:
-        value = _decode_bool(item)
-    elif item_type == _ITEM_INT:
-        value = _decode_int(item)
-    elif item_type == _ITEM_REAL:
-        value = _decode_real(item)
-    elif item_type == _ITEM_UTF8:
-        value = str(item, 'utf-8')
-    elif item_type == _ITEM_HASH:
-        value = _decode_hash(item)
-    elif item_type == _ITEM_LIST:
-        value = _decode_array(item)
-    elif item_type == _ITEM_NULL:
-        value = None
-    else:
-        raise DecodeError("Unknown item type in decode: %02x" % item_type)
-
-    return (value, data)
-
-def _decode_bool(data):
-    return data == b'1'
-
-def _decode_int(data):
-    return int(str(data, 'utf-8'))
-
-def _decode_real(data):
-    return float(str(data, 'utf-8'))
-
-def _decode_hash(data):
-    ret = {}
-    while len(data) > 0:
-        tag, data = _decode_tag(data)
-        value, data = _decode_item(data)
-        ret[tag] = value
-    return ret
-
-def _decode_array(data):
-    ret = []
-    while len(data) > 0:
-        value, data = _decode_item(data)
-        ret.append(value)
-    return ret
+    '''Decodes the given bytes and parses it with the builtin JSON
+       parser. Raises a ValueError if the data is not valid JSON.
+       Raises an AttributeError if the given object has no decode()
+       method (which should return a string).
+       '''
+    return json.loads(data.decode('utf8'))
 
 if __name__ == "__main__":
     import doctest

Modified: branches/trac191/src/lib/python/isc/cc/tests/Makefile.am
==============================================================================
--- branches/trac191/src/lib/python/isc/cc/tests/Makefile.am (original)
+++ branches/trac191/src/lib/python/isc/cc/tests/Makefile.am Fri Jul  9 10:04:58 2010
@@ -1,4 +1,4 @@
-PYTESTS = data_test.py session_test.py test.py
+PYTESTS = message_test.py data_test.py session_test.py
 # NOTE: test_session.py is to be run manually, so not automated.
 EXTRA_DIST = $(PYTESTS)
 EXTRA_DIST += sendcmd.py

Modified: branches/trac191/src/lib/python/isc/cc/tests/session_test.py
==============================================================================
--- branches/trac191/src/lib/python/isc/cc/tests/session_test.py (original)
+++ branches/trac191/src/lib/python/isc/cc/tests/session_test.py Fri Jul  9 10:04:58 2010
@@ -39,10 +39,7 @@
         pass
 
     def send(self, data):
-        #print("[XX] send called:")
-        #print(data)
         self.sendqueue.extend(data);
-        pass
 
     def readsent(self, length):
         if length > len(self.sendqueue):
@@ -70,7 +67,6 @@
         return result
 
     def recv(self, length):
-        #print("[XX] recv(" + str(length) + ") of " + str(len(self.recvqueue)))
         if length > len(self.recvqueue):
             raise Exception("Buffer underrun in test, does the test provide the right data?")
         result = self.recvqueue[:length]
@@ -79,11 +75,19 @@
         #print("[XX] queue now: " + str(self.recvqueue))
         return result
 
-    def addrecv(self, data):
-        self.recvqueue.extend(data)
-
-    def addrecvmsg(self, env, msg = None):
-        self.addrecv(self.preparemsg(env, msg))
+    def addrecv(self, env, msg = None):
+        if type(env) == dict:
+            env = isc.cc.message.to_wire(env)
+        if type(msg) == dict:
+            msg = isc.cc.message.to_wire(msg)
+        length = 2 + len(env);
+        if msg:
+            length += len(msg)
+        self.recvqueue.extend(struct.pack("!I", length))
+        self.recvqueue.extend(struct.pack("!H", len(env)))
+        self.recvqueue.extend(env)
+        if msg:
+            self.recvqueue.extend(msg)
 
 #
 # We subclass the Session class we're testing here, only
@@ -120,7 +124,7 @@
         sess = MySession()
         sess.sendmsg({}, {"hello": "a"})
         sent = sess._socket.readsentmsg();
-        self.assertEqual(sent, b'\x00\x00\x00\x13\x00\x04SkanSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00\x12\x00\x02{}{"hello": "a"}')
         sess.close()
         self.assertRaises(SessionError, sess.sendmsg, {}, {"hello": "a"})
 
@@ -147,41 +151,37 @@
     def test_session_recvmsg(self):
         sess = MySession()
         # {'to': "someone"}, {"hello": "a"}
-        self.recv_and_compare(sess,
-                              b'\x00\x00\x00\x1f\x00\x10Skan\x02to(\x07someoneSkan\x05hello(\x01a',
-                              {'to': "someone"}, {"hello": "a"})
+        #self.recv_and_compare(sess,
+        #                      b'\x00\x00\x00\x1f\x00\x10Skan\x02to(\x07someoneSkan\x05hello(\x01a',
+        #                      {'to': "someone"}, {"hello": "a"})
 
         # 'malformed' messages
         # shouldn't some of these raise exceptions?
-        self.recv_and_compare(sess, 
-                              b'\x00',
-                              None, None)
-        self.recv_and_compare(sess, 
-                              b'\x00\x00\x00\x10',
-                              None, None)
-        self.recv_and_compare(sess, 
-                              b'\x00\x00\x00\x02\x00\x00',
-                              None, None)
-        self.recv_and_compare(sess, 
-                              b'\x00\x00\x00\x02\x00\x02',
-                              None, None)
-        self.recv_and_compare(sess, 
-                              b'',
-                              None, None)
-
-        # incomplete data field
-        sess._socket.addrecv(b'\x00\x00\x00\x1f\x00\x10Skan\x02to(\x07someoneSkan\x05hello(\x01')
-        self.assertRaises(isc.cc.message.DecodeError, sess.recvmsg)
+        #self.recv_and_compare(sess, 
+        #                      b'\x00',
+        #                      None, None)
+        #self.recv_and_compare(sess, 
+        #                      b'\x00\x00\x00\x10',
+        #                      None, None)
+        #self.recv_and_compare(sess, 
+        #                      b'\x00\x00\x00\x02\x00\x00',
+        #                      None, None)
+        #self.recv_and_compare(sess, 
+        #                      b'\x00\x00\x00\x02\x00\x02',
+        #                      None, None)
+        #self.recv_and_compare(sess, 
+        #                      b'',
+        #                      None, None)
+
         # need to clear
         sess._socket.recvqueue = bytearray()
         
         # 'queueing' system
         # sending message {'to': 'someone', 'reply': 1}, {"hello": "a"}
         #print("sending message {'to': 'someone', 'reply': 1}, {'hello': 'a'}")
-
         # get no message without asking for a specific sequence number reply
         self.assertFalse(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00(\x00\x19Skan\x02to(\x07someone\x05reply&\x011Skan\x05hello(\x01a')
+        sess._socket.addrecv({'to': 'someone', 'reply': 1}, {"hello": "a"})
         env, msg = sess.recvmsg(False)
         self.assertEqual(None, env)
         self.assertTrue(sess.has_queued_msgs())
@@ -193,7 +193,7 @@
         # ask for a differe sequence number reply (that doesn't exist)
         # then ask for the one that is there
         self.assertFalse(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00(\x00\x19Skan\x02to(\x07someone\x05reply&\x011Skan\x05hello(\x01a')
+        sess._socket.addrecv({'to': 'someone', 'reply': 1}, {"hello": "a"})
         env, msg = sess.recvmsg(False, 2)
         self.assertEqual(None, env)
         self.assertEqual(None, msg)
@@ -206,7 +206,7 @@
         # ask for a differe sequence number reply (that doesn't exist)
         # then ask for any message
         self.assertFalse(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00(\x00\x19Skan\x02to(\x07someone\x05reply&\x011Skan\x05hello(\x01a')
+        sess._socket.addrecv({'to': 'someone', 'reply': 1}, {"hello": "a"})
         env, msg = sess.recvmsg(False, 2)
         self.assertEqual(None, env)
         self.assertEqual(None, msg)
@@ -222,12 +222,12 @@
         # send a new message, ask for specific message (get the first)
         # then ask for any message (get the second)
         self.assertFalse(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00(\x00\x19Skan\x02to(\x07someone\x05reply&\x011Skan\x05hello(\x01a')
+        sess._socket.addrecv({'to': 'someone', 'reply': 1}, {'hello': 'a'})
         env, msg = sess.recvmsg(False, 2)
         self.assertEqual(None, env)
         self.assertEqual(None, msg)
         self.assertTrue(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00\x1f\x00\x10Skan\x02to(\x07someoneSkan\x05hello(\x01b')
+        sess._socket.addrecv({'to': 'someone' }, {'hello': 'b'})
         env, msg = sess.recvmsg(False, 1)
         self.assertEqual({'to': 'someone', 'reply': 1 }, env)
         self.assertEqual({"hello": "a"}, msg)
@@ -241,8 +241,8 @@
         # ask for that specific message (get the second)
         # then ask for any message (get the first)
         self.assertFalse(sess.has_queued_msgs())
-        sess._socket.addrecv(b'\x00\x00\x00\x1f\x00\x10Skan\x02to(\x07someoneSkan\x05hello(\x01b')
-        sess._socket.addrecv(b'\x00\x00\x00(\x00\x19Skan\x02to(\x07someone\x05reply&\x011Skan\x05hello(\x01a')
+        sess._socket.addrecv({'to': 'someone' }, {'hello': 'b'})
+        sess._socket.addrecv({'to': 'someone', 'reply': 1}, {'hello': 'a'})
         env, msg = sess.recvmsg(False, 1)
         self.assertEqual({'to': 'someone', 'reply': 1}, env)
         self.assertEqual({"hello": "a"}, msg)
@@ -266,29 +266,29 @@
         sess = MySession()
         sess.group_subscribe("mygroup")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x001\x00/Skan\x05group(\x07mygroup\x04type(\tsubscribe\x08instance(\x01*')
+        self.assertEqual(sent, b'\x00\x00\x00<\x00:{"group": "mygroup", "type": "subscribe", "instance": "*"}')
         
         sess.group_subscribe("mygroup")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x001\x00/Skan\x05group(\x07mygroup\x04type(\tsubscribe\x08instance(\x01*')
+        self.assertEqual(sent, b'\x00\x00\x00<\x00:{"group": "mygroup", "type": "subscribe", "instance": "*"}')
         
         sess.group_subscribe("mygroup", "my_instance")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x00;\x009Skan\x05group(\x07mygroup\x04type(\tsubscribe\x08instance(\x0bmy_instance')
+        self.assertEqual(sent, b'\x00\x00\x00F\x00D{"group": "mygroup", "type": "subscribe", "instance": "my_instance"}')
 
     def test_group_unsubscribe(self):
         sess = MySession()
         sess.group_unsubscribe("mygroup")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x003\x001Skan\x05group(\x07mygroup\x04type(\x0bunsubscribe\x08instance(\x01*')
+        self.assertEqual(sent, b'\x00\x00\x00>\x00<{"group": "mygroup", "type": "unsubscribe", "instance": "*"}')
         
         sess.group_unsubscribe("mygroup")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x003\x001Skan\x05group(\x07mygroup\x04type(\x0bunsubscribe\x08instance(\x01*')
+        self.assertEqual(sent, b'\x00\x00\x00>\x00<{"group": "mygroup", "type": "unsubscribe", "instance": "*"}')
         
         sess.group_unsubscribe("mygroup", "my_instance")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x00=\x00;Skan\x05group(\x07mygroup\x04type(\x0bunsubscribe\x08instance(\x0bmy_instance')
+        self.assertEqual(sent, b'\x00\x00\x00H\x00F{"group": "mygroup", "type": "unsubscribe", "instance": "my_instance"}')
 
     def test_group_sendmsg(self):
         sess = MySession()
@@ -296,17 +296,17 @@
 
         sess.group_sendmsg({ 'hello': 'a' }, "my_group")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x00W\x00HSkan\x04from(\ttest_name\x03seq&\x012\x02to(\x01*\x08instance(\x01*\x05group(\x08my_group\x04type(\x04sendSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00p\x00`{"from": "test_name", "seq": 2, "to": "*", "instance": "*", "group": "my_group", "type": "send"}{"hello": "a"}')
         self.assertEqual(sess._sequence, 2)
 
         sess.group_sendmsg({ 'hello': 'a' }, "my_group", "my_instance")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x00a\x00RSkan\x04from(\ttest_name\x03seq&\x013\x02to(\x01*\x08instance(\x0bmy_instance\x05group(\x08my_group\x04type(\x04sendSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00z\x00j{"from": "test_name", "seq": 3, "to": "*", "instance": "my_instance", "group": "my_group", "type": "send"}{"hello": "a"}')
         self.assertEqual(sess._sequence, 3)
         
         sess.group_sendmsg({ 'hello': 'a' }, "your_group", "your_instance")
         sent = sess._socket.readsentmsg()
-        self.assertEqual(sent, b'\x00\x00\x00e\x00VSkan\x04from(\ttest_name\x03seq&\x014\x02to(\x01*\x08instance(\ryour_instance\x05group(\nyour_group\x04type(\x04sendSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00~\x00n{"from": "test_name", "seq": 4, "to": "*", "instance": "your_instance", "group": "your_group", "type": "send"}{"hello": "a"}')
         self.assertEqual(sess._sequence, 4)
 
     def test_group_recvmsg(self):
@@ -318,11 +318,11 @@
         sess = MySession()
         sess.group_reply({ 'from': 'me', 'group': 'our_group', 'instance': 'other_instance', 'seq': 4}, {"hello": "a"})
         sent = sess._socket.readsentmsg();
-        self.assertEqual(sent, b'\x00\x00\x00o\x00`Skan\x04from(\ttest_name\x03seq&\x012\x02to(\x02me\x08instance(\x0eother_instance\x05reply&\x014\x05group(\tour_group\x04type(\x04sendSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00\x8b\x00{{"from": "test_name", "seq": 2, "to": "me", "instance": "other_instance", "reply": 4, "group": "our_group", "type": "send"}{"hello": "a"}')
         
         sess.group_reply({ 'from': 'me', 'group': 'our_group', 'instance': 'other_instance', 'seq': 9}, {"hello": "a"})
         sent = sess._socket.readsentmsg();
-        self.assertEqual(sent, b'\x00\x00\x00o\x00`Skan\x04from(\ttest_name\x03seq&\x013\x02to(\x02me\x08instance(\x0eother_instance\x05reply&\x019\x05group(\tour_group\x04type(\x04sendSkan\x05hello(\x01a')
+        self.assertEqual(sent, b'\x00\x00\x00\x8b\x00{{"from": "test_name", "seq": 3, "to": "me", "instance": "other_instance", "reply": 9, "group": "our_group", "type": "send"}{"hello": "a"}')
         
         
 if __name__ == "__main__":

Modified: branches/trac191/src/lib/python/isc/config/ccsession.py
==============================================================================
--- branches/trac191/src/lib/python/isc/config/ccsession.py (original)
+++ branches/trac191/src/lib/python/isc/config/ccsession.py Fri Jul  9 10:04:58 2010
@@ -149,6 +149,11 @@
         self._session.group_subscribe(self._module_name, "*")
 
         self._remote_module_configs = {}
+
+    def __del__(self):
+        self._session.group_unsubscribe(self._module_name, "*")
+        for module_name in self._remote_module_configs:
+            self._session.group_unsubscribe(module_name)
 
     def start(self):
         """Send the specification for this module to the configuration
@@ -244,7 +249,7 @@
         self._session.group_subscribe(module_name);
 
         # Get the current config for that module now
-        seq = self._session.group_sendmsg({ "command": [ "get_config", { "module_name": module_name } ] }, "ConfigManager")
+        seq = self._session.group_sendmsg(create_command(COMMAND_GET_CONFIG, { "module_name": module_name }), "ConfigManager")
         answer, env = self._session.group_recvmsg(False, seq)
         if answer:
             rcode, value = parse_answer(answer)
@@ -259,6 +264,7 @@
     def remove_remote_config(self, module_name):
         """Removes the remote configuration access for this module"""
         if module_name in self._remote_module_configs:
+            self._session.group_unsubscribe(module_name)
             del self._remote_module_configs[module_name]
 
     def get_remote_config_value(self, module_name, identifier):
@@ -280,7 +286,7 @@
     def __request_config(self):
         """Asks the configuration manager for the current configuration, and call the config handler if set.
            Raises a ModuleCCSessionError if there is no answer from the configuration manager"""
-        seq = self._session.group_sendmsg({ "command": [ "get_config", { "module_name": self._module_name } ] }, "ConfigManager")
+        seq = self._session.group_sendmsg(create_command(COMMAND_GET_CONFIG, { "module_name": self._module_name }), "ConfigManager")
         answer, env = self._session.group_recvmsg(False, seq)
         if answer:
             rcode, value = parse_answer(answer)
@@ -317,6 +323,10 @@
         for module in specs.keys():
             self.set_specification(isc.config.ModuleSpec(specs[module]))
 
+    def update_specs_and_config(self):
+        self.request_specifications();
+        self.request_current_config();
+
     def request_current_config(self):
         """Requests the current configuration from the configuration
            manager through b10-cmdctl, and stores those as CURRENT"""
@@ -324,6 +334,7 @@
         if 'version' not in config or config['version'] != 1:
             raise ModuleCCSessionError("Bad config version")
         self._set_current_config(config)
+
 
     def add_value(self, identifier, value_str):
         """Add a value to a configuration list. Raises a DataTypeError

Modified: branches/trac191/src/lib/python/isc/config/cfgmgr.py
==============================================================================
--- branches/trac191/src/lib/python/isc/config/cfgmgr.py (original)
+++ branches/trac191/src/lib/python/isc/config/cfgmgr.py Fri Jul  9 10:04:58 2010
@@ -347,7 +347,7 @@
         # passes both specification and commands at once
         spec_update = ccsession.create_command(ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE,
                                                [ spec.get_module_name(), spec.get_full_spec() ])
-        self.cc.group_sendmsg(spec_update, "Cmd-Ctrld")
+        self.cc.group_sendmsg(spec_update, "Cmdctl")
         return ccsession.create_answer(0)
 
     def handle_msg(self, msg):

Modified: branches/trac191/src/lib/python/isc/config/tests/ccsession_test.py
==============================================================================
--- branches/trac191/src/lib/python/isc/config/tests/ccsession_test.py (original)
+++ branches/trac191/src/lib/python/isc/config/tests/ccsession_test.py Fri Jul  9 10:04:58 2010
@@ -119,7 +119,10 @@
 
     def test_start1(self):
         fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        self.assertFalse("Spec1" in fake_session.subscriptions)
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        self.assertTrue("Spec1" in fake_session.subscriptions)
+
         self.assertEqual(len(fake_session.message_queue), 0)
         self.assertRaises(ModuleCCSessionError, mccs.start)
         self.assertEqual(len(fake_session.message_queue), 2)
@@ -139,6 +142,9 @@
         self.assertEqual({'command': ['get_config', {'module_name': 'Spec1'}]},
                          fake_session.get_message('ConfigManager', None))
 
+        mccs = None
+        self.assertFalse("Spec1" in fake_session.subscriptions)
+
     def test_start2(self):
         fake_session = FakeModuleCCSession()
         mccs = self.create_session("spec2.spec", None, None, fake_session)
@@ -352,7 +358,9 @@
 
         self.assertRaises(ModuleCCSessionError, mccs.get_remote_config_value, "Spec2", "item1")
 
+        self.assertFalse("Spec2" in fake_session.subscriptions)
         rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+        self.assertTrue("Spec2" in fake_session.subscriptions)
         self.assertEqual("Spec2", rmodname)
         self.assertRaises(isc.cc.data.DataNotFoundError, mccs.get_remote_config_value, rmodname, "asdf")
         value, default = mccs.get_remote_config_value(rmodname, "item1")
@@ -360,7 +368,15 @@
         self.assertEqual(True, default)
 
         mccs.remove_remote_config(rmodname)
+        self.assertFalse("Spec2" in fake_session.subscriptions)
         self.assertRaises(ModuleCCSessionError, mccs.get_remote_config_value, "Spec2", "item1")
+
+        # test if unsubscription is alse sent when object is deleted
+        rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+        self.assertTrue("Spec2" in fake_session.subscriptions)
+        mccs = None
+        self.assertFalse("Spec2" in fake_session.subscriptions)
+        
     
 class fakeUIConn():
     def __init__(self):

Modified: branches/trac191/src/lib/python/isc/config/tests/cfgmgr_test.py
==============================================================================
--- branches/trac191/src/lib/python/isc/config/tests/cfgmgr_test.py (original)
+++ branches/trac191/src/lib/python/isc/config/tests/cfgmgr_test.py Fri Jul  9 10:04:58 2010
@@ -271,9 +271,9 @@
         # the name here is actually wrong (and hardcoded), but needed in the current version
         # TODO: fix that
         #self.assertEqual({'specification_update': [ self.name, self.spec ] },
-        #                 self.fake_session.get_message("Cmd-Ctrld", None))
+        #                 self.fake_session.get_message("Cmdctl", None))
         #self.assertEqual({'commands_update': [ self.name, self.commands ] },
-        #                 self.fake_session.get_message("Cmd-Ctrld", None))
+        #                 self.fake_session.get_message("Cmdctl", None))
 
         self._handle_msg_helper({ "command": 
                                   ["shutdown"]

Modified: branches/trac191/src/lib/python/isc/config/tests/unittest_fakesession.py
==============================================================================
--- branches/trac191/src/lib/python/isc/config/tests/unittest_fakesession.py (original)
+++ branches/trac191/src/lib/python/isc/config/tests/unittest_fakesession.py Fri Jul  9 10:04:58 2010
@@ -15,6 +15,16 @@
             self.subscriptions[group_name] = []
         if instance_name:
             self.subscriptions[group_name].append(instance_name)
+            
+    def group_unsubscribe(self, group_name, instance_name = None):
+        if group_name in self.subscriptions:
+            if instance_name:
+                if len(self.subscriptions[group_name]) > 1:
+                    del self.subscriptions[group_name][instance_name]
+                else:
+                    del self.subscriptions[group_name]
+            else:
+                del self.subscriptions[group_name]
             
 
     def has_subscription(self, group_name, instance_name = None):

Modified: branches/trac191/src/lib/python/isc/datasrc/master.py
==============================================================================
--- branches/trac191/src/lib/python/isc/datasrc/master.py (original)
+++ branches/trac191/src/lib/python/isc/datasrc/master.py Fri Jul  9 10:04:58 2010
@@ -16,7 +16,8 @@
 # $Id$
 
 import sys, re, string
-
+import time
+import os
 #########################################################################
 # define exceptions
 #########################################################################
@@ -102,7 +103,7 @@
 # isttl: check whether a string is a valid TTL specifier.
 # returns: boolean
 #########################################################################
-ttl_regex = re.compile('[0-9]+[wdhms]?', re.I)
+ttl_regex = re.compile('([0-9]+[wdhms]?)+', re.I)
 def isttl(s):
     global ttl_regex
     if ttl_regex.match(s):
@@ -121,19 +122,26 @@
 #   MasterFileError
 #########################################################################
 def parse_ttl(s):
-    m = re.match('([0-9]+)(.*)', s)
-    if not m:
+    sum = 0
+    if not isttl(s):
         raise MasterFileError('Invalid TTL: ' + s)
-    ttl, suffix = int(m.group(1)), m.group(2)
-    if suffix.lower() == 'w':
-        ttl *= 604800
-    elif suffix.lower() == 'd':
-        ttl *= 86400
-    elif suffix.lower() == 'h':
-        ttl *= 3600
-    elif suffix.lower() == 'm':
-        ttl *= 60
-    return str(ttl)
+    for ttl_expr in re.findall('\d+[wdhms]?', s, re.I):
+        if ttl_expr.isdigit():
+            ttl = int(ttl_expr)
+            sum += ttl
+            continue
+        ttl = int(ttl_expr[:-1])
+        suffix = ttl_expr[-1].lower()
+        if suffix == 'w':
+            ttl *= 604800
+        elif suffix == 'd':
+            ttl *= 86400
+        elif suffix == 'h':
+            ttl *= 3600
+        elif suffix == 'm':
+            ttl *= 60
+        sum += ttl
+    return str(sum)
 
 #########################################################################
 # records: generator function to return complete RRs from the zone file,
@@ -147,7 +155,9 @@
     record = []
     complete = True
     paren = 0
+    size = 0
     for line in input:
+        size += len(line)
         list = cleanup(line).split()
         for word in list:
             if paren == 0:
@@ -169,10 +179,12 @@
 
         if paren == 1 or not record:
             continue
-
+        
         ret = ' '.join(record)
         record = []
-        yield ret
+        oldsize = size
+        size = 0
+        yield ret, oldsize
 
 #########################################################################
 # define the MasterFile class for reading zone master files
@@ -181,24 +193,62 @@
     __rrclass = 'IN'
     __maxttl = 0x7fffffff
     __ttl = ''
+    __lastttl = ''
     __zonefile = ''
     __name = ''
-
-    def __init__(self, filename, initial_origin = ''):
-        if initial_origin == '.':
-            initial_origin = ''
+    __file_level = 0
+    __file_type = ""
+    __init_time = time.time()
+    __records_num = 0
+
+    def __init__(self, filename, initial_origin = '', verbose = False):
         self.__initial_origin = initial_origin
         self.__origin = initial_origin
+        self.__datafile = filename
+
         try:
             self.__zonefile = open(filename, 'r')
         except:
             raise MasterFileError("Could not open " + filename)
+        self.__filesize = os.fstat(self.__zonefile.fileno()).st_size
+
+        self.__cur = 0
+        self.__numback = 0
+        self.__verbose = verbose
+        try:
+            self.__zonefile = open(filename, 'r')
+        except:
+            raise MasterFileError("Could not open " + filename)
+
+    def __status(self):
+        interval = time.time() - MasterFile.__init_time
+        if self.__filesize == 0:
+            percent = 100
+        else:
+            percent = (self.__cur * 100)/self.__filesize
+
+        sys.stdout.write("\r" + (80 * " "))
+        sys.stdout.write("\r%d RR(s) loaded in %.2f second(s) (%.2f%% of %s%s)"\
+                % (MasterFile.__records_num, interval, percent, MasterFile.__file_type, self.__datafile))
 
     def __del__(self):
         if self.__zonefile:
             self.__zonefile.close()
-
-    #########################################################################
+    ########################################################################
+    # check if the zonename is relative
+    # no then return
+    # yes , sets the relative domain name to the stated name
+    #######################################################################
+    def __statedname(self, name, record):
+        if name[-1] != '.':
+            if not self.__origin:
+                raise MasterFileError("Cannot parse RR, No $ORIGIN: " + record)
+            elif self.__origin == '.':
+                name += '.'
+            else:
+                name += '.' + self.__origin
+        return name
+    #####################################################################
     # handle $ORIGIN, $TTL and $GENERATE directives
     # (currently only $ORIGIN and $TTL are implemented)
     # input:
@@ -216,20 +266,22 @@
                 raise MasterFileError('Invalid $ORIGIN')
             if more:
                 raise MasterFileError('Invalid $ORIGIN')
-            if second == '.':
-                self.__origin = ''
-            elif second[-1] == '.':
+            if second[-1] == '.':
                 self.__origin = second
+            elif not self.__origin:
+                raise MasterFileError("$ORIGIN is not absolute in record:%s" % s)
+            elif self.__origin != '.':
+                self.__origin = second + '.' + self.__origin
             else:
-                self.__origin = second + '.' + self.__origin
+                self.__origin = second + '.'
             return True
         elif re.match('\$ttl', first, re.I):
             if not second or not isttl(second):
                 raise MasterFileError('Invalid TTL: "' + second + '"')
             if more:
                 raise MasterFileError('Invalid $TTL statement')
-            self.__ttl = parse_ttl(second)
-            if int(self.__ttl) > self.__maxttl:
+            MasterFile.__ttl = parse_ttl(second)
+            if int(MasterFile.__ttl) > self.__maxttl:
                 raise MasterFileError('TTL too high: ' + second)
             return True
         elif re.match('\$generate', first, re.I):
@@ -246,14 +298,28 @@
     # throws:
     #   MasterFileError
     #########################################################################
-    __filename = re.compile('[\"\']*([^\'\"]+)[\"\']*')
+    __include_syntax1 = re.compile('\s+(\S+)(?:\s+(\S+))?$', re.I)
+    __include_syntax2 = re.compile('\s+"([^"]+)"(?:\s+(\S+))?$', re.I)
+    __include_syntax3 = re.compile("\s+'([^']+)'(?:\s+(\S+))?$", re.I)
     def __include(self, s):
-        first, rest = pop(s)
-        if re.match('\$include', first, re.I):
-            m = self.__filename.match(rest)
-            if m:
-                file = m.group(1)
-                return file
+        if not s.lower().startswith('$include'):
+            return "", ""
+        s = s[len('$include'):]
+        m = self.__include_syntax1.match(s)
+        if not m:
+            m = self.__include_syntax2.match(s)
+        if not m:
+            m = self.__include_syntax3.match(s)
+        if not m:
+            raise MasterFileError('Invalid $include format')
+        file = m.group(1)
+        if m.group(2):
+            if not isname(m.group(2)):
+                raise MasterFileError('Invalid $include format (invalid origin)')
+            origin = self.__statedname(m.group(2), s)
+        else:
+            origin = self.__origin
+        return file, origin
 
     #########################################################################
     # try parsing an RR on the assumption that the type is specified in
@@ -272,6 +338,14 @@
         if istype(list[3]):
             if isclass(list[2]) and isttl(list[1]) and isname(list[0]):
                 name, ttl, rrclass, rrtype = list[0:4]
+                ttl = parse_ttl(ttl)
+                MasterFile.__lastttl = ttl or MasterFile.__lastttl
+                rdata = ' '.join(list[4:])
+                ret = name, ttl, rrclass, rrtype, rdata
+            elif isclass(list[1]) and isttl(list[2]) and isname(list[0]):
+                name, rrclass, ttl, rrtype = list[0:4]
+                ttl = parse_ttl(ttl)
+                MasterFile.__lastttl = ttl or MasterFile.__lastttl
                 rdata = ' '.join(list[4:])
                 ret = name, ttl, rrclass, rrtype, rdata
         return ret
@@ -284,6 +358,9 @@
     # returns:
     #   empty list if parse failed, else name, ttl, class, type, rdata
     #########################################################################
+    def __getttl(self):
+        return MasterFile.__ttl or MasterFile.__lastttl
+
     def __three(self, record, curname):
         ret = ''
         list = record.split()
@@ -292,19 +369,25 @@
         if istype(list[2]) and not istype(list[1]):
             if isclass(list[1]) and not isttl(list[0]) and isname(list[0]):
                 rrclass = list[1]
-                ttl = self.__ttl
+                ttl = self.__getttl()
                 name = list[0]
-            elif not isclass(list[1]) and isttl(list[1]) and isname(list[0]):
+            elif not isclass(list[1]) and isttl(list[1]) and not isclass(list[0]) and isname(list[0]):
                 rrclass = self.__rrclass
                 ttl = parse_ttl(list[1])
+                MasterFile.__lastttl = ttl or MasterFile.__lastttl
                 name = list[0]
             elif curname and isclass(list[1]) and isttl(list[0]):
-                rrclass = self.__rrclass
+                rrclass = list[1]
                 ttl = parse_ttl(list[0])
+                MasterFile.__lastttl = ttl or MasterFile.__lastttl
+                name = curname
+            elif curname and isttl(list[1]) and isclass(list[0]):
+                rrclass = list[0]
+                ttl = parse_ttl(list[1])
+                MasterFile.__lastttl = ttl or MasterFile.__lastttl
                 name = curname
             else:
                 return ret
-
             rrtype = list[2]
             rdata = ' '.join(list[3:])
             ret = name, ttl, rrclass, rrtype, rdata
@@ -325,29 +408,37 @@
         list = record.split()
         if len(list) <= 2:
             return ret
-
         if istype(list[1]):
             rrclass = self.__rrclass
             rrtype = list[1]
             if list[0].lower() == 'rrsig':
                 name = curname
-                ttl = self.__ttl
+                ttl = self.__getttl()
                 rrtype = list[0]
                 rdata = ' '.join(list[1:])
             elif isttl(list[0]):
                 ttl = parse_ttl(list[0])
                 name = curname
                 rdata = ' '.join(list[2:])
+            elif isclass(list[0]):
+                ttl = self.__getttl()
+                name = curname
+                rdata = ' '.join(list[2:])
             elif isname(list[0]):
                 name = list[0]
-                ttl = self.__ttl
+                ttl = self.__getttl()
                 rdata = ' '.join(list[2:])
             else:
                 raise MasterFileError("Cannot parse RR: " + record)
 
             ret = name, ttl, rrclass, rrtype, rdata
-
         return ret
+
+    ########################################################################
+    #close verbose
+    ######################################################################
+    def closeverbose(self):
+        self.__status()
 
     #########################################################################
     # zonedata: generator function to parse a zone master file and return
@@ -355,16 +446,43 @@
     #########################################################################
     def zonedata(self):
         name = ''
-
-        for record in records(self.__zonefile):
+        last_status = 0.0
+        flag = 1
+
+        for record, size in records(self.__zonefile):
+            if self.__verbose:
+                now = time.time()
+                if flag == 1:
+                    self.__status()
+                    flag = 0
+                if now - last_status >= 1.0:
+                    self.__status()
+                    last_status = now
+
+            self.__cur += size
             if self.__directive(record):
                 continue
 
-            incl = self.__include(record)
+            incl, suborigin = self.__include(record)
             if incl:
-                sub = MasterFile(incl, self.__origin)
-                for name, ttl, rrclass, rrtype, rdata in sub.zonedata():
-                    yield (name, ttl, rrclass, rrtype, rdata)
+                if self.__filesize == 0:
+                    percent = 100
+                else:
+                    percent = (self.__cur * 100)/self.__filesize
+                if self.__verbose:
+                    sys.stdout.write("\r" + (80 * " "))
+                    sys.stdout.write("\rIncluding \"%s\" from \"%s\"\n" % (incl, self.__datafile))
+                MasterFile.__file_level += 1
+                MasterFile.__file_type = "included "
+                sub = MasterFile(incl, suborigin, self.__verbose)
+
+                for rrname, ttl, rrclass, rrtype, rdata in sub.zonedata():
+                    yield (rrname, ttl, rrclass, rrtype, rdata)
+                if self.__verbose:
+                    sub.closeverbose()
+                MasterFile.__file_level -= 1
+                if MasterFile.__file_level == 0:
+                    MasterFile.__file_type = ""
                 del sub
                 continue
 
@@ -373,7 +491,7 @@
             if rl[0] == '@':
                 rl[0] = self.__origin
                 if not self.__origin:
-                    rl[0] = '.'
+                    raise MasterFileError("Cannot parse RR, No $ORIGIN: " + record)
                 record = ' '.join(rl)
 
             result = self.__four(record, name)
@@ -387,36 +505,43 @@
             if not result:
                 first, rdata = pop(record)
                 if istype(first):
-                    result = name, self.__ttl, self.__rrclass, first, rdata
+                    result = name, self.__getttl(), self.__rrclass, first, rdata
 
             if not result:
                 raise MasterFileError("Cannot parse RR: " + record)
 
             name, ttl, rrclass, rrtype, rdata = result
-            if name[-1] != '.':
-                name += '.' + self.__origin
+            name = self.__statedname(name, record)
 
             if rrclass.lower() != 'in':
                 raise MasterFileError("CH and HS zones not supported")
-
-            if not ttl:
-                raise MasterFileError("No TTL specified; zone rejected")
 
             # add origin to rdata containing names, if necessary
             if rrtype.lower() in ('cname', 'dname', 'ns', 'ptr'):
                 if not isname(rdata):
                     raise MasterFileError("Invalid " + rrtype + ": " + rdata)
-                if rdata[-1] != '.':
-                    rdata += '.' + self.__origin
+                rdata = self.__statedname(rdata, record)
+
             if rrtype.lower() == 'soa':
                 soa = rdata.split()
                 if len(soa) < 2 or not isname(soa[0]) or not isname(soa[1]):
                     raise MasterFileError("Invalid " + rrtype + ": " + rdata)
-                if soa[0][-1] != '.':
-                    soa[0] += '.' + self.__origin
-                if soa[1][-1] != '.':
-                    soa[1] += '.' + self.__origin
+                soa[0] = self.__statedname(soa[0], record)
+                soa[1] = self.__statedname(soa[1], record)
+                if not MasterFile.__ttl and not ttl:
+                    MasterFile.__ttl = MasterFile.__ttl or parse_ttl(soa[-1])
+                    ttl = MasterFile.__ttl
+
+                for index in range(3, len(soa)):
+                    if isttl(soa[index]):
+                        soa[index] = parse_ttl(soa[index])
+                    else :
+                        raise MasterFileError("No TTL specified; in soa record!")
                 rdata = ' '.join(soa)
+
+            if not ttl:
+                raise MasterFileError("No TTL specified; zone rejected")
+
             if rrtype.lower() == 'mx':
                 mx = rdata.split()
                 if len(mx) != 2 or not isname(mx[1]):
@@ -424,7 +549,7 @@
                 if mx[1][-1] != '.':
                     mx[1] += '.' + self.__origin
                     rdata = ' '.join(mx)
-
+            MasterFile.__records_num += 1
             yield (name, ttl, rrclass, rrtype, rdata)
 
     #########################################################################
@@ -436,16 +561,22 @@
             return self.__name
         old_origin = self.__origin
         self.__origin = self.__initial_origin
+        cur_value = self.__cur
         old_location = self.__zonefile.tell()
+        old_verbose = self.__verbose
+        self.__verbose = False
         self.__zonefile.seek(0)
+
         for name, ttl, rrclass, rrtype, rdata in self.zonedata():
             if rrtype.lower() == 'soa':
                 break
         self.__zonefile.seek(old_location)
         self.__origin = old_origin
+        self.__cur = cur_value
         if rrtype.lower() != 'soa':
             raise MasterFileError("No SOA found")
         self.__name = name
+        self.__verbose = old_verbose
         return name
 
     #########################################################################
@@ -454,7 +585,8 @@
     def reset(self):
         self.__zonefile.seek(0)
         self.__origin = self.__initial_origin
-        self.__ttl = ''
+        MasterFile.__ttl = ''
+        MasterFile.__lastttl = ''
 
 #########################################################################
 # main: used for testing; parse a zone file and print out each record

Modified: branches/trac191/src/lib/xfr/Makefile.am
==============================================================================
--- branches/trac191/src/lib/xfr/Makefile.am (original)
+++ branches/trac191/src/lib/xfr/Makefile.am Fri Jul  9 10:04:58 2010
@@ -10,20 +10,10 @@
 libxfr_la_SOURCES = xfrout_client.h xfrout_client.cc 
 libxfr_la_SOURCES += fd_share.h fd_share.cc
 
-if HAVE_BOOST_PYTHON
-pyexec_LTLIBRARIES = bind10_xfr.la
-bind10_xfr_la_SOURCES = python_xfr.cc fd_share.cc fd_share.h
-bind10_xfr_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
-bind10_xfr_la_CXXFLAGS = $(AM_CXXFLAGS)
-if GCC_WERROR_OK
-# XXX: Boost.Python triggers strict aliasing violation, so if we use -Werror
-# we need to suppress the warnings.
-bind10_xfr_la_CXXFLAGS += -fno-strict-aliasing
-endif
-bind10_xfr_la_LDFLAGS = $(BOOST_LDFLAGS) $(PYTHON_LDFLAGS)
+pyexec_LTLIBRARIES = libxfr_python.la
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # suffix for dynamic objects.  -module is necessary to work this around.
-bind10_xfr_la_LDFLAGS += -module
-bind10_xfr_la_LIBADD = $(BOOST_PYTHON_LIB) $(PYTHON_LIB)
-endif
-
+libxfr_python_la_LDFLAGS = -module
+libxfr_python_la_SOURCES = fdshare_python.cc fd_share.cc fd_share.h
+libxfr_python_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+libxfr_python_la_CXXFLAGS = $(AM_CXXFLAGS)

Modified: branches/trac191/src/lib/xfr/fd_share.cc
==============================================================================
--- branches/trac191/src/lib/xfr/fd_share.cc (original)
+++ branches/trac191/src/lib/xfr/fd_share.cc Fri Jul  9 10:04:58 2010
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
+#include <stdlib.h>             // for malloc and free
 #include "fd_share.h"
 
 namespace isc {

Modified: branches/trac191/src/lib/xfr/fd_share.h
==============================================================================
--- branches/trac191/src/lib/xfr/fd_share.h (original)
+++ branches/trac191/src/lib/xfr/fd_share.h Fri Jul  9 10:04:58 2010
@@ -23,12 +23,12 @@
 // Receive socket descriptor on unix domain socket 'sock'.
 // Returned value is the socket descriptor received.
 // Errors are indicated by a return value of -1.
-int recv_fd(int sock);
+int recv_fd(const int sock);
 
 // Send socket descriptor "fd" to server over unix domain socket 'sock', 
 // the connection from socket 'sock' to unix domain server should be established first.
 // Errors are indicated by a return value of -1.
-int send_fd(int sock, int fd);
+int send_fd(const int sock, const int fd);
 
 } // End for namespace xfr
 } // End for namespace isc

Modified: branches/trac191/src/lib/xfr/python_xfr.cc
==============================================================================
--- branches/trac191/src/lib/xfr/python_xfr.cc (original)
+++ branches/trac191/src/lib/xfr/python_xfr.cc Fri Jul  9 10:04:58 2010
@@ -1,4 +1,4 @@
-// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above

Modified: branches/trac191/src/lib/xfr/xfrout_client.cc
==============================================================================
--- branches/trac191/src/lib/xfr/xfrout_client.cc (original)
+++ branches/trac191/src/lib/xfr/xfrout_client.cc Fri Jul  9 10:04:58 2010
@@ -18,6 +18,8 @@
 #include <cstring>
 #include <iostream>
 
+// for some IPC/network system calls in asio/detail/pipe_select_interrupter.hpp
+#include <unistd.h>
 #include <asio.hpp>
 
 #include "fd_share.h"
@@ -66,7 +68,7 @@
 {
     if (-1 == send_fd(impl_->socket_.native(), tcp_sock)) {
         isc_throw(XfroutError,
-                  "Fail to send socket descriptor to xfrout module");
+                  "Fail to send the socket file descriptor to xfrout module");
     }
 
     // XXX: this shouldn't be blocking send, even though it's unlikely to block.

Modified: branches/trac191/tools/import_boost.sh
==============================================================================
--- branches/trac191/tools/import_boost.sh (original)
+++ branches/trac191/tools/import_boost.sh Fri Jul  9 10:04:58 2010
@@ -30,6 +30,7 @@
 boost/iterator
 boost/mpl
 boost/preprocessor
+boost/python
 boost/range
 boost/smart_ptr
 boost/type_traits




More information about the bind10-changes mailing list