[svn] commit: r2663 - in /branches/trac241: ./ doc/ doc/guide/ ext/asio/ src/bin/auth/ src/bin/auth/tests/ src/bin/bind10/ src/bin/bind10/tests/ src/bin/bindctl/ src/bin/bindctl/tests/ src/bin/cfgmgr/ src/bin/cfgmgr/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/testdata/ 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/dns/util/ src/lib/exceptions/ 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
Mon Aug 9 18:42:00 UTC 2010


Author: jinmei
Date: Mon Aug  9 18:42:00 2010
New Revision: 2663

Log:
sync w/ trunk

Added:
    branches/trac241/ext/asio/README
      - copied unchanged from r2662, trunk/ext/asio/README
    branches/trac241/src/bin/auth/tests/asio_link_unittest.cc
      - copied unchanged from r2662, trunk/src/bin/auth/tests/asio_link_unittest.cc
    branches/trac241/src/bin/bind10/tests/args_test.py
      - copied unchanged from r2662, trunk/src/bin/bind10/tests/args_test.py
    branches/trac241/src/bin/cfgmgr/tests/
      - copied from r2662, trunk/src/bin/cfgmgr/tests/
    branches/trac241/src/bin/cmdctl/cmdctl.spec.pre.in
      - copied unchanged from r2662, trunk/src/bin/cmdctl/cmdctl.spec.pre.in
    branches/trac241/src/bin/loadzone/tests/
      - copied from r2662, trunk/src/bin/loadzone/tests/
    branches/trac241/src/lib/config/testdata/spec28.spec
      - copied unchanged from r2662, trunk/src/lib/config/testdata/spec28.spec
    branches/trac241/src/lib/datasrc/cache.cc
      - copied unchanged from r2662, trunk/src/lib/datasrc/cache.cc
    branches/trac241/src/lib/datasrc/cache.h
      - copied unchanged from r2662, trunk/src/lib/datasrc/cache.h
    branches/trac241/src/lib/datasrc/tests/cache_unittest.cc
      - copied unchanged from r2662, trunk/src/lib/datasrc/tests/cache_unittest.cc
    branches/trac241/src/lib/dns/python/
      - copied from r2662, trunk/src/lib/dns/python/
    branches/trac241/src/lib/dns/tests/base32hex_unittest.cc
      - copied unchanged from r2662, trunk/src/lib/dns/tests/base32hex_unittest.cc
    branches/trac241/src/lib/dns/util/
      - copied from r2662, trunk/src/lib/dns/util/
    branches/trac241/src/lib/python/isc/cc/tests/message_test.py
      - copied unchanged from r2662, trunk/src/lib/python/isc/cc/tests/message_test.py
    branches/trac241/src/lib/python/isc/dns/
      - copied from r2662, trunk/src/lib/python/isc/dns/
    branches/trac241/src/lib/python/isc/log/
      - copied from r2662, trunk/src/lib/python/isc/log/
    branches/trac241/src/lib/xfr/fdshare_python.cc
      - copied unchanged from r2662, trunk/src/lib/xfr/fdshare_python.cc
Removed:
    branches/trac241/src/bin/bindctl/bindctl.pem
    branches/trac241/src/bin/cmdctl/cmdctl.spec
    branches/trac241/src/bin/loadzone/testdata/
    branches/trac241/src/lib/datasrc/tests/testdata/q_cname
    branches/trac241/src/lib/datasrc/tests/testdata/q_cname_ext
    branches/trac241/src/lib/datasrc/tests/testdata/q_cname_int
    branches/trac241/src/lib/datasrc/tests/testdata/q_dname
    branches/trac241/src/lib/datasrc/tests/testdata/q_example_ns
    branches/trac241/src/lib/datasrc/tests/testdata/q_example_ptr
    branches/trac241/src/lib/datasrc/tests/testdata/q_glork
    branches/trac241/src/lib/datasrc/tests/testdata/q_spork
    branches/trac241/src/lib/datasrc/tests/testdata/q_sql1
    branches/trac241/src/lib/datasrc/tests/testdata/q_subzone
    branches/trac241/src/lib/datasrc/tests/testdata/q_subzone_any
    branches/trac241/src/lib/datasrc/tests/testdata/q_subzone_dname
    branches/trac241/src/lib/datasrc/tests/testdata/q_subzone_ds
    branches/trac241/src/lib/datasrc/tests/testdata/q_subzone_ns
    branches/trac241/src/lib/datasrc/tests/testdata/q_subzone_nsec
    branches/trac241/src/lib/datasrc/tests/testdata/q_wild2_a
    branches/trac241/src/lib/datasrc/tests/testdata/q_wild2_aaaa
    branches/trac241/src/lib/datasrc/tests/testdata/q_wild3_a
    branches/trac241/src/lib/datasrc/tests/testdata/q_wild_a
    branches/trac241/src/lib/datasrc/tests/testdata/q_wild_aaaa
    branches/trac241/src/lib/dns/base32.cc
    branches/trac241/src/lib/dns/base32.h
    branches/trac241/src/lib/dns/base64.cc
    branches/trac241/src/lib/dns/base64.h
    branches/trac241/src/lib/dns/hex.cc
    branches/trac241/src/lib/dns/hex.h
    branches/trac241/src/lib/dns/python_dns.cc
    branches/trac241/src/lib/dns/sha1.cc
    branches/trac241/src/lib/dns/sha1.h
    branches/trac241/src/lib/dns/tests/base32_unittest.cc
    branches/trac241/src/lib/python/isc/cc/tests/test.py
Modified:
    branches/trac241/   (props changed)
    branches/trac241/ChangeLog
    branches/trac241/Makefile.am
    branches/trac241/README
    branches/trac241/configure.ac
    branches/trac241/doc/Doxyfile
    branches/trac241/doc/guide/bind10-guide.html
    branches/trac241/doc/guide/bind10-guide.xml
    branches/trac241/src/bin/auth/Makefile.am
    branches/trac241/src/bin/auth/asio_link.cc
    branches/trac241/src/bin/auth/asio_link.h
    branches/trac241/src/bin/auth/auth.spec.pre.in
    branches/trac241/src/bin/auth/auth_srv.cc
    branches/trac241/src/bin/auth/auth_srv.h
    branches/trac241/src/bin/auth/main.cc
    branches/trac241/src/bin/auth/tests/Makefile.am
    branches/trac241/src/bin/auth/tests/auth_srv_unittest.cc
    branches/trac241/src/bin/bind10/bind10.py.in
    branches/trac241/src/bin/bind10/run_bind10.sh.in
    branches/trac241/src/bin/bind10/tests/bind10_test.in
    branches/trac241/src/bin/bind10/tests/bind10_test.py
    branches/trac241/src/bin/bindctl/Makefile.am
    branches/trac241/src/bin/bindctl/TODO
    branches/trac241/src/bin/bindctl/bindcmd.py
    branches/trac241/src/bin/bindctl/bindctl-source.py.in
    branches/trac241/src/bin/bindctl/cmdparse.py
    branches/trac241/src/bin/bindctl/exception.py
    branches/trac241/src/bin/bindctl/tests/bindctl_test.py
    branches/trac241/src/bin/cfgmgr/Makefile.am
    branches/trac241/src/bin/cfgmgr/b10-cfgmgr.py.in
    branches/trac241/src/bin/cmdctl/Makefile.am
    branches/trac241/src/bin/cmdctl/TODO
    branches/trac241/src/bin/cmdctl/cmdctl.py.in
    branches/trac241/src/bin/cmdctl/tests/Makefile.am
    branches/trac241/src/bin/cmdctl/tests/cmdctl_test.in
    branches/trac241/src/bin/cmdctl/tests/cmdctl_test.py
    branches/trac241/src/bin/host/Makefile.am
    branches/trac241/src/bin/host/host.cc
    branches/trac241/src/bin/loadzone/Makefile.am
    branches/trac241/src/bin/loadzone/b10-loadzone.py.in
    branches/trac241/src/bin/loadzone/run_loadzone.sh.in
    branches/trac241/src/bin/xfrin/   (props changed)
    branches/trac241/src/bin/xfrin/tests/Makefile.am
    branches/trac241/src/bin/xfrin/tests/xfrin_test.py
    branches/trac241/src/bin/xfrin/xfrin.py.in
    branches/trac241/src/bin/xfrin/xfrin.spec.pre.in
    branches/trac241/src/bin/xfrout/run_b10-xfrout.sh.in
    branches/trac241/src/bin/xfrout/tests/Makefile.am
    branches/trac241/src/bin/xfrout/tests/xfrout_test.py
    branches/trac241/src/bin/xfrout/xfrout.py.in
    branches/trac241/src/bin/xfrout/xfrout.spec.pre.in
    branches/trac241/src/lib/Makefile.am
    branches/trac241/src/lib/cc/   (props changed)
    branches/trac241/src/lib/cc/Makefile.am
    branches/trac241/src/lib/cc/data.cc
    branches/trac241/src/lib/cc/data.h
    branches/trac241/src/lib/cc/data_unittests.cc
    branches/trac241/src/lib/cc/session.cc
    branches/trac241/src/lib/cc/session.h
    branches/trac241/src/lib/cc/session_unittests.cc
    branches/trac241/src/lib/config/Makefile.am
    branches/trac241/src/lib/config/ccsession.cc
    branches/trac241/src/lib/config/ccsession.h
    branches/trac241/src/lib/config/config_data.cc
    branches/trac241/src/lib/config/config_data.h
    branches/trac241/src/lib/config/module_spec.cc
    branches/trac241/src/lib/config/module_spec.h
    branches/trac241/src/lib/config/testdata/b10-config.db
    branches/trac241/src/lib/config/testdata/data22_1.data
    branches/trac241/src/lib/config/testdata/data22_2.data
    branches/trac241/src/lib/config/testdata/data22_3.data
    branches/trac241/src/lib/config/testdata/data22_4.data
    branches/trac241/src/lib/config/testdata/data22_5.data
    branches/trac241/src/lib/config/testdata/data22_6.data
    branches/trac241/src/lib/config/testdata/data22_7.data
    branches/trac241/src/lib/config/testdata/data22_8.data
    branches/trac241/src/lib/config/testdata/spec10.spec
    branches/trac241/src/lib/config/testdata/spec11.spec
    branches/trac241/src/lib/config/testdata/spec12.spec
    branches/trac241/src/lib/config/testdata/spec13.spec
    branches/trac241/src/lib/config/testdata/spec14.spec
    branches/trac241/src/lib/config/testdata/spec15.spec
    branches/trac241/src/lib/config/testdata/spec17.spec
    branches/trac241/src/lib/config/testdata/spec2.spec
    branches/trac241/src/lib/config/testdata/spec20.spec
    branches/trac241/src/lib/config/testdata/spec22.spec
    branches/trac241/src/lib/config/testdata/spec23.spec
    branches/trac241/src/lib/config/testdata/spec24.spec
    branches/trac241/src/lib/config/testdata/spec27.spec
    branches/trac241/src/lib/config/testdata/spec3.spec
    branches/trac241/src/lib/config/testdata/spec4.spec
    branches/trac241/src/lib/config/testdata/spec6.spec
    branches/trac241/src/lib/config/testdata/spec9.spec
    branches/trac241/src/lib/config/tests/Makefile.am
    branches/trac241/src/lib/config/tests/ccsession_unittests.cc
    branches/trac241/src/lib/config/tests/config_data_unittests.cc
    branches/trac241/src/lib/config/tests/fake_session.cc
    branches/trac241/src/lib/config/tests/fake_session.h
    branches/trac241/src/lib/config/tests/module_spec_unittests.cc
    branches/trac241/src/lib/datasrc/   (props changed)
    branches/trac241/src/lib/datasrc/Makefile.am
    branches/trac241/src/lib/datasrc/data_source.cc
    branches/trac241/src/lib/datasrc/data_source.h
    branches/trac241/src/lib/datasrc/query.cc
    branches/trac241/src/lib/datasrc/query.h
    branches/trac241/src/lib/datasrc/sqlite3_datasrc.cc
    branches/trac241/src/lib/datasrc/sqlite3_datasrc.h
    branches/trac241/src/lib/datasrc/static_datasrc.cc
    branches/trac241/src/lib/datasrc/static_datasrc.h
    branches/trac241/src/lib/datasrc/tests/Makefile.am
    branches/trac241/src/lib/datasrc/tests/datasrc_unittest.cc
    branches/trac241/src/lib/datasrc/tests/query_unittest.cc
    branches/trac241/src/lib/datasrc/tests/sqlite3_unittest.cc
    branches/trac241/src/lib/datasrc/tests/static_unittest.cc
    branches/trac241/src/lib/datasrc/tests/test_datasrc.cc
    branches/trac241/src/lib/datasrc/tests/test_datasrc.h
    branches/trac241/src/lib/datasrc/tests/testdata/example.org
    branches/trac241/src/lib/datasrc/tests/testdata/example.org.sqlite3
    branches/trac241/src/lib/dns/   (props changed)
    branches/trac241/src/lib/dns/Makefile.am
    branches/trac241/src/lib/dns/message.cc
    branches/trac241/src/lib/dns/message.h
    branches/trac241/src/lib/dns/name.cc
    branches/trac241/src/lib/dns/question.h
    branches/trac241/src/lib/dns/rdata/generic/dnskey_48.cc
    branches/trac241/src/lib/dns/rdata/generic/ds_43.cc
    branches/trac241/src/lib/dns/rdata/generic/nsec3_50.cc
    branches/trac241/src/lib/dns/rdata/generic/nsec3param_51.cc
    branches/trac241/src/lib/dns/rdata/generic/nsec_47.cc
    branches/trac241/src/lib/dns/rdata/generic/rrsig_46.cc   (contents, props changed)
    branches/trac241/src/lib/dns/rrsetlist.cc
    branches/trac241/src/lib/dns/rrsetlist.h
    branches/trac241/src/lib/dns/tests/   (props changed)
    branches/trac241/src/lib/dns/tests/Makefile.am
    branches/trac241/src/lib/dns/tests/base64_unittest.cc
    branches/trac241/src/lib/dns/tests/hex_unittest.cc
    branches/trac241/src/lib/dns/tests/message_unittest.cc
    branches/trac241/src/lib/dns/tests/messagerenderer_unittest.cc
    branches/trac241/src/lib/dns/tests/name_unittest.cc
    branches/trac241/src/lib/dns/tests/question_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_cname_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_dname_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_dnskey_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_ds_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_in_a_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_mx_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_ns_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_nsec3_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_nsec3param_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_nsec_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_opt_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_ptr_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_rrsig_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_soa_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_txt_unittest.cc
    branches/trac241/src/lib/dns/tests/rdata_unittest.cc
    branches/trac241/src/lib/dns/tests/rrclass_unittest.cc
    branches/trac241/src/lib/dns/tests/rrset_unittest.cc
    branches/trac241/src/lib/dns/tests/rrsetlist_unittest.cc
    branches/trac241/src/lib/dns/tests/rrttl_unittest.cc
    branches/trac241/src/lib/dns/tests/rrtype_unittest.cc
    branches/trac241/src/lib/dns/tests/run_unittests.cc
    branches/trac241/src/lib/dns/tests/sha1_unittest.cc
    branches/trac241/src/lib/dns/tests/tsig_unittest.cc
    branches/trac241/src/lib/dns/tests/unittest_util.cc
    branches/trac241/src/lib/exceptions/Makefile.am
    branches/trac241/src/lib/exceptions/exceptions.cc
    branches/trac241/src/lib/exceptions/exceptions.h
    branches/trac241/src/lib/exceptions/exceptions_unittest.cc
    branches/trac241/src/lib/python/isc/Makefile.am
    branches/trac241/src/lib/python/isc/__init__.py
    branches/trac241/src/lib/python/isc/cc/data.py
    branches/trac241/src/lib/python/isc/cc/message.py
    branches/trac241/src/lib/python/isc/cc/tests/Makefile.am
    branches/trac241/src/lib/python/isc/cc/tests/data_test.py
    branches/trac241/src/lib/python/isc/cc/tests/session_test.py
    branches/trac241/src/lib/python/isc/config/ccsession.py
    branches/trac241/src/lib/python/isc/config/cfgmgr.py
    branches/trac241/src/lib/python/isc/config/config_data.py
    branches/trac241/src/lib/python/isc/config/module_spec.py
    branches/trac241/src/lib/python/isc/config/tests/ccsession_test.py
    branches/trac241/src/lib/python/isc/config/tests/cfgmgr_test.py
    branches/trac241/src/lib/python/isc/config/tests/config_data_test.py
    branches/trac241/src/lib/python/isc/config/tests/module_spec_test.py
    branches/trac241/src/lib/python/isc/config/tests/unittest_fakesession.py
    branches/trac241/src/lib/python/isc/datasrc/master.py
    branches/trac241/src/lib/xfr/Makefile.am
    branches/trac241/src/lib/xfr/fd_share.cc
    branches/trac241/src/lib/xfr/fd_share.h
    branches/trac241/src/lib/xfr/python_xfr.cc
    branches/trac241/src/lib/xfr/xfrout_client.cc
    branches/trac241/src/lib/xfr/xfrout_client.h
    branches/trac241/tools/import_boost.sh

Modified: branches/trac241/ChangeLog
==============================================================================
--- branches/trac241/ChangeLog (original)
+++ branches/trac241/ChangeLog Mon Aug  9 18:42:00 2010
@@ -1,16 +1,189 @@
-  54.   [bug]      zhanglikun
+  80.	[bug]		jelte
+	bindctl no longer accepts configuration changes for unknown or
+	non-running modules (for the latter, this is until we have a
+	way to verify those options, at which point it'll be allowed
+	again).
+	(Trac #99, r2657)
+
+  79.	[func]		feng, jinmei
+	Refactored the ASIO link interfaces to move incoming XFR and
+	NOTIFY processing to the auth server class.  Wrapper classes for
+	ASIO specific concepts were also provided, so that other BIND 10
+	modules can (eventually) use the interface without including the
+	ASIO header file directly.  On top of these changes, AXFR and
+	NOTIFY processing was massively improved in terms of message
+	validation and protocol conformance.  Detailed tests were provided
+	to confirm the behavior.
+	Note: Right now, NOTIFY doesn't actually trigger subsequent zone
+	transfer due to security reasons. (Trac #221, r2565)
+
+  78.	[bug]		jinmei
+	lib/dns: Fixed miscellaneous bugs in the base32 (hex) and hex
+	(base16) implementation, including incorrect padding handling,
+	parser failure in decoding with a SunStudio build, missing
+	validation on the length of encoded hex string.  Test cases were
+	more detailed to identify these bugs and confirm the fix.  Also
+	renamed the incorrect term of "base32" to "base32hex".  This
+	changed the API, but they are not intended to be used outside
+	libdns++, so we don't consider it a backward incompatible change.
+	(Trac #256, r2549)
+
+  77.	[func]		zhanglikun
+	Make error message be more friendly when running cmdctl and it's 
+	already running(listening on same port)(Trac #277, r2540)
+
+  76.	[bug]		jelte
+	Fixed a bug in the handling of 'remote' config modules (i.e.
+	modules that peek at the configuration of other modules), where
+	they answered 'unknown command' to commands for those other
+	modules. (Trac #278, r2506)
+
+  75.	[bug]		jinmei
+	Fixed a bug in the sqlite3 data source where temporary strings
+	could be referenced after destruction.  It caused various lookup
+	failures with SunStudio build. (Trac #288, r2494)
+
+  74.	[func]*		jinmei
+	Refactored the cc::Session class by introducing an abstract base
+	class.  Test code can use their own derived mock class so that
+	tests can be done without establishing a real CC session.  This
+	change also modified some public APIs, mainly in the config
+	module. (Trac #275, r2459)
+
+  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
+	application crashes, sometimes it blocked in the ASIO module.
+	(Trac #248, svn r2187, r2190)
+
+  57.	[func]		jinmei
+	lib/datasrc: used a simpler version of Name::split (change 31) for
+	better readability.  No behavior change. (Trac #200, svn r2159)
+
+  56.	[func]*		jinmei
+	lib/dns: renamed the library name to libdns++ to avoid confusion
+	with the same name of library of BIND 9.
+	(Trac #190, svn r2153)
+
+  55.	[bug]		shane
+	bin/xfrout: xfrout exception on Ctrl-C now no longer generates
+	exception for 'Interrupted system call'
+	(Track #136, svn r2147)
+
+  54.	[bug]		zhanglikun
+>>>>>>> .merge-right.r2662
 	bin/xfrout: Enable b10-xfrout can be launched in source
 	code tree.
 	(Trac #224, svn r2103)
 
-  53.   [bug]      zhanglikun
+  53.	[bug]		zhanglikun
 	bin/bindctl: Generate a unique session ID by using 
 	socket.gethostname() instead of socket.gethostbyname(), 
 	since the latter one could make bindctl	stall if its own 
 	host name can't be resolved.
 	(Trac #228, svn r2096)
 
-  52.   [func]      zhanglikun
+  52.	[func]		zhanglikun
 	bin/xfrout: When xfrout is launched, check whether the
 	socket file is being used by one running xfrout process, 
 	if it is, exit from python.	If the file isn't a socket file 
@@ -20,7 +193,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.
@@ -72,6 +245,10 @@
 	Renamed libauth to libdatasrc.
 
   38.   [bug]           zhanglikun
+	Send command 'shutdown' to Xfrin and Xfrout when boss receive SIGINT.
+	Remove unused socket file when Xfrout process exits. Make sure Xfrout
+	exit by itself when it receives SIGINT, instead of being killed by the
+	signal SIGTERM or SIGKILL sent from boss.
 	(Trac #135, #151, #134, svn r1797)
 
   37.   [build]         jinmei
@@ -132,7 +309,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/trac241/Makefile.am
==============================================================================
--- branches/trac241/Makefile.am (original)
+++ branches/trac241/Makefile.am Mon Aug  9 18:42:00 2010
@@ -41,8 +41,8 @@
 coverage: clean-coverage perform-coverage report-coverage
 
 #### include external sources in the distributed tarball:
-# EXTRA_DIST = ext/asio/README
-EXTRA_DIST = ext/asio/asio/local/stream_protocol.hpp
+EXTRA_DIST = ext/asio/README
+EXTRA_DIST += ext/asio/asio/local/stream_protocol.hpp
 EXTRA_DIST += ext/asio/asio/local/basic_endpoint.hpp
 EXTRA_DIST += ext/asio/asio/local/datagram_protocol.hpp
 EXTRA_DIST += ext/asio/asio/local/connect_pair.hpp

Modified: branches/trac241/README
==============================================================================
--- branches/trac241/README (original)
+++ branches/trac241/README Mon Aug  9 18:42:00 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/trac241/configure.ac
==============================================================================
--- branches/trac241/configure.ac (original)
+++ branches/trac241/configure.ac Mon Aug  9 18:42:00 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
 
@@ -393,8 +398,11 @@
                  src/bin/bindctl/Makefile
                  src/bin/bindctl/tests/Makefile
                  src/bin/cfgmgr/Makefile
+                 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,19 +424,25 @@
                  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
                  src/lib/xfr/Makefile
                ])
 AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
+           src/bin/cfgmgr/tests/b10-cfgmgr_test.py
            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
@@ -444,6 +458,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
@@ -455,6 +471,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
@@ -470,6 +487,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
@@ -497,8 +516,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/trac241/doc/Doxyfile
==============================================================================
--- branches/trac241/doc/Doxyfile (original)
+++ branches/trac241/doc/Doxyfile Mon Aug  9 18:42:00 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 ../src/lib/bench
+INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/lib/bench
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

Modified: branches/trac241/doc/guide/bind10-guide.html
==============================================================================
--- branches/trac241/doc/guide/bind10-guide.html (original)
+++ branches/trac241/doc/guide/bind10-guide.html Mon Aug  9 18:42:00 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/trac241/doc/guide/bind10-guide.xml
==============================================================================
--- branches/trac241/doc/guide/bind10-guide.xml (original)
+++ branches/trac241/doc/guide/bind10-guide.xml Mon Aug  9 18:42:00 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/trac241/src/bin/auth/Makefile.am
==============================================================================
--- branches/trac241/src/bin/auth/Makefile.am (original)
+++ branches/trac241/src/bin/auth/Makefile.am Mon Aug  9 18:42:00 2010
@@ -1,8 +1,10 @@
 SUBDIRS = . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
@@ -36,7 +38,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 
@@ -45,15 +50,13 @@
 b10_auth_SOURCES += common.h
 b10_auth_SOURCES += main.cc
 b10_auth_LDADD =  $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a
-b10_auth_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a
+b10_auth_LDADD += $(top_builddir)/src/lib/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 += $(top_builddir)/src/lib/xfr/.libs/libxfr.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/trac241/src/bin/auth/asio_link.cc
==============================================================================
--- branches/trac241/src/bin/auth/asio_link.cc (original)
+++ branches/trac241/src/bin/auth/asio_link.cc Mon Aug  9 18:42:00 2010
@@ -16,81 +16,168 @@
 
 #include <config.h>
 
+#include <unistd.h>             // for some IPC/network system calls
+#include <sys/socket.h>
+#include <netinet/in.h>
+
 #include <asio.hpp>
+#include <boost/lexical_cast.hpp>
 #include <boost/bind.hpp>
+
+#include <boost/shared_ptr.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 <auth/auth_srv.h>
+#include <auth/common.h>
 
 using namespace asio;
-using ip::udp;
-using ip::tcp;
+using asio::ip::udp;
+using asio::ip::tcp;
 
 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
-//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) {
-    if (msg_len < 15) {
-        return false;
-    }
-
-    const uint16_t query_type = *(uint16_t *)(msg_data + (msg_len - 4));
-    if ( query_type == 0xFC00) {
-        return true;
-    }
-    
-    return false;
-}
-
-//TODO. Send the xfr query to xfrout module, the code should be merged to auth server class
-void
-dispatch_axfr_query(const int tcp_sock, char const axfr_query[],
-                    const uint16_t query_len)
+
+namespace asio_link {
+IOAddress::IOAddress(const string& address_str)
+    // XXX: we cannot simply construct the address in the initialization list
+    // because we'd like to throw our own exception on failure.
 {
-    string path;
-    if (getenv("B10_FROM_BUILD")) {
-        path = string(getenv("B10_FROM_BUILD")) + "/auth_xfrout_conn";
-    } else {
-        path = UNIX_SOCKET_FILE;
-    }
-    
-    XfroutClient xfr_client(path);
-    try {
-        xfr_client.connect();
-        xfr_client.sendXfroutRequestInfo(tcp_sock, (uint8_t *)axfr_query,
-                                         query_len);
-        xfr_client.disconnect();
-    }
-    catch (const exception & err) {
-        //if (verbose_mode)
-        cerr << "error handle xfr query:" << err.what() << endl;
-    }
-}
-#endif
-}
-
-namespace asio_link {
+    error_code err;
+    asio_address_ = ip::address::from_string(address_str, err);
+    if (err) {
+        isc_throw(IOError, "Failed to convert string to address '"
+                  << address_str << "': " << err.message());
+    }
+}
+
+IOAddress::IOAddress(const ip::address& asio_address) :
+    asio_address_(asio_address)
+{}
+
+string
+IOAddress::toText() const {
+    return (asio_address_.to_string());
+}
+
+// Note: this implementation is optimized for the case where this object
+// is created from an ASIO endpoint object in a receiving code path
+// by avoiding to make a copy of the base endpoint.  For TCP it may not be
+// a bug deal, but when we receive UDP packets at a high rate, the copy
+// overhead might be significant.
+class TCPEndpoint : public IOEndpoint {
+public:
+    TCPEndpoint(const IOAddress& address, const unsigned short port) :
+        asio_endpoint_placeholder_(
+            new tcp::endpoint(ip::address::from_string(address.toText()),
+                              port)),
+        asio_endpoint_(*asio_endpoint_placeholder_)
+    {}
+    TCPEndpoint(const tcp::endpoint& asio_endpoint) :
+        asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
+    {}
+        
+    ~TCPEndpoint() { delete asio_endpoint_placeholder_; }
+    virtual IOAddress getAddress() const {
+        return (asio_endpoint_.address());
+    }
+private:
+    const tcp::endpoint* asio_endpoint_placeholder_;
+    const tcp::endpoint& asio_endpoint_;
+};
+
+class UDPEndpoint : public IOEndpoint {
+public:
+    UDPEndpoint(const IOAddress& address, const unsigned short port) :
+        asio_endpoint_placeholder_(
+            new udp::endpoint(ip::address::from_string(address.toText()),
+                              port)),
+        asio_endpoint_(*asio_endpoint_placeholder_)
+    {}
+    UDPEndpoint(const udp::endpoint& asio_endpoint) :
+        asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
+    {}
+    ~UDPEndpoint() { delete asio_endpoint_placeholder_; }
+    virtual IOAddress getAddress() const {
+        return (asio_endpoint_.address());
+    }
+private:
+    const udp::endpoint* asio_endpoint_placeholder_;
+    const udp::endpoint& asio_endpoint_;
+};
+
+const IOEndpoint*
+IOEndpoint::create(const int protocol, const IOAddress& address,
+                   const unsigned short port)
+{
+    if (protocol == IPPROTO_UDP) {
+        return (new UDPEndpoint(address, port));
+    } else if (protocol == IPPROTO_TCP) {
+        return (new TCPEndpoint(address, port));
+    }
+    isc_throw(IOError,
+              "IOEndpoint creation attempt for unsupported protocol: " <<
+              protocol);
+}
+
+class TCPSocket : public IOSocket {
+private:
+    TCPSocket(const TCPSocket& source);
+    TCPSocket& operator=(const TCPSocket& source);
+public:
+    TCPSocket(tcp::socket& socket) : socket_(socket) {}
+    virtual int getNative() const { return (socket_.native()); }
+    virtual int getProtocol() const { return (IPPROTO_TCP); }
+private:
+    tcp::socket& socket_;
+};
+
+class UDPSocket : public IOSocket {
+private:
+    UDPSocket(const UDPSocket& source);
+    UDPSocket& operator=(const UDPSocket& source);
+public:
+    UDPSocket(udp::socket& socket) : socket_(socket) {}
+    virtual int getNative() const { return (socket_.native()); }
+    virtual int getProtocol() const { return (IPPROTO_UDP); }
+private:
+    udp::socket& socket_;
+};
+
+class DummySocket : public IOSocket {
+private:
+    DummySocket(const DummySocket& source);
+    DummySocket& operator=(const DummySocket& source);
+public:
+    DummySocket(const int protocol) : protocol_(protocol) {}
+    virtual int getNative() const { return (-1); }
+    virtual int getProtocol() const { return (protocol_); }
+private:
+    const int protocol_;
+};
+
+IOSocket&
+IOSocket::getDummyUDPSocket() {
+    static DummySocket socket(IPPROTO_UDP);
+    return (socket);
+}
+
+IOSocket&
+IOSocket::getDummyTCPSocket() {
+    static DummySocket socket(IPPROTO_TCP);
+    return (socket);
+}
+
+IOMessage::IOMessage(const void* data, const size_t data_size,
+                     IOSocket& io_socket, const IOEndpoint& remote_endpoint) :
+    data_(data), data_size_(data_size), io_socket_(io_socket),
+    remote_endpoint_(remote_endpoint)
+{}
+
 //
 // Helper classes for asynchronous I/O using asio
 //
@@ -99,15 +186,18 @@
     TCPClient(AuthSrv* auth_server, io_service& io_service) :
         auth_server_(auth_server),
         socket_(io_service),
+        io_socket_(socket_),
         response_buffer_(0),
         responselen_buffer_(TCP_MESSAGE_LENGTHSIZE),
         response_renderer_(response_buffer_),
-        dns_message_(Message::PARSE)
+        dns_message_(Message::PARSE),
+        custom_callback_(NULL)
     {}
 
     void start() {
         // Check for queued configuration commands
-        if (auth_server_->configSession()->hasQueuedMsgs()) {
+        if (auth_server_ != NULL &&
+            auth_server_->configSession()->hasQueuedMsgs()) {
             auth_server_->configSession()->checkCommand();
         }
         async_read(socket_, asio::buffer(data_, TCP_MESSAGE_LENGTHSIZE),
@@ -126,7 +216,6 @@
 
             uint16_t msglen = dnsbuffer.readUint16();
             async_read(socket_, asio::buffer(data_, msglen),
-
                        boost::bind(&TCPClient::requestRead, this,
                                    placeholders::error,
                                    placeholders::bytes_transferred));
@@ -139,30 +228,29 @@
                      size_t bytes_transferred)
     {
         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 ?
+            const TCPEndpoint remote_endpoint(socket_.remote_endpoint());
+            const IOMessage io_message(data_, bytes_transferred, io_socket_,
+                                       remote_endpoint);
+            // currently, for testing purpose only
+            if (custom_callback_ != NULL) {
+                (*custom_callback_)(io_message);
                 start();
+                return;
+            }
+
+            if (auth_server_->processMessage(io_message, dns_message_,
+                                             response_renderer_)) {
+                responselen_buffer_.writeUint16(
+                    response_buffer_.getLength());
+                async_write(socket_,
+                            asio::buffer(
+                                responselen_buffer_.getData(),
+                                responselen_buffer_.getLength()),
+                            boost::bind(&TCPClient::responseWrite, this,
+                                        placeholders::error));
             } else {
-#endif          
-                if (auth_server_->processMessage(dnsbuffer, dns_message_,
-                                                response_renderer_, false)) {
-                    responselen_buffer_.writeUint16(
-                        response_buffer_.getLength());
-                    async_write(socket_,
-                                asio::buffer(
-                                    responselen_buffer_.getData(),
-                                    responselen_buffer_.getLength()),
-                                boost::bind(&TCPClient::responseWrite, this,
-                                            placeholders::error));
-                } else {
-                    delete this;
-                }
-#ifdef USE_XFROUT
+                delete this;
             }
-#endif
         } else {
             delete this;
         }
@@ -172,9 +260,9 @@
         if (!error) {
                 async_write(socket_,
                             asio::buffer(response_buffer_.getData(),
-                                                response_buffer_.getLength()),
-                        boost::bind(&TCPClient::handleWrite, this,
-                                    placeholders::error));
+                                         response_buffer_.getLength()),
+                            boost::bind(&TCPClient::handleWrite, this,
+                                        placeholders::error));
         } else {
             delete this;
         }
@@ -188,9 +276,15 @@
       }
     }
 
+    // Currently this is for tests only
+    void setCallBack(const IOService::IOCallBack* callback) {
+        custom_callback_ = callback;
+    }
+
 private:
     AuthSrv* auth_server_;
     tcp::socket socket_;
+    TCPSocket io_socket_;
     OutputBuffer response_buffer_;
     OutputBuffer responselen_buffer_;
     MessageRenderer response_renderer_;
@@ -198,21 +292,25 @@
     enum { MAX_LENGTH = 65535 };
     static const size_t TCP_MESSAGE_LENGTHSIZE = 2;
     char data_[MAX_LENGTH];
+
+    // currently, for testing purpose only.
+    const IOService::IOCallBack* custom_callback_;
 };
 
 class TCPServer {
 public:
     TCPServer(AuthSrv* auth_server, io_service& io_service,
-              int af, short port) :
+              const ip::address& addr, const uint16_t port) :
         auth_server_(auth_server), io_service_(io_service),
         acceptor_(io_service_), listening_(new TCPClient(auth_server_,
-                                                         io_service_))
-    {
-        tcp::endpoint endpoint(af == AF_INET6 ? tcp::v6() : tcp::v4(), port);
+                                                         io_service_)),
+        custom_callback_(NULL)
+    {
+        tcp::endpoint endpoint(addr, port);
         acceptor_.open(endpoint.protocol());
         // Set v6-only (we use a different instantiation for v4,
         // otherwise asio will bind to both v4 and v6
-        if (af == AF_INET6) {
+        if (addr.is_v6()) {
             acceptor_.set_option(ip::v6_only(true));
         }
         acceptor_.set_option(tcp::acceptor::reuse_address(true));
@@ -230,6 +328,7 @@
     {
         if (!error) {
             assert(new_client == listening_);
+            new_client->setCallBack(custom_callback_);
             new_client->start();
             listening_ = new TCPClient(auth_server_, io_service_);
             acceptor_.async_accept(listening_->getSocket(),
@@ -241,31 +340,41 @@
         }
     }
 
+    // Currently this is for tests only
+    void setCallBack(const IOService::IOCallBack* callback) {
+        custom_callback_ = callback;
+    }
+
 private:
     AuthSrv* auth_server_;
     io_service& io_service_;
     tcp::acceptor acceptor_;
     TCPClient* listening_;
+
+    // currently, for testing purpose only.
+    const IOService::IOCallBack* custom_callback_;
 };
 
 class UDPServer {
 public:
     UDPServer(AuthSrv* auth_server, io_service& io_service,
-              int af, short port) :
+              const ip::address& addr, const uint16_t port) :
         auth_server_(auth_server),
         io_service_(io_service),
-        socket_(io_service, af == AF_INET6 ? udp::v6() : udp::v4()),
+        socket_(io_service, addr.is_v6() ? udp::v6() : udp::v4()),
+        io_socket_(socket_),
         response_buffer_(0),
         response_renderer_(response_buffer_),
-        dns_message_(Message::PARSE)
+        dns_message_(Message::PARSE),
+        custom_callback_(NULL)
     {
         // Set v6-only (we use a different instantiation for v4,
         // otherwise asio will bind to both v4 and v6
-        if (af == AF_INET6) {
+        if (addr.is_v6()) {
             socket_.set_option(asio::ip::v6_only(true));
-            socket_.bind(udp::endpoint(udp::v6(), port));
+            socket_.bind(udp::endpoint(addr, port));
         } else {
-            socket_.bind(udp::endpoint(udp::v4(), port));
+            socket_.bind(udp::endpoint(addr, port));
         }
         startReceive();
     }
@@ -274,16 +383,25 @@
                        size_t bytes_recvd)
     {
         // Check for queued configuration commands
-        if (auth_server_->configSession()->hasQueuedMsgs()) {
+        if (auth_server_ != NULL &&
+            auth_server_->configSession()->hasQueuedMsgs()) {
             auth_server_->configSession()->checkCommand();
         }
         if (!error && bytes_recvd > 0) {
-            InputBuffer request_buffer(data_, bytes_recvd);
+            const UDPEndpoint remote_endpoint(sender_endpoint_);
+            const IOMessage io_message(data_, bytes_recvd, io_socket_,
+                                       remote_endpoint);
+            // currently, for testing purpose only
+            if (custom_callback_ != NULL) {
+                (*custom_callback_)(io_message);
+                startReceive();
+                return;
+            }
 
             dns_message_.clear(Message::PARSE);
             response_renderer_.clear();
-            if (auth_server_->processMessage(request_buffer, dns_message_,
-                                            response_renderer_, true)) {
+            if (auth_server_->processMessage(io_message, dns_message_,
+                                             response_renderer_)) {
                 socket_.async_send_to(
                     asio::buffer(response_buffer_.getData(),
                                         response_buffer_.getLength()),
@@ -307,6 +425,11 @@
         // the next request.
         startReceive();
     }
+
+    // Currently this is for tests only
+    void setCallBack(const IOService::IOCallBack* callback) {
+        custom_callback_ = callback;
+    }
 private:
     void startReceive() {
         socket_.async_receive_from(
@@ -320,86 +443,107 @@
     AuthSrv* auth_server_;
     io_service& io_service_;
     udp::socket socket_;
+    UDPSocket io_socket_;
     OutputBuffer response_buffer_;
     MessageRenderer response_renderer_;
     Message dns_message_;
     udp::endpoint sender_endpoint_;
     enum { MAX_LENGTH = 4096 };
     char data_[MAX_LENGTH];
-};
-
-// This is a helper structure just to make the construction of IOServiceImpl
-// exception safe.  If the constructor of {UDP/TCP}Server throws an exception,
-// the destructor of this class will automatically perform the necessary
-// cleanup.
-struct ServerSet {
-    ServerSet() : udp4_server(NULL), udp6_server(NULL),
-                  tcp4_server(NULL), tcp6_server(NULL)
-    {}
-    ~ServerSet() {
-        delete udp4_server;
-        delete udp6_server;
-        delete tcp4_server;
-        delete tcp6_server;
-    }
-    UDPServer* udp4_server;
-    UDPServer* udp6_server;
-    TCPServer* tcp4_server;
-    TCPServer* tcp6_server;
+
+    // currently, for testing purpose only.
+    const IOService::IOCallBack* custom_callback_;
 };
 
 class IOServiceImpl {
 public:
-    IOServiceImpl(AuthSrv* auth_server, const char* port,
-                  const bool use_ipv4, const bool use_ipv6);
-    ~IOServiceImpl();
+    IOServiceImpl(AuthSrv* auth_server, const char& port,
+                  const ip::address* v4addr, const ip::address* v6addr);
     asio::io_service io_service_;
     AuthSrv* auth_server_;
-    UDPServer* udp4_server_;
-    UDPServer* udp6_server_;
-    TCPServer* tcp4_server_;
-    TCPServer* tcp6_server_;
-};
-
-IOServiceImpl::IOServiceImpl(AuthSrv* auth_server, 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)
+
+    typedef boost::shared_ptr<UDPServer> UDPServerPtr;
+    typedef boost::shared_ptr<TCPServer> TCPServerPtr;
+    UDPServerPtr udp4_server_;
+    UDPServerPtr udp6_server_;
+    TCPServerPtr tcp4_server_;
+    TCPServerPtr tcp6_server_;
+
+    // This member is used only for testing at the moment.
+    IOService::IOCallBack callback_;
+};
+
+IOServiceImpl::IOServiceImpl(AuthSrv* auth_server, const char& port,
+                             const ip::address* const v4addr,
+                             const ip::address* const v6addr) :
+    auth_server_(auth_server),
+    udp4_server_(UDPServerPtr()), udp6_server_(UDPServerPtr()),
+    tcp4_server_(TCPServerPtr()), tcp6_server_(TCPServerPtr())
 {
-    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);
-    }
-
-    // Now we don't have to worry about exception, and need to make sure that
-    // the server objects won't be accidentally cleaned up.
-    servers.udp4_server = NULL;
-    servers.udp6_server = NULL;
-    servers.tcp4_server = NULL;
-    servers.tcp6_server = NULL;
-}
-
-IOServiceImpl::~IOServiceImpl() {
-    delete udp4_server_;
-    delete udp6_server_;
-    delete tcp4_server_;
-    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);
+    uint16_t portnum;
+
+    try {
+        // XXX: SunStudio with stlport4 doesn't reject some invalid
+        // representation such as "-1" by lexical_cast<uint16_t>, so
+        // we convert it into a signed integer of a larger size and perform
+        // range check ourselves.
+        const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
+        if (portnum32 < 0 || portnum32 > 65535) {
+            isc_throw(IOError, "Invalid port number '" << &port);
+        }
+        portnum = portnum32;
+    } catch (const boost::bad_lexical_cast& ex) {
+        isc_throw(IOError, "Invalid port number '" << &port << "': " <<
+                  ex.what());
+    }
+
+    try {
+        if (v4addr != NULL) {
+            udp4_server_ = UDPServerPtr(new UDPServer(auth_server, io_service_,
+                                                      *v4addr, portnum));
+            tcp4_server_ = TCPServerPtr(new TCPServer(auth_server, io_service_,
+                                                      *v4addr, portnum));
+        }
+        if (v6addr != NULL) {
+            udp6_server_ = UDPServerPtr(new UDPServer(auth_server, io_service_,
+                                                      *v6addr, portnum));
+            tcp6_server_ = TCPServerPtr(new TCPServer(auth_server, io_service_,
+                                                      *v6addr, portnum));
+        }
+    } catch (const asio::system_error& err) {
+        // We need to catch and convert any ASIO level exceptions.
+        // This can happen for unavailable address, binding a privilege port
+        // without the privilege, etc.
+        isc_throw(IOError, "Failed to initialize network servers: " <<
+                  err.what());
+    }
+}
+
+IOService::IOService(AuthSrv* auth_server, const char& port,
+                     const char& address) :
+    impl_(NULL)
+{
+    error_code err;
+    const ip::address addr = ip::address::from_string(&address, err);
+    if (err) {
+        isc_throw(IOError, "Invalid IP address '" << &address << "': "
+                  << err.message());
+    }
+
+    impl_ = new IOServiceImpl(auth_server, port,
+                              addr.is_v4() ? &addr : NULL,
+                              addr.is_v6() ? &addr : NULL);
+}
+
+IOService::IOService(AuthSrv* auth_server, const char& port,
+                     const bool use_ipv4, const bool use_ipv6) :
+    impl_(NULL)
+{
+    const ip::address v4addr_any = ip::address(ip::address_v4::any());
+    const ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL; 
+    const ip::address v6addr_any = ip::address(ip::address_v6::any());
+    const ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
+    impl_ = new IOServiceImpl(auth_server, port, v4addrp, v6addrp);
 }
 
 IOService::~IOService() {
@@ -420,4 +564,21 @@
 IOService::get_io_service() {
     return impl_->io_service_;
 }
-}
+
+void
+IOService::setCallBack(const IOCallBack callback) {
+    impl_->callback_ = callback;
+    if (impl_->udp4_server_ != NULL) {
+        impl_->udp4_server_->setCallBack(&impl_->callback_);
+    }
+    if (impl_->udp6_server_ != NULL) {
+        impl_->udp6_server_->setCallBack(&impl_->callback_);
+    }
+    if (impl_->tcp4_server_ != NULL) {
+        impl_->tcp4_server_->setCallBack(&impl_->callback_);
+    }
+    if (impl_->tcp6_server_ != NULL) {
+        impl_->tcp6_server_->setCallBack(&impl_->callback_);
+    }
+}
+}

Modified: branches/trac241/src/bin/auth/asio_link.h
==============================================================================
--- branches/trac241/src/bin/auth/asio_link.h (original)
+++ branches/trac241/src/bin/auth/asio_link.h Mon Aug  9 18:42:00 2010
@@ -17,19 +17,430 @@
 #ifndef __ASIO_LINK_H
 #define __ASIO_LINK_H 1
 
+// IMPORTANT NOTE: only very few ASIO headers files can be included in
+// this file.  In particular, asio.hpp should never be included here.
+// See the description of the namespace below.
+#include <unistd.h>             // for some network system calls
+#include <asio/ip/address.hpp>
+
+#include <functional>
+#include <string>
+
+#include <boost/function.hpp>
+
+#include <exceptions/exceptions.h>
+
+namespace asio {
+// forward declaration for IOService::get_io_service() below
+class io_service;
+}
+
 class AuthSrv;
+
+/// \namespace asio_link
+/// \brief A wrapper interface for the ASIO library.
+///
+/// The \c asio_link namespace is used to define a set of wrapper interfaces
+/// for the ASIO library.
+///
+/// BIND 10 uses the non-Boost version of ASIO because it's header-only,
+/// i.e., does not require a separate library object to be linked, and thus
+/// lowers the bar for introduction.
+///
+/// But the advantage comes with its own costs: since the header-only version
+/// includes more definitions in public header files, it tends to trigger
+/// more compiler warnings for our own sources, and, depending on the
+/// compiler options, may make the build fail.
+///
+/// We also found it may be tricky to use ASIO and standard C++ libraries
+/// in a single translation unit, i.e., a .cc file: depending on the order
+/// of including header files, ASIO may or may not work on some platforms.
+///
+/// This wrapper interface is intended to centralize these
+/// problematic issues in a single sub module.  Other BIND 10 modules should
+/// simply include \c asio_link.h and use the wrapper API instead of
+/// including ASIO header files and using ASIO-specific classes directly.
+///
+/// This wrapper may be used for other IO libraries if and when we want to
+/// switch, but generality for that purpose is not the primary goal of
+/// this module.  The resulting interfaces are thus straightforward mapping
+/// to the ASIO counterparts.
+///
+/// Notes to developers:
+/// Currently the wrapper interface is specific to the authoritative
+/// server implementation.  But the plan is to generalize it and have
+/// other modules use it.
+///
+/// One obvious drawback of this approach is performance overhead
+/// due to the additional layer.  We should eventually evaluate the cost
+/// of the wrapper abstraction in benchmark tests. Another drawback is
+/// that the wrapper interfaces don't provide all features of ASIO
+/// (at least for the moment).  We should also re-evaluate the
+/// maintenance overhead of providing necessary wrappers as we develop
+/// more.
+///
+/// On the other hand, we may be able to exploit the wrapper approach to
+/// simplify the interfaces (by limiting the usage) and unify performance
+/// optimization points.
+///
+/// As for optimization, we may want to provide a custom allocator for
+/// the placeholder of callback handlers:
+/// http://think-async.com/Asio/asio-1.3.1/doc/asio/reference/asio_handler_allocate.html
 
 namespace asio_link {
 struct IOServiceImpl;
 
+/// \brief An exception that is thrown if an error occurs within the IO
+/// module.  This is mainly intended to be a wrapper exception class for
+/// ASIO specific exceptions.
+class IOError : public isc::Exception {
+public:
+    IOError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// \brief The \c IOAddress class represents an IP addresses (version
+/// agnostic)
+///
+/// This class is a wrapper for the ASIO \c ip::address class.
+class IOAddress {
+public:
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// This class is copyable.  We use default versions of copy constructor
+    /// and the assignment operator.
+    /// We use the default destructor.
+    //@{
+    /// \brief Constructor from string.
+    ///
+    /// This constructor converts a textual representation of IPv4 and IPv6
+    /// addresses into an IOAddress object.
+    /// If \c address_str is not a valid representation of any type of
+    /// address, an exception of class \c IOError will be thrown.
+    /// This constructor allocates memory for the object, and if that fails
+    /// a corresponding standard exception will be thrown.
+    ///
+    /// \param address_str Textual representation of address.
+    IOAddress(const std::string& address_str);
+
+    /// \brief Constructor from an ASIO \c ip::address object.
+    ///
+    /// This constructor is intended to be used within the wrapper
+    /// implementation; user applications of the wrapper API won't use it.
+    ///
+    /// This constructor never throws an exception.
+    ///
+    /// \param asio_address The ASIO \c ip::address to be converted.
+    IOAddress(const asio::ip::address& asio_adress);
+    //@}
+
+    /// \brief Convert the address to a string.
+    ///
+    /// This method is basically expected to be exception free, but
+    /// generating the string will involve resource allocation,
+    /// and if it fails the corresponding standard exception will be thrown.
+    ///
+    /// \return A string representation of the address.
+    std::string toText() const;
+private:
+    asio::ip::address asio_address_;
+};
+
+/// \brief The \c IOEndpoint class is an abstract base class to represent
+/// a communication endpoint.
+///
+/// This class is a wrapper for the ASIO endpoint classes such as
+/// \c ip::tcp::endpoint and \c ip::udp::endpoint.
+///
+/// Derived class implementations are completely hidden within the
+/// implementation.  User applications only get access to concrete
+/// \c IOEndpoint objects via the abstract interfaces.
+class IOEndpoint {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IOEndpoint(const IOEndpoint& source);
+    IOEndpoint& operator=(const IOEndpoint& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class
+    /// should never be instantiated (except as part of a derived class).
+    IOEndpoint() {}
+public:
+    /// The destructor.
+    virtual ~IOEndpoint() {}
+    //@}
+
+    /// \brief Returns the address of the endpoint.
+    ///
+    /// This method returns an IOAddress object corresponding to \c this
+    /// endpoint.
+    /// Note that the return value is a real object, not a reference or
+    /// a pointer.
+    /// This is aligned with the interface of the ASIO counterpart:
+    /// the \c address() method of \c ip::xxx::endpoint classes returns
+    /// an \c ip::address object.
+    /// This also means handling the address of an endpoint using this method
+    /// can be expensive.  If the address information is necessary in a
+    /// performance sensitive context and there's a more efficient interface
+    /// for that purpose, it's probably better to avoid using this method.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A copy of \c IOAddress object corresponding to the endpoint.
+    virtual IOAddress getAddress() const = 0;
+
+    /// \brief A polymorphic factory of endpoint from address and port.
+    ///
+    /// This method creates a new instance of (a derived class of)
+    /// \c IOEndpoint object that identifies the pair of given address
+    /// and port.
+    /// The appropriate derived class is chosen based on the specified
+    /// transport protocol.  If the \c protocol doesn't specify a protocol
+    /// supported in this implementation, an exception of class \c IOError
+    /// will be thrown.
+    ///
+    /// Memory for the created object will be dynamically allocated.  It's
+    /// caller's responsibility to \c delete it later.
+    /// If resource allocation for the new object fails, a corresponding
+    /// standard exception will be thrown.
+    ///
+    /// \param protocol The transport protocol used for the endpoint.
+    /// Currently, only \c IPPROTO_UDP and \c IPPROTO_TCP can be specified.
+    /// \param address The (IP) address of the endpoint.
+    /// \param port The transport port number of the endpoint
+    /// \return A pointer to a newly created \c IOEndpoint object.
+    static const IOEndpoint* create(const int protocol,
+                                    const IOAddress& address,
+                                    const unsigned short port);
+};
+
+/// \brief The \c IOSocket class is an abstract base class to represent
+/// various types of network sockets.
+///
+/// This class is a wrapper for the ASIO socket classes such as
+/// \c ip::tcp::socket and \c ip::udp::socket.
+///
+/// Derived class implementations are completely hidden within the
+/// implementation.  User applications only get access to concrete
+/// \c IOSocket objects via the abstract interfaces.
+/// We may revisit this decision when we generalize the wrapper and more
+/// modules use it.  Also, at that point we may define a separate (visible)
+/// derived class for testing purposes rather than providing factory methods
+/// (i.e., getDummy variants below).
+class IOSocket {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IOSocket(const IOSocket& source);
+    IOSocket& operator=(const IOSocket& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class
+    /// should never be instantiated (except as part of a derived class).
+    IOSocket() {}
+public:
+    /// The destructor.
+    virtual ~IOSocket() {}
+    //@}
+
+    /// \brief Return the "native" representation of the socket.
+    ///
+    /// In practice, this is the file descriptor of the socket for
+    /// UNIX-like systems so the current implementation simply uses
+    /// \c int as the type of the return value.
+    /// We may have to need revisit this decision later.
+    ///
+    /// In general, the application should avoid using this method;
+    /// it essentially discloses an implementation specific "handle" that
+    /// can change the internal state of the socket (consider the
+    /// application closes it, for example).
+    /// But we sometimes need to perform very low-level operations that
+    /// requires the native representation.  Passing the file descriptor
+    /// to a different process is one example.
+    /// This method is provided as a necessary evil for such limited purposes.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return The native representation of the socket.  This is the socket
+    /// file descriptor for UNIX-like systems.
+    virtual int getNative() const = 0;
+
+    /// \brief Return the transport protocol of the socket.
+    ///
+    /// Currently, it returns \c IPPROTO_UDP for UDP sockets, and
+    /// \c IPPROTO_TCP for TCP sockets.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return IPPROTO_UDP for UDP sockets
+    /// \return IPPROTO_TCP for TCP sockets
+    virtual int getProtocol() const = 0;
+
+    /// \brief Return a non-usable "dummy" UDP socket for testing.
+    ///
+    /// This is a class method that returns a "mock" of UDP socket.
+    /// This is not associated with any actual socket, and its only
+    /// responsibility is to return \c IPPROTO_UDP from \c getProtocol().
+    /// The only feasible usage of this socket is for testing so that
+    /// the test code can prepare some "UDP data" even without opening any
+    /// actual socket.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A reference to an \c IOSocket object whose \c getProtocol()
+    /// returns \c IPPROTO_UDP.
+    static IOSocket& getDummyUDPSocket();
+
+    /// \brief Return a non-usable "dummy" TCP socket for testing.
+    ///
+    /// See \c getDummyUDPSocket().  This method is its TCP version.
+    ///
+    /// \return A reference to an \c IOSocket object whose \c getProtocol()
+    /// returns \c IPPROTO_TCP.
+    static IOSocket& getDummyTCPSocket();
+};
+
+/// \brief The \c IOMessage class encapsulates an incoming message received
+/// on a socket.
+///
+/// An \c IOMessage object represents a tuple of a chunk of data
+/// (a UDP packet or some segment of TCP stream), the socket over which the
+/// data is passed, the information about the other end point of the
+/// communication, and perhaps more.
+///
+/// The current design and interfaces of this class is tentative.
+/// It only provides a minimal level of support that is necessary for
+/// the current implementation of the authoritative server.
+/// A future version of this class will definitely support more.
+class IOMessage {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IOMessage(const IOMessage& source);
+    IOMessage& operator=(const IOMessage& source);
+public:
+    /// \brief Constructor from message information.
+    ///
+    /// This constructor needs to handle the ASIO \c ip::address class,
+    /// and is intended to be used within this wrapper implementation.
+    /// Once the \c IOMessage object is created, the application can
+    /// get access to the information via the wrapper interface such as
+    /// \c getRemoteAddress().
+    ///
+    /// This constructor never throws an exception.
+    ///
+    /// \param data A pointer to the message data.
+    /// \param data_size The size of the message data in bytes.
+    /// \param io_socket The socket over which the data is given.
+    /// \param remote_endpoint The other endpoint of the socket, that is,
+    /// the sender of the message.
+    IOMessage(const void* data, const size_t data_size, IOSocket& io_socket,
+              const IOEndpoint& remote_endpoint);
+    //@}
+
+    /// \brief Returns a pointer to the received data.
+    const void* getData() const { return (data_); }
+
+    /// \brief Returns the size of the received data in bytes.
+    size_t getDataSize() const { return (data_size_); }
+
+    /// \brief Returns the socket on which the message arrives.
+    const IOSocket& getSocket() const { return (io_socket_); }
+
+    /// \brief Returns the endpoint that sends the message.
+    const IOEndpoint& getRemoteEndpoint() const { return (remote_endpoint_); }
+private:
+    const void* data_;
+    const size_t data_size_;
+    IOSocket& io_socket_;
+    const IOEndpoint& remote_endpoint_;
+};
+
+/// \brief The \c IOService class is a wrapper for the ASIO \c io_service
+/// class.
+///
+/// Currently, the interface of this class is very specific to the
+/// authoritative server implementation as indicated in the signature of
+/// the constructor, but the plan is to generalize it so that other BIND 10
+/// modules can use this interface, too.
 class IOService {
-public:
-    IOService(AuthSrv* auth_server, const char* port,
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// These are currently very specific to the authoritative server
+    /// implementation.
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IOService(const IOService& source);
+    IOService& operator=(const IOService& source);
+public:
+    /// \brief The constructor with a specific IP address and port on which
+    /// the services listen on.
+    IOService(AuthSrv* auth_server, const char& port, const char& address);
+    /// \brief The constructor with a specific port on which the services
+    /// listen on.
+    ///
+    /// It effectively listens on "any" IPv4 and/or IPv6 addresses.
+    /// IPv4/IPv6 services will be available if and only if \c use_ipv4
+    /// or \c use_ipv6 is \c true, respectively.
+    IOService(AuthSrv* auth_server, const char& port,
               const bool use_ipv4, const bool use_ipv6);
+    /// \brief The destructor.
     ~IOService();
+    //@}
+
+    /// \brief Start the underlying event loop.
+    ///
+    /// This method does not return control to the caller until
+    /// the \c stop() method is called via some handler.
     void run();
+
+    /// \brief Stop the underlying event loop.
+    ///
+    /// This will return the control to the caller of the \c run() method.
     void stop();
+
+    /// \brief Return the native \c io_service object used in this wrapper.
+    ///
+    /// This is a short term work around to support other BIND 10 modules
+    /// that share the same \c io_service with the authoritative server.
+    /// It will eventually be removed once the wrapper interface is
+    /// generalized.
     asio::io_service& get_io_service();
+
+    /// \brief A functor(-like) class that specifies a custom call back
+    /// invoked from the event loop instead of the embedded authoritative
+    /// server callbacks.
+    ///
+    /// Currently, the callback is intended to be used only for testing
+    /// purposes.  But we'll need a generic callback type like this to
+    /// generalize the wrapper interface.
+    typedef boost::function<void(const IOMessage& io_message)> IOCallBack;
+
+    /// \brief Set the custom call back invoked from the event loop.
+    ///
+    /// Right now this method is only for testing, but will eventually be
+    /// generalized.
+    void setCallBack(IOCallBack callback);
 private:
     IOServiceImpl* impl_;
 };

Modified: branches/trac241/src/bin/auth/auth.spec.pre.in
==============================================================================
--- branches/trac241/src/bin/auth/auth.spec.pre.in (original)
+++ branches/trac241/src/bin/auth/auth.spec.pre.in Mon Aug  9 18:42:00 2010
@@ -5,7 +5,7 @@
     "config_data": [
       { "item_name": "database_file",
         "item_type": "string",
-        "item_optional": True,
+        "item_optional": true,
         "item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3"
       }
     ],

Modified: branches/trac241/src/bin/auth/auth_srv.cc
==============================================================================
--- branches/trac241/src/bin/auth/auth_srv.cc (original)
+++ branches/trac241/src/bin/auth/auth_srv.cc Mon Aug  9 18:42:00 2010
@@ -14,6 +14,8 @@
 
 // $Id$
 
+#include <netinet/in.h>
+
 #include <algorithm>
 #include <cassert>
 #include <iostream>
@@ -40,19 +42,23 @@
 
 #include <cc/data.h>
 
-#include "common.h"
-#include "auth_srv.h"
-
-#include <boost/lexical_cast.hpp>
+#include <xfr/xfrout_client.h>
+
+#include <auth/common.h>
+#include <auth/auth_srv.h>
+#include <auth/asio_link.h>
 
 using namespace std;
 
 using namespace isc;
+using namespace isc::cc;
 using namespace isc::datasrc;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 using namespace isc::data;
 using namespace isc::config;
+using namespace isc::xfr;
+using namespace asio_link;
 
 class AuthSrvImpl {
 private:
@@ -60,12 +66,18 @@
     AuthSrvImpl(const AuthSrvImpl& source);
     AuthSrvImpl& operator=(const AuthSrvImpl& source);
 public:
-    AuthSrvImpl();
-
+    AuthSrvImpl(const bool use_cache, AbstractXfroutClient& xfrout_client);
+    ~AuthSrvImpl();
     isc::data::ElementPtr setDbFile(const isc::data::ElementPtr config);
 
+    bool processNormalQuery(const IOMessage& io_message, Message& message,
+                            MessageRenderer& response_renderer);
+    bool processAxfrQuery(const IOMessage& io_message, Message& message,
+                            MessageRenderer& response_renderer);
+    bool processNotify(const IOMessage& io_message, Message& message, 
+                            MessageRenderer& response_renderer);
     std::string db_file_;
-    ModuleCCSession* cs_;
+    ModuleCCSession* config_session_;
     MetaDataSrc data_sources_;
     /// We keep a pointer to the currently running sqlite datasource
     /// so that we can specifically remove that one should the database
@@ -74,11 +86,24 @@
 
     bool verbose_mode_;
 
+    AbstractSession* xfrin_session_;
+
+    bool xfrout_connected_;
+    AbstractXfroutClient& xfrout_client_;
+
     /// 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,
+                         AbstractXfroutClient& xfrout_client) :
+    config_session_(NULL), verbose_mode_(false),
+    xfrin_session_(NULL),
+    xfrout_connected_(false),
+    xfrout_client_(xfrout_client)
 {
     // cur_datasrc_ is automatically initialized by the default constructor,
     // effectively being an empty (sqlite) data source.  once ccsession is up
@@ -86,10 +111,21 @@
 
     // add static data source
     data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
-}
-
-AuthSrv::AuthSrv() : impl_(new AuthSrvImpl) {
-}
+
+    // enable or disable the cache
+    cache_.setEnabled(use_cache);
+}
+
+AuthSrvImpl::~AuthSrvImpl() {
+    if (xfrout_connected_) {
+        xfrout_client_.disconnect();
+        xfrout_connected_ = false;
+    }
+}
+
+AuthSrv::AuthSrv(const bool use_cache, AbstractXfroutClient& xfrout_client) :
+    impl_(new AuthSrvImpl(use_cache, xfrout_client))
+{}
 
 AuthSrv::~AuthSrv() {
     delete impl_;
@@ -118,8 +154,9 @@
     const Opcode& opcode = message.getOpcode();
     vector<QuestionPtr> questions;
 
-    // If this is an error to a query, we should also copy the question section.
-    if (opcode == Opcode::QUERY()) {
+    // If this is an error to a query or notify, we should also copy the
+    // question section.
+    if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
         questions.assign(message.beginQuestion(), message.endQuestion());
     }
 
@@ -140,8 +177,7 @@
 
     if (verbose_mode) {
         cerr << "[b10-auth] sending an error response (" <<
-            boost::lexical_cast<string>(renderer.getLength())
-             << " bytes):\n" << message.toText() << endl;
+            renderer.getLength() << " bytes):\n" << message.toText() << endl;
     }
 }
 }
@@ -157,20 +193,26 @@
 }
 
 void
-AuthSrv::setConfigSession(ModuleCCSession* cs) {
-    impl_->cs_ = cs;
+AuthSrv::setXfrinSession(AbstractSession* xfrin_session) {
+    impl_->xfrin_session_ = xfrin_session;
+}
+
+void
+AuthSrv::setConfigSession(ModuleCCSession* config_session) {
+    impl_->config_session_ = config_session;
 }
 
 ModuleCCSession*
 AuthSrv::configSession() const {
-    return (impl_->cs_);
+    return (impl_->config_session_);
 }
 
 bool
-AuthSrv::processMessage(InputBuffer& request_buffer, Message& message,
-                        MessageRenderer& response_renderer,
-                        const bool udp_buffer)
+AuthSrv::processMessage(const IOMessage& io_message, Message& message,
+                        MessageRenderer& response_renderer)
 {
+    InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
+
     // First, check the header part.  If we fail even for the base header,
     // just drop the message.
     try {
@@ -179,7 +221,8 @@
         // Ignore all responses.
         if (message.getHeaderFlag(MessageFlag::QR())) {
             if (impl_->verbose_mode_) {
-                cerr << "[b10-auth] received unexpected response, ignoring" << endl;
+                cerr << "[b10-auth] received unexpected response, ignoring"
+                     << endl;
             }
             return (false);
         }
@@ -192,8 +235,8 @@
         message.fromWire(request_buffer);
     } catch (const DNSProtocolError& error) {
         if (impl_->verbose_mode_) {
-            cerr << "[b10-auth] returning " <<  error.getRcode().toText() << ": "
-                 << error.what() << endl;
+            cerr << "[b10-auth] returning " <<  error.getRcode().toText()
+                 << ": " << error.what() << endl;
         }
         makeErrorMessage(message, response_renderer, error.getRcode(),
                          impl_->verbose_mode_);
@@ -213,8 +256,9 @@
 
     // Perform further protocol-level validation.
 
-    // In this implementation, we only support normal queries
-    if (message.getOpcode() != Opcode::QUERY()) {
+    if (message.getOpcode() == Opcode::NOTIFY()) {
+        return (impl_->processNotify(io_message, message, response_renderer));
+    } else if (message.getOpcode() != Opcode::QUERY()) {
         if (impl_->verbose_mode_) {
             cerr << "[b10-auth] unsupported opcode" << endl;
         }
@@ -229,6 +273,25 @@
         return (true);
     }
 
+    ConstQuestionPtr question = *message.beginQuestion();
+    const RRType &qtype = question->getType();
+    if (qtype == RRType::AXFR()) {
+        return (impl_->processAxfrQuery(io_message, message,
+                                        response_renderer));
+    } else if (qtype == RRType::IXFR()) {
+        makeErrorMessage(message, response_renderer, Rcode::NOTIMP(),
+                         impl_->verbose_mode_);
+        return (true);
+    } else {
+        return (impl_->processNormalQuery(io_message, message,
+                                          response_renderer));
+    }
+}
+
+bool
+AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
+                                MessageRenderer& response_renderer)
+{
     const bool dnssec_ok = message.isDNSSECSupported();
     const uint16_t remote_bufsize = message.getUDPSize();
 
@@ -239,25 +302,159 @@
     message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
 
     try {
-        Query query(message, dnssec_ok);
-        impl_->data_sources_.doQuery(query);
+        Query query(message, cache_, dnssec_ok);
+        data_sources_.doQuery(query);
     } catch (const Exception& ex) {
-        if (impl_->verbose_mode_) {
-            cerr << "[b10-auth] Internal error, returning SERVFAIL: " << ex.what() << endl;
+        if (verbose_mode_) {
+            cerr << "[b10-auth] Internal error, returning SERVFAIL: " <<
+                ex.what() << endl;
         }
         makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
-                         impl_->verbose_mode_);
-        return (true);
-    }
-
+                         verbose_mode_);
+        return (true);
+    }
+
+    const bool udp_buffer =
+        (io_message.getSocket().getProtocol() == IPPROTO_UDP);
     response_renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
     message.toWire(response_renderer);
-    if (impl_->verbose_mode_) {
-        cerr << "[b10-auth] sending a response (" <<
-            boost::lexical_cast<string>(response_renderer.getLength())
+    if (verbose_mode_) {
+        cerr << "[b10-auth] sending a response ("
+             << response_renderer.getLength()
              << " bytes):\n" << message.toText() << endl;
     }
 
+    return (true);
+}
+
+
+bool
+AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, Message& message,
+                            MessageRenderer& response_renderer)
+{
+    if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
+        if (verbose_mode_) {
+            cerr << "[b10-auth] AXFR query over UDP isn't allowed" << endl;
+        }
+        makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
+                         verbose_mode_);
+        return (true);
+    }
+
+    try {
+        if (!xfrout_connected_) {
+            xfrout_client_.connect();
+            xfrout_connected_ = true;
+        }
+        xfrout_client_.sendXfroutRequestInfo(
+            io_message.getSocket().getNative(),
+            io_message.getData(),
+            io_message.getDataSize());
+    } catch (const XfroutError& err) {
+        if (xfrout_connected_) {
+            // disconnect() may trigger an exception, but since we try it
+            // only if we've successfully opened it, it shouldn't happen in
+            // normal condition.  Should this occur, we'll propagate it to the
+            // upper layer.
+            xfrout_client_.disconnect();
+            xfrout_connected_ = false;
+        }
+        
+        if (verbose_mode_) {
+            cerr << "[b10-auth] Error in handling XFR request: " << err.what()
+                 << endl;
+        }
+        makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
+                         verbose_mode_);
+        return (true);
+    }
+    return (false);
+}
+
+bool
+AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message, 
+                           MessageRenderer& response_renderer) 
+{
+    // The incoming notify must contain exactly one question for SOA of the
+    // zone name.
+    if (message.getRRCount(Section::QUESTION()) != 1) {
+        if (verbose_mode_) {
+                cerr << "[b10-auth] invalid number of questions in notify: "
+                     << message.getRRCount(Section::QUESTION()) << endl;
+        }
+        makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
+                         verbose_mode_);
+        return (true);
+    }
+    ConstQuestionPtr question = *message.beginQuestion();
+    if (question->getType() != RRType::SOA()) {
+        if (verbose_mode_) {
+                cerr << "[b10-auth] invalid question RR type in notify: "
+                     << question->getType() << endl;
+        }
+        makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
+                         verbose_mode_);
+        return (true);
+    }
+
+    // According to RFC 1996, rcode should be "no error" and AA bit should be
+    // on, but we don't check these conditions.  This behavior is compatible
+    // with BIND 9.
+
+    // TODO check with the conf-mgr whether current server is the auth of the
+    // zone
+
+    // In the code that follows, we simply ignore the notify if any internal
+    // error happens rather than returning (e.g.) SERVFAIL.  RFC 1996 is
+    // silent about such cases, but there doesn't seem to be anything we can
+    // improve at the primary server side by sending an error anyway.
+    if (xfrin_session_ == NULL) {
+        if (verbose_mode_) {
+            cerr << "[b10-auth] "
+                "session interface for xfrin is not available" << endl;
+        }
+        return (false);
+    }
+    
+    const string remote_ip_address =
+        io_message.getRemoteEndpoint().getAddress().toText();
+    static const string command_template_start =
+        "{\"command\": [\"notify\", {\"zone_name\" : \"";
+    static const string command_template_master = "\", \"master\" : \"";
+    static const string command_template_rrclass = "\", \"rrclass\" : \"";
+    static const string command_template_end = "\"}]}";
+
+    try {
+        ElementPtr notify_command = Element::fromJSON(
+                command_template_start + question->getName().toText() + 
+                command_template_master + remote_ip_address +
+                command_template_rrclass + question->getClass().toText() +
+                command_template_end);
+        const unsigned int seq =
+            xfrin_session_->group_sendmsg(notify_command, "Xfrin",
+                                          "*", "*");
+        ElementPtr env, answer, parsed_answer;
+        xfrin_session_->group_recvmsg(env, answer, false, seq);
+        int rcode;
+        parsed_answer = parseAnswer(rcode, answer);
+        if (rcode != 0) {
+            if (verbose_mode_) {
+                cerr << "[b10-auth] failed to notify Xfrin: "
+                     << parsed_answer->str() << endl; 
+            }
+            return (false);
+        }
+    } catch (const Exception& ex) {
+        if (verbose_mode_) {
+            cerr << "[b10-auth] failed to notify Xfrin: " << ex.what() << endl;
+        }
+        return (false);
+    }
+
+    message.makeResponse();
+    message.setHeaderFlag(MessageFlag::AA());
+    message.setRcode(Rcode::NOERROR());
+    message.toWire(response_renderer);
     return (true);
 }
 
@@ -269,13 +466,26 @@
     if (config && config->contains("database_file")) {
         db_file_ = config->get("database_file")->stringValue();
         final = config;
-    } else if (cs_ != NULL) {
+    } else if (config_session_ != NULL) {
         bool is_default;
         string item("database_file");
-        ElementPtr value = cs_->getValue(is_default, item);
+        ElementPtr value = config_session_->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/trac241/src/bin/auth/auth_srv.h
==============================================================================
--- branches/trac241/src/bin/auth/auth_srv.h (original)
+++ branches/trac241/src/bin/auth/auth_srv.h Mon Aug  9 18:42:00 2010
@@ -28,6 +28,14 @@
 class Message;
 class MessageRenderer;
 }
+
+namespace xfr {
+class AbstractXfroutClient;
+};
+}
+
+namespace asio_link {
+class IOMessage;
 }
 
 class AuthSrvImpl;
@@ -36,28 +44,49 @@
     ///
     /// \name Constructors, Assignment Operator and Destructor.
     ///
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private.
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private.
     //@{
 private:
     AuthSrv(const AuthSrv& source);
     AuthSrv& operator=(const AuthSrv& source);
 public:
-    explicit AuthSrv();
+    /// The constructor.
+    ///
+    /// \param use_cache Whether to enable hot spot cache for lookup results.
+    /// \param xfrout_client Communication interface with a separate xfrout
+    /// process.  It's normally a reference to an xfr::XfroutClient object,
+    /// but can refer to a local mock object for testing (or other
+    /// experimental) purposes.
+    AuthSrv(const bool use_cache,
+            isc::xfr::AbstractXfroutClient& xfrout_client);
     ~AuthSrv();
     //@}
     /// \return \c true if the \message contains a response to be returned;
     /// otherwise \c false.
-    bool processMessage(isc::dns::InputBuffer& request_buffer,
+    bool processMessage(const asio_link::IOMessage& io_message,
                         isc::dns::Message& message,
-                        isc::dns::MessageRenderer& response_renderer,
-                        bool udp_buffer);
+                        isc::dns::MessageRenderer& response_renderer);
     void setVerbose(bool on);
     bool getVerbose() const;
-    void serve(std::string zone_name);
     isc::data::ElementPtr updateConfig(isc::data::ElementPtr config);
     isc::config::ModuleCCSession* configSession() const;
-    void setConfigSession(isc::config::ModuleCCSession* cs);
+    void setConfigSession(isc::config::ModuleCCSession* config_session);
+
+    ///
+    /// Note: this interface is tentative.  We'll revisit the ASIO and session
+    /// frameworks, at which point the session will probably be passed on
+    /// construction of the server.
+    ///
+    /// \param xfrin_session A Session object over which NOTIFY message
+    /// information is exchanged with a XFRIN handler.
+    /// The session must be established before setting in the server
+    /// object.
+    /// Ownership isn't transferred: the caller is responsible for keeping
+    /// this object to be valid while the server object is working and for
+    /// disconnecting the session and destroying the object when the server
+    ///
+    void setXfrinSession(isc::cc::AbstractSession* xfrin_session);
 private:
     AuthSrvImpl* impl_;
 };

Modified: branches/trac241/src/bin/auth/main.cc
==============================================================================
--- branches/trac241/src/bin/auth/main.cc (original)
+++ branches/trac241/src/bin/auth/main.cc Mon Aug  9 18:42:00 2010
@@ -14,8 +14,6 @@
 
 // $Id$
 
-#include "config.h"
-
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/select.h>
@@ -39,16 +37,19 @@
 #include <cc/data.h>
 #include <config/ccsession.h>
 
-#include "spec_config.h"
-#include "common.h"
-#include "auth_srv.h"
-#include "asio_link.h"
+#include <xfr/xfrout_client.h>
+
+#include <auth/spec_config.h>
+#include <auth/common.h>
+#include <auth/auth_srv.h>
+#include <auth/asio_link.h>
 
 using namespace std;
 using namespace isc::data;
 using namespace isc::cc;
 using namespace isc::config;
 using namespace isc::dns;
+using namespace isc::xfr;
 
 namespace {
 
@@ -86,7 +87,7 @@
 
 void
 usage() {
-    cerr << "Usage: b10-auth [-p port] [-4|-6]" << endl;
+    cerr << "Usage: b10-auth [-a address] [-p port] [-4|-6] [-nv]" << endl;
     exit(1);
 }
 } // end of anonymous namespace
@@ -95,9 +96,10 @@
 main(int argc, char* argv[]) {
     int ch;
     const char* port = DNSPORT;
-    bool use_ipv4 = true, use_ipv6 = true;
-
-    while ((ch = getopt(argc, argv, "46p:v")) != -1) {
+    const char* address = NULL;
+    bool use_ipv4 = true, use_ipv6 = true, cache = true;
+
+    while ((ch = getopt(argc, argv, "46a:np:v")) != -1) {
         switch (ch) {
         case '4':
             // Note that -4 means "ipv4 only", we need to set "use_ipv6" here,
@@ -110,6 +112,12 @@
             // The same note as -4 applies.
             use_ipv4 = false;
             break;
+        case 'n':
+            cache = false;
+            break;
+        case 'a':
+            address = optarg;
+            break;
         case 'p':
             port = optarg;
             break;
@@ -131,8 +139,19 @@
         usage();
     }
 
-    // initialize command channel
+    if ((!use_ipv4 || !use_ipv6) && address != NULL) {
+        cerr << "[b10-auth] Error: -4|-6 and -a can't coexist" << endl;
+        usage();
+    }
+
     int ret = 0;
+
+    // XXX: we should eventually pass io_service here.
+    Session* cc_session = NULL;
+    Session* xfrin_session = NULL;
+    bool xfrin_session_established = false; // XXX (see Trac #287)
+    ModuleCCSession* config_session = NULL;
+    XfroutClient xfrout_client(UNIX_SOCKET_FILE);
     try {
         string specfile;
         if (getenv("B10_FROM_BUILD")) {
@@ -142,26 +161,64 @@
             specfile = string(AUTH_SPECFILE_LOCATION);
         }
 
-        auth_server = new AuthSrv;
+        auth_server = new AuthSrv(cache, xfrout_client);
         auth_server->setVerbose(verbose_mode);
-
-        io_service = new asio_link::IOService(auth_server, port, use_ipv4,
-                                              use_ipv6);
-
-        ModuleCCSession cs(specfile, io_service->get_io_service(), my_config_handler, my_command_handler);
-
-        auth_server->setConfigSession(&cs);
+        cout << "[b10-auth] Server created." << endl;
+
+        if (address != NULL) {
+            // XXX: we can only specify at most one explicit address.
+            // This also means the server cannot run in the dual address
+            // family mode if explicit addresses need to be specified.
+            // We don't bother to fix this problem, however.  The -a option
+            // is a short term workaround until we support dynamic listening
+            // port allocation.
+            io_service = new asio_link::IOService(auth_server, *port,
+                                                  *address);
+        } else {
+            io_service = new asio_link::IOService(auth_server, *port,
+                                                  use_ipv4, use_ipv6);
+        }
+        cout << "[b10-auth] IOService created." << endl;
+
+        cc_session = new Session(io_service->get_io_service());
+        cout << "[b10-auth] Configuration session channel created." << endl;
+
+        config_session = new ModuleCCSession(specfile, *cc_session,
+                                             my_config_handler,
+                                             my_command_handler);
+        cout << "[b10-auth] Configuration channel established." << endl;
+
+        xfrin_session = new Session(io_service->get_io_service());
+        cout << "[b10-auth] Xfrin session channel created." << endl;
+        xfrin_session->establish(NULL);
+        xfrin_session_established = true;
+        cout << "[b10-auth] Xfrin session channel established." << endl;
+
+        // XXX: with the current interface to asio_link we have to create
+        // auth_server before io_service while Session needs io_service.
+        // In a next step of refactoring we should make asio_link independent
+        // from auth_server, and create io_service, auth_server, and
+        // sessions in that order.
+        auth_server->setXfrinSession(xfrin_session);
+        auth_server->setConfigSession(config_session);
         auth_server->updateConfig(ElementPtr());
 
-        
         cout << "[b10-auth] Server started." << endl;
         io_service->run();
     } catch (const std::exception& ex) {
-        cerr << "[b10-auth] " << ex.what() << endl;
+        cerr << "[b10-auth] Initialization failed: " << ex.what() << endl;
         ret = 1;
     }
 
+    if (xfrin_session_established) {
+        xfrin_session->disconnect();
+    }
+
+    delete xfrin_session;
+    delete config_session;
+    delete cc_session;
     delete io_service;
     delete auth_server;
+
     return (ret);
 }

Modified: branches/trac241/src/bin/auth/tests/Makefile.am
==============================================================================
--- branches/trac241/src/bin/auth/tests/Makefile.am (original)
+++ branches/trac241/src/bin/auth/tests/Makefile.am Mon Aug  9 18:42:00 2010
@@ -14,16 +14,19 @@
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
 run_unittests_SOURCES += auth_srv_unittest.cc
+run_unittests_SOURCES += asio_link_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD +=  $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a
-run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/.libs/libdns.a
+run_unittests_LDADD +=  $(top_builddir)/src/lib/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
+run_unittests_LDADD += $(top_builddir)/src/bin/auth/libasio_link.a
+run_unittests_LDADD += $(top_builddir)/src/lib/xfr/.libs/libxfr.a
 endif
 
 noinst_PROGRAMS = $(TESTS)

Modified: branches/trac241/src/bin/auth/tests/auth_srv_unittest.cc
==============================================================================
--- branches/trac241/src/bin/auth/tests/auth_srv_unittest.cc (original)
+++ branches/trac241/src/bin/auth/tests/auth_srv_unittest.cc Mon Aug  9 18:42:00 2010
@@ -14,6 +14,8 @@
 
 // $Id$
 
+#include <config.h>
+
 #include <gtest/gtest.h>
 
 #include <dns/buffer.h>
@@ -24,33 +26,105 @@
 #include <dns/rrtype.h>
 
 #include <cc/data.h>
+#include <cc/session.h>
+
+#include <xfr/xfrout_client.h>
 
 #include <auth/auth_srv.h>
+#include <auth/asio_link.h>
 
 #include <dns/tests/unittest_util.h>
 
 using isc::UnitTestUtil;
 using namespace std;
+using namespace isc::cc;
 using namespace isc::dns;
 using namespace isc::data;
+using namespace isc::xfr;
+using namespace asio_link;
 
 namespace {
-const char* CONFIG_TESTDB =
+const char* const CONFIG_TESTDB =
     "{\"database_file\": \"" TEST_DATA_DIR "/example.sqlite3\"}";
 // The following file must be non existent and must be non"creatable" (see
 // the sqlite3 test).
-const char* BADCONFIG_TESTDB =
+const char* const BADCONFIG_TESTDB =
     "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}";
+const char* const DEFAULT_REMOTE_ADDRESS = "192.0.2.1";
 
 class AuthSrvTest : public ::testing::Test {
+private:
+    class MockXfroutClient : public AbstractXfroutClient {
+    public:
+        MockXfroutClient() :
+            is_connected_(false), connect_ok_(true), send_ok_(true),
+            disconnect_ok_(true)
+        {}
+        virtual void connect();
+        virtual void disconnect();
+        virtual int sendXfroutRequestInfo(int tcp_sock, const void* msg_data,
+                                          uint16_t msg_len);
+        bool isConnected() const { return (is_connected_); }
+        void disableConnect() { connect_ok_ = false; }
+        void disableDisconnect() { disconnect_ok_ = false; }
+        void enableDisconnect() { disconnect_ok_ = true; }
+        void disableSend() { send_ok_ = false; }
+    private:
+        bool is_connected_;
+        bool connect_ok_;
+        bool send_ok_;
+        bool disconnect_ok_;
+    };
+
+    class MockSession : public AbstractSession {
+    public:
+        MockSession() :
+            // by default we return a simple "success" message.
+            msg_(Element::fromJSON("{\"result\": [0, \"SUCCESS\"]}")),
+            send_ok_(true), receive_ok_(true)
+        {}
+        virtual void establish(const char* socket_file);
+        virtual void disconnect();
+        virtual int group_sendmsg(ElementPtr msg, string group,
+                                  string instance, string to);
+        virtual bool group_recvmsg(ElementPtr& envelope, ElementPtr& msg,
+                                   bool nonblock, int seq);
+        virtual void subscribe(string group, string instance);
+        virtual void unsubscribe(string group, string instance);
+        virtual void startRead(boost::function<void()> read_callback);
+        virtual int reply(ElementPtr& envelope, ElementPtr& newmsg);
+        virtual bool hasQueuedMsgs();
+
+        void setMessage(ElementPtr msg) { msg_ = msg; }
+        void disableSend() { send_ok_ = false; }
+        void disableReceive() { receive_ok_ = false; }
+
+        ElementPtr sent_msg;
+        string msg_destination;
+    private:
+        ElementPtr msg_;
+        bool send_ok_;
+        bool receive_ok_;
+    };
+
 protected:
-    AuthSrvTest() : request_message(Message::RENDER),
+    AuthSrvTest() : server(true, xfrout),
+                    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),
-                    request_obuffer(0), request_renderer(request_obuffer),
+                    qclass(RRClass::IN()), qtype(RRType::A()),
+                    io_message(NULL), endpoint(NULL), request_obuffer(0),
+                    request_renderer(request_obuffer),
                     response_obuffer(0), response_renderer(response_obuffer)
-    {}
+    {
+        server.setXfrinSession(&notify_session);
+    }
+    ~AuthSrvTest() {
+        delete io_message;
+        delete endpoint;
+    }
+    MockSession notify_session;
+    MockXfroutClient xfrout;
     AuthSrv server;
     Message request_message;
     Message parse_message;
@@ -59,15 +133,113 @@
     const Name qname;
     const RRClass qclass;
     const RRType qtype;
-    InputBuffer* ibuffer;
+    IOMessage* io_message;
+    const IOEndpoint* endpoint;
     OutputBuffer request_obuffer;
     MessageRenderer request_renderer;
     OutputBuffer response_obuffer;
     MessageRenderer response_renderer;
     vector<uint8_t> data;
 
-    void createDataFromFile(const char* const datafile);
+    void createDataFromFile(const char* const datafile, int protocol);
+    void createRequestMessage(const Opcode& opcode, const Name& request_name,
+                              const RRClass& rrclass, const RRType& rrtype);
+    void createRequestPacket(const Opcode& opcode, const Name& request_name,
+                             const RRClass& rrclass, const RRType& rrtype,
+                             int protocol);
+    void createRequestPacket(int protocol);
 };
+
+void
+AuthSrvTest::MockSession::establish(const char* socket_file UNUSED_PARAM) {}
+
+void
+AuthSrvTest::MockSession::disconnect() {}
+
+void
+AuthSrvTest::MockSession::subscribe(string group UNUSED_PARAM,
+                                    string instance UNUSED_PARAM)
+{}
+
+void
+AuthSrvTest::MockSession::unsubscribe(string group UNUSED_PARAM,
+                                      string instance UNUSED_PARAM)
+{}
+
+void
+AuthSrvTest::MockSession::startRead(
+    boost::function<void()> read_callback UNUSED_PARAM)
+{}
+
+int
+AuthSrvTest::MockSession::reply(ElementPtr& envelope UNUSED_PARAM,
+                                ElementPtr& newmsg UNUSED_PARAM)
+{
+    return (-1);
+}
+
+bool
+AuthSrvTest::MockSession::hasQueuedMsgs() {
+    return (false);
+}
+
+int
+AuthSrvTest::MockSession::group_sendmsg(ElementPtr msg, string group,
+                                        string instance UNUSED_PARAM,
+                                        string to UNUSED_PARAM)
+{
+    if (!send_ok_) {
+        isc_throw(XfroutError, "mock session send is disabled for test");
+    }
+
+    sent_msg = msg;
+    msg_destination = group;
+    return (0);
+}
+
+bool
+AuthSrvTest::MockSession::group_recvmsg(ElementPtr& envelope UNUSED_PARAM,
+                                        ElementPtr& msg,
+                                        bool nonblock UNUSED_PARAM,
+                                        int seq UNUSED_PARAM)
+{
+    if (!receive_ok_) {
+        isc_throw(XfroutError, "mock session receive is disabled for test");
+    }
+
+    msg = msg_;
+    return (true);
+}
+
+void
+AuthSrvTest::MockXfroutClient::connect() {
+    if (!connect_ok_) {
+        isc_throw(XfroutError, "xfrout connection disabled for test");
+    }
+    is_connected_ = true;
+}
+
+void
+AuthSrvTest::MockXfroutClient::disconnect() {
+    if (!disconnect_ok_) {
+        isc_throw(XfroutError,
+                  "closing xfrout connection is disabled for test");
+    }
+    is_connected_ = false;
+}
+
+int
+AuthSrvTest::MockXfroutClient::sendXfroutRequestInfo(
+    const int tcp_sock UNUSED_PARAM,
+    const void* msg_data UNUSED_PARAM,
+    const uint16_t msg_len UNUSED_PARAM)
+{
+    if (!send_ok_) {
+        isc_throw(XfroutError, "xfrout connection send is disabled for test");
+    }
+    return (0);
+}
+
 
 // These are flags to indicate whether the corresponding flag bit of the
 // DNS header is to be set in the test cases.  (Note that the flag values
@@ -81,12 +253,56 @@
 const unsigned int CD_FLAG = 0x40;
 
 void
-AuthSrvTest::createDataFromFile(const char* const datafile) {
-    delete ibuffer;
+AuthSrvTest::createDataFromFile(const char* const datafile,
+                                const int protocol = IPPROTO_UDP)
+{
+    delete io_message;
     data.clear();
 
+    delete endpoint;
+    endpoint = IOEndpoint::create(protocol,
+                                  IOAddress(DEFAULT_REMOTE_ADDRESS), 5300);
     UnitTestUtil::readWireData(datafile, data);
-    ibuffer = new InputBuffer(&data[0], data.size());
+    io_message = new IOMessage(&data[0], data.size(),
+                               protocol == IPPROTO_UDP ?
+                               IOSocket::getDummyUDPSocket() :
+                               IOSocket::getDummyTCPSocket(), *endpoint);
+}
+
+void
+AuthSrvTest::createRequestMessage(const Opcode& opcode,
+                                  const Name& request_name,
+                                  const RRClass& rrclass,
+                                  const RRType& rrtype)
+{
+    request_message.clear(Message::RENDER);
+    request_message.setOpcode(opcode);
+    request_message.setQid(default_qid);
+    request_message.addQuestion(Question(request_name, rrclass, rrtype));
+}
+
+void
+AuthSrvTest::createRequestPacket(const Opcode& opcode,
+                                 const Name& request_name,
+                                 const RRClass& rrclass, const RRType& rrtype,
+                                 const int protocol = IPPROTO_UDP)
+{
+    createRequestMessage(opcode, request_name, rrclass, rrtype);
+    createRequestPacket(protocol);
+}
+
+void
+AuthSrvTest::createRequestPacket(const int protocol = IPPROTO_UDP) {
+    request_message.toWire(request_renderer);
+
+    delete io_message;
+    endpoint = IOEndpoint::create(protocol,
+                                  IOAddress(DEFAULT_REMOTE_ADDRESS), 5300);
+    io_message = new IOMessage(request_renderer.getData(),
+                               request_renderer.getLength(),
+                               protocol == IPPROTO_UDP ?
+                               IOSocket::getDummyUDPSocket() :
+                               IOSocket::getDummyTCPSocket(), *endpoint);
 }
 
 void
@@ -115,15 +331,19 @@
 
 // Unsupported requests.  Should result in NOTIMP.
 TEST_F(AuthSrvTest, unsupportedRequest) {
-    for (unsigned int i = 1; i < 16; ++i) {
+    for (unsigned int i = 0; i < 16; ++i) {
         // set Opcode to 'i', which iterators over all possible codes except
-        // the standard query (0)
+        // the standard query and notify
+        if (i == Opcode::QUERY().getCode() ||
+            i == Opcode::NOTIFY().getCode()) {
+            continue;
+        }
         createDataFromFile("simplequery_fromWire");
         data[2] = ((i << 3) & 0xff);
 
         parse_message.clear(Message::PARSE);
-        EXPECT_EQ(true, server.processMessage(*ibuffer, parse_message,
-                                              response_renderer, true));
+        EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                              response_renderer));
         headerCheck(parse_message, default_qid, Rcode::NOTIMP(), i, QR_FLAG,
                     0, 0, 0, 0);
     }
@@ -141,8 +361,8 @@
 // Multiple questions.  Should result in FORMERR.
 TEST_F(AuthSrvTest, multiQuestion) {
     createDataFromFile("multiquestion_fromWire");
-    EXPECT_EQ(true, server.processMessage(*ibuffer, parse_message,
-                                          response_renderer, true));
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
     headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                 QR_FLAG, 2, 0, 0, 0);
 
@@ -162,8 +382,8 @@
 // dropped.
 TEST_F(AuthSrvTest, shortMessage) {
     createDataFromFile("shortmessage_fromWire");
-    EXPECT_EQ(false, server.processMessage(*ibuffer, parse_message,
-                                           response_renderer, true));
+    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
+                                           response_renderer));
 }
 
 // Response messages.  Must be silently dropped, whether it's a valid response
@@ -171,26 +391,26 @@
 TEST_F(AuthSrvTest, response) {
     // A valid (although unusual) response
     createDataFromFile("simpleresponse_fromWire");
-    EXPECT_EQ(false, server.processMessage(*ibuffer, parse_message,
-                                           response_renderer, true));
+    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
+                                           response_renderer));
 
     // A response with a broken question section.  must be dropped rather than
     // returning FORMERR.
     createDataFromFile("shortresponse_fromWire");
-    EXPECT_EQ(false, server.processMessage(*ibuffer, parse_message,
-                                           response_renderer, true));
+    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
+                                           response_renderer));
 
     // A response to iquery.  must be dropped rather than returning NOTIMP.
     createDataFromFile("iqueryresponse_fromWire");
-    EXPECT_EQ(false, server.processMessage(*ibuffer, parse_message,
-                                           response_renderer, true));
+    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
+                                           response_renderer));
 }
 
 // Query with a broken question
 TEST_F(AuthSrvTest, shortQuestion) {
     createDataFromFile("shortquestion_fromWire");
-    EXPECT_EQ(true, server.processMessage(*ibuffer, parse_message,
-                                          response_renderer, true));
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
     // Since the query's question is broken, the question section of the
     // response should be empty.
     headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
@@ -200,8 +420,8 @@
 // Query with a broken answer section
 TEST_F(AuthSrvTest, shortAnswer) {
     createDataFromFile("shortanswer_fromWire");
-    EXPECT_EQ(true, server.processMessage(*ibuffer, parse_message,
-                                          response_renderer, true));
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
 
     // This is a bogus query, but question section is valid.  So the response
     // should copy the question section.
@@ -219,8 +439,8 @@
 // Query with unsupported version of EDNS.
 TEST_F(AuthSrvTest, ednsBadVers) {
     createDataFromFile("queryBadEDNS_fromWire");
-    EXPECT_EQ(true, server.processMessage(*ibuffer, parse_message,
-                                          response_renderer, true));
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
 
     // The response must have an EDNS OPT RR in the additional section.
     // Note that the DNSSEC DO bit is cleared even if this bit in the query
@@ -231,12 +451,248 @@
     EXPECT_FALSE(parse_message.isDNSSECSupported());
 }
 
+TEST_F(AuthSrvTest, AXFROverUDP) {
+    // AXFR over UDP is invalid and should result in FORMERR.
+    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
+                        RRType::AXFR(), IPPROTO_UDP);
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
+    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
+                QR_FLAG, 1, 0, 0, 0);
+}
+
+TEST_F(AuthSrvTest, AXFRSuccess) {
+    EXPECT_FALSE(xfrout.isConnected());
+    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
+                        RRType::AXFR(), IPPROTO_TCP);
+    // On success, the AXFR query has been passed to a separate process,
+    // so we shouldn't have to respond.
+    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
+                                           response_renderer));
+    EXPECT_TRUE(xfrout.isConnected());
+}
+
+TEST_F(AuthSrvTest, AXFRConnectFail) {
+    EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
+    xfrout.disableConnect();
+    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
+                        RRType::AXFR(), IPPROTO_TCP);
+    EXPECT_TRUE(server.processMessage(*io_message, parse_message,
+                                      response_renderer));
+    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
+                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+    EXPECT_FALSE(xfrout.isConnected());
+}
+
+TEST_F(AuthSrvTest, AXFRSendFail) {
+    // first send a valid query, making the connection with the xfr process
+    // open.
+    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
+                        RRType::AXFR(), IPPROTO_TCP);
+    server.processMessage(*io_message, parse_message, response_renderer);
+    EXPECT_TRUE(xfrout.isConnected());
+
+    xfrout.disableSend();
+    parse_message.clear(Message::PARSE);
+    response_renderer.clear();
+    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
+                        RRType::AXFR(), IPPROTO_TCP);
+    EXPECT_TRUE(server.processMessage(*io_message, parse_message,
+                                      response_renderer));
+    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
+                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+
+    // The connection should have been closed due to the send failure.
+    EXPECT_FALSE(xfrout.isConnected());
+}
+
+TEST_F(AuthSrvTest, AXFRDisconnectFail) {
+    // In our usage disconnect() shouldn't fail.  So we'll see the exception
+    // should it be thrown.
+    xfrout.disableSend();
+    xfrout.disableDisconnect();
+    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
+                        RRType::AXFR(), IPPROTO_TCP);
+    EXPECT_THROW(server.processMessage(*io_message, parse_message,
+                                       response_renderer),
+                 XfroutError);
+    EXPECT_TRUE(xfrout.isConnected());
+    // XXX: we need to re-enable disconnect.  otherwise an exception would be
+    // thrown via the destructor of the server.
+    xfrout.enableDisconnect();
+}
+
+TEST_F(AuthSrvTest, notify) {
+    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
+                        RRType::SOA());
+    request_message.setHeaderFlag(MessageFlag::AA());
+    createRequestPacket(IPPROTO_UDP);
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
+
+    // An internal command message should have been created and sent to an
+    // external module.  Check them.
+    EXPECT_EQ("Xfrin", notify_session.msg_destination);
+    EXPECT_EQ("notify",
+              notify_session.sent_msg->get("command")->get(0)->stringValue());
+    ElementPtr notify_args = notify_session.sent_msg->get("command")->get(1);
+    EXPECT_EQ("example.com.", notify_args->get("zone_name")->stringValue());
+    EXPECT_EQ(DEFAULT_REMOTE_ADDRESS,
+              notify_args->get("master")->stringValue());
+    EXPECT_EQ("IN", notify_args->get("rrclass")->stringValue());
+
+    // On success, the server should return a response to the notify.
+    headerCheck(parse_message, default_qid, Rcode::NOERROR(),
+                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
+
+    // The question must be identical to that of the received notify
+    ConstQuestionPtr question = *parse_message.beginQuestion();
+    EXPECT_EQ(Name("example.com"), question->getName());
+    EXPECT_EQ(RRClass::IN(), question->getClass());
+    EXPECT_EQ(RRType::SOA(), question->getType());
+}
+
+TEST_F(AuthSrvTest, notifyForCHClass) {
+    // Same as the previous test, but for the CH RRClass.
+    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::CH(),
+                        RRType::SOA());
+    request_message.setHeaderFlag(MessageFlag::AA());
+    createRequestPacket(IPPROTO_UDP);
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
+
+    // Other conditions should be the same, so simply confirm the RR class is
+    // set correctly.
+    ElementPtr notify_args = notify_session.sent_msg->get("command")->get(1);
+    EXPECT_EQ("CH", notify_args->get("rrclass")->stringValue());
+}
+
+TEST_F(AuthSrvTest, notifyEmptyQuestion) {
+    request_message.clear(Message::RENDER);
+    request_message.setOpcode(Opcode::NOTIFY());
+    request_message.setHeaderFlag(MessageFlag::AA());
+    request_message.setQid(default_qid);
+    request_message.toWire(request_renderer);
+    createRequestPacket(IPPROTO_UDP);
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
+    headerCheck(parse_message, default_qid, Rcode::FORMERR(),
+                Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
+}
+
+TEST_F(AuthSrvTest, notifyMultiQuestions) {
+    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
+                        RRType::SOA());
+    // add one more SOA question
+    request_message.addQuestion(Question(Name("example.com"), RRClass::IN(),
+                                         RRType::SOA()));
+    request_message.setHeaderFlag(MessageFlag::AA());
+    createRequestPacket(IPPROTO_UDP);
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
+    headerCheck(parse_message, default_qid, Rcode::FORMERR(),
+                Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
+}
+
+TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
+    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
+                        RRType::NS());
+    request_message.setHeaderFlag(MessageFlag::AA());
+    createRequestPacket(IPPROTO_UDP);
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
+    headerCheck(parse_message, default_qid, Rcode::FORMERR(),
+                Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
+}
+
+TEST_F(AuthSrvTest, notifyWithoutAA) {
+    // implicitly leave the AA bit off.  our implementation will accept it.
+    createRequestPacket(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
+                        RRType::SOA());
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
+    headerCheck(parse_message, default_qid, Rcode::NOERROR(),
+                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
+}
+
+TEST_F(AuthSrvTest, notifyWithErrorRcode) {
+    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
+                        RRType::SOA());
+    request_message.setHeaderFlag(MessageFlag::AA());
+    request_message.setRcode(Rcode::SERVFAIL());
+    createRequestPacket(IPPROTO_UDP);
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
+    headerCheck(parse_message, default_qid, Rcode::NOERROR(),
+                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
+}
+
+TEST_F(AuthSrvTest, notifyWithoutSession) {
+    server.setXfrinSession(NULL);
+
+    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
+                        RRType::SOA());
+    request_message.setHeaderFlag(MessageFlag::AA());
+    createRequestPacket(IPPROTO_UDP);
+
+    // we simply ignore the notify and let it be resent if an internal error
+    // happens.
+    EXPECT_FALSE(server.processMessage(*io_message, parse_message,
+                                       response_renderer));
+}
+
+TEST_F(AuthSrvTest, notifySendFail) {
+    notify_session.disableSend();
+
+    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
+                        RRType::SOA());
+    request_message.setHeaderFlag(MessageFlag::AA());
+    createRequestPacket(IPPROTO_UDP);
+
+    EXPECT_FALSE(server.processMessage(*io_message, parse_message,
+                                       response_renderer));
+}
+
+TEST_F(AuthSrvTest, notifyReceiveFail) {
+    notify_session.disableReceive();
+
+    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
+                        RRType::SOA());
+    request_message.setHeaderFlag(MessageFlag::AA());
+    createRequestPacket(IPPROTO_UDP);
+    EXPECT_FALSE(server.processMessage(*io_message, parse_message,
+                                       response_renderer));
+}
+
+TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
+    notify_session.setMessage(Element::fromJSON("{\"foo\": 1}"));
+
+    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
+                        RRType::SOA());
+    request_message.setHeaderFlag(MessageFlag::AA());
+    createRequestPacket(IPPROTO_UDP);
+    EXPECT_FALSE(server.processMessage(*io_message, parse_message,
+                                       response_renderer));
+}
+
+TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
+    notify_session.setMessage(
+        Element::fromJSON("{\"result\": [1, \"FAIL\"]}"));
+
+    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
+                        RRType::SOA());
+    request_message.setHeaderFlag(MessageFlag::AA());
+    createRequestPacket(IPPROTO_UDP);
+    EXPECT_FALSE(server.processMessage(*io_message, parse_message,
+                                       response_renderer));
+}
+
 void
 updateConfig(AuthSrv* server, const char* const dbfile,
              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"));
 
@@ -253,8 +709,8 @@
     // response should have the AA flag on, and have an RR in each answer
     // and authority section.
     createDataFromFile("examplequery_fromWire");
-    EXPECT_EQ(true, server.processMessage(*ibuffer, parse_message,
-                                          response_renderer, true));
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
     headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                 QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
@@ -267,10 +723,10 @@
     // in a SERVFAIL response, and the answer and authority sections should
     // be empty.
     createDataFromFile("badExampleQuery_fromWire");
-    EXPECT_EQ(true, server.processMessage(*ibuffer, parse_message,
-                                          response_renderer, true));
-    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(), opcode.getCode(),
-                QR_FLAG, 1, 0, 0, 0);
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
+    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
+                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
 }
 
 TEST_F(AuthSrvTest, updateConfigFail) {
@@ -282,8 +738,8 @@
 
     // The original data source should still exist.
     createDataFromFile("examplequery_fromWire");
-    EXPECT_EQ(true, server.processMessage(*ibuffer, parse_message,
-                                          response_renderer, true));
+    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
+                                          response_renderer));
     headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                 QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }

Modified: branches/trac241/src/bin/bind10/bind10.py.in
==============================================================================
--- branches/trac241/src/bin/bind10/bind10.py.in (original)
+++ branches/trac241/src/bin/bind10/bind10.py.in Mon Aug  9 18:42:00 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 the b10-cmdctl
         # XXX: we hardcode port 8080
@@ -344,7 +414,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
 
@@ -353,7 +424,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")
@@ -435,11 +506,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()
@@ -507,20 +583,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:
@@ -543,11 +675,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.
@@ -584,6 +718,7 @@
     # shutdown
     signal.signal(signal.SIGCHLD, signal.SIG_DFL)
     boss_of_bind.shutdown()
+    sys.exit(0)
 
 if __name__ == "__main__":
     main()

Modified: branches/trac241/src/bin/bind10/run_bind10.sh.in
==============================================================================
--- branches/trac241/src/bin/bind10/run_bind10.sh.in (original)
+++ branches/trac241/src/bin/bind10/run_bind10.sh.in Mon Aug  9 18:42:00 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/trac241/src/bin/bind10/tests/bind10_test.in
==============================================================================
--- branches/trac241/src/bin/bind10/tests/bind10_test.in (original)
+++ branches/trac241/src/bin/bind10/tests/bind10_test.in Mon Aug  9 18:42:00 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/trac241/src/bin/bind10/tests/bind10_test.py
==============================================================================
--- branches/trac241/src/bin/bind10/tests/bind10_test.py (original)
+++ branches/trac241/src/bin/bind10/tests/bind10_test.py Mon Aug  9 18:42:00 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/trac241/src/bin/bindctl/Makefile.am
==============================================================================
--- branches/trac241/src/bin/bindctl/Makefile.am (original)
+++ branches/trac241/src/bin/bindctl/Makefile.am Mon Aug  9 18:42:00 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/trac241/src/bin/bindctl/TODO
==============================================================================
--- branches/trac241/src/bin/bindctl/TODO (original)
+++ branches/trac241/src/bin/bindctl/TODO Mon Aug  9 18:42:00 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/trac241/src/bin/bindctl/bindcmd.py
==============================================================================
--- branches/trac241/src/bin/bindctl/bindcmd.py (original)
+++ branches/trac241/src/bin/bindctl/bindcmd.py Mon Aug  9 18:42:00 2010
@@ -35,7 +35,10 @@
 import getpass
 from hashlib import sha1
 import csv
-import ast
+import json
+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):
@@ -488,6 +543,16 @@
                     identifier = cmd.params['identifier']
                 else:
                     identifier += cmd.params['identifier']
+
+                # Check if the module is known; for unknown modules
+                # we currently deny setting preferences, as we have
+                # no way yet to determine if they are ok.
+                module_name = identifier.split('/')[1]
+                if self.config_data is None or \
+                   not self.config_data.have_specification(module_name):
+                    print("Error: Module '" + module_name + "' unknown or not running")
+                    return
+
             if cmd.command == "show":
                 values = self.config_data.get_value_maps(identifier)
                 for value_map in values:
@@ -513,7 +578,7 @@
                 else:
                     parsed_value = None
                     try:
-                        parsed_value = ast.literal_eval(cmd.params['value'])
+                        parsed_value = json.loads(cmd.params['value'])
                     except Exception as exc:
                         # ok could be an unquoted string, interpret as such
                         parsed_value = cmd.params['value']
@@ -556,7 +621,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/trac241/src/bin/bindctl/bindctl-source.py.in
==============================================================================
--- branches/trac241/src/bin/bindctl/bindctl-source.py.in (original)
+++ branches/trac241/src/bin/bindctl/bindctl-source.py.in Mon Aug  9 18:42:00 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/trac241/src/bin/bindctl/cmdparse.py
==============================================================================
--- branches/trac241/src/bin/bindctl/cmdparse.py (original)
+++ branches/trac241/src/bin/bindctl/cmdparse.py Mon Aug  9 18:42:00 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/trac241/src/bin/bindctl/exception.py
==============================================================================
--- branches/trac241/src/bin/bindctl/exception.py (original)
+++ branches/trac241/src/bin/bindctl/exception.py Mon Aug  9 18:42:00 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/trac241/src/bin/bindctl/tests/bindctl_test.py
==============================================================================
--- branches/trac241/src/bin/bindctl/tests/bindctl_test.py (original)
+++ branches/trac241/src/bin/bindctl/tests/bindctl_test.py Mon Aug  9 18:42:00 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,37 @@
             assert self.random_names[i] == cmd_names[i+1]
             assert self.random_names[i] == module_names[i+1]
             i = i + 1
-        
-    
+
+    def test_apply_cfg_command(self):
+        self.tool.location = '/'
+        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/bar\" value=\"5\"")
+        self.tool.apply_config_cmd(cmd)
+    
+class FakeBindCmdInterpreter(bindcmd.BindCmdInterpreter):
+    def __init__(self):
+        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/trac241/src/bin/cfgmgr/Makefile.am
==============================================================================
--- branches/trac241/src/bin/cfgmgr/Makefile.am (original)
+++ branches/trac241/src/bin/cfgmgr/Makefile.am Mon Aug  9 18:42:00 2010
@@ -1,8 +1,10 @@
+SUBDIRS = tests
+
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 pkglibexec_SCRIPTS = b10-cfgmgr
 
-CLEANFILES = b10-cfgmgr
+CLEANFILES = b10-cfgmgr b10-cfgmgr.pyc
 
 b10_cfgmgrdir = @localstatedir@/@PACKAGE@
 #B10_cfgmgr_DATA = 

Modified: branches/trac241/src/bin/cfgmgr/b10-cfgmgr.py.in
==============================================================================
--- branches/trac241/src/bin/cfgmgr/b10-cfgmgr.py.in (original)
+++ branches/trac241/src/bin/cfgmgr/b10-cfgmgr.py.in Mon Aug  9 18:42:00 2010
@@ -40,7 +40,8 @@
     if cm:
         cm.running = False
 
-if __name__ == "__main__":
+def main():
+    global cm
     try:
         cm = ConfigManager(DATA_PATH)
         signal.signal(signal.SIGINT, signal_handler)
@@ -55,3 +56,6 @@
         print("[b10-cfgmgr] Interrupted, exiting")
     if cm:
         cm.write_config()
+
+if __name__ == "__main__":
+    main()

Modified: branches/trac241/src/bin/cmdctl/Makefile.am
==============================================================================
--- branches/trac241/src/bin/cmdctl/Makefile.am (original)
+++ branches/trac241/src/bin/cmdctl/Makefile.am Mon Aug  9 18:42:00 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/trac241/src/bin/cmdctl/TODO
==============================================================================
--- branches/trac241/src/bin/cmdctl/TODO (original)
+++ branches/trac241/src/bin/cmdctl/TODO Mon Aug  9 18:42:00 2010
@@ -1,8 +1,7 @@
-. 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 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)
+. 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 more test cases, especially about the cases where CmdctlException
+  is raised

Modified: branches/trac241/src/bin/cmdctl/cmdctl.py.in
==============================================================================
--- branches/trac241/src/bin/cmdctl/cmdctl.py.in (original)
+++ branches/trac241/src/bin/cmdctl/cmdctl.py.in Mon Aug  9 18:42:00 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,161 @@
 
     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)
+        try:
+            http.server.HTTPServer.__init__(self, server_address, RequestHandlerClass)
+        except socket.error as err:
+            raise CmdctlException("Error creating server, because: %s \n" % str(err))
+
         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 +553,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,22 +590,24 @@
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
             help="display more about what is going on")
 
-
 if __name__ == '__main__':
+    set_signal_handler()
+    parser = OptionParser(version = __version__)
+    set_cmd_options(parser)
+    (options, args) = parser.parse_args()
+    result = 1                  # in case of failure
     try:
-        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:
+        result = 0
+    except isc.cc.SessionError as err:
         sys.stderr.write("[b10-cmdctl] Error creating b10-cmdctl, "
-                "is the command channel daemon running?\n")        
+                         "is the command channel daemon running?\n")        
     except KeyboardInterrupt:
-        sys.stderr.write("[b10-cmdctl] exit http server\n")
+        sys.stderr.write("[b10-cmdctl] exit from Cmdctl\n")
+    except CmdctlException as err:
+        sys.stderr.write("[b10-cmdctl] " + str(err) + "\n")
 
     if httpd:
         httpd.shutdown()
 
-
-
+    sys.exit(result)

Modified: branches/trac241/src/bin/cmdctl/tests/Makefile.am
==============================================================================
--- branches/trac241/src/bin/cmdctl/tests/Makefile.am (original)
+++ branches/trac241/src/bin/cmdctl/tests/Makefile.am Mon Aug  9 18:42:00 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/trac241/src/bin/cmdctl/tests/cmdctl_test.in
==============================================================================
--- branches/trac241/src/bin/cmdctl/tests/cmdctl_test.in (original)
+++ branches/trac241/src/bin/cmdctl/tests/cmdctl_test.in Mon Aug  9 18:42:00 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/trac241/src/bin/cmdctl/tests/cmdctl_test.py
==============================================================================
--- branches/trac241/src/bin/cmdctl/tests/cmdctl_test.py (original)
+++ branches/trac241/src/bin/cmdctl/tests/cmdctl_test.py Mon Aug  9 18:42:00 2010
@@ -16,7 +16,17 @@
 
 import unittest
 import socket
+import tempfile
+import sys
 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 +52,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 +73,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 +161,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 +170,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 +182,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 +192,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 +201,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 +213,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 +263,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 +275,178 @@
 
         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
+        self.old_stderr = sys.stderr
+        sys.stdout = open(os.devnull, 'w')
+        sys.stderr = sys.stdout
+        self.server = MySecureHTTPServer(('localhost', 8080), 
+                                         MySecureHTTPRequestHandler,
+                                         MyCommandControl, verbose=True)
+
+    def tearDown(self):
+        sys.stdout = self.old_stdout
+        sys.stderr = self.old_stderr
+
+    def test_addr_in_use(self):
+        server_one = None
+        try:
+            server_one = SecureHTTPServer(('localhost', 53531),
+                                        MySecureHTTPRequestHandler,
+                                        MyCommandControl)
+        except CmdctlException:
+            pass
+        else:
+            self.assertRaises(CmdctlException, SecureHTTPServer,
+                              ('localhost', 53531),
+                              MySecureHTTPRequestHandler, MyCommandControl)
+        if server_one:
+            server_one.server_close()
+
+    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/trac241/src/bin/host/Makefile.am
==============================================================================
--- branches/trac241/src/bin/host/Makefile.am (original)
+++ branches/trac241/src/bin/host/Makefile.am Mon Aug  9 18:42:00 2010
@@ -7,7 +7,7 @@
 
 bin_PROGRAMS = host
 host_SOURCES = host.cc
-host_LDADD = $(top_builddir)/src/lib/dns/.libs/libdns.a
+host_LDADD = $(top_builddir)/src/lib/dns/.libs/libdns++.a
 host_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
 
 #man_MANS = host.1

Modified: branches/trac241/src/bin/host/host.cc
==============================================================================
--- branches/trac241/src/bin/host/host.cc (original)
+++ branches/trac241/src/bin/host/host.cc Mon Aug  9 18:42:00 2010
@@ -19,17 +19,19 @@
 #include <sys/time.h>       // for gettimeofday
 #include <sys/socket.h>     // networking functions and definitions on FreeBSD
 
+#include <unistd.h>
+
 #include <string>
 #include <iostream>
 
-#include "dns/buffer.h"
-#include "dns/name.h"
-#include "dns/message.h"
-#include "dns/messagerenderer.h"
-#include "dns/rrclass.h"
-#include "dns/rrtype.h"
-#include "dns/rrset.h"
-#include "dns/message.h"
+#include <dns/buffer.h>
+#include <dns/name.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
+#include <dns/message.h>
 
 using namespace std;
 using namespace isc::dns;

Modified: branches/trac241/src/bin/loadzone/Makefile.am
==============================================================================
--- branches/trac241/src/bin/loadzone/Makefile.am (original)
+++ branches/trac241/src/bin/loadzone/Makefile.am Mon Aug  9 18:42:00 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/trac241/src/bin/loadzone/b10-loadzone.py.in
==============================================================================
--- branches/trac241/src/bin/loadzone/b10-loadzone.py.in (original)
+++ branches/trac241/src/bin/loadzone/b10-loadzone.py.in Mon Aug  9 18:42:00 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/trac241/src/bin/loadzone/run_loadzone.sh.in
==============================================================================
--- branches/trac241/src/bin/loadzone/run_loadzone.sh.in (original)
+++ branches/trac241/src/bin/loadzone/run_loadzone.sh.in Mon Aug  9 18:42:00 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/trac241/src/bin/xfrin/tests/Makefile.am
==============================================================================
--- branches/trac241/src/bin/xfrin/tests/Makefile.am (original)
+++ branches/trac241/src/bin/xfrin/tests/Makefile.am Mon Aug  9 18:42:00 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/trac241/src/bin/xfrin/tests/xfrin_test.py
==============================================================================
--- branches/trac241/src/bin/xfrin/tests/xfrin_test.py (original)
+++ branches/trac241/src/bin/xfrin/tests/xfrin_test.py Mon Aug  9 18:42:00 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,
@@ -410,21 +412,30 @@
         return self.xfr._parse_cmd_params(self.args)
 
     def test_parse_cmd_params(self):
-        name, master_addrinfo, db_file = self._do_parse()
+        name, rrclass, master_addrinfo, db_file = self._do_parse()
         self.assertEqual(master_addrinfo[4][1], int(TEST_MASTER_PORT))
         self.assertEqual(name, TEST_ZONE_NAME)
+        self.assertEqual(rrclass, TEST_RRCLASS)
         self.assertEqual(master_addrinfo[4][0], TEST_MASTER_IPV4_ADDRESS)
         self.assertEqual(db_file, TEST_DB_FILE)
 
     def test_parse_cmd_params_default_port(self):
         del self.args['port']
-        master_addrinfo = self._do_parse()[1]
+        master_addrinfo = self._do_parse()[2]
         self.assertEqual(master_addrinfo[4][1], 53)
 
     def test_parse_cmd_params_ip6master(self):
         self.args['master'] = TEST_MASTER_IPV6_ADDRESS
-        master_addrinfo = self._do_parse()[1]
+        master_addrinfo = self._do_parse()[2]
         self.assertEqual(master_addrinfo[4][0], TEST_MASTER_IPV6_ADDRESS)
+
+    def test_parse_cmd_params_chclass(self):
+        self.args['rrclass'] = 'CH'
+        self.assertEqual(self._do_parse()[1], RRClass.CH())
+
+    def test_parse_cmd_params_bogusclass(self):
+        self.args['rrclass'] = 'XXX'
+        self.assertRaises(XfrinException, self._do_parse)
 
     def test_parse_cmd_params_nozone(self):
         # zone name is mandatory.
@@ -488,12 +499,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.
@@ -502,6 +513,13 @@
         self.assertEqual(self.xfr.command_handler("refresh",
                                                   self.args)['result'][0], 0)
 
+    def test_command_handler_notify(self):
+        # at this level, refresh is no different than retransfer.
+        self.args['master'] = TEST_MASTER_IPV6_ADDRESS
+        # ...but right now we disable the feature due to security concerns.
+        self.assertEqual(self.xfr.command_handler("notify",
+                                                  self.args)['result'][0], 1)
+
     def test_command_handler_unknown(self):
         self.assertEqual(self.xfr.command_handler("xxx", None)['result'][0], 1)
 

Modified: branches/trac241/src/bin/xfrin/xfrin.py.in
==============================================================================
--- branches/trac241/src/bin/xfrin/xfrin.py.in (original)
+++ branches/trac241/src/bin/xfrin/xfrin.py.in Mon Aug  9 18:42:00 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:
@@ -408,13 +404,36 @@
             if command == 'shutdown':
                 self._shutdown_event.set()
             elif command == 'retransfer' or command == 'refresh':
-                # 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()
-                zone_name, master_addr, db_file = self._parse_cmd_params(args)
-                ret = self.xfrin_start(zone_name, rrclass, db_file, master_addr,
+                (zone_name, rrclass,
+                 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)
+                answer = create_answer(ret[0], ret[1])
+            elif command == 'notify':
+                # This is the temporary implementation for notify.
+                # actually the notfiy command should be sent to the
+                # Zone Manager module.  Being temporary, we separate this case
+                # from refresh/retransfer while we could (and should otherwise)
+                # share the code.
+                (zone_name, rrclass,
+                 master_addr, db_file) = self._parse_cmd_params(args)
+
+                # XXX: master_addr is the sender of the notify message.
+                # It's very dangerous to naively trust it as the source of
+                # subsequent zone transfer; any remote node can easily exploit
+                # it to mount zone poisoning or DoS attacks.  We should
+                # locally identify the appropriate set of master servers.
+                # For now, we disable the code below.
+                master_is_valid = False
+
+                if master_is_valid:
+                    ret = self.xfrin_start(zone_name, rrclass, db_file,
+                                           master_addr, True)
+                else:
+                    errmsg = 'Failed to validate the master address ('
+                    errmsg += args['master'] + '), ignoring notify'
+                    ret = [1, errmsg]
                 answer = create_answer(ret[0], ret[1])
             else:
                 answer = create_answer(1, 'unknown command: ' + command)
@@ -429,6 +448,18 @@
         if not zone_name:
             raise XfrinException('zone name should be provided')
 
+        rrclass = args.get('rrclass')
+        if not rrclass:
+            # The default RR class is IN.  We should fix this so that
+            # the class is always passed in the command arg (where we specify
+            # the default)
+            rrclass = RRClass.IN()
+        else:
+            try:
+                rrclass = RRClass(rrclass)
+            except InvalidRRClass as e:
+                raise XfrinException('invalid RRClass: ' + rrclass)
+
         master = args.get('master')
         if not master:
             raise XfrinException('master address should be provided')
@@ -441,9 +472,20 @@
         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'
-
-        return (zone_name, master_addrinfo, db_file)
+            # 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, rrclass, master_addrinfo, db_file)
 
     def startup(self):
         while not self._shutdown_event.is_set():
@@ -451,8 +493,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/trac241/src/bin/xfrin/xfrin.spec.pre.in
==============================================================================
--- branches/trac241/src/bin/xfrin/xfrin.spec.pre.in (original)
+++ branches/trac241/src/bin/xfrin/xfrin.spec.pre.in Mon Aug  9 18:42:00 2010
@@ -6,37 +6,37 @@
       {
         "item_name": "transfers_in",
         "item_type": "integer",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 10
       }
     ],
     "commands": [
      {
-        'command_name': 'retransfer',
-        "command_description": 'retransfer a single zone without checking zone serial number',
-        'command_args': [ {
+        "command_name": "retransfer",
+        "command_description": "retransfer a single zone without checking zone serial number",
+        "command_args": [ {
             "item_name": "zone_name",
             "item_type": "string",
-            "item_optional": False,
+            "item_optional": false,
             "item_default": ""
           },
           {
             "item_name": "master",
             "item_type": "string",
-            "item_optional": False,
+            "item_optional": false,
             "item_default": ""
           },
           {
             "item_name": "port",
             "item_type": "integer",
-            "item_optional": True,
+            "item_optional": true,
             "item_default": 53
           },
           {
             "item_name": "db_file",
             "item_type": "string",
-            "item_optional": True,
-            "item_default": '@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3'
+            "item_optional": true,
+            "item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3"
           }
         ]
       },

Modified: branches/trac241/src/bin/xfrout/run_b10-xfrout.sh.in
==============================================================================
--- branches/trac241/src/bin/xfrout/run_b10-xfrout.sh.in (original)
+++ branches/trac241/src/bin/xfrout/run_b10-xfrout.sh.in Mon Aug  9 18:42:00 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/trac241/src/bin/xfrout/tests/Makefile.am
==============================================================================
--- branches/trac241/src/bin/xfrout/tests/Makefile.am (original)
+++ branches/trac241/src/bin/xfrout/tests/Makefile.am Mon Aug  9 18:42:00 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/trac241/src/bin/xfrout/tests/xfrout_test.py
==============================================================================
--- branches/trac241/src/bin/xfrout/tests/xfrout_test.py (original)
+++ branches/trac241/src/bin/xfrout/tests/xfrout_test.py Mon Aug  9 18:42:00 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/trac241/src/bin/xfrout/xfrout.py.in
==============================================================================
--- branches/trac241/src/bin/xfrout/xfrout.py.in (original)
+++ branches/trac241/src/bin/xfrout/xfrout.py.in Mon Aug  9 18:42:00 2010
@@ -26,13 +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.
@@ -40,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)
@@ -72,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()
 
 
@@ -106,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_):
@@ -126,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)
 
 
@@ -151,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    
@@ -195,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_
          
@@ -218,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):
@@ -239,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 
@@ -328,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
@@ -363,29 +383,45 @@
         self._lock.release()
 
 def listen_on_xfr_query(unix_socket_server):
-
     '''Listen xfr query in one single thread. Polls for shutdown 
     every 0.1 seconds, is there a better time?
     '''
-    unix_socket_server.serve_forever(poll_interval = 0.1)
+
+    while True:
+        try:
+            unix_socket_server.serve_forever(poll_interval = 0.1)
+        except select.error as err:
+            # serve_forever() calls select.select(), which can be 
+            # interrupted.
+            # If it is interrupted, it raises select.error with the 
+            # errno set to EINTR. We ignore this case, and let the
+            # normal program flow continue by trying serve_forever()
+            # again.
+            if err.args[0] != errno.EINTR: raise
+
    
 
 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()
 
@@ -399,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)
 
@@ -424,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: 
@@ -460,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/trac241/src/bin/xfrout/xfrout.spec.pre.in
==============================================================================
--- branches/trac241/src/bin/xfrout/xfrout.spec.pre.in (original)
+++ branches/trac241/src/bin/xfrout/xfrout.spec.pre.in Mon Aug  9 18:42:00 2010
@@ -5,14 +5,44 @@
        {
          "item_name": "transfers_out",
          "item_type": "integer",
-         "item_optional": False,
+         "item_optional": false,
          "item_default": 10
        },
        {
          "item_name": "db_file",
          "item_type": "string",
-         "item_optional": False,
-         "item_default": '@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3'
+         "item_optional": false,
+         "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/trac241/src/lib/Makefile.am
==============================================================================
--- branches/trac241/src/lib/Makefile.am (original)
+++ branches/trac241/src/lib/Makefile.am Mon Aug  9 18:42:00 2010
@@ -1,4 +1,1 @@
-SUBDIRS = exceptions dns cc config datasrc bench python
-if HAVE_BOOST_PYTHON
-SUBDIRS += xfr
-endif
+SUBDIRS = exceptions dns cc config datasrc python xfr

Modified: branches/trac241/src/lib/cc/Makefile.am
==============================================================================
--- branches/trac241/src/lib/cc/Makefile.am (original)
+++ branches/trac241/src/lib/cc/Makefile.am Mon Aug  9 18:42:00 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,8 +29,8 @@
 # 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 +=  $(top_builddir)/src/lib/dns/.libs/libdns.a
+run_unittests_LDADD = libcc.la $(GTEST_LDADD)
+run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/.libs/libdns++.a
 run_unittests_LDADD +=  $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
 
 endif

Modified: branches/trac241/src/lib/cc/data.cc
==============================================================================
--- branches/trac241/src/lib/cc/data.cc (original)
+++ branches/trac241/src/lib/cc/data.cc Mon Aug  9 18:42:00 2010
@@ -14,11 +14,12 @@
 
 // $Id$
 
-#include "config.h"
-
-#include "data.h"
+#include <config.h>
+
+#include <cc/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/trac241/src/lib/cc/data.h
==============================================================================
--- branches/trac241/src/lib/cc/data.h (original)
+++ branches/trac241/src/lib/cc/data.h Mon Aug  9 18:42:00 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/trac241/src/lib/cc/data_unittests.cc
==============================================================================
--- branches/trac241/src/lib/cc/data_unittests.cc (original)
+++ branches/trac241/src/lib/cc/data_unittests.cc Mon Aug  9 18:42:00 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/trac241/src/lib/cc/session.cc
==============================================================================
--- branches/trac241/src/lib/cc/session.cc (original)
+++ branches/trac241/src/lib/cc/session.cc Mon Aug  9 18:42:00 2010
@@ -15,9 +15,20 @@
 // $Id$
 
 #include <config.h>
-#include "session_config.h"
+#include <cc/session_config.h>
 
 #include <stdint.h>
+
+// XXX: there seems to be a strange dependency between ASIO and std library
+// definitions.  On some platforms if we include std headers before ASIO
+// headers unexpected behaviors will happen.
+// 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>
 
 #include <cstdio>
 #include <vector>
@@ -29,14 +40,10 @@
 #include <boost/bind.hpp>
 #include <boost/function.hpp>
 
-#include <asio.hpp>
-#include <asio/error_code.hpp>
-#include <asio/system_error.hpp>
-
 #include <exceptions/exceptions.h>
 
-#include "data.h"
-#include "session.h"
+#include <cc/data.h>
+#include <cc/session.h>
 
 using namespace std;
 using namespace isc::cc;
@@ -45,44 +52,26 @@
 // some of the asio names conflict with socket API system calls
 // (e.g. write(2)) so we don't import the entire asio namespace.
 using asio::io_service;
-using asio::ip::tcp;
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
 
 namespace isc {
 namespace cc {
-
 class SessionImpl {
 public:
-    SessionImpl() : sequence_(-1) { queue_ = Element::createFromString("[]"); }
-    virtual ~SessionImpl() {}
-    virtual void establish(const char& socket_file) = 0;
-    virtual int getSocket() = 0;
-    virtual void disconnect() = 0;
-    virtual void writeData(const void* data, size_t datalen) = 0;
-    virtual size_t readDataLength() = 0;
-    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
+    SessionImpl(io_service& io_service) :
+        sequence_(-1), queue_(Element::createList()),
+        io_service_(io_service), socket_(io_service_), data_length_(0)
+    {}
+    void establish(const char& socket_file);
+    void disconnect();
+    void writeData(const void* data, size_t datalen);
+    size_t readDataLength();
+    void readData(void* data, size_t datalen);
+    void startRead(boost::function<void()> user_handler);
+
+    long int sequence_; // the next sequence number to use
     std::string lname_;
     ElementPtr queue_;
-};
-
-class ASIOSession : public SessionImpl {
-public:
-    ASIOSession(io_service& io_service) :
-        io_service_(io_service), socket_(io_service_), data_length_(0)
-    {}
-    virtual void establish(const char& socket_file);
-    virtual void disconnect();
-    virtual int getSocket() { return (socket_.native()); }
-    virtual void writeData(const void* data, size_t datalen);
-    virtual size_t readDataLength();
-    virtual void readData(void* data, size_t datalen);
-    virtual void startRead(boost::function<void()> user_handler);
+
 private:
     void internalRead(const asio::error_code& error,
                       size_t bytes_transferred);
@@ -95,28 +84,28 @@
     asio::error_code error_;
 };
 
-
-
-void
-ASIOSession::establish(const char& socket_file) {
+void
+SessionImpl::establish(const char& socket_file) {
     try {
-        socket_.connect(asio::local::stream_protocol::endpoint(&socket_file), error_);
-    } catch (asio::system_error& se) {
+        socket_.connect(asio::local::stream_protocol::endpoint(&socket_file),
+                        error_);
+    } catch(const asio::system_error& se) {
         isc_throw(SessionError, se.what());
     }
     if (error_) {
-        isc_throw(SessionError, "Unable to connect to message queue.");
-    }
-}
-
-void
-ASIOSession::disconnect() {
+        isc_throw(SessionError, "Unable to connect to message queue: " <<
+                  error_.message());
+    }
+}
+
+void
+SessionImpl::disconnect() {
     socket_.close();
     data_length_ = 0;
 }
 
 void
-ASIOSession::writeData(const void* data, size_t datalen) {
+SessionImpl::writeData(const void* data, size_t datalen) {
     try {
         asio::write(socket_, asio::buffer(data, datalen));
     } catch (const asio::system_error& asio_ex) {
@@ -125,7 +114,7 @@
 }
 
 size_t
-ASIOSession::readDataLength() {
+SessionImpl::readDataLength() {
     size_t ret_len = data_length_;
     
     if (ret_len == 0) {
@@ -141,7 +130,7 @@
 }
 
 void
-ASIOSession::readData(void* data, size_t datalen) {
+SessionImpl::readData(void* data, size_t datalen) {
     try {
         asio::read(socket_, asio::buffer(data, datalen));
     } catch (const asio::system_error& asio_ex) {
@@ -152,18 +141,18 @@
 }
 
 void
-ASIOSession::startRead(boost::function<void()> user_handler) {
+SessionImpl::startRead(boost::function<void()> user_handler) {
     data_length_ = 0;
     user_handler_ = user_handler;
     async_read(socket_, asio::buffer(&data_length_,
                                             sizeof(data_length_)),
-               boost::bind(&ASIOSession::internalRead, this,
+               boost::bind(&SessionImpl::internalRead, this,
                            asio::placeholders::error,
                            asio::placeholders::bytes_transferred));
 }
 
 void
-ASIOSession::internalRead(const asio::error_code& error,
+SessionImpl::internalRead(const asio::error_code& error,
                           size_t bytes_transferred)
 {
     if (!error) {
@@ -178,27 +167,22 @@
     }
 }
 
-class SocketSession : public SessionImpl {
-public:
-    SocketSession() : sock_(-1) {}
-    virtual ~SocketSession() { disconnect(); }
-    virtual int getSocket() { return (sock_); }
-    void establish(const char& socket_file);
-    virtual void disconnect()
-    {
-        if (sock_ >= 0) {
-            close(sock_);
-        }
-        sock_ = -1;
-    }
-    virtual void writeData(const void* data, size_t datalen);
-    virtual void readData(void* data, size_t datalen);
-    virtual size_t readDataLength();
-    virtual void startRead(boost::function<void()> user_handler UNUSED_PARAM)
-    {} // nothing to do for this class
-private:
-    int sock_;
-};
+Session::Session(io_service& io_service) : impl_(new SessionImpl(io_service))
+{}
+
+Session::~Session() {
+    delete impl_;
+}
+
+void
+Session::disconnect() {
+    impl_->disconnect();
+}
+
+void
+Session::startRead(boost::function<void()> read_callback) {
+    impl_->startRead(read_callback);
+}
 
 namespace {                     // maybe unnecessary.
 // This is a helper class to make the establish() method (below) exception-safe
@@ -218,83 +202,6 @@
 }
 
 void
-SocketSession::establish(const char& socket_file) {
-    struct sockaddr_un s_un;
-#ifdef HAVE_SA_LEN
-    s_un.sun_len = sizeof(struct sockaddr_un);
-#endif
-
-    if (strlen(&socket_file) >= sizeof(s_un.sun_path)) {
-        isc_throw(SessionError, "Unable to connect to message queue; "
-                  "socket file path too long: " << socket_file);
-    }
-    s_un.sun_family = AF_UNIX;
-    strncpy(s_un.sun_path, &socket_file, sizeof(s_un.sun_path) - 1);
-
-    int s = socket(AF_UNIX, SOCK_STREAM, 0);
-    if (s < 0) {
-        isc_throw(SessionError, "socket() failed");
-    }
-
-    if (connect(s, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) {
-        close(s);
-        isc_throw(SessionError, "Unable to connect to message queue");
-    }
-
-    sock_ = s;
-}
-
-void
-SocketSession::writeData(const void* data, const size_t datalen) {
-    int cc = write(sock_, data, datalen);
-    if (cc != datalen) {
-        isc_throw(SessionError, "Write failed: expect " << datalen <<
-                  ", actual " << cc);
-    }
-}
-
-size_t
-SocketSession::readDataLength() {
-    uint32_t length;
-    readData(&length, sizeof(length));
-    return (ntohl(length));
-}
-
-void
-SocketSession::readData(void* data, const size_t datalen) {
-    int cc = read(sock_, data, datalen);
-    if (cc != datalen) {
-        isc_throw(SessionError, "Read failed: expect " << datalen <<
-                  ", actual " << cc);
-    }
-}
-
-Session::Session() : impl_(new SocketSession)
-{}
-
-Session::Session(io_service& io_service) : impl_(new ASIOSession(io_service))
-{}
-
-Session::~Session() {
-    delete impl_;
-}
-
-void
-Session::disconnect() {
-    impl_->disconnect();
-}
-
-int
-Session::getSocket() const {
-    return (impl_->getSocket());
-}
-
-void
-Session::startRead(boost::function<void()> read_callback) {
-    impl_->startRead(read_callback);
-}
-
-void
 Session::establish(const char* socket_file) {
     if (socket_file == NULL) {
         socket_file = getenv("BIND10_MSGQ_SOCKET_FILE");
@@ -314,7 +221,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;
@@ -327,7 +234,8 @@
 }
 
 //
-// Convert to wire format and send this on the TCP stream with its length prefix
+// Convert to wire format and send this via the stream socket with its length
+// prefix.
 //
 void
 Session::sendmsg(ElementPtr& msg) {
@@ -422,7 +330,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);
@@ -433,7 +341,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));
@@ -444,7 +352,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));
@@ -457,8 +365,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_));
@@ -481,8 +389,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/trac241/src/lib/cc/session.h
==============================================================================
--- branches/trac241/src/lib/cc/session.h (original)
+++ branches/trac241/src/lib/cc/session.h Mon Aug  9 18:42:00 2010
@@ -23,8 +23,8 @@
 
 #include <exceptions/exceptions.h>
 
-#include "data.h"
-#include "session_config.h"
+#include <cc/data.h>
+#include <cc/session_config.h>
 
 namespace asio {
 class io_service;
@@ -40,7 +40,57 @@
                 isc::Exception(file, line, what) {}
         };
 
-        class Session {
+        /// \brief The AbstractSession class is an abstract base class that
+        /// defines the interfaces of Session.
+        /// The intended primary usage of abstraction is to allow tests for the
+        /// user class of Session without requiring actual communication
+        /// channels.
+        /// For simplicity we only define the methods that are necessary for
+        /// existing test cases that use this base class.  Eventually we'll
+        /// probably have to extend them.
+        class AbstractSession {
+            ///
+            /// \name Constructors, Assignment Operator and Destructor.
+            ///
+            /// Note: The copy constructor and the assignment operator are
+            /// intentionally defined as private to make it explicit that
+            /// this is a pure base class.
+            //@{
+        private:
+            AbstractSession(const AbstractSession& source);
+            AbstractSession& operator=(const AbstractSession& source);
+        protected:
+            /// \brief The default constructor.
+            ///
+            /// This is intentionally defined as \c protected as this base
+            /// class should never be instantiated (except as part of a
+            /// derived class).
+            AbstractSession() {}
+        public:
+            /// \brief The destructor.
+            virtual ~AbstractSession() {}
+            //@}
+            virtual void establish(const char* socket_file) = 0;
+            virtual void disconnect() = 0;
+            virtual int group_sendmsg(isc::data::ElementPtr msg,
+                                      std::string group,
+                                      std::string instance = "*",
+                                      std::string to = "*") = 0;
+            virtual bool group_recvmsg(isc::data::ElementPtr& envelope,
+                                       isc::data::ElementPtr& msg,
+                                       bool nonblock = true,
+                                       int seq = -1) = 0;
+            virtual void subscribe(std::string group,
+                                   std::string instance = "*") = 0;
+            virtual void unsubscribe(std::string group,
+                             std::string instance = "*") = 0;
+            virtual void startRead(boost::function<void()> read_callback) = 0;
+            virtual int reply(isc::data::ElementPtr& envelope,
+                               isc::data::ElementPtr& newmsg) = 0;
+            virtual bool hasQueuedMsgs() = 0;
+        };
+
+    class Session : public AbstractSession {
         private:
             SessionImpl* impl_;
 
@@ -49,17 +99,29 @@
             Session& operator=(const Session& source);
 
         public:
-            Session();
             Session(asio::io_service& ioservice);
-            ~Session();
+            virtual ~Session();
 
-            // XXX: quick hack to allow the user to watch the socket directly.
-            int getSocket() const;
+            virtual void startRead(boost::function<void()> read_callback);
 
-            void startRead(boost::function<void()> read_callback);
-
-            void establish(const char* socket_file = NULL);
-            void disconnect();
+            virtual void establish(const char* socket_file = NULL);
+            virtual void disconnect();
+            virtual void subscribe(std::string group,
+                                   std::string instance = "*");
+            virtual void unsubscribe(std::string group,
+                             std::string instance = "*");
+            virtual int group_sendmsg(isc::data::ElementPtr msg,
+                                      std::string group,
+                                      std::string instance = "*",
+                                      std::string to = "*");
+            virtual bool group_recvmsg(isc::data::ElementPtr& envelope,
+                                       isc::data::ElementPtr& msg,
+                                       bool nonblock = true,
+                                       int seq = -1);
+            virtual int reply(isc::data::ElementPtr& envelope,
+                              isc::data::ElementPtr& newmsg);
+            virtual bool hasQueuedMsgs();
+    private:
             void sendmsg(isc::data::ElementPtr& msg);
             void sendmsg(isc::data::ElementPtr& env,
                          isc::data::ElementPtr& msg);
@@ -70,21 +132,6 @@
                          isc::data::ElementPtr& msg,
                          bool nonblock = true,
                          int seq = -1);
-            void subscribe(std::string group,
-                           std::string instance = "*");
-            void unsubscribe(std::string group,
-                             std::string instance = "*");
-            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);
-            int reply(isc::data::ElementPtr& envelope,
-                               isc::data::ElementPtr& newmsg);
-            bool hasQueuedMsgs();
         };
     } // namespace cc
 } // namespace isc

Modified: branches/trac241/src/lib/cc/session_unittests.cc
==============================================================================
--- branches/trac241/src/lib/cc/session_unittests.cc (original)
+++ branches/trac241/src/lib/cc/session_unittests.cc Mon Aug  9 18:42:00 2010
@@ -14,11 +14,16 @@
 
 // $Id: data_unittests.cc 1899 2010-05-21 12:03:59Z jelte $
 
-#include "config.h"
+#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>
+
 #include <gtest/gtest.h>
 #include <session.h>
 
-#include <asio.hpp>
 #include <exceptions/exceptions.h>
 
 using namespace isc::cc;
@@ -42,22 +47,3 @@
     );
                   
 }
-
-TEST(Session, establish) {
-    Session sess;
-
-    EXPECT_THROW(
-        sess.establish("/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
-                       "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
-                       "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
-                       "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
-                       "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
-                       "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
-                       "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
-                       "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
-                       "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
-                       "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
-                  ), isc::cc::SessionError
-    );
-                  
-}

Modified: branches/trac241/src/lib/config/Makefile.am
==============================================================================
--- branches/trac241/src/lib/config/Makefile.am (original)
+++ branches/trac241/src/lib/config/Makefile.am Mon Aug  9 18:42:00 2010
@@ -51,3 +51,4 @@
 EXTRA_DIST += testdata/spec25.spec
 EXTRA_DIST += testdata/spec26.spec
 EXTRA_DIST += testdata/spec27.spec
+EXTRA_DIST += testdata/spec28.spec

Modified: branches/trac241/src/lib/config/ccsession.cc
==============================================================================
--- branches/trac241/src/lib/config/ccsession.cc (original)
+++ branches/trac241/src/lib/config/ccsession.cc Mon Aug  9 18:42:00 2010
@@ -20,7 +20,7 @@
 //               react on config change announcements)
 //
 
-#include "config.h"
+#include <config.h>
 
 #include <stdexcept>
 #include <stdlib.h>
@@ -40,14 +40,13 @@
 #include <cc/session.h>
 #include <exceptions/exceptions.h>
 
-#include "ccsession.h"
-#include "config.h"
+#include <config/ccsession.h>
 
 using namespace std;
 
 using isc::data::Element;
 using isc::data::ElementPtr;
-using isc::data::ParseError;
+using isc::data::JSONError;
 
 namespace isc {
 namespace config {
@@ -56,7 +55,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 +67,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 +77,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 +124,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 +174,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) {
@@ -197,37 +196,13 @@
 }
 
 ModuleCCSession::ModuleCCSession(
-    std::string spec_file_name,
-    asio::io_service& io_service,
+    const std::string& spec_file_name,
+    isc::cc::AbstractSession& session,
     isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config),
     isc::data::ElementPtr(*command_handler)(
         const std::string& command, const isc::data::ElementPtr args)
     ) throw (isc::cc::SessionError) :
-    session_(io_service)
-{
-    init(spec_file_name, config_handler, command_handler);
-
-    // register callback for asynchronous read
-    session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
-}
-
-ModuleCCSession::ModuleCCSession(
-    std::string spec_file_name,
-    isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config),
-    isc::data::ElementPtr(*command_handler)(
-        const std::string& command, const isc::data::ElementPtr args)
-    ) throw (isc::cc::SessionError)
-{
-    init(spec_file_name, config_handler, command_handler);
-}
-
-void
-ModuleCCSession::init(
-    std::string spec_file_name,
-    isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config),
-    isc::data::ElementPtr(*command_handler)(
-        const std::string& command, const isc::data::ElementPtr args)
-    ) throw (isc::cc::SessionError)
+    session_(session)
 {
     module_specification_ = readModuleSpecification(spec_file_name);
     setModuleSpec(module_specification_);
@@ -238,7 +213,7 @@
 
     ElementPtr answer, env;
 
-    session_.establish();
+    session_.establish(NULL);
     session_.subscribe(module_name_, "*");
     //session_.subscribe("Boss", "*");
     //session_.subscribe("statistics", "*");
@@ -252,10 +227,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);
@@ -265,6 +240,9 @@
             std::cerr << "[" << module_name_ << "] Error getting config: " << new_config << std::endl;
         }
     }
+
+    // register callback for asynchronous read
+    session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
 }
 
 /// Validates the new config values, if they are correct,
@@ -274,7 +252,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)) {
@@ -300,12 +278,6 @@
     return answer;
 }
 
-int
-ModuleCCSession::getSocket()
-{
-    return (session_.getSocket());
-}
-
 bool
 ModuleCCSession::hasQueuedMsgs()
 {
@@ -327,8 +299,8 @@
         ElementPtr answer;
         try {
             std::string cmd_str = parseCommand(arg, data);
+            std::string target_module = routing->get("group")->stringValue();
             if (cmd_str == "config_update") {
-                std::string target_module = routing->get("group")->stringValue();
                 if (target_module == module_name_) {
                     answer = handleConfigUpdate(arg);
                 } else {
@@ -339,16 +311,22 @@
                     return 0;
                 }
             } else {
-                if (command_handler_) {
-                    answer = command_handler_(cmd_str, arg);
-                } else {
-                    answer = createAnswer(1, "Command given but no command handler for module");
+                if (target_module == module_name_) {
+                    if (command_handler_) {
+                        answer = command_handler_(cmd_str, arg);
+                    } else {
+                        answer = createAnswer(1, "Command given but no command handler for module");
+                    }
                 }
             }
         } catch (CCSessionError re) {
+            // TODO: Once we have logging and timeouts, we should not
+            // answer here (potential interference)
             answer = createAnswer(1, re.what());
         }
-        session_.reply(routing, answer);
+        if (!isNull(answer)) {
+            session_.reply(routing, answer);
+        }
     }
     
     return 0;
@@ -363,7 +341,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/trac241/src/lib/config/ccsession.h
==============================================================================
--- branches/trac241/src/lib/config/ccsession.h (original)
+++ branches/trac241/src/lib/config/ccsession.h Mon Aug  9 18:42:00 2010
@@ -24,10 +24,6 @@
 #include <cc/session.h>
 #include <cc/data.h>
 
-namespace asio {
-class io_service;
-}
-
 namespace isc {
 namespace config {
 
@@ -112,7 +108,7 @@
 };
 
 ///
-/// \brief This modules keeps a connection to the command channel,
+/// \brief This module keeps a connection to the command channel,
 /// holds configuration information, and handles messages from
 /// the command channel
 ///
@@ -120,32 +116,29 @@
 public:
     /**
      * Initialize a config/command session
-     * @param module_name: The name of this module. This is not a
-     *                     reference because we expect static strings
-     *                     to be passed here.
-     * @param spec_file_name: The name of the file containing the
+     *
+     * @param spec_file_name The name of the file containing the
      *                        module specification.
-     */
-    ModuleCCSession(std::string spec_file_name,
-                    isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config) = NULL,
-                    isc::data::ElementPtr(*command_handler)(const std::string& command, const isc::data::ElementPtr args) = NULL
+     * @param session A Session object over which configuration and command
+     * data are exchanged.
+     * @param config_handler A callback function pointer to be called when
+     * configuration of the local module needs to be updated.
+     * This must refer to a valid object of a concrete derived class of
+     * AbstractSession without establishing the session.
+     * Note: the design decision on who is responsible for establishing the
+     * session is in flux, and may change in near future.
+     * @param command_handler A callback function pointer to be called when
+     * a control command from a remote agent needs to be performed on the
+     * local module.
+     */
+    ModuleCCSession(const std::string& spec_file_name,
+                    isc::cc::AbstractSession& session,
+                    isc::data::ElementPtr(*config_handler)(
+                        isc::data::ElementPtr new_config) = NULL,
+                    isc::data::ElementPtr(*command_handler)(
+                        const std::string& command,
+                        const isc::data::ElementPtr args) = NULL
                     ) throw (isc::cc::SessionError);
-    ModuleCCSession(std::string spec_file_name,
-                    asio::io_service& io_service,
-                    isc::data::ElementPtr(*config_handler)(isc::data::ElementPtr new_config) = NULL,
-                    isc::data::ElementPtr(*command_handler)(const std::string& command, const isc::data::ElementPtr args) = NULL
-                    ) throw (isc::cc::SessionError);
-
-    /**
-     * Returns the socket that is used to communicate with the msgq
-     * command channel. This socket should *only* be used to run a
-     * select() loop over it. And if not time-critical, it is strongly
-     * recommended to only use checkCommand() to check for messages
-     *
-     * @return The socket used to communicate with the msgq command
-     *         channel.
-     */
-    int getSocket();
 
     /**
      * Optional optimization for checkCommand loop; returns true
@@ -227,18 +220,11 @@
     ElementPtr getRemoteConfigValue(const std::string& module_name, const std::string& identifier);
     
 private:
-    void init(
-        std::string spec_file_name,
-        isc::data::ElementPtr(*config_handler)(
-            isc::data::ElementPtr new_config),
-        isc::data::ElementPtr(*command_handler)(
-            const std::string& command, const isc::data::ElementPtr args)
-        ) throw (isc::cc::SessionError);
     ModuleSpec readModuleSpecification(const std::string& filename);
     void startCheck();
     
     std::string module_name_;
-    isc::cc::Session session_;
+    isc::cc::AbstractSession& session_;
     ModuleSpec module_specification_;
     ElementPtr handleConfigUpdate(ElementPtr new_config);
 

Modified: branches/trac241/src/lib/config/config_data.cc
==============================================================================
--- branches/trac241/src/lib/config/config_data.cc (original)
+++ branches/trac241/src/lib/config/config_data.cc Mon Aug  9 18:42:00 2010
@@ -14,7 +14,7 @@
 
 // $Id$
 
-#include "config_data.h"
+#include <config/config_data.h>
 
 #include <boost/foreach.hpp>
 
@@ -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/trac241/src/lib/config/config_data.h
==============================================================================
--- branches/trac241/src/lib/config/config_data.h (original)
+++ branches/trac241/src/lib/config/config_data.h Mon Aug  9 18:42:00 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/trac241/src/lib/config/module_spec.cc
==============================================================================
--- branches/trac241/src/lib/config/module_spec.cc (original)
+++ branches/trac241/src/lib/config/module_spec.cc Mon Aug  9 18:42:00 2010
@@ -13,7 +13,7 @@
 // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 // WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-#include "module_spec.h"
+#include <config/module_spec.h>
 
 #include <sstream>
 #include <iostream>
@@ -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/trac241/src/lib/config/module_spec.h
==============================================================================
--- branches/trac241/src/lib/config/module_spec.h (original)
+++ branches/trac241/src/lib/config/module_spec.h Mon Aug  9 18:42:00 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/trac241/src/lib/config/testdata/b10-config.db
==============================================================================
--- branches/trac241/src/lib/config/testdata/b10-config.db (original)
+++ branches/trac241/src/lib/config/testdata/b10-config.db Mon Aug  9 18:42:00 2010
@@ -1,1 +1,1 @@
-{'TestModule': {'test': 125}, 'version': 1}
+{"version": 1, "TestModule": {"test": 125}}

Modified: branches/trac241/src/lib/config/testdata/data22_1.data
==============================================================================
--- branches/trac241/src/lib/config/testdata/data22_1.data (original)
+++ branches/trac241/src/lib/config/testdata/data22_1.data Mon Aug  9 18:42:00 2010
@@ -1,9 +1,9 @@
 {
     "value1": 1,
     "value2": 2.3,
-    "value3": True,
+    "value3": true,
     "value4": "foo",
     "value5": [ 1, 2, 3 ],
-    "value6": { "v61": "bar", "v62": True },
+    "value6": { "v61": "bar", "v62": true },
     "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } }
 }

Modified: branches/trac241/src/lib/config/testdata/data22_2.data
==============================================================================
--- branches/trac241/src/lib/config/testdata/data22_2.data (original)
+++ branches/trac241/src/lib/config/testdata/data22_2.data Mon Aug  9 18:42:00 2010
@@ -1,8 +1,8 @@
 {
     "value1": "asdf",
     "value2": 2.3,
-    "value3": True,
+    "value3": true,
     "value4": "foo",
     "value5": [ 1, 2, 3 ],
-    "value6": { "v61": "bar", "v62": True }
+    "value6": { "v61": "bar", "v62": true }
 }

Modified: branches/trac241/src/lib/config/testdata/data22_3.data
==============================================================================
--- branches/trac241/src/lib/config/testdata/data22_3.data (original)
+++ branches/trac241/src/lib/config/testdata/data22_3.data Mon Aug  9 18:42:00 2010
@@ -1,8 +1,8 @@
 {
     "value1": 1,
-    "value2": False,
-    "value3": True,
+    "value2": false,
+    "value3": true,
     "value4": "foo",
     "value5": [ 1, 2, 3 ],
-    "value6": { "v61": "bar", "v62": True }
+    "value6": { "v61": "bar", "v62": true }
 }

Modified: branches/trac241/src/lib/config/testdata/data22_4.data
==============================================================================
--- branches/trac241/src/lib/config/testdata/data22_4.data (original)
+++ branches/trac241/src/lib/config/testdata/data22_4.data Mon Aug  9 18:42:00 2010
@@ -1,8 +1,8 @@
 {
     "value1": 1,
     "value2": 2.3,
-    "value3": True,
+    "value3": true,
     "value4": "foo",
     "value5": [ 1, 2, "a" ],
-    "value6": { "v61": "bar", "v62": True }
+    "value6": { "v61": "bar", "v62": true }
 }

Modified: branches/trac241/src/lib/config/testdata/data22_5.data
==============================================================================
--- branches/trac241/src/lib/config/testdata/data22_5.data (original)
+++ branches/trac241/src/lib/config/testdata/data22_5.data Mon Aug  9 18:42:00 2010
@@ -1,7 +1,7 @@
 {
     "value1": 1,
     "value2": 2.3,
-    "value3": True,
+    "value3": true,
     "value4": "foo",
     "value5": [ 1, 2, 3 ],
     "value6": { "v61": "bar", "v62": "Break" }

Modified: branches/trac241/src/lib/config/testdata/data22_6.data
==============================================================================
--- branches/trac241/src/lib/config/testdata/data22_6.data (original)
+++ branches/trac241/src/lib/config/testdata/data22_6.data Mon Aug  9 18:42:00 2010
@@ -1,10 +1,10 @@
 {
     "value1": 1,
     "value2": 2.3,
-    "value3": True,
+    "value3": true,
     "value4": "foo",
     "value5": [ 1, 2, 3 ],
-    "value6": { "v61": "bar", "v62": True },
-    "value7": [ 1, 2.2, "str", True ],
+    "value6": { "v61": "bar", "v62": true },
+    "value7": [ 1, 2.2, "str", true ],
     "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } }
 }

Modified: branches/trac241/src/lib/config/testdata/data22_7.data
==============================================================================
--- branches/trac241/src/lib/config/testdata/data22_7.data (original)
+++ branches/trac241/src/lib/config/testdata/data22_7.data Mon Aug  9 18:42:00 2010
@@ -1,10 +1,10 @@
 {
     "value1": 1,
     "value2": 2.3,
-    "value3": True,
+    "value3": true,
     "value4": "foo",
     "value5": [ 1, 2, 3 ],
-    "value6": { "v61": "bar", "v62": True },
+    "value6": { "v61": "bar", "v62": true },
     "value8": [ { "a": "d" }, { "a": "e" } ],
     "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } }
 }

Modified: branches/trac241/src/lib/config/testdata/data22_8.data
==============================================================================
--- branches/trac241/src/lib/config/testdata/data22_8.data (original)
+++ branches/trac241/src/lib/config/testdata/data22_8.data Mon Aug  9 18:42:00 2010
@@ -1,9 +1,9 @@
 {
     "value1": 1,
     "value2": 2.3,
-    "value3": True,
+    "value3": true,
     "value4": "foo",
     "value5": [ 1, 2, 3 ],
-    "value6": { "v61": "bar", "v62": True },
+    "value6": { "v61": "bar", "v62": true },
     "value8": [ { "a": "d" }, { "a": 1 } ]
 }

Modified: branches/trac241/src/lib/config/testdata/spec10.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec10.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec10.spec Mon Aug  9 18:42:00 2010
@@ -4,7 +4,7 @@
     "config_data": [
       { "item_name": "item1",
         "item_type": "real",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 1
       }
     ]

Modified: branches/trac241/src/lib/config/testdata/spec11.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec11.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec11.spec Mon Aug  9 18:42:00 2010
@@ -4,7 +4,7 @@
     "config_data": [
       { "item_name": "item1",
         "item_type": "boolean",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 1
       }
     ]

Modified: branches/trac241/src/lib/config/testdata/spec12.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec12.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec12.spec Mon Aug  9 18:42:00 2010
@@ -4,7 +4,7 @@
     "config_data": [
       { "item_name": "item1",
         "item_type": "string",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 1
       }
     ]

Modified: branches/trac241/src/lib/config/testdata/spec13.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec13.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec13.spec Mon Aug  9 18:42:00 2010
@@ -4,7 +4,7 @@
     "config_data": [
       { "item_name": "item1",
         "item_type": "list",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 1
       }
     ]

Modified: branches/trac241/src/lib/config/testdata/spec14.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec14.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec14.spec Mon Aug  9 18:42:00 2010
@@ -4,7 +4,7 @@
     "config_data": [
       { "item_name": "item1",
         "item_type": "map",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 1
       }
     ]

Modified: branches/trac241/src/lib/config/testdata/spec15.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec15.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec15.spec Mon Aug  9 18:42:00 2010
@@ -4,7 +4,7 @@
     "config_data": [
       { "item_name": "item1",
         "item_type": "badname",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 1
       }
     ]

Modified: branches/trac241/src/lib/config/testdata/spec17.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec17.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec17.spec Mon Aug  9 18:42:00 2010
@@ -7,7 +7,7 @@
         "command_args": [ {
           "item_name": "message",
           "item_type": "string",
-          "item_optional": False,
+          "item_optional": false,
           "item_default": ""
         } ]
       }

Modified: branches/trac241/src/lib/config/testdata/spec2.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec2.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec2.spec Mon Aug  9 18:42:00 2010
@@ -4,48 +4,48 @@
     "config_data": [
       { "item_name": "item1",
         "item_type": "integer",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 1
       },
       { "item_name": "item2",
         "item_type": "real",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 1.1
       },
       { "item_name": "item3",
         "item_type": "boolean",
-        "item_optional": False,
-        "item_default": True
+        "item_optional": false,
+        "item_default": true
       },
       { "item_name": "item4",
         "item_type": "string",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": "test"
       },
       { "item_name": "item5",
         "item_type": "list",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": [ "a", "b" ],
         "list_item_spec": {
           "item_name": "list_element",
           "item_type": "string",
-          "item_optional": False,
+          "item_optional": false,
           "item_default": ""
         }
       },
       { "item_name": "item6",
         "item_type": "map",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": {},
         "map_item_spec": [
           { "item_name": "value1",
             "item_type": "string",
-            "item_optional": True,
+            "item_optional": true,
             "item_default": "default"
           },
           { "item_name": "value2",
             "item_type": "integer",
-            "item_optional": True
+            "item_optional": true
           }
         ]
       }
@@ -57,7 +57,7 @@
         "command_args": [ {
           "item_name": "message",
           "item_type": "string",
-          "item_optional": False,
+          "item_optional": false,
           "item_default": ""
         } ]
       },

Modified: branches/trac241/src/lib/config/testdata/spec20.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec20.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec20.spec Mon Aug  9 18:42:00 2010
@@ -8,7 +8,7 @@
         "command_args": [ {
           "item_name": "message",
           "item_type": "somethingbad",
-          "item_optional": False,
+          "item_optional": false,
           "item_default": ""
         } ]
       }

Modified: branches/trac241/src/lib/config/testdata/spec22.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec22.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec22.spec Mon Aug  9 18:42:00 2010
@@ -4,75 +4,75 @@
     "config_data": [
       { "item_name": "value1",
         "item_type": "integer",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 9
       },
       { "item_name": "value2",
         "item_type": "real",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 9.9
       },
       { "item_name": "value3",
         "item_type": "boolean",
-        "item_optional": False,
-        "item_default": False
+        "item_optional": false,
+        "item_default": false
       },
       { "item_name": "value4",
         "item_type": "string",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": "default_string"
       },
       { "item_name": "value5",
         "item_type": "list",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": [ "a", "b" ],
         "list_item_spec": {
           "item_name": "list_element",
           "item_type": "integer",
-          "item_optional": False,
+          "item_optional": false,
           "item_default": 8
         }
       },
       { "item_name": "value6",
         "item_type": "map",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": {},
         "map_item_spec": [
           { "item_name": "v61",
             "item_type": "string",
-            "item_optional": False,
+            "item_optional": false,
             "item_default": "def"
           },
           { "item_name": "v62",
             "item_type": "boolean",
-            "item_optional": False,
-            "item_default": False
+            "item_optional": false,
+            "item_default": false
           }
         ]
       },
       { "item_name": "value7",
         "item_type": "list",
-        "item_optional": True,
+        "item_optional": true,
         "item_default": [ ],
         "list_item_spec": {
           "item_name": "list_element",
           "item_type": "any",
-          "item_optional": True
+          "item_optional": true
         }
       },
       { "item_name": "value8",
         "item_type": "list",
-        "item_optional": True,
+        "item_optional": true,
         "item_default": [ ],
         "list_item_spec": {
           "item_name": "list_element",
           "item_type": "map",
-          "item_optional": True,
+          "item_optional": true,
           "item_default": { "a": "b" },
           "map_item_spec": [
             { "item_name": "a",
               "item_type": "string",
-              "item_optional": True,
+              "item_optional": true,
               "item_default": "empty"
             }
           ]
@@ -80,28 +80,28 @@
       },
       { "item_name": "value9",
         "item_type": "map",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": {},
         "map_item_spec": [
           { "item_name": "v91",
             "item_type": "string",
-            "item_optional": False,
+            "item_optional": false,
             "item_default": "def"
           },
           { "item_name": "v92",
             "item_type": "map",
-            "item_optional": False,
+            "item_optional": false,
             "item_default": {},
             "map_item_spec": [
               { "item_name": "v92a",
                 "item_type": "string",
-                "item_optional": False,
+                "item_optional": false,
                 "item_default": "Hello"
               } ,
               {
                 "item_name": "v92b",
                 "item_type": "integer",
-                "item_optional": False,
+                "item_optional": false,
                 "item_default": 47806
               }
             ]

Modified: branches/trac241/src/lib/config/testdata/spec23.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec23.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec23.spec Mon Aug  9 18:42:00 2010
@@ -8,7 +8,7 @@
         "command_args": [ {
           "item_name": "message",
           "item_type": "string",
-          "item_optional": False,
+          "item_optional": false,
           "item_default": ""
         } ]
       }

Modified: branches/trac241/src/lib/config/testdata/spec24.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec24.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec24.spec Mon Aug  9 18:42:00 2010
@@ -4,11 +4,11 @@
     "config_data": [
       { "item_name": "item",
         "item_type": "list",
-        "item_optional": True,
+        "item_optional": true,
         "list_item_spec": {
           "item_name": "list_element",
           "item_type": "string",
-          "item_optional": False,
+          "item_optional": false,
           "item_default": ""
         }
       }

Modified: branches/trac241/src/lib/config/testdata/spec27.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec27.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec27.spec Mon Aug  9 18:42:00 2010
@@ -3,81 +3,81 @@
     "module_name": "Spec27",
     "commands": [
     {
-        'command_name': 'cmd1',
+        "command_name": "cmd1",
         "command_description": "command_for_unittest",
-        'command_args': [ 
+        "command_args": [ 
           {
             "item_name": "value1",
             "item_type": "integer",
-            "item_optional": False,
+            "item_optional": false,
             "item_default": 9
           },
           { "item_name": "value2",
             "item_type": "real",
-            "item_optional": False,
+            "item_optional": false,
             "item_default": 9.9
           },
           { "item_name": "value3",
             "item_type": "boolean",
-            "item_optional": False,
-            "item_default": False
+            "item_optional": false,
+            "item_default": false
           },
           { "item_name": "value4",
             "item_type": "string",
-            "item_optional": False,
+            "item_optional": false,
             "item_default": "default_string"
           },
           { "item_name": "value5",
             "item_type": "list",
-            "item_optional": False,
+            "item_optional": false,
             "item_default": [ "a", "b" ],
             "list_item_spec": {
               "item_name": "list_element",
               "item_type": "integer",
-              "item_optional": False,
+              "item_optional": false,
               "item_default": 8
             }
           },
           { "item_name": "value6",
             "item_type": "map",
-            "item_optional": False,
+            "item_optional": false,
             "item_default": {},
             "map_item_spec": [
               { "item_name": "v61",
                 "item_type": "string",
-                "item_optional": False,
+                "item_optional": false,
                 "item_default": "def"
               },
               { "item_name": "v62",
                 "item_type": "boolean",
-                "item_optional": False,
-                "item_default": False
+                "item_optional": false,
+                "item_default": false
               }
             ]
           },
           { "item_name": "value7",
             "item_type": "list",
-            "item_optional": True,
+            "item_optional": true,
             "item_default": [ ],
             "list_item_spec": {
               "item_name": "list_element",
               "item_type": "any",
-              "item_optional": True
+              "item_optional": true
             }
           },
           { "item_name": "value8",
             "item_type": "list",
-            "item_optional": True,
+            "item_optional": true,
             "item_default": [ ],
             "list_item_spec": {
               "item_name": "list_element",
               "item_type": "map",
-              "item_optional": True,
+              "item_optional": true,
               "item_default": { "a": "b" },
               "map_item_spec": [
                 { "item_name": "a",
                   "item_type": "string",
-                  "item_optional": True,
+                  "item_optional": true,
                   "item_default": "empty"
                 }
               ]
@@ -85,28 +85,28 @@
           },
           { "item_name": "value9",
             "item_type": "map",
-            "item_optional": False,
+            "item_optional": false,
             "item_default": {},
             "map_item_spec": [
               { "item_name": "v91",
                 "item_type": "string",
-                "item_optional": False,
+                "item_optional": false,
                 "item_default": "def"
               },
               { "item_name": "v92",
                 "item_type": "map",
-                "item_optional": False,
+                "item_optional": false,
                 "item_default": {},
                 "map_item_spec": [
                   { "item_name": "v92a",
                     "item_type": "string",
-                    "item_optional": False,
+                    "item_optional": false,
                     "item_default": "Hello"
                   } ,
                   {
                     "item_name": "v92b",
                     "item_type": "integer",
-                    "item_optional": False,
+                    "item_optional": false,
                     "item_default": 47806
                   }
                 ]

Modified: branches/trac241/src/lib/config/testdata/spec3.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec3.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec3.spec Mon Aug  9 18:42:00 2010
@@ -4,7 +4,7 @@
     "config_data": [
       {
         "item_type": "integer",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 1
       }
     ]

Modified: branches/trac241/src/lib/config/testdata/spec4.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec4.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec4.spec Mon Aug  9 18:42:00 2010
@@ -3,7 +3,7 @@
     "module_name": "Spec2",
     "config_data": [
       { "item_name": "item1",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": 1
       }
     ]

Modified: branches/trac241/src/lib/config/testdata/spec6.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec6.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec6.spec Mon Aug  9 18:42:00 2010
@@ -4,7 +4,7 @@
     "config_data": [
       { "item_name": "item1",
         "item_type": "integer",
-        "item_optional": False
+        "item_optional": false
       }
     ]
   }

Modified: branches/trac241/src/lib/config/testdata/spec9.spec
==============================================================================
--- branches/trac241/src/lib/config/testdata/spec9.spec (original)
+++ branches/trac241/src/lib/config/testdata/spec9.spec Mon Aug  9 18:42:00 2010
@@ -4,7 +4,7 @@
     "config_data": [
       { "item_name": "item1",
         "item_type": "integer",
-        "item_optional": False,
+        "item_optional": false,
         "item_default": "asdf"
       }
     ]

Modified: branches/trac241/src/lib/config/tests/Makefile.am
==============================================================================
--- branches/trac241/src/lib/config/tests/Makefile.am (original)
+++ branches/trac241/src/lib/config/tests/Makefile.am Mon Aug  9 18:42:00 2010
@@ -1,8 +1,10 @@
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 
 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/trac241/src/lib/config/tests/ccsession_unittests.cc
==============================================================================
--- branches/trac241/src/lib/config/tests/ccsession_unittests.cc (original)
+++ branches/trac241/src/lib/config/tests/ccsession_unittests.cc Mon Aug  9 18:42:00 2010
@@ -14,72 +14,64 @@
 
 // $Id: module_spec_unittests.cc 1321 2010-03-11 10:17:03Z jelte $
 
-#include "config.h"
+#include <config.h>
 
 #include <gtest/gtest.h>
 
-#include "fake_session.h"
+#include <config/tests/fake_session.h>
 
 #include <config/ccsession.h>
 
 #include <fstream>
 
-#include "data_def_unittests_config.h"
+#include <config/tests/data_def_unittests_config.h>
 
 using namespace isc::data;
 using namespace isc::config;
+using namespace isc::cc;
 using namespace std;
 
-std::string ccspecfile(const std::string name) {
+namespace {
+std::string
+ccspecfile(const std::string name) {
     return std::string(TEST_DATA_PATH) + "/" + name;
 }
 
-static ElementPtr
-el(const std::string& str)
-{
-    return Element::createFromString(str);
-}
-
-// upon creation of a ModuleCCSession, the class
-// sends its specification to the config manager
-// it expects an ok answer back, so everytime we
-// create a ModuleCCSession, we must set an initial
-// ok answer
-void
-initFakeSession()
-{
-    initial_messages = el("[]");
-    msg_queue = el("[]");
-    subscriptions = el("[]");
-    initial_messages->add(createAnswer());
-}
-
-void
-endFakeSession()
-{
-    initial_messages = ElementPtr();
-    msg_queue = ElementPtr();
-    subscriptions = ElementPtr();
-}
-
-TEST(CCSession, createAnswer)
-{
+ElementPtr
+el(const std::string& str) {
+    return Element::fromJSON(str);
+}
+
+class CCSessionTest : public ::testing::Test {
+protected:
+    CCSessionTest() : session(el("[]"), el("[]"), el("[]")) {
+        // upon creation of a ModuleCCSession, the class
+        // sends its specification to the config manager.
+        // it expects an ok answer back, so everytime we
+        // create a ModuleCCSession, we must set an initial
+        // ok answer.
+        session.getMessages()->add(createAnswer());
+    }
+    ~CCSessionTest() {}
+    FakeSession session;
+};
+
+TEST_F(CCSessionTest, createAnswer) {
     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());
-}
-
-TEST(CCSession, parseAnswer)
-{
+    EXPECT_EQ("{ \"result\": [ 0, [ \"just\", \"some\", \"data\" ] ] }", answer->str());
+}
+
+TEST_F(CCSessionTest, parseAnswer) {
     ElementPtr answer;
     ElementPtr arg;
     int rcode;
@@ -87,116 +79,108 @@
     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);
+    EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1 ] }")), CCSessionError);
+    EXPECT_THROW(parseAnswer(rcode, el("{ \"result\": [ 1, 1 ] }")), CCSessionError);
     
-    answer = el("{\"result\": [ 0 ]}");
+    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());
 }
 
-TEST(CCSession, createCommand)
-{
+TEST_F(CCSessionTest, createCommand) {
     ElementPtr command;
     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());
-}
-
-TEST(CCSession, parseCommand)
-{
+    ASSERT_EQ("{ \"command\": [ \"foo\", { \"a\": \"map\" } ] }", command->str());
+}
+
+TEST_F(CCSessionTest, parseCommand) {
     ElementPtr arg;
     std::string cmd;
 
     // 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());
 
 }
 
-TEST(CCSession, session1)
-{
-    initFakeSession();
-    EXPECT_EQ(false, haveSubscription("Spec1", "*"));
-    ModuleCCSession mccs(ccspecfile("spec1.spec"), NULL, NULL);
-    EXPECT_EQ(true, haveSubscription("Spec1", "*"));
-
-    EXPECT_EQ(1, msg_queue->size());
+TEST_F(CCSessionTest, session1) {
+    EXPECT_EQ(false, session.haveSubscription("Spec1", "*"));
+    ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL);
+    EXPECT_EQ(true, session.haveSubscription("Spec1", "*"));
+
+    EXPECT_EQ(1, session.getMsgQueue()->size());
     ElementPtr msg;
     std::string group, to;
-    msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"command\": [ \"module_spec\", {\"module_name\": \"Spec1\"} ]}", msg->str());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"command\": [ \"module_spec\", { \"module_name\": \"Spec1\" } ] }", msg->str());
     EXPECT_EQ("ConfigManager", group);
     EXPECT_EQ("*", to);
-    EXPECT_EQ(0, msg_queue->size());
-    endFakeSession();
-}
-
-TEST(CCSession, session2)
+    EXPECT_EQ(0, session.getMsgQueue()->size());
+}
+
+TEST_F(CCSessionTest, session2)
 {
-    initFakeSession();
-    EXPECT_EQ(false, haveSubscription("Spec2", "*"));
-    ModuleCCSession mccs(ccspecfile("spec2.spec"), NULL, NULL);
-    EXPECT_EQ(true, haveSubscription("Spec2", "*"));
-
-    EXPECT_EQ(1, msg_queue->size());
+    EXPECT_EQ(false, session.haveSubscription("Spec2", "*"));
+    ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL);
+    EXPECT_EQ(true, session.haveSubscription("Spec2", "*"));
+
+    EXPECT_EQ(1, session.getMsgQueue()->size());
     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());
+    msg = session.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_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());
-    endFakeSession();
-}
-
-ElementPtr my_config_handler(ElementPtr new_config)
-{
+    EXPECT_EQ(0, session.getMsgQueue()->size());
+}
+
+ElementPtr my_config_handler(ElementPtr new_config) {
     if (new_config && new_config->contains("item1") &&
         new_config->get("item1")->intValue() == 5) {
         return createAnswer(6, "I do not like the number 5");
@@ -204,7 +188,8 @@
     return createAnswer();
 }
 
-ElementPtr my_command_handler(const std::string& command, ElementPtr arg UNUSED_PARAM)
+ElementPtr my_command_handler(const std::string& command,
+                              ElementPtr arg UNUSED_PARAM)
 {
     if (command == "good_command") {
         return createAnswer();
@@ -223,185 +208,215 @@
     }
 }
 
-TEST(CCSession, session3)
-{
-    initFakeSession();
+TEST_F(CCSessionTest, session3) {
     // client will ask for config
-    initial_messages->add(createAnswer(0, el("{}")));
-
-    EXPECT_EQ(false, haveSubscription("Spec2", "*"));
-    ModuleCCSession mccs(ccspecfile("spec2.spec"), my_config_handler, my_command_handler);
-    EXPECT_EQ(true, haveSubscription("Spec2", "*"));
-
-    EXPECT_EQ(2, msg_queue->size());
+    session.getMessages()->add(createAnswer(0, el("{}")));
+
+    EXPECT_EQ(false, session.haveSubscription("Spec2", "*"));
+    ModuleCCSession mccs(ccspecfile("spec2.spec"), session, my_config_handler,
+                         my_command_handler);
+    EXPECT_EQ(true, session.haveSubscription("Spec2", "*"));
+
+    EXPECT_EQ(2, session.getMsgQueue()->size());
     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());
+    msg = session.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_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(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"command\": [ \"get_config\", { \"module_name\": \"Spec2\" } ] }", msg->str());
     EXPECT_EQ("ConfigManager", group);
     EXPECT_EQ("*", to);
-    EXPECT_EQ(0, msg_queue->size());
-    endFakeSession();
-}
-
-TEST(CCSession, checkCommand)
-{
-    initFakeSession();
+    EXPECT_EQ(0, session.getMsgQueue()->size());
+}
+
+TEST_F(CCSessionTest, checkCommand) {
     // client will ask for config
-    initial_messages->add(createAnswer(0, el("{}")));
-
-    EXPECT_EQ(false, haveSubscription("Spec2", "*"));
-    ModuleCCSession mccs(ccspecfile("spec2.spec"), my_config_handler, my_command_handler);
-    EXPECT_EQ(true, haveSubscription("Spec2", "*"));
-
-    EXPECT_EQ(2, msg_queue->size());
+    session.getMessages()->add(createAnswer(0, el("{}")));
+
+    EXPECT_EQ(false, session.haveSubscription("Spec2", "*"));
+    ModuleCCSession mccs(ccspecfile("spec2.spec"), session, my_config_handler,
+                         my_command_handler);
+    EXPECT_EQ(true, session.haveSubscription("Spec2", "*"));
+
+    EXPECT_EQ(2, session.getMsgQueue()->size());
     ElementPtr msg;
     std::string group, to;
     // checked above, drop em
-    msg = getFirstMessage(group, to);
-    msg = getFirstMessage(group, to);
+    msg = session.getFirstMessage(group, to);
+    msg = session.getFirstMessage(group, to);
 
     int result;
     result = mccs.checkCommand();
     EXPECT_EQ(0, result);
 
     // not a command, should be ignored
-    addMessage(el("1"), "Spec2", "*");
-    result = mccs.checkCommand();
-    EXPECT_EQ(0, result);
-
-    addMessage(el("{ \"command\": [ \"good_command\" ] }"), "Spec2", "*");
-    result = mccs.checkCommand();
-    EXPECT_EQ(1, msg_queue->size());
-    msg = getFirstMessage(group, to);
-    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(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(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(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(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());
+    session.addMessage(el("1"), "Spec2", "*");
+    result = mccs.checkCommand();
+    EXPECT_EQ(0, result);
+
+    session.addMessage(el("{ \"command\": [ \"good_command\" ] }"), "Spec2",
+                       "*");
+    result = mccs.checkCommand();
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"result\": [ 0 ] }", msg->str());
+    EXPECT_EQ(0, result);
+
+    session.addMessage(el("{ \"command\": \"bad_command\" }"), "Spec2", "*");
+    result = mccs.checkCommand();
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"result\": [ 1, \"Command part in command message missing, empty, or not a list\" ] }", msg->str());
+    EXPECT_EQ(0, result);
+
+    session.addMessage(el("{ \"command\": [ \"bad_command\" ] }"),
+                       "Spec2", "*");
+    result = mccs.checkCommand();
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"result\": [ 1, \"bad command\" ] }", msg->str());
+    EXPECT_EQ(0, result);
+
+    session.addMessage(el("{ \"command\": [ \"command_with_arg\", 1 ] }"),
+                       "Spec2", "*");
+    result = mccs.checkCommand();
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"result\": [ 0, 2 ] }", msg->str());
+    EXPECT_EQ(0, result);
+
+    session.addMessage(el("{ \"command\": [ \"command_with_arg\" ] }"), "Spec2", "*");
+    result = mccs.checkCommand();
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"result\": [ 1, \"arg missing\" ] }", msg->str());
+    EXPECT_EQ(0, result);
+
+    session.addMessage(el("{ \"command\": [ \"command_with_arg\", \"asdf\" ] }"), "Spec2", "*");
+    result = mccs.checkCommand();
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"result\": [ 1, \"arg bad type\" ] }", msg->str());
     EXPECT_EQ(0, result);
 
     mccs.setCommandHandler(NULL);
-    addMessage(el("{ \"command\": [ \"whatever\" ] }"), "Spec2", "*");
-    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());
+    session.addMessage(el("{ \"command\": [ \"whatever\" ] }"), "Spec2", "*");
+    result = mccs.checkCommand();
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    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());
-    addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 2 } ] }"), "Spec2", "*");
-    result = mccs.checkCommand();
-    EXPECT_EQ(1, msg_queue->size());
-    msg = getFirstMessage(group, to);
-    EXPECT_EQ("{\"result\": [ 0 ]}", msg->str());
+    session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 2 } ] }"), "Spec2", "*");
+    result = mccs.checkCommand();
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"result\": [ 0 ] }", msg->str());
     EXPECT_EQ(0, result);
     EXPECT_EQ(2, mccs.getValue("item1")->intValue());
 
-    addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": \"asdf\" } ] }"), "Spec2", "*");
-    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());
+    session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": \"asdf\" } ] }"), "Spec2", "*");
+    result = mccs.checkCommand();
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"result\": [ 2, \"Error in config validation: Type mismatch\" ] }", msg->str());
     EXPECT_EQ(0, result);
     EXPECT_EQ(2, mccs.getValue("item1")->intValue());
 
-    addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 5 } ] }"), "Spec2", "*");
-    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());
+    session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 5 } ] }"), "Spec2", "*");
+    result = mccs.checkCommand();
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"result\": [ 6, \"I do not like the number 5\" ] }", msg->str());
     EXPECT_EQ(0, result);
     EXPECT_EQ(2, mccs.getValue("item1")->intValue());
-
-    endFakeSession();
-}
-
-TEST(CCSession, remoteConfig)
-{
+}
+
+TEST_F(CCSessionTest, remoteConfig) {
     std::string module_name;
     int item1;
     
-    initFakeSession();
-    ModuleCCSession mccs(ccspecfile("spec1.spec"), NULL, NULL);
-    EXPECT_EQ(true, haveSubscription("Spec1", "*"));
+    ModuleCCSession mccs(ccspecfile("spec1.spec"), session, NULL, NULL);
+    EXPECT_EQ(true, session.haveSubscription("Spec1", "*"));
     
     // first simply connect, with no config values, and see we get
     // the default
-    initial_messages->add(createAnswer(0, el("{}")));
-
-    EXPECT_EQ(false, haveSubscription("Spec2", "*"));
+    session.getMessages()->add(createAnswer(0, el("{}")));
+
+    EXPECT_EQ(false, session.haveSubscription("Spec2", "*"));
     module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"));
     EXPECT_EQ("Spec2", module_name);
-    EXPECT_EQ(true, haveSubscription("Spec2", "*"));
+    EXPECT_EQ(true, session.haveSubscription("Spec2", "*"));
 
     item1 = mccs.getRemoteConfigValue(module_name, "item1")->intValue();
     EXPECT_EQ(1, item1);
 
     // Remove it and see we get an error asking for a config value
     mccs.removeRemoteConfig(module_name);
-    EXPECT_EQ(false, haveSubscription("Spec2", "*"));
+    EXPECT_EQ(false, session.haveSubscription("Spec2", "*"));
     EXPECT_THROW(mccs.getRemoteConfigValue(module_name, "item1"), CCSessionError);
 
     // Now re-add it, with a specific config value, and see we get that
-    initial_messages->add(createAnswer(0, el("{ \"item1\": 2 }")));
+    session.getMessages()->add(createAnswer(0, el("{ \"item1\": 2 }")));
     module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"));
     item1 = mccs.getRemoteConfigValue(module_name, "item1")->intValue();
     EXPECT_EQ(2, item1);
 
     // Try a config_update command
-    addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 3 } ] }"), module_name, "*");
+    session.addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 3 } ] }"), module_name, "*");
     mccs.checkCommand();
     item1 = mccs.getRemoteConfigValue(module_name, "item1")->intValue();
     EXPECT_EQ(3, item1);
 
     // remove, re-add, now with a *bad* config request answer
     mccs.removeRemoteConfig(module_name);
-    initial_messages->add(el("{}"));
+    session.getMessages()->add(el("{}"));
     EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
     
-    initial_messages->add(createAnswer(1, "my_error"));
+    session.getMessages()->add(createAnswer(1, "my_error"));
     EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
     
-    initial_messages->add(createAnswer());
+    session.getMessages()->add(createAnswer());
     mccs.addRemoteConfig(ccspecfile("spec2.spec"));
-    
-    endFakeSession();
-}
-
+}
+
+TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
+    // client will ask for config
+    session.getMessages()->add(createAnswer(0, el("{  }")));
+
+    EXPECT_EQ(false, session.haveSubscription("Spec2", "*"));
+    ModuleCCSession mccs(ccspecfile("spec2.spec"), session, my_config_handler, my_command_handler);
+    EXPECT_EQ(true, session.haveSubscription("Spec2", "*"));
+
+    EXPECT_EQ(2, session.getMsgQueue()->size());
+    ElementPtr msg;
+    std::string group, to;
+    // drop the module_spec and config commands
+    session.getFirstMessage(group, to);
+    session.getFirstMessage(group, to);
+
+    session.getMessages()->add(createAnswer(0, el("{  }")));
+    mccs.addRemoteConfig(ccspecfile("spec1.spec"));
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+
+    // Check if commands for the module are handled
+    session.addMessage(el("{ \"command\": [ \"good_command\" ] }"), "Spec2", "*");
+    int result = mccs.checkCommand();
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    msg = session.getFirstMessage(group, to);
+    EXPECT_EQ("{ \"result\": [ 0 ] }", msg->str());
+    EXPECT_EQ(0, result);
+
+    // Check if commands for the other module are ignored
+    session.addMessage(el("{ \"command\": [ \"good_command\" ] }"), "Spec1", "*");
+    EXPECT_EQ(1, session.getMsgQueue()->size());
+    result = mccs.checkCommand();
+    EXPECT_EQ(0, session.getMsgQueue()->size());
+}
+
+}

Modified: branches/trac241/src/lib/config/tests/config_data_unittests.cc
==============================================================================
--- branches/trac241/src/lib/config/tests/config_data_unittests.cc (original)
+++ branches/trac241/src/lib/config/tests/config_data_unittests.cc Mon Aug  9 18:42:00 2010
@@ -17,7 +17,7 @@
 
 #include <gtest/gtest.h>
 
-#include "data_def_unittests_config.h"
+#include <config/tests/data_def_unittests_config.h>
 #include <config/config_data.h>
 
 #include <iostream>
@@ -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/trac241/src/lib/config/tests/fake_session.cc
==============================================================================
--- branches/trac241/src/lib/config/tests/fake_session.cc (original)
+++ branches/trac241/src/lib/config/tests/fake_session.cc Mon Aug  9 18:42:00 2010
@@ -14,7 +14,7 @@
 
 // $Id: session.cc 1250 2010-03-09 22:52:15Z jinmei $
 
-#include "config.h"
+#include <config.h>
 
 #include <stdint.h>
 
@@ -28,7 +28,7 @@
 #include <exceptions/exceptions.h>
 
 #include <cc/data.h>
-#include "fake_session.h"
+#include <config/tests/fake_session.h>
 
 using namespace std;
 using namespace isc::cc;
@@ -38,26 +38,21 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 
-isc::data::ElementPtr initial_messages;
-isc::data::ElementPtr subscriptions;
-isc::data::ElementPtr msg_queue;
-
 // ok i want these in cc/data 
-static bool
-listContains(ElementPtr list, ElementPtr el)
-{
+bool
+listContains(ElementPtr list, ElementPtr el) {
     if (!list) {
-        return false;
+        return (false);
     }
     BOOST_FOREACH(ElementPtr l_el, list->listValue()) {
         if (l_el == el) {
-            return true;
+            return (true);
         }
     }
-    return false;
-}
-
-static void
+    return (false);
+}
+
+void
 listRemove(ElementPtr list, ElementPtr el) {
     int i = -1;
     BOOST_FOREACH(ElementPtr s_el, list->listValue()) {
@@ -72,210 +67,214 @@
 }
 // endwant
 
+namespace isc {
+namespace cc {
+
+FakeSession::FakeSession(isc::data::ElementPtr initial_messages,
+                         isc::data::ElementPtr subscriptions,
+                         isc::data::ElementPtr msg_queue) :
+    messages_(initial_messages),
+    subscriptions_(subscriptions),
+    msg_queue_(msg_queue)
+{
+}
+
+FakeSession::~FakeSession() {
+}
+
+bool
+FakeSession::connect() {
+    return (true);
+}
+
+void
+FakeSession::disconnect() {
+}
+
+void
+FakeSession::startRead(boost::function<void()> read_callback UNUSED_PARAM) {
+}
+
+void
+FakeSession::establish(const char* socket_file) {
+}
+
+//
+// Convert to wire format and send this on the TCP stream with its length prefix
+//
+void
+FakeSession::sendmsg(ElementPtr& msg) {
+    //cout << "[XX] client sends message: " << msg << endl;
+    // err, to where?
+    addMessage(msg, "*", "*");
+}
+
+void
+FakeSession::sendmsg(ElementPtr& env, ElementPtr& msg) {
+    //cout << "[XX] client sends message: " << msg << endl;
+    //cout << "[XX] env: " << env << endl;
+    addMessage(msg, env->get("group")->stringValue(), env->get("to")->stringValue());
+}
+
+bool
+FakeSession::recvmsg(ElementPtr& msg, bool nonblock UNUSED_PARAM,
+                     int seq UNUSED_PARAM)
+{
+    //cout << "[XX] client asks for message " << endl;
+    if (messages_ &&
+        messages_->getType() == Element::list &&
+        messages_->size() > 0) {
+        msg = messages_->get(0);
+        messages_->remove(0);
+    } else {
+        msg = ElementPtr();
+    }
+    return (true);
+}
+
+bool
+FakeSession::recvmsg(ElementPtr& env, ElementPtr& msg,
+                     bool nonblock UNUSED_PARAM,
+                     int seq UNUSED_PARAM)
+{
+    //cout << "[XX] client asks for message and env" << endl;
+    env = ElementPtr();
+    if (messages_ &&
+        messages_->getType() == Element::list &&
+        messages_->size() > 0) {
+        // do we need initial message to have env[group] and [to] too?
+        msg = messages_->get(0);
+        messages_->remove(0);
+        return true;
+    } else if (msg_queue_) {
+        BOOST_FOREACH(ElementPtr c_m, msg_queue_->listValue()) {
+            ElementPtr to_remove = ElementPtr();
+            if (haveSubscription(c_m->get(0), c_m->get(1))) {
+                env = Element::createMap();
+                env->set("group", c_m->get(0));
+                env->set("to", c_m->get(1));
+                msg = c_m->get(2);
+                to_remove = c_m;
+            }
+            if (to_remove) {
+                listRemove(msg_queue_, to_remove);
+                return true;
+            }
+        }
+    }
+    msg = ElementPtr();
+    env = ElementPtr();
+    return false;
+}
+
+void
+FakeSession::subscribe(std::string group, std::string instance) {
+    //cout << "[XX] client subscribes to " << group << " . " << instance << endl;
+    ElementPtr s_el = Element::createList();
+    s_el->add(Element::create(group));
+    s_el->add(Element::create(instance));
+    if (!subscriptions_) {
+        subscriptions_ = Element::createList();
+    }
+    subscriptions_->add(s_el);
+}
+
+void
+FakeSession::unsubscribe(std::string group, std::string instance) {
+    //cout << "[XX] client unsubscribes from " << group << " . " << instance << endl;
+    ElementPtr s_el = Element::createList();
+    s_el->add(Element::create(group));
+    s_el->add(Element::create(instance));
+    if (!subscriptions_) {
+        return;
+    }
+    listRemove(subscriptions_, s_el);
+}
+
+int
+FakeSession::group_sendmsg(ElementPtr msg, std::string group,
+                           std::string to, std::string instance UNUSED_PARAM)
+{
+    //cout << "[XX] client sends message: " << msg << endl;
+    //cout << "[XX] to: " << group << " . " << instance << "." << to << endl;
+    addMessage(msg, group, to);
+    return 1;
+}
+
+bool
+FakeSession::group_recvmsg(ElementPtr& envelope, ElementPtr& msg,
+                           bool nonblock, int seq)
+{
+    return (recvmsg(envelope, msg, nonblock, seq));
+}
+
+int
+FakeSession::reply(ElementPtr& envelope, ElementPtr& newmsg) {
+    //cout << "[XX] client sends reply: " << newmsg << endl;
+    //cout << "[XX] env: " << envelope << endl;
+    addMessage(newmsg, envelope->get("group")->stringValue(), envelope->get("to")->stringValue());
+    return 1;
+}
+
+bool
+FakeSession::hasQueuedMsgs() {
+    return false;
+}
+
 ElementPtr
-getFirstMessage(std::string& group, std::string& to)
-{
+FakeSession::getFirstMessage(std::string& group, std::string& to) {
     ElementPtr el;
-    if (msg_queue && msg_queue->size() > 0) {
-        el = msg_queue->get(0);
-        msg_queue->remove(0);
+    if (msg_queue_ && msg_queue_->size() > 0) {
+        el = msg_queue_->get(0);
+        msg_queue_->remove(0);
         group = el->get(0)->stringValue();
         to = el->get(1)->stringValue();
         return el->get(2);
     } else {
         group = "";
         to = "";
-        return ElementPtr();
-    }
-}
-
-void
-addMessage(ElementPtr msg, const std::string& group, const std::string& to)
-{
-    ElementPtr m_el = Element::createFromString("[]");
+        return (ElementPtr());
+    }
+}
+
+void
+FakeSession::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->add(m_el);
-}
-
-bool
-haveSubscription(const std::string& group, const std::string& instance)
-{
-    if (!subscriptions) {
-        return false;
-    }
-    ElementPtr s1 = Element::createFromString("[]");
-    ElementPtr s2 = Element::createFromString("[]");
+    if (!msg_queue_) {
+        msg_queue_ = Element::createList();
+    }
+    msg_queue_->add(m_el);
+}
+
+bool
+FakeSession::haveSubscription(const std::string& group,
+                              const std::string& instance)
+{
+    if (!subscriptions_) {
+        return (false);
+    }
+    ElementPtr s1 = Element::createList();
+    ElementPtr s2 = Element::createList();
     s1->add(Element::create(group));
     s1->add(Element::create(instance));
     s2->add(Element::create(group));
     s2->add(Element::create("*"));
-    bool result = (listContains(subscriptions, s1) || listContains(subscriptions, s2));
-    return result;
-}
-
-bool
-haveSubscription(const ElementPtr group, const ElementPtr instance)
-{
-    return haveSubscription(group->stringValue(), instance->stringValue());
-}
-
-namespace isc {
-namespace cc {
-
-Session::Session()
-{
-}
-
-Session::Session(asio::io_service& io_service UNUSED_PARAM)
-{
-}
-
-Session::~Session() {
-}
-
-bool
-Session::connect() {
-    return true;
-}
-
-void
-Session::disconnect() {
-}
-
-int
-Session::getSocket() const {
-    return 1;
-}
-
-void
-Session::startRead(boost::function<void()> read_callback UNUSED_PARAM) {
-}
-
-void
-Session::establish(const char* socket_file) {
-}
-
-//
-// Convert to wire format and send this on the TCP stream with its length prefix
-//
-void
-Session::sendmsg(ElementPtr& msg) {
-    //cout << "[XX] client sends message: " << msg << endl;
-    // err, to where?
-    addMessage(msg, "*", "*");
-}
-
-void
-Session::sendmsg(ElementPtr& env, ElementPtr& msg) {
-    //cout << "[XX] client sends message: " << msg << endl;
-    //cout << "[XX] env: " << env << endl;
-    addMessage(msg, env->get("group")->stringValue(), env->get("to")->stringValue());
-}
-
-bool
-Session::recvmsg(ElementPtr& msg, bool nonblock UNUSED_PARAM, int seq UNUSED_PARAM) {
-    //cout << "[XX] client asks for message " << endl;
-    if (initial_messages &&
-        initial_messages->getType() == Element::list &&
-        initial_messages->size() > 0) {
-        msg = initial_messages->get(0);
-        initial_messages->remove(0);
-    } else {
-        msg = ElementPtr();
-    }
-    return (true);
-}
-
-bool
-Session::recvmsg(ElementPtr& env, ElementPtr& msg, bool nonblock UNUSED_PARAM, int seq UNUSED_PARAM) {
-    //cout << "[XX] client asks for message and env" << endl;
-    env = ElementPtr();
-    if (initial_messages &&
-        initial_messages->getType() == Element::list &&
-        initial_messages->size() > 0) {
-        // do we need initial message to have env[group] and [to] too?
-        msg = initial_messages->get(0);
-        initial_messages->remove(0);
-        return true;
-    } else if (msg_queue) {
-        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->set("group", c_m->get(0));
-                env->set("to", c_m->get(1));
-                msg = c_m->get(2);
-                to_remove = c_m;
-            }
-            if (to_remove) {
-                listRemove(msg_queue, to_remove);
-                return true;
-            }
-        }
-    }
-    msg = ElementPtr();
-    env = ElementPtr();
-    return false;
-}
-
-void
-Session::subscribe(std::string group, std::string instance) {
-    //cout << "[XX] client subscribes to " << group << " . " << instance << endl;
-    ElementPtr s_el = Element::createFromString("[]");
-    s_el->add(Element::create(group));
-    s_el->add(Element::create(instance));
-    if (!subscriptions) {
-        subscriptions = Element::createFromString("[]");
-    }
-    subscriptions->add(s_el);
-}
-
-void
-Session::unsubscribe(std::string group, std::string instance) {
-    //cout << "[XX] client unsubscribes from " << group << " . " << instance << endl;
-    ElementPtr s_el = Element::createFromString("[]");
-    s_el->add(Element::create(group));
-    s_el->add(Element::create(instance));
-    if (!subscriptions) {
-        return;
-    }
-    listRemove(subscriptions, s_el);
-}
-
-unsigned int
-Session::group_sendmsg(ElementPtr msg, std::string group,
-                       std::string to, std::string instance UNUSED_PARAM)
-{
-    //cout << "[XX] client sends message: " << msg << endl;
-    //cout << "[XX] to: " << group << " . " << instance << "." << to << endl;
-    addMessage(msg, group, to);
-    return 1;
-}
-
-bool
-Session::group_recvmsg(ElementPtr& envelope, ElementPtr& msg,
-                       bool nonblock, int seq)
-{
-    return (recvmsg(envelope, msg, nonblock, seq));
-}
-
-unsigned int
-Session::reply(ElementPtr& envelope, ElementPtr& newmsg) {
-    //cout << "[XX] client sends reply: " << newmsg << endl;
-    //cout << "[XX] env: " << envelope << endl;
-    addMessage(newmsg, envelope->get("group")->stringValue(), envelope->get("to")->stringValue());
-    return 1;
-}
-
-bool
-Session::hasQueuedMsgs() {
-    return false;
-}
-
-}
-}
+    bool result = (listContains(subscriptions_, s1) ||
+                   listContains(subscriptions_, s2));
+    return (result);
+}
+
+bool
+FakeSession::haveSubscription(const ElementPtr group,
+                              const ElementPtr instance)
+{
+    return (haveSubscription(group->stringValue(), instance->stringValue()));
+}
+
+}
+}

Modified: branches/trac241/src/lib/config/tests/fake_session.h
==============================================================================
--- branches/trac241/src/lib/config/tests/fake_session.h (original)
+++ branches/trac241/src/lib/config/tests/fake_session.h Mon Aug  9 18:42:00 2010
@@ -14,8 +14,8 @@
 
 // $Id: session.h 1250 2010-03-09 22:52:15Z jinmei $
 
-#ifndef _ISC_SESSION_H
-#define _ISC_SESSION_H 1
+#ifndef _ISC_FAKESESSION_H
+#define _ISC_FAKESESSION_H 1
 
 #include <string>
 
@@ -24,85 +24,78 @@
 #include <exceptions/exceptions.h>
 
 #include <cc/data.h>
-
-namespace asio {
-class io_service;
-}
-
-// global variables so tests can insert
-// update and check, before, during and after
-// the actual session object was created/destroyed
-
-// if initial_messages contains a list of messages,
-// these are sent when recv_msg or group_recvmsg is called
-// instead of whatever is in the msg queue
-extern isc::data::ElementPtr initial_messages;
-extern isc::data::ElementPtr subscriptions;
-extern isc::data::ElementPtr msg_queue;
-
-bool haveSubscription(const std::string& group, const std::string& instance);
-bool haveSubscription(const isc::data::ElementPtr group, const isc::data::ElementPtr instance);
-isc::data::ElementPtr getFirstMessage(std::string& group, std::string& to);
-void addMessage(isc::data::ElementPtr, const std::string& group, const std::string& to);
+#include <cc/session.h>
 
 namespace isc {
-    namespace cc {
+namespace cc {
+class FakeSession : public AbstractSession {
+private:
+    FakeSession(const Session& source);
+    FakeSession& operator=(const Session& source);
 
-        class SessionError : public isc::Exception {
-        public:
-            SessionError(const char* file, size_t line, const char* what) :
-                isc::Exception(file, line, what) {}
-        };
+public:
+    // if initial_messages contains a list of messages,
+    // these are sent when recv_msg or group_recvmsg is called
+    // instead of whatever is in the msg queue.
+    // The test can also add data to a copy of the message later to tweak
+    // the group_recvmsg() behavior.  See getMessages() below.
+    FakeSession(isc::data::ElementPtr initial_messages,
+                isc::data::ElementPtr subscriptions,
+                isc::data::ElementPtr msg_queue);
+    virtual ~FakeSession();
 
-        class Session {
-        private:
-            Session(const Session& source);
-            Session& operator=(const Session& source);
+    virtual void startRead(boost::function<void()> read_callback);
 
-        public:
-            // public so tests can inspect them
-        
-            Session();
-            Session(asio::io_service& ioservice);
-            ~Session();
-
-            // XXX: quick hack to allow the user to watch the socket directly.
-            int getSocket() const;
-
-            void startRead(boost::function<void()> read_callback);
-
-            void establish(const char* socket_file = NULL);
-            bool connect();
-            void disconnect();
-            void sendmsg(isc::data::ElementPtr& msg);
-            void sendmsg(isc::data::ElementPtr& env,
-                         isc::data::ElementPtr& msg);
-            bool recvmsg(isc::data::ElementPtr& msg,
-                         bool nonblock = true, int seq = -1);
-            bool recvmsg(isc::data::ElementPtr& env,
-                         isc::data::ElementPtr& msg,
-                         bool nonblock = true, int seq = -1);
-            void subscribe(std::string group,
+    virtual void establish(const char* socket_file = NULL);
+    bool connect();
+    virtual void disconnect();
+    void sendmsg(isc::data::ElementPtr& msg);
+    void sendmsg(isc::data::ElementPtr& env,
+                 isc::data::ElementPtr& msg);
+    bool recvmsg(isc::data::ElementPtr& msg,
+                 bool nonblock = true, int seq = -1);
+    bool recvmsg(isc::data::ElementPtr& env,
+                 isc::data::ElementPtr& msg,
+                 bool nonblock = true, int seq = -1);
+    virtual void subscribe(std::string group,
                            std::string instance = "*");
-            void unsubscribe(std::string group,
+    virtual 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 = "*");
-            bool group_recvmsg(isc::data::ElementPtr& envelope,
+    virtual int group_sendmsg(isc::data::ElementPtr msg,
+                              std::string group,
+                              std::string instance = "*",
+                              std::string to = "*");
+    virtual 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);
-            bool hasQueuedMsgs();
+    virtual int reply(isc::data::ElementPtr& envelope,
+                      isc::data::ElementPtr& newmsg);
+    virtual bool hasQueuedMsgs();
+    isc::data::ElementPtr getFirstMessage(std::string& group, std::string& to);
+    void addMessage(isc::data::ElementPtr, const std::string& group,
+                    const std::string& to);
+    bool haveSubscription(const std::string& group,
+                          const std::string& instance);
+    bool haveSubscription(const isc::data::ElementPtr group,
+                          const isc::data::ElementPtr instance);
 
-        };
-    } // namespace cc
+    // For the convenience of tests, we share these internal members
+    // with the tester.  The test code may insert update and check,
+    // before (via the constructor parameters), during and after the actual
+    // session object was created/destroyed.
+    isc::data::ElementPtr getMessages() { return (messages_); }
+    isc::data::ElementPtr getMsgQueue() { return (msg_queue_); }
+
+private:
+    const isc::data::ElementPtr messages_;
+    isc::data::ElementPtr subscriptions_;
+    isc::data::ElementPtr msg_queue_;
+};
+} // namespace cc
 } // namespace isc
 
-#endif // _ISC_SESSION_H
+#endif // _ISC_FAKESESSION_H
 
 // Local Variables:
 // mode: c++

Modified: branches/trac241/src/lib/config/tests/module_spec_unittests.cc
==============================================================================
--- branches/trac241/src/lib/config/tests/module_spec_unittests.cc (original)
+++ branches/trac241/src/lib/config/tests/module_spec_unittests.cc Mon Aug  9 18:42:00 2010
@@ -20,7 +20,7 @@
 
 #include <fstream>
 
-#include "data_def_unittests_config.h"
+#include <config/tests/data_def_unittests_config.h>
 
 using namespace isc::data;
 using namespace isc::config;
@@ -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/trac241/src/lib/datasrc/Makefile.am
==============================================================================
--- branches/trac241/src/lib/datasrc/Makefile.am (original)
+++ branches/trac241/src/lib/datasrc/Makefile.am Mon Aug  9 18:42:00 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/trac241/src/lib/datasrc/data_source.cc
==============================================================================
--- branches/trac241/src/lib/datasrc/data_source.cc (original)
+++ branches/trac241/src/lib/datasrc/data_source.cc Mon Aug  9 18:42:00 2010
@@ -14,7 +14,7 @@
 
 // $Id$
 
-#include "config.h"
+#include <config.h>
 
 #include <cassert>
 #include <iomanip>
@@ -24,19 +24,20 @@
 #include <boost/shared_ptr.hpp>
 #include <boost/foreach.hpp>
 
-#include <dns/base32.h>
+#include <datasrc/cache.h>
+#include <datasrc/data_source.h>
+#include <datasrc/query.h>
+
+#include <dns/util/base32hex.h>
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/name.h>
 #include <dns/rdataclass.h>
 #include <dns/rrset.h>
 #include <dns/rrsetlist.h>
-#include <dns/sha1.h>
+#include <dns/util/sha1.h>
 
 #include <cc/data.h>
-
-#include "data_source.h"
-#include "query.h"
 
 #define RETERR(x) do { \
                       DataSrc::Result r = (x); \
@@ -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);
@@ -202,19 +518,25 @@
 }
 
 // Match downward, from the zone apex to the query name, looking for
-// referrals.
+// 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)
-{
-    const int nlen = task->qname.getLabelCount();
-    const int diff = nlen - zonename->getLabelCount();
+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; i > 1; --i) {
-            const Name sub(task->qname.split(i - 1, nlen - i));
-            if (refQuery(sub, q.qclass(), ds, zonename, ref)) {
+        for (int i = diff - 1; i > 0; --i) {
+            const Name sub(task->qname.split(i));
+            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,12 +682,12 @@
         }
 
         // Find the closest provable enclosing name for QNAME
-        Name enclosure(zonename);
-        const int nlen = task->qname.getLabelCount();
-        const int diff = nlen - enclosure.getLabelCount();
+        Name enclosure(*zonename);
+        const int diff = task->qname.getLabelCount() -
+            enclosure.getLabelCount();
         string hash2;
         for (int i = 1; i <= diff; ++i) {
-            enclosure = task->qname.split(i, nlen - i);
+            enclosure = task->qname.split(i);
             string nodehash(nsec3->getHash(enclosure));
             if (nodehash == hash1) {
                 break;
@@ -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,8 +760,8 @@
         return (DataSrc::SUCCESS);
     }
 
-    const int nlen = task->qname.getLabelCount();
-    const int diff = nlen - zonename->getLabelCount();
+    const Name* const zonename = zoneinfo.getEnclosingZone();
+    const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
     if (diff < 1) {
         return (DataSrc::SUCCESS);
     }
@@ -445,10 +771,10 @@
     bool cname = false;
 
     for (int i = 1; i <= diff; ++i) {
-        const Name& wname(star.concatenate(task->qname.split(i, nlen - i)));
-        QueryTask newtask(wname, task->qclass, task->qtype, Section::ANSWER(),
+        const Name& wname(star.concatenate(task->qname.split(i)));
+        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;
@@ -474,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);
@@ -488,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);
             }
 
@@ -508,7 +834,6 @@
 
     return (DataSrc::SUCCESS);
 }
-} // end of anonymous namespace
 
 //
 // doQuery: Processes a query.
@@ -518,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();
@@ -536,61 +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.getLabelCount() - 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) {
@@ -599,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;
                     }
@@ -614,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);
@@ -649,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;
@@ -658,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);
                 }
@@ -682,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;
@@ -704,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;
@@ -737,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);
             }
         }
     }
@@ -842,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;
@@ -868,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;
@@ -901,7 +1234,7 @@
         inlength = SHA1_HASHSIZE;
     } while (n++ < iterations_);
 
-    return (encodeBase32(vector<uint8_t>(digest, digest + SHA1_HASHSIZE)));
+    return (encodeBase32Hex(vector<uint8_t>(digest, digest + SHA1_HASHSIZE)));
 }
 
 //

Modified: branches/trac241/src/lib/datasrc/data_source.h
==============================================================================
--- branches/trac241/src/lib/datasrc/data_source.h (original)
+++ branches/trac241/src/lib/datasrc/data_source.h Mon Aug  9 18:42:00 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/trac241/src/lib/datasrc/query.cc
==============================================================================
--- branches/trac241/src/lib/datasrc/query.cc (original)
+++ branches/trac241/src/lib/datasrc/query.cc Mon Aug  9 18:42:00 2010
@@ -21,43 +21,44 @@
 
 #include <cc/data.h>
 
-#include "query.h"
+#include <datasrc/query.h>
 
 using namespace isc::dns;
 
 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/trac241/src/lib/datasrc/query.h
==============================================================================
--- branches/trac241/src/lib/datasrc/query.h (original)
+++ branches/trac241/src/lib/datasrc/query.h Mon Aug  9 18:42:00 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;
@@ -80,7 +88,7 @@
         AUTH_QUERY,
         GLUE_QUERY,
         NOGLUE_QUERY,
-        REF_QUERY,
+        REF_QUERY
     } op;
 
     // The state field indicates the state of the query; it controls
@@ -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/trac241/src/lib/datasrc/sqlite3_datasrc.cc
==============================================================================
--- branches/trac241/src/lib/datasrc/sqlite3_datasrc.cc (original)
+++ branches/trac241/src/lib/datasrc/sqlite3_datasrc.cc Mon Aug  9 18:42:00 2010
@@ -19,7 +19,7 @@
 
 #include <sqlite3.h>
 
-#include "sqlite3_datasrc.h"
+#include <datasrc/sqlite3_datasrc.h>
 
 #include <dns/rrttl.h>
 #include <dns/rdata.h>
@@ -263,15 +263,16 @@
         isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
                   " to SQL statement (query)");
     }
-    const string s_name = name.toText();
-    rc = sqlite3_bind_text(query, 2, s_name.c_str(), -1, SQLITE_STATIC);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind name " << s_name <<
+    const string name_text = name.toText();
+    rc = sqlite3_bind_text(query, 2, name_text.c_str(), -1, SQLITE_STATIC);
+    if (rc != SQLITE_OK) {
+        isc_throw(Sqlite3Error, "Could not bind name " << name_text <<
                   " to SQL statement (query)");
     }
 
+    const string rdtype_text = rdtype.toText();
     if (query == dbparameters->q_record_) {
-        rc = sqlite3_bind_text(query, 3, rdtype.toText().c_str(), -1,
+        rc = sqlite3_bind_text(query, 3, rdtype_text.c_str(), -1,
                                SQLITE_STATIC);
         if (rc != SQLITE_OK) {
             isc_throw(Sqlite3Error, "Could not bind RR type " <<
@@ -300,8 +301,9 @@
                   " to SQL statement (qcount)");
     }
 
-    rc = sqlite3_bind_text(dbparameters->q_count_, 2,
-                           name.reverse().toText().c_str(), -1, SQLITE_STATIC);
+    const string revname_text = name.reverse().toText();
+    rc = sqlite3_bind_text(dbparameters->q_count_, 2, revname_text.c_str(),
+                           -1, SQLITE_STATIC);
     if (rc != SQLITE_OK) {
         isc_throw(Sqlite3Error, "Could not bind name " << name.reverse() <<
                   " to SQL statement (qcount)");
@@ -330,7 +332,7 @@
 Sqlite3DataSrc::findClosest(const Name& name, unsigned int* position) const {
     const unsigned int nlabels = name.getLabelCount();
     for (unsigned int i = 0; i < nlabels; ++i) {
-        const Name matchname(name.split(i, nlabels - i));
+        const Name matchname(name.split(i));
         const int rc = hasExactZone(matchname.toText().c_str());
         if (rc >= 0) {
             if (position != NULL) {
@@ -344,21 +346,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.qname().getLabelCount() -
-                                            position));
+    match.update(*this, match.getName().split(position));
 }
 
 DataSrc::Result
@@ -380,8 +378,9 @@
         isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
                   " to SQL statement (qprevious)");        
     }
+    const string revname_text = qname.reverse().toText();
     rc = sqlite3_bind_text(dbparameters->q_previous_, 2,
-                           qname.reverse().toText().c_str(), -1, SQLITE_STATIC);
+                           revname_text.c_str(), -1, SQLITE_STATIC);
     if (rc != SQLITE_OK) {
         isc_throw(Sqlite3Error, "Could not bind name " << qname <<
                   " to SQL statement (qprevious)");

Modified: branches/trac241/src/lib/datasrc/sqlite3_datasrc.h
==============================================================================
--- branches/trac241/src/lib/datasrc/sqlite3_datasrc.h (original)
+++ branches/trac241/src/lib/datasrc/sqlite3_datasrc.h Mon Aug  9 18:42:00 2010
@@ -21,7 +21,7 @@
 
 #include <exceptions/exceptions.h>
 
-#include "data_source.h"
+#include <datasrc/data_source.h>
 
 namespace isc {
 
@@ -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/trac241/src/lib/datasrc/static_datasrc.cc
==============================================================================
--- branches/trac241/src/lib/datasrc/static_datasrc.cc (original)
+++ branches/trac241/src/lib/datasrc/static_datasrc.cc Mon Aug  9 18:42:00 2010
@@ -14,7 +14,7 @@
 
 // $Id$
 
-#include "config.h"
+#include <config.h>
 
 #include <cassert>
 
@@ -26,8 +26,8 @@
 #include <dns/rrtype.h>
 #include <dns/rrttl.h>
 
-#include "data_source.h"
-#include "static_datasrc.h"
+#include <datasrc/data_source.h>
+#include <datasrc/static_datasrc.h>
 
 using namespace std;
 using namespace isc::dns;
@@ -70,6 +70,7 @@
 {
     authors = RRsetPtr(new RRset(authors_name, RRClass::CH(),
                                  RRType::TXT(), RRTTL(0)));
+    authors->addRdata(generic::TXT("Chen Zhengzhang")); // Jerry
     authors->addRdata(generic::TXT("Evan Hunt"));
     authors->addRdata(generic::TXT("Han Feng"));
     authors->addRdata(generic::TXT("Jelte Jansen"));
@@ -80,6 +81,8 @@
     authors->addRdata(generic::TXT("Michael Graff"));
     authors->addRdata(generic::TXT("Naoki Kambe"));
     authors->addRdata(generic::TXT("Shane Kerr"));
+    authors->addRdata(generic::TXT("Shen Tingting"));
+    authors->addRdata(generic::TXT("Stephen Morris"));
     authors->addRdata(generic::TXT("Zhang Likun"));
 
     authors_ns = RRsetPtr(new RRset(authors_name, RRClass::CH(),
@@ -129,11 +132,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/trac241/src/lib/datasrc/static_datasrc.h
==============================================================================
--- branches/trac241/src/lib/datasrc/static_datasrc.h (original)
+++ branches/trac241/src/lib/datasrc/static_datasrc.h Mon Aug  9 18:42:00 2010
@@ -25,7 +25,7 @@
 #ifndef __STATIC_DATA_SOURCE_H
 #define __STATIC_DATA_SOURCE_H
 
-#include "data_source.h"
+#include <datasrc/data_source.h>
 
 namespace isc {
 
@@ -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/trac241/src/lib/datasrc/tests/Makefile.am
==============================================================================
--- branches/trac241/src/lib/datasrc/tests/Makefile.am (original)
+++ branches/trac241/src/lib/datasrc/tests/Makefile.am Mon Aug  9 18:42:00 2010
@@ -16,14 +16,15 @@
 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)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/.libs/libdatasrc.a 
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a 
-run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.a 
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns++.a 
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/.libs/libcc.a
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
 endif
 
@@ -36,26 +37,6 @@
 EXTRA_DIST += testdata/example2.com
 EXTRA_DIST += testdata/example2.com.sqlite3
 EXTRA_DIST += testdata/mkbrokendb.c
-EXTRA_DIST += testdata/q_cname
-EXTRA_DIST += testdata/q_cname_ext
-EXTRA_DIST += testdata/q_cname_int
-EXTRA_DIST += testdata/q_dname
-EXTRA_DIST += testdata/q_example_ns
-EXTRA_DIST += testdata/q_example_ptr
-EXTRA_DIST += testdata/q_glork
-EXTRA_DIST += testdata/q_spork
-EXTRA_DIST += testdata/q_sql1
-EXTRA_DIST += testdata/q_subzone
-EXTRA_DIST += testdata/q_subzone_any
-EXTRA_DIST += testdata/q_subzone_dname
-EXTRA_DIST += testdata/q_subzone_ds
-EXTRA_DIST += testdata/q_subzone_ns
-EXTRA_DIST += testdata/q_subzone_nsec
-EXTRA_DIST += testdata/q_wild2_a
-EXTRA_DIST += testdata/q_wild2_aaaa
-EXTRA_DIST += testdata/q_wild3_a
-EXTRA_DIST += testdata/q_wild_a
-EXTRA_DIST += testdata/q_wild_aaaa
 EXTRA_DIST += testdata/root.zone
 EXTRA_DIST += testdata/sql1.example.com.signed
 EXTRA_DIST += testdata/sql2.example.com.signed

Modified: branches/trac241/src/lib/datasrc/tests/datasrc_unittest.cc
==============================================================================
--- branches/trac241/src/lib/datasrc/tests/datasrc_unittest.cc (original)
+++ branches/trac241/src/lib/datasrc/tests/datasrc_unittest.cc Mon Aug  9 18:42:00 2010
@@ -39,7 +39,7 @@
 #include <datasrc/static_datasrc.h>
 
 #include <dns/tests/unittest_util.h>
-#include "test_datasrc.h"
+#include <datasrc/tests/test_datasrc.h>
 
 using isc::UnitTestUtil;
 using namespace std;
@@ -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 {
@@ -65,10 +65,10 @@
         meta_source.addDataSrc(static_source);
     }
     void QueryCommon(const RRClass& qclass);
-    void readAndProcessQuery(const char* datafile);
     void createAndProcessQuery(const Name& qname, const RRClass& qclass,
                                const RRType& qtype);
 
+    HotCache cache;
     MetaDataSrc meta_source;
     OutputBuffer obuffer;
     MessageRenderer renderer;
@@ -76,23 +76,11 @@
 };
 
 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);
-}
-
-void
-DataSrcTest::readAndProcessQuery(const char* datafile) {
-    std::vector<unsigned char> data;
-    UnitTestUtil::readWireData(datafile, data);
-
-    InputBuffer buffer(&data[0], data.size());
-    msg.fromWire(buffer);
-
-    msg.makeResponse();
-    performQuery(meta_source, msg);
 }
 
 void
@@ -103,7 +91,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
@@ -192,8 +180,8 @@
 }
 
 TEST_F(DataSrcTest, NSQuery) {
-    readAndProcessQuery("q_example_ns");
-
+    createAndProcessQuery(Name("example.com"), RRClass::IN(),
+                          RRType::NS());
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 0, 6);
 
     RRsetIterator rit = msg.beginSection(Section::ANSWER());
@@ -213,8 +201,89 @@
     EXPECT_TRUE(it->isLast());
 }
 
+// Make sure two successive queries have the same result
+TEST_F(DataSrcTest, DuplicateQuery) {
+    createAndProcessQuery(Name("example.com"), RRClass::IN(),
+                          RRType::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);
+    createAndProcessQuery(Name("example.com"), RRClass::IN(),
+                          RRType::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) {
+    createAndProcessQuery(Name("example.com"), RRClass::IN(),
+                          RRType::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) {
+    createAndProcessQuery(Name("example.com"), RRClass::IN(),
+                          RRType::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);
+    createAndProcessQuery(Name("example.com"), RRClass::IN(),
+                          RRType::DNSKEY());
+    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");
+    createAndProcessQuery(Name("example.com"), RRClass::IN(),
+                          RRType::PTR());
 
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 4, 0);
 
@@ -225,7 +294,8 @@
 }
 
 TEST_F(DataSrcTest, Nxdomain) {
-    readAndProcessQuery("q_glork");
+    createAndProcessQuery(Name("glork.example.com"), RRClass::IN(),
+                          RRType::A());
 
     headerCheck(msg, Rcode::NXDOMAIN(), true, true, true, 0, 6, 0);
 
@@ -238,7 +308,8 @@
 }
 
 TEST_F(DataSrcTest, NxZone) {
-    readAndProcessQuery("q_spork");
+    createAndProcessQuery(Name("spork.example"), RRClass::IN(),
+                          RRType::A());
 
     headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
 
@@ -249,7 +320,8 @@
 }
 
 TEST_F(DataSrcTest, Wildcard) {
-    readAndProcessQuery("q_wild_a");
+    createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
+                          RRType::A());
 
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 6, 6);
 
@@ -302,17 +374,26 @@
 }
 
 TEST_F(DataSrcTest, WildcardNodata) {
-
     // Check that a query for a data type not covered by the wildcard
     // returns NOERROR
-    readAndProcessQuery("q_wild_aaaa");
+    createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
+                          RRType::AAAA());
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 2, 0);
+}
+
+TEST_F(DataSrcTest, DISABLED_WildcardAgainstMultiLabel) {
+    // this qname shouldn't match *.wild.com.com (because * can only match
+    // a single label), and it should result in NXDOMAIN.
+    createAndProcessQuery(Name("www.xxx.wild.example.com"), RRClass::IN(),
+                          RRType::A());
+    headerCheck(msg, Rcode::NXDOMAIN(), true, true, true, 0, 1, 0);
 }
 
 TEST_F(DataSrcTest, WildcardCname) {
     // Check that wildcard answers containing CNAMES are followed
     // correctly
-    readAndProcessQuery("q_wild2_a");
+    createAndProcessQuery(Name("www.wild2.example.com"), RRClass::IN(),
+                          RRType::A());
 
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 6, 6);
 
@@ -380,7 +461,8 @@
 TEST_F(DataSrcTest, WildcardCnameNodata) {
     // A wildcard containing a CNAME whose target does not include
     // data of this type.
-    readAndProcessQuery("q_wild2_aaaa");
+    createAndProcessQuery(Name("www.wild2.example.com"), RRClass::IN(),
+                          RRType::AAAA());
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 4, 0);
 
     RRsetIterator rit = msg.beginSection(Section::ANSWER());
@@ -411,7 +493,8 @@
 
 TEST_F(DataSrcTest, WildcardCnameNxdomain) {
     // A wildcard containing a CNAME whose target does not exist
-    readAndProcessQuery("q_wild3_a");
+    createAndProcessQuery(Name("www.wild3.example.com"), RRClass::IN(),
+                          RRType::A());
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 6, 0);
 
     RRsetIterator rit = msg.beginSection(Section::ANSWER());
@@ -447,7 +530,8 @@
     EXPECT_EQ(RRClass::IN(), rrset->getClass());
 }
 TEST_F(DataSrcTest, AuthDelegation) {
-    readAndProcessQuery("q_sql1");
+    createAndProcessQuery(Name("www.sql1.example.com"), RRClass::IN(),
+                          RRType::A());
 
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 4, 6);
 
@@ -493,7 +577,8 @@
 }
 
 TEST_F(DataSrcTest, Dname) {
-    readAndProcessQuery("q_dname");
+    createAndProcessQuery(Name("www.dname.example.com"), RRClass::IN(),
+                          RRType::A());
 
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 5, 4, 6);
 
@@ -540,8 +625,18 @@
     EXPECT_TRUE(it->isLast());
 }
 
+TEST_F(DataSrcTest, DnameExact) {
+    // The example.org test zone has a DNAME RR for dname2.foo.example.org.
+    // A query for that name with a different RR type than DNAME shouldn't
+    // confuse delegation processing.
+    createAndProcessQuery(Name("dname2.foo.example.org"), RRClass::IN(),
+                          RRType::A());
+    headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 1, 0);
+}
+
 TEST_F(DataSrcTest, Cname) {
-    readAndProcessQuery("q_cname");
+    createAndProcessQuery(Name("foo.example.com"), RRClass::IN(),
+                          RRType::A());
 
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 0, 0);
 
@@ -559,7 +654,8 @@
 }
 
 TEST_F(DataSrcTest, CnameInt) {
-    readAndProcessQuery("q_cname_int");
+    createAndProcessQuery(Name("cname-int.example.com"), RRClass::IN(),
+                          RRType::A());
 
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
 
@@ -585,7 +681,8 @@
 }
 
 TEST_F(DataSrcTest, CnameExt) {
-    readAndProcessQuery("q_cname_ext");
+    createAndProcessQuery(Name("cname-ext.example.com"), RRClass::IN(),
+                          RRType::A());
 
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
 
@@ -609,7 +706,8 @@
 }
 
 TEST_F(DataSrcTest, Delegation) {
-    readAndProcessQuery("q_subzone");
+    createAndProcessQuery(Name("www.subzone.example.com"), RRClass::IN(),
+                          RRType::A());
 
     headerCheck(msg, Rcode::NOERROR(), true, false, true, 0, 5, 2);
 
@@ -639,7 +737,8 @@
 }
 
 TEST_F(DataSrcTest, NSDelegation) {
-    readAndProcessQuery("q_subzone_ns");
+    createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
+                          RRType::NS());
 
     headerCheck(msg, Rcode::NOERROR(), true, false, true, 0, 5, 2);
 
@@ -671,12 +770,13 @@
 TEST_F(DataSrcTest, ANYZonecut) {
     // An ANY query at a zone cut should behave the same as any other
     // delegation
-    readAndProcessQuery("q_subzone_any");
-
+    createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
+                          RRType::ANY());
 }
 
 TEST_F(DataSrcTest, NSECZonecut) {
-    readAndProcessQuery("q_subzone_nsec");
+    createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
+                          RRType::NSEC());
 
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 4, 6);
 
@@ -704,7 +804,8 @@
 }
 
 TEST_F(DataSrcTest, DNAMEZonecut) {
-    readAndProcessQuery("q_subzone_dname");
+    createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
+                          RRType::DNAME());
 
     headerCheck(msg, Rcode::NOERROR(), true, false, true, 0, 5, 2);
     RRsetIterator rit = msg.beginSection(Section::AUTHORITY());
@@ -733,7 +834,8 @@
 }
 
 TEST_F(DataSrcTest, DS) {
-    readAndProcessQuery("q_subzone_ds");
+    createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
+                          RRType::DS());
 
     headerCheck(msg, Rcode::NOERROR(), true, true, true, 3, 4, 6);
 
@@ -761,8 +863,14 @@
 }
 
 TEST_F(DataSrcTest, CNAMELoop) {
-    createAndProcessQuery(Name("loop1.example.com"), RRClass::IN(),
-                          RRType::A());
+    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.
@@ -843,8 +951,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(
@@ -854,6 +980,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/trac241/src/lib/datasrc/tests/query_unittest.cc
==============================================================================
--- branches/trac241/src/lib/datasrc/tests/query_unittest.cc (original)
+++ branches/trac241/src/lib/datasrc/tests/query_unittest.cc Mon Aug  9 18:42:00 2010
@@ -16,43 +16,57 @@
 
 #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
+createQuery(Message& m, const Name& qname, const RRClass& qclass,
+            const RRType& qtype)
+{
+    m.setOpcode(Opcode::QUERY());
+    m.setHeaderFlag(MessageFlag::RD());
+    m.addQuestion(Question(qname, qclass, qtype));
+}
 
 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);
+TEST(QueryTest, constructWithTemporary) {
+    HotCache cache;
+
+    Message m1(Message::RENDER);
+    createQuery(m1, Name("www.wild.example.com"), RRClass::IN(), RRType::A()); 
+    QueryTaskPtr task_a = createTask(m1, Name("www.wild.example.com"),
+                                        RRType::A(), cache);
+    EXPECT_EQ(RRType::A(), task_a->qtype);
+
+    Message m2(Message::RENDER);
+    createQuery(m2, Name("www.wild.example.com"), RRClass::IN(),
+                RRType::AAAA());
+    QueryTaskPtr task_aaaa = createTask(m2, Name("www.wild.example.com"),
+                                        RRType::AAAA(), cache);
+    EXPECT_EQ(RRType::AAAA(), task_aaaa->qtype);
+
 }
 
 }

Modified: branches/trac241/src/lib/datasrc/tests/sqlite3_unittest.cc
==============================================================================
--- branches/trac241/src/lib/datasrc/tests/sqlite3_unittest.cc (original)
+++ branches/trac241/src/lib/datasrc/tests/sqlite3_unittest.cc Mon Aug  9 18:42:00 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/trac241/src/lib/datasrc/tests/static_unittest.cc
==============================================================================
--- branches/trac241/src/lib/datasrc/tests/static_unittest.cc (original)
+++ branches/trac241/src/lib/datasrc/tests/static_unittest.cc Mon Aug  9 18:42:00 2010
@@ -18,7 +18,7 @@
 #include <string>
 #include <vector>
 
-#include "config.h"
+#include <config.h>
 
 #include <gtest/gtest.h>
 
@@ -54,6 +54,7 @@
         version_data.push_back(PACKAGE_STRING);
 
         // XXX: in addition, the order the following items matter.
+        authors_data.push_back("Chen Zhengzhang");
         authors_data.push_back("Evan Hunt");
         authors_data.push_back("Han Feng");
         authors_data.push_back("Jelte Jansen");
@@ -64,6 +65,8 @@
         authors_data.push_back("Michael Graff");
         authors_data.push_back("Naoki Kambe");
         authors_data.push_back("Shane Kerr");
+        authors_data.push_back("Shen Tingting");
+        authors_data.push_back("Stephen Morris");
         authors_data.push_back("Zhang Likun");
 
         version_ns_data.push_back("version.bind.");
@@ -196,55 +199,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/trac241/src/lib/datasrc/tests/test_datasrc.cc
==============================================================================
--- branches/trac241/src/lib/datasrc/tests/test_datasrc.cc (original)
+++ branches/trac241/src/lib/datasrc/tests/test_datasrc.cc Mon Aug  9 18:42:00 2010
@@ -14,12 +14,14 @@
 
 // $Id$
 
-#include "config.h"
+#include <config.h>
 
 #include <cassert>
 
+#include <algorithm>
+
 #include <dns/tests/unittest_util.h>
-#include "test_datasrc.h"
+#include <datasrc/tests/test_datasrc.h>
 
 #include <datasrc/data_source.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/trac241/src/lib/datasrc/tests/test_datasrc.h
==============================================================================
--- branches/trac241/src/lib/datasrc/tests/test_datasrc.h (original)
+++ branches/trac241/src/lib/datasrc/tests/test_datasrc.h Mon Aug  9 18:42:00 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/trac241/src/lib/datasrc/tests/testdata/example.org
==============================================================================
--- branches/trac241/src/lib/datasrc/tests/testdata/example.org (original)
+++ branches/trac241/src/lib/datasrc/tests/testdata/example.org Mon Aug  9 18:42:00 2010
@@ -11,3 +11,4 @@
 sub.example.org. NS ns.sub.example.org.
 ns.sub.example.org. A 192.0.2.101
 dname.example.org. DNAME dname.example.info.
+dname2.foo.example.org. DNAME dname2.example.info.

Modified: branches/trac241/src/lib/datasrc/tests/testdata/example.org.sqlite3
==============================================================================
Binary files - no diff available.

Modified: branches/trac241/src/lib/dns/Makefile.am
==============================================================================
--- branches/trac241/src/lib/dns/Makefile.am (original)
+++ branches/trac241/src/lib/dns/Makefile.am Mon Aug  9 18:42:00 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)
@@ -55,51 +55,32 @@
 BUILT_SOURCES = rrclass.h rrtype.h rrparamregistry.cc
 #TODO: check this###BUILT_SOURCES = rdataclass.h rdataclass.cc
 
-lib_LTLIBRARIES = libdns.la
+lib_LTLIBRARIES = libdns++.la
 
-libdns_la_SOURCES = base32.h base32.cc
-libdns_la_SOURCES += base64.h base64.cc
-libdns_la_SOURCES += buffer.h
-libdns_la_SOURCES += dnssectime.h dnssectime.cc
-libdns_la_SOURCES += exceptions.h exceptions.cc
-libdns_la_SOURCES += hex.h hex.cc
-libdns_la_SOURCES += message.h message.cc
-libdns_la_SOURCES += messagerenderer.h messagerenderer.cc
-libdns_la_SOURCES += name.h name.cc
-libdns_la_SOURCES += rdata.h rdata.cc
-libdns_la_SOURCES += rrclass.cc
-libdns_la_SOURCES += rrparamregistry.h
-libdns_la_SOURCES += rrset.h rrset.cc
-libdns_la_SOURCES += rrsetlist.h rrsetlist.cc
-libdns_la_SOURCES += rrttl.h rrttl.cc
-libdns_la_SOURCES += rrtype.cc
-libdns_la_SOURCES += question.h question.cc
-libdns_la_SOURCES += sha1.h sha1.cc
-libdns_la_SOURCES += tsig.h tsig.cc
+libdns___la_SOURCES = util/base32hex.h util/base64.h util/base_n.cc
+libdns___la_SOURCES += util/base32hex_from_binary.h
+libdns___la_SOURCES += util/binary_from_base32hex.h
+libdns___la_SOURCES += util/base16_from_binary.h util/binary_from_base16.h
+libdns___la_SOURCES += buffer.h
+libdns___la_SOURCES += dnssectime.h dnssectime.cc
+libdns___la_SOURCES += exceptions.h exceptions.cc
+libdns___la_SOURCES += util/hex.h
+libdns___la_SOURCES += message.h message.cc
+libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
+libdns___la_SOURCES += name.h name.cc
+libdns___la_SOURCES += rdata.h rdata.cc
+libdns___la_SOURCES += rrclass.cc
+libdns___la_SOURCES += rrparamregistry.h
+libdns___la_SOURCES += rrset.h rrset.cc
+libdns___la_SOURCES += rrsetlist.h rrsetlist.cc
+libdns___la_SOURCES += rrttl.h rrttl.cc
+libdns___la_SOURCES += rrtype.cc
+libdns___la_SOURCES += question.h question.cc
+libdns___la_SOURCES += util/sha1.h util/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 rrparamregistry.cc
+nodist_libdns___la_SOURCES = rdataclass.cc rrclass.h rrtype.h
+nodist_libdns___la_SOURCES += rrparamregistry.cc
 
 rrclass.h: rrclass-placeholder.h
 rrtype.h: rrtype-placeholder.h
@@ -107,8 +88,8 @@
 rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: Makefile
 	./gen-rdatacode.py
 
-libdns_includedir = $(includedir)/dns
-libdns_include_HEADERS = \
+libdns++_includedir = $(includedir)/dns
+libdns++_include_HEADERS = \
 	buffer.h \
 	dnssectime.h \
 	exceptions.h \
@@ -126,9 +107,6 @@
 	rrtype.h \
 	tsig.h
 # Purposely not installing these headers:
-# base32.h # used only internally, and not actually DNS specific
-# base64.h # used only internally, and not actually DNS specific
-# hex.h # used only internally, and not actually DNS specific
-# sha1.h # used only internally, and not actually DNS specific
+# util/*.h: used only internally, and not actually DNS specific
 # rrclass-placeholder.h
 # rrtype-placeholder.h

Modified: branches/trac241/src/lib/dns/message.cc
==============================================================================
--- branches/trac241/src/lib/dns/message.cc (original)
+++ branches/trac241/src/lib/dns/message.cc Mon Aug  9 18:42:00 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/trac241/src/lib/dns/message.h
==============================================================================
--- branches/trac241/src/lib/dns/message.h (original)
+++ branches/trac241/src/lib/dns/message.h Mon Aug  9 18:42:00 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/trac241/src/lib/dns/name.cc
==============================================================================
--- branches/trac241/src/lib/dns/name.cc (original)
+++ branches/trac241/src/lib/dns/name.cc Mon Aug  9 18:42:00 2010
@@ -267,7 +267,7 @@
         if (state == ft_ordinary) {
             assert(count != 0);
             ndata.at(offsets.back()) = count;
-            
+
             offsets.push_back(ndata.size());
             // add a trailing \0
             ndata.push_back('\0');

Modified: branches/trac241/src/lib/dns/question.h
==============================================================================
--- branches/trac241/src/lib/dns/question.h (original)
+++ branches/trac241/src/lib/dns/question.h Mon Aug  9 18:42:00 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/trac241/src/lib/dns/rdata/generic/dnskey_48.cc
==============================================================================
--- branches/trac241/src/lib/dns/rdata/generic/dnskey_48.cc (original)
+++ branches/trac241/src/lib/dns/rdata/generic/dnskey_48.cc Mon Aug  9 18:42:00 2010
@@ -22,7 +22,7 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/foreach.hpp>
 
-#include <dns/base64.h>
+#include <dns/util/base64.h>
 #include <dns/buffer.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>

Modified: branches/trac241/src/lib/dns/rdata/generic/ds_43.cc
==============================================================================
--- branches/trac241/src/lib/dns/rdata/generic/ds_43.cc (original)
+++ branches/trac241/src/lib/dns/rdata/generic/ds_43.cc Mon Aug  9 18:42:00 2010
@@ -22,7 +22,7 @@
 #include <boost/lexical_cast.hpp>
 
 #include <dns/buffer.h>
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/rdata.h>

Modified: branches/trac241/src/lib/dns/rdata/generic/nsec3_50.cc
==============================================================================
--- branches/trac241/src/lib/dns/rdata/generic/nsec3_50.cc (original)
+++ branches/trac241/src/lib/dns/rdata/generic/nsec3_50.cc Mon Aug  9 18:42:00 2010
@@ -22,10 +22,10 @@
 
 #include <boost/lexical_cast.hpp>
 
-#include <dns/base32.h>
+#include <dns/util/base32hex.h>
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/rrtype.h>
@@ -88,7 +88,7 @@
     if (iss.bad() || iss.fail()) {
         isc_throw(InvalidRdataText, "Invalid NSEC3 hash algorithm");
     }
-    decodeBase32(nextstr, next);
+    decodeBase32Hex(nextstr, next);
 
     uint8_t bitmap[8 * 1024];       // 64k bits
     vector<uint8_t> typebits;
@@ -237,7 +237,7 @@
         " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) +
         " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) +
         " " + encodeHex(impl_->salt_) +
-        " " + encodeBase32(impl_->next_) + s.str());
+        " " + encodeBase32Hex(impl_->next_) + s.str());
 }
 
 void

Modified: branches/trac241/src/lib/dns/rdata/generic/nsec3param_51.cc
==============================================================================
--- branches/trac241/src/lib/dns/rdata/generic/nsec3param_51.cc (original)
+++ branches/trac241/src/lib/dns/rdata/generic/nsec3param_51.cc Mon Aug  9 18:42:00 2010
@@ -22,7 +22,7 @@
 #include <boost/lexical_cast.hpp>
 
 #include <dns/buffer.h>
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/rdata.h>

Modified: branches/trac241/src/lib/dns/rdata/generic/nsec_47.cc
==============================================================================
--- branches/trac241/src/lib/dns/rdata/generic/nsec_47.cc (original)
+++ branches/trac241/src/lib/dns/rdata/generic/nsec_47.cc Mon Aug  9 18:42:00 2010
@@ -19,7 +19,7 @@
 #include <sstream>
 #include <vector>
 
-#include <dns/base64.h>
+#include <dns/util/base64.h>
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>

Modified: branches/trac241/src/lib/dns/rdata/generic/rrsig_46.cc
==============================================================================
--- branches/trac241/src/lib/dns/rdata/generic/rrsig_46.cc (original)
+++ branches/trac241/src/lib/dns/rdata/generic/rrsig_46.cc Mon Aug  9 18:42:00 2010
@@ -22,7 +22,7 @@
 
 #include <boost/lexical_cast.hpp>
 
-#include <dns/base64.h>
+#include <dns/util/base64.h>
 #include <dns/buffer.h>
 #include <dns/dnssectime.h>
 #include <dns/messagerenderer.h>

Modified: branches/trac241/src/lib/dns/rrsetlist.cc
==============================================================================
--- branches/trac241/src/lib/dns/rrsetlist.cc (original)
+++ branches/trac241/src/lib/dns/rrsetlist.cc Mon Aug  9 18:42:00 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/trac241/src/lib/dns/rrsetlist.h
==============================================================================
--- branches/trac241/src/lib/dns/rrsetlist.h (original)
+++ branches/trac241/src/lib/dns/rrsetlist.h Mon Aug  9 18:42:00 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/trac241/src/lib/dns/tests/Makefile.am
==============================================================================
--- branches/trac241/src/lib/dns/tests/Makefile.am (original)
+++ branches/trac241/src/lib/dns/tests/Makefile.am Mon Aug  9 18:42:00 2010
@@ -31,7 +31,7 @@
 run_unittests_SOURCES += question_unittest.cc
 run_unittests_SOURCES += rrparamregistry_unittest.cc
 run_unittests_SOURCES += message_unittest.cc
-run_unittests_SOURCES += base32_unittest.cc
+run_unittests_SOURCES += base32hex_unittest.cc
 run_unittests_SOURCES += base64_unittest.cc
 run_unittests_SOURCES += hex_unittest.cc
 run_unittests_SOURCES += sha1_unittest.cc
@@ -40,7 +40,7 @@
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns++.a
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
 endif
 

Modified: branches/trac241/src/lib/dns/tests/base64_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/base64_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/base64_unittest.cc Mon Aug  9 18:42:00 2010
@@ -18,11 +18,14 @@
 #include <utility>
 #include <vector>
 
-#include <dns/base64.h>
+#include <exceptions/exceptions.h>
+
+#include <dns/util/base64.h>
 
 #include <gtest/gtest.h>
 
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 
 namespace {
@@ -54,8 +57,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) {
@@ -68,22 +70,21 @@
     decodeCheck("Zm9vYmE=\n", decoded_data, "fooba");
 
     // only up to 2 padding characters are allowed
-    EXPECT_THROW(decodeBase64("A===", decoded_data), BadBase64String);
-    EXPECT_THROW(decodeBase64("A= ==", decoded_data), BadBase64String);
+    EXPECT_THROW(decodeBase64("A===", decoded_data), BadValue);
+    EXPECT_THROW(decodeBase64("A= ==", decoded_data), BadValue);
 
     // intermediate padding isn't allowed
-    EXPECT_THROW(decodeBase64("YmE=YmE=", decoded_data), BadBase64String);
+    EXPECT_THROW(decodeBase64("YmE=YmE=", decoded_data), BadValue);
 
     // Non canonical form isn't allowed.
     // Z => 25(011001), m => 38(100110), 9 => 60(111101), so the padding
     // byte would be 0100 0000.
-    EXPECT_THROW(decodeBase64("Zm9=", decoded_data), BadBase64String);
+    EXPECT_THROW(decodeBase64("Zm9=", decoded_data), BadValue);
     // Same for the 1st padding byte.  This would make it 01100000.
-    EXPECT_THROW(decodeBase64("Zm==", decoded_data), BadBase64String);
+    EXPECT_THROW(decodeBase64("Zm==", decoded_data), BadValue);
 }
 
-TEST_F(Base64Test, encode)
-{
+TEST_F(Base64Test, encode) {
     for (vector<StringPair>::const_iterator it = test_sequence.begin();
          it != test_sequence.end();
          ++it) {

Modified: branches/trac241/src/lib/dns/tests/hex_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/hex_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/hex_unittest.cc Mon Aug  9 18:42:00 2010
@@ -19,25 +19,27 @@
 #include <vector>
 #include <string>
 
-#include <dns/hex.h>
+#include <exceptions/exceptions.h>
+
+#include <dns/util/hex.h>
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-
-using isc::UnitTestUtil;
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 
 namespace {
+const string hex_txt("DEADBEEFDECADE");
+const string hex_txt_space("DEAD BEEF DECADE");
+const string hex_txt_lower("deadbeefdecade");
+
 class HexTest : public ::testing::Test {
 protected:
-    HexTest() {}
+    HexTest() : encoding_chars("0123456789ABCDEF") {}
+    vector<uint8_t> decoded_data;
+    const string encoding_chars;
 };
-
-const std::string hex_txt("DEADBEEFDECADE");
-const std::string hex_txt_space("DEAD BEEF DECADE");
-const std::string hex_txt_lower("deadbeefdecade");
 
 TEST_F(HexTest, encodeHex) {
     std::vector<uint8_t> data;
@@ -53,8 +55,7 @@
 }
 
 void
-compareData(const std::vector<uint8_t>& data)
-{
+compareData(const std::vector<uint8_t>& data) {
     EXPECT_EQ(0xde, data[0]);
     EXPECT_EQ(0xad, data[1]);
     EXPECT_EQ(0xbe, data[2]);
@@ -82,7 +83,40 @@
 
     // Bogus input: should fail
     result.clear();
-    EXPECT_THROW(decodeHex("1x", result), BadHexString);
+    EXPECT_THROW(decodeHex("1x", result), BadValue);
+
+    // Bogus input: encoded string must have an even number of characters.
+    result.clear();
+    EXPECT_THROW(decodeHex("dea", result), BadValue);
+}
+
+// For Hex encode/decode we use handmade mappings, so it's prudent to test the
+// entire mapping table explicitly.
+TEST_F(HexTest, decodeMap) {
+    string input("00");       // input placeholder
+
+    // See Base32HexTest.decodeMap for details of the following tests.
+    for (int i = 0; i < 256; ++i) {
+        input[1] = i;
+
+        const char ch = toupper(i);
+        const size_t pos = encoding_chars.find(ch);
+        if (pos == string::npos) {
+            EXPECT_THROW(decodeHex(input, decoded_data), BadValue);
+        } else {
+            decodeHex(input, decoded_data);
+            EXPECT_EQ(1, decoded_data.size());
+            EXPECT_EQ(pos, decoded_data[0]);
+        }
+    }
+}
+
+TEST_F(HexTest, encodeMap) {
+    for (int i = 0; i < 16; ++i) {
+        decoded_data.clear();
+        decoded_data.push_back(i);
+        EXPECT_EQ(encoding_chars[i], encodeHex(decoded_data)[1]);
+    }
 }
 
 }

Modified: branches/trac241/src/lib/dns/tests/message_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/message_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/message_unittest.cc Mon Aug  9 18:42:00 2010
@@ -28,7 +28,7 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/messagerenderer_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/messagerenderer_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/messagerenderer_unittest.cc Mon Aug  9 18:42:00 2010
@@ -20,7 +20,7 @@
 #include <dns/name.h>
 #include <dns/messagerenderer.h>
 
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 #include <gtest/gtest.h>
 

Modified: branches/trac241/src/lib/dns/tests/name_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/name_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/name_unittest.cc Mon Aug  9 18:42:00 2010
@@ -26,7 +26,7 @@
 #include <dns/name.h>
 #include <dns/messagerenderer.h>
 
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 #include <gtest/gtest.h>
 

Modified: branches/trac241/src/lib/dns/tests/question_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/question_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/question_unittest.cc Mon Aug  9 18:42:00 2010
@@ -29,7 +29,7 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 using isc::UnitTestUtil;
 using namespace std;
@@ -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/trac241/src/lib/dns/tests/rdata_cname_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_cname_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_cname_unittest.cc Mon Aug  9 18:42:00 2010
@@ -24,8 +24,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rdata_dname_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_dname_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_dname_unittest.cc Mon Aug  9 18:42:00 2010
@@ -24,8 +24,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rdata_dnskey_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_dnskey_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_dnskey_unittest.cc Mon Aug  9 18:42:00 2010
@@ -16,7 +16,8 @@
 
 #include <string>
 
-#include <dns/base64.h>
+#include <exceptions/exceptions.h>
+
 #include <dns/buffer.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
@@ -26,11 +27,12 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 
@@ -48,21 +50,18 @@
                   "7+ysyLKOOedS39Z7SDmsn2eA0FKtQpwA6LXeG2w+jxmw3oA"
                   "8lVUgEf/rzeC/bByBNsO70aEFTd");
 
-TEST_F(Rdata_DNSKEY_Test, fromText)
-{
+TEST_F(Rdata_DNSKEY_Test, fromText) {
     generic::DNSKEY rdata_dnskey(dnskey_txt);
     EXPECT_EQ(dnskey_txt, rdata_dnskey.toText());
 }
 
-TEST_F(Rdata_DNSKEY_Test, assign)
-{
+TEST_F(Rdata_DNSKEY_Test, assign) {
     generic::DNSKEY rdata_dnskey(dnskey_txt);
     generic::DNSKEY rdata_dnskey2 = rdata_dnskey;
     EXPECT_EQ(0, rdata_dnskey.compare(rdata_dnskey2));
 }
 
-TEST_F(Rdata_DNSKEY_Test, badText)
-{
+TEST_F(Rdata_DNSKEY_Test, badText) {
     EXPECT_THROW(generic::DNSKEY("257 3 5"),
                  InvalidRdataText);
     EXPECT_THROW(generic::DNSKEY("99999 3 5 BAAAAAAAAAAAD"),
@@ -71,9 +70,10 @@
                  InvalidRdataText);
     EXPECT_THROW(generic::DNSKEY("257 3 500 BAAAAAAAAAAAD"),
                  InvalidRdataText);
-    EXPECT_THROW(generic::DNSKEY("257 3 5 BAAAAAAAAAAAD"),
-                 BadBase64String);
-#if 0
+    EXPECT_THROW(generic::DNSKEY("257 3 5 BAAAAAAAAAAAD"), BadValue);
+}
+
+TEST_F(Rdata_DNSKEY_Test, DISABLED_badText) {
     // Should this be allowed?  Probably not.  But the test currently fails.
     EXPECT_THROW(generic::DNSKEY("257 3 5BEAAEFTd"),
                  InvalidRdataText);
@@ -81,11 +81,9 @@
     // it could be ambiguous '51 EAAA' vs '5 1EAA..'
     EXPECT_THROW(generic::DNSKEY("257 3 51EAAEFTd"),
                  InvalidRdataText);
-#endif
 }
 
-TEST_F(Rdata_DNSKEY_Test, toWireRenderer)
-{
+TEST_F(Rdata_DNSKEY_Test, toWireRenderer) {
     renderer.skip(2);
     generic::DNSKEY rdata_dnskey(dnskey_txt);
     rdata_dnskey.toWire(renderer);
@@ -97,34 +95,29 @@
                         obuffer.getLength() - 2, &data[2], data.size() - 2);
 }
 
-TEST_F(Rdata_DNSKEY_Test, toWireBuffer)
-{
+TEST_F(Rdata_DNSKEY_Test, toWireBuffer) {
     generic::DNSKEY rdata_dnskey(dnskey_txt);
     rdata_dnskey.toWire(obuffer);
 }
 
-TEST_F(Rdata_DNSKEY_Test, createFromWire)
-{
+TEST_F(Rdata_DNSKEY_Test, createFromWire) {
     generic::DNSKEY rdata_dnskey(dnskey_txt);
     EXPECT_EQ(0, rdata_dnskey.compare(
                   *rdataFactoryFromFile(RRType("DNSKEY"), RRClass("IN"),
                                         "rdata_dnskey_fromWire")));
 }
 
-TEST_F(Rdata_DNSKEY_Test, getTag)
-{
+TEST_F(Rdata_DNSKEY_Test, getTag) {
     generic::DNSKEY rdata_dnskey(dnskey_txt);
     EXPECT_EQ(12892, rdata_dnskey.getTag());
 }
 
-TEST_F(Rdata_DNSKEY_Test, getAlgorithm)
-{
+TEST_F(Rdata_DNSKEY_Test, getAlgorithm) {
     generic::DNSKEY rdata_dnskey(dnskey_txt);
     EXPECT_EQ(5, rdata_dnskey.getAlgorithm());
 }
 
-TEST_F(Rdata_DNSKEY_Test, getFlags)
-{
+TEST_F(Rdata_DNSKEY_Test, getFlags) {
     generic::DNSKEY rdata_dnskey(dnskey_txt);
     EXPECT_EQ(257, rdata_dnskey.getFlags());
 }

Modified: branches/trac241/src/lib/dns/tests/rdata_ds_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_ds_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_ds_unittest.cc Mon Aug  9 18:42:00 2010
@@ -25,8 +25,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rdata_in_a_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_in_a_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_in_a_unittest.cc Mon Aug  9 18:42:00 2010
@@ -24,8 +24,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_in_aaaa_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_in_aaaa_unittest.cc Mon Aug  9 18:42:00 2010
@@ -24,8 +24,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rdata_mx_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_mx_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_mx_unittest.cc Mon Aug  9 18:42:00 2010
@@ -23,8 +23,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;
@@ -38,14 +38,12 @@
 
 const generic::MX rdata_mx(10, Name("mx.example.com"));
 
-TEST_F(Rdata_MX_Test, createFromText)
-{
+TEST_F(Rdata_MX_Test, createFromText) {
     const generic::MX rdata_mx2("10 mx.example.com");
     EXPECT_EQ(0, rdata_mx2.compare(rdata_mx));
 }
 
-TEST_F(Rdata_MX_Test, badText)
-{
+TEST_F(Rdata_MX_Test, badText) {
     EXPECT_THROW(const generic::MX rdata_mx("99999999 mx."), InvalidRdataText);
     EXPECT_THROW(const generic::MX rdata_mx("10"), InvalidRdataText);
     EXPECT_THROW(const generic::MX rdata_mx("SPOON"), InvalidRdataText);
@@ -53,22 +51,19 @@
                  InvalidRdataText);
 }
 
-TEST_F(Rdata_MX_Test, copy)
-{
+TEST_F(Rdata_MX_Test, copy) {
     const generic::MX rdata_mx2(rdata_mx);
     EXPECT_EQ(0, rdata_mx.compare(rdata_mx2));
 }
 
-TEST_F(Rdata_MX_Test, createFromWire)
-{
+TEST_F(Rdata_MX_Test, createFromWire) {
     EXPECT_EQ(0, rdata_mx.compare(
                   *rdataFactoryFromFile(RRType("MX"), RRClass("IN"),
                                         "rdata_mx_fromWire")));
     // TBD: more tests
 }
 
-TEST_F(Rdata_MX_Test, toWireRenderer)
-{
+TEST_F(Rdata_MX_Test, toWireRenderer) {
     renderer.writeName(Name("example.com"));
     rdata_mx.toWire(renderer);
 
@@ -78,37 +73,32 @@
                         obuffer.getLength(), &data[0], data.size());
 }
 
-TEST_F(Rdata_MX_Test, toWireBuffer)
-{
+TEST_F(Rdata_MX_Test, toWireBuffer) {
     renderer.writeName(Name("example.com"));
     rdata_mx.toWire(obuffer);
+}
 
-#if 0
+TEST_F(Rdata_MX_Test, DISABLED_toWireBuffer) {
 // XXX: does not pass
     vector<unsigned char> data;
     UnitTestUtil::readWireData("rdata_mx_toWire1", data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
                         obuffer.getLength(), &data[0], data.size());
-#endif
 }
 
-TEST_F(Rdata_MX_Test, toText)
-{
+TEST_F(Rdata_MX_Test, toText) {
     EXPECT_EQ("10 mx.example.com.", rdata_mx.toText());
 }
 
-TEST_F(Rdata_MX_Test, getMXName)
-{
+TEST_F(Rdata_MX_Test, getMXName) {
     EXPECT_EQ(Name("mx.example.com."), rdata_mx.getMXName());
 }
 
-TEST_F(Rdata_MX_Test, getMXPref)
-{
+TEST_F(Rdata_MX_Test, getMXPref) {
     EXPECT_EQ(10, rdata_mx.getMXPref());
 }
 
-TEST_F(Rdata_MX_Test, compare)
-{
+TEST_F(Rdata_MX_Test, compare) {
     generic::MX small1(1, Name("mx.example.com"));
     generic::MX small2(10, Name("mx.example.com"));
     generic::MX large1(65535, Name("mx.example.com"));

Modified: branches/trac241/src/lib/dns/tests/rdata_ns_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_ns_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_ns_unittest.cc Mon Aug  9 18:42:00 2010
@@ -24,8 +24,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rdata_nsec3_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_nsec3_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_nsec3_unittest.cc Mon Aug  9 18:42:00 2010
@@ -16,10 +16,11 @@
 
 #include <string>
 
-#include <dns/base32.h>
+#include <exceptions/exceptions.h>
+
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
@@ -28,60 +29,64 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 
 namespace {
 class Rdata_NSEC3_Test : public RdataTest {
     // there's nothing to specialize
+public:
+    Rdata_NSEC3_Test() :
+        nsec3_txt("1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
+                  "NS SOA RRSIG DNSKEY NSEC3PARAM") {}
+    string nsec3_txt;
 };
-string nsec3_txt("1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
-                 "NS SOA RRSIG DNSKEY NSEC3PARAM");
 
-
-TEST_F(Rdata_NSEC3_Test, toText)
-{
+TEST_F(Rdata_NSEC3_Test, toText) {
     const generic::NSEC3 rdata_nsec3(nsec3_txt);
     EXPECT_EQ(nsec3_txt, rdata_nsec3.toText());
 }
 
-TEST_F(Rdata_NSEC3_Test, badText)
-{
-    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1 ADDAFEE "
+TEST_F(Rdata_NSEC3_Test, badText) {
+    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1 ADDAFEEE "
                                             "0123456789ABCDEFGHIJKLMNOPQRSTUV "
                                             "BIFF POW SPOON"),
                  InvalidRdataText);
     EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1 ADDAFEE "
                                             "WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW "
                                             "A NS SOA"),
-                 BadBase32String);
-    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1000000 1 1 ADDAFEE "
+                 BadValue);     // bad hex
+    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1 ADDAFEEE "
+                                            "WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW "
+                                            "A NS SOA"),
+                 BadValue);     // bad base32hex
+    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1000000 1 1 ADDAFEEE "
                                             "0123456789ABCDEFGHIJKLMNOPQRSTUV "
                                             "A NS SOA"),
                  InvalidRdataText);
-    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1000000 1 ADDAFEE "
+    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1000000 1 ADDAFEEE "
                                             "0123456789ABCDEFGHIJKLMNOPQRSTUV "
                                             "A NS SOA"),
                  InvalidRdataText);
-    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1000000 ADDAFEE "
+    EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1000000 ADDAFEEE "
                                             "0123456789ABCDEFGHIJKLMNOPQRSTUV "
                                             "A NS SOA"),
                  InvalidRdataText);
+}
 
-#if 0 // this currently fails
+TEST_F(Rdata_NSEC3_Test, DISABLED_badText) { // this currently fails
     EXPECT_THROW(generic::NSEC3(
                      "1 1 1D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
                      "NS SOA RRSIG DNSKEY NSEC3PARAM"), InvalidRdataText);
-#endif
 }
 
-TEST_F(Rdata_NSEC3_Test, createFromWire)
-{
+TEST_F(Rdata_NSEC3_Test, createFromWire) {
     const generic::NSEC3 rdata_nsec3(nsec3_txt);
     EXPECT_EQ(0, rdata_nsec3.compare(
                   *rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
@@ -98,8 +103,7 @@
                  DNSMessageFORMERR);
 }
 
-TEST_F(Rdata_NSEC3_Test, toWireRenderer)
-{
+TEST_F(Rdata_NSEC3_Test, toWireRenderer) {
     renderer.skip(2);
     const generic::NSEC3 rdata_nsec3(nsec3_txt);
     rdata_nsec3.toWire(renderer);
@@ -111,14 +115,12 @@
                         obuffer.getLength() - 2, &data[2], data.size() - 2);
 }
 
-TEST_F(Rdata_NSEC3_Test, toWireBuffer)
-{
+TEST_F(Rdata_NSEC3_Test, toWireBuffer) {
     const generic::NSEC3 rdata_nsec3(nsec3_txt);
     rdata_nsec3.toWire(obuffer);
 }
 
-TEST_F(Rdata_NSEC3_Test, assign)
-{
+TEST_F(Rdata_NSEC3_Test, assign) {
     generic::NSEC3 rdata_nsec3(nsec3_txt);
     generic::NSEC3 other_nsec3 = rdata_nsec3;
     EXPECT_EQ(0, rdata_nsec3.compare(other_nsec3));

Modified: branches/trac241/src/lib/dns/tests/rdata_nsec3param_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_nsec3param_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_nsec3param_unittest.cc Mon Aug  9 18:42:00 2010
@@ -16,9 +16,11 @@
 
 #include <string>
 
-#include <dns/base32.h>
+#include <exceptions/exceptions.h>
+
+#include <dns/util/base32hex.h>
 #include <dns/buffer.h>
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
@@ -27,11 +29,12 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 
@@ -41,35 +44,32 @@
 };
 string nsec3param_txt("1 0 1 D399EAAB");
 
-TEST_F(Rdata_NSEC3PARAM_Test, toText)
-{
+TEST_F(Rdata_NSEC3PARAM_Test, toText) {
     const generic::NSEC3PARAM rdata_nsec3param(nsec3param_txt);
     EXPECT_EQ(nsec3param_txt, rdata_nsec3param.toText());
 }
 
-TEST_F(Rdata_NSEC3PARAM_Test, badText)
-{
-    EXPECT_THROW(generic::NSEC3PARAM("1 1 1 SPORK"), BadHexString);
+TEST_F(Rdata_NSEC3PARAM_Test, badText) {
+    EXPECT_THROW(generic::NSEC3PARAM("1 1 1 SPORK"), BadValue); // bad hex
     EXPECT_THROW(generic::NSEC3PARAM("100000 1 1 ADDAFEE"), InvalidRdataText);
     EXPECT_THROW(generic::NSEC3PARAM("1 100000 1 ADDAFEE"), InvalidRdataText);
     EXPECT_THROW(generic::NSEC3PARAM("1 1 100000 ADDAFEE"), InvalidRdataText);
     EXPECT_THROW(generic::NSEC3PARAM("1"), InvalidRdataText);
-
-#if 0                           // this currently fails
-    EXPECT_THROW(generic::NSEC3PARAM("1 0 1D399EAAB"), InvalidRdataText);
-#endif
 }
 
-TEST_F(Rdata_NSEC3PARAM_Test, createFromWire)
-{
+TEST_F(Rdata_NSEC3PARAM_Test, DISABLED_badText) {
+    // this currently fails
+    EXPECT_THROW(generic::NSEC3PARAM("1 0 1D399EAAB"), InvalidRdataText);
+}
+
+TEST_F(Rdata_NSEC3PARAM_Test, createFromWire) {
     const generic::NSEC3PARAM rdata_nsec3param(nsec3param_txt);
     EXPECT_EQ(0, rdata_nsec3param.compare(
                   *rdataFactoryFromFile(RRType::NSEC3PARAM(), RRClass::IN(),
                                        "rdata_nsec3param_fromWire1")));
 }
 
-TEST_F(Rdata_NSEC3PARAM_Test, toWireRenderer)
-{
+TEST_F(Rdata_NSEC3PARAM_Test, toWireRenderer) {
     renderer.skip(2);
     const generic::NSEC3PARAM rdata_nsec3param(nsec3param_txt);
     rdata_nsec3param.toWire(renderer);
@@ -81,14 +81,12 @@
                         obuffer.getLength() - 2, &data[2], data.size() - 2);
 }
 
-TEST_F(Rdata_NSEC3PARAM_Test, toWireBuffer)
-{
+TEST_F(Rdata_NSEC3PARAM_Test, toWireBuffer) {
     const generic::NSEC3PARAM rdata_nsec3param(nsec3param_txt);
     rdata_nsec3param.toWire(obuffer);
 }
 
-TEST_F(Rdata_NSEC3PARAM_Test, assign)
-{
+TEST_F(Rdata_NSEC3PARAM_Test, assign) {
     generic::NSEC3PARAM rdata_nsec3param(nsec3param_txt);
     generic::NSEC3PARAM other_nsec3param = rdata_nsec3param;
     EXPECT_EQ(0, rdata_nsec3param.compare(other_nsec3param));

Modified: branches/trac241/src/lib/dns/tests/rdata_nsec_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_nsec_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_nsec_unittest.cc Mon Aug  9 18:42:00 2010
@@ -26,8 +26,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rdata_opt_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_opt_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_opt_unittest.cc Mon Aug  9 18:42:00 2010
@@ -23,8 +23,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rdata_ptr_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_ptr_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_ptr_unittest.cc Mon Aug  9 18:42:00 2010
@@ -24,8 +24,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rdata_rrsig_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_rrsig_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_rrsig_unittest.cc Mon Aug  9 18:42:00 2010
@@ -14,7 +14,8 @@
 
 // $Id$
 
-#include <dns/base64.h>
+#include <exceptions/exceptions.h>
+
 #include <dns/buffer.h>
 #include <dns/dnssectime.h>
 #include <dns/messagerenderer.h>
@@ -25,11 +26,12 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 
@@ -38,8 +40,7 @@
     // there's nothing to specialize
 };
 
-TEST_F(Rdata_RRSIG_Test, fromText)
-{
+TEST_F(Rdata_RRSIG_Test, fromText) {
     string rrsig_txt("A 5 4 43200 20100223214617 20100222214617 8496 isc.org. "
                      "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz"
                      "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
@@ -50,8 +51,7 @@
 
 }
 
-TEST_F(Rdata_RRSIG_Test, badText)
-{
+TEST_F(Rdata_RRSIG_Test, badText) {
     EXPECT_THROW(const generic::RRSIG sig("SPORK"), InvalidRdataText);
     EXPECT_THROW(const generic::RRSIG sig("A 555 4 43200 "
                      "20100223214617 20100222214617 8496 isc.org. "
@@ -83,20 +83,21 @@
                      "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
                      "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU"
                      "f49t+sXKPzbipN9g+s1ZPiIyofc="), InvalidRdataText);
-    EXPECT_THROW(const generic::RRSIG sig("A 5 4 43200 "
+    EXPECT_THROW(const generic::RRSIG sig(
+                     "A 5 4 43200 "
                      "20100223214617 20100222214617 8496 isc.org. "
                      "EEeeeeeeEEEeeeeeeGaaahAAAAAAAAHHHHHHHHHHH!="),
-                     BadBase64String);
+                 BadValue);     // bad base64 input
+}
 
-#if 0                           // this currently fails
+TEST_F(Rdata_RRSIG_Test, DISABLED_badText) {
+    // this currently fails
     // no space between the tag and signer
     EXPECT_THROW(generic::RRSIG("A 5 4 43200 20100223214617 20100222214617 "
                                 "8496isc.org. ofc="), InvalidRdataText);
-#endif
 }
 
-TEST_F(Rdata_RRSIG_Test, toWireRenderer)
-{
+TEST_F(Rdata_RRSIG_Test, toWireRenderer) {
     string rrsig_txt("A 5 4 43200 20100223214617 20100222214617 8496 isc.org. "
                      "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz"
                      "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
@@ -106,8 +107,7 @@
     rdata_rrsig.toWire(renderer);
 }
 
-TEST_F(Rdata_RRSIG_Test, toWireBuffer)
-{
+TEST_F(Rdata_RRSIG_Test, toWireBuffer) {
     string rrsig_txt("A 5 4 43200 20100223214617 20100222214617 8496 isc.org. "
                      "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz"
                      "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
@@ -117,8 +117,7 @@
     rdata_rrsig.toWire(obuffer);
 }
 
-TEST_F(Rdata_RRSIG_Test, createFromWire)
-{
+TEST_F(Rdata_RRSIG_Test, createFromWire) {
     string rrsig_txt("A 5 2 43200 20100327070149 20100225070149 2658 isc.org. "
                 "HkJk/xZTvzePU8NENl/ley8bbUumhk1hXciyqhLnz1VQFzkDooej6neX"
                 "ZgWZzQKeTKPOYWrnYtdZW4PnPQFeUl3orgLev7F8J6FZlDn0y/J/ThR5"

Modified: branches/trac241/src/lib/dns/tests/rdata_soa_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_soa_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_soa_unittest.cc Mon Aug  9 18:42:00 2010
@@ -23,8 +23,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rdata_txt_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_txt_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_txt_unittest.cc Mon Aug  9 18:42:00 2010
@@ -24,8 +24,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rdata_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rdata_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rdata_unittest.cc Mon Aug  9 18:42:00 2010
@@ -27,8 +27,8 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
-#include "rdata_unittest.h"
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/rrclass_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rrclass_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rrclass_unittest.cc Mon Aug  9 18:42:00 2010
@@ -20,7 +20,7 @@
 #include <dns/messagerenderer.h>
 #include <dns/rrclass.h>
 
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 using namespace std;
 using namespace isc;

Modified: branches/trac241/src/lib/dns/tests/rrset_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rrset_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rrset_unittest.cc Mon Aug  9 18:42:00 2010
@@ -28,7 +28,7 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 using isc::UnitTestUtil;
 

Modified: branches/trac241/src/lib/dns/tests/rrsetlist_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rrsetlist_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rrsetlist_unittest.cc Mon Aug  9 18:42:00 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/trac241/src/lib/dns/tests/rrttl_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rrttl_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rrttl_unittest.cc Mon Aug  9 18:42:00 2010
@@ -20,7 +20,7 @@
 #include <dns/messagerenderer.h>
 #include <dns/rrttl.h>
 
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 using namespace std;
 using namespace isc;

Modified: branches/trac241/src/lib/dns/tests/rrtype_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/rrtype_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/rrtype_unittest.cc Mon Aug  9 18:42:00 2010
@@ -20,7 +20,7 @@
 #include <dns/messagerenderer.h>
 #include <dns/rrtype.h>
 
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 using namespace std;
 using namespace isc;

Modified: branches/trac241/src/lib/dns/tests/run_unittests.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/run_unittests.cc (original)
+++ branches/trac241/src/lib/dns/tests/run_unittests.cc Mon Aug  9 18:42:00 2010
@@ -16,7 +16,7 @@
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 int
 main(int argc, char* argv[]) {

Modified: branches/trac241/src/lib/dns/tests/sha1_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/sha1_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/sha1_unittest.cc Mon Aug  9 18:42:00 2010
@@ -17,11 +17,11 @@
 #include <stdint.h>
 #include <string>
 
-#include <dns/sha1.h>
+#include <dns/util/sha1.h>
 
 #include <gtest/gtest.h>
 
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/tsig_unittest.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/tsig_unittest.cc (original)
+++ branches/trac241/src/lib/dns/tests/tsig_unittest.cc Mon Aug  9 18:42:00 2010
@@ -18,7 +18,7 @@
 
 #include <dns/tsig.h>
 
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 using isc::UnitTestUtil;
 using namespace std;

Modified: branches/trac241/src/lib/dns/tests/unittest_util.cc
==============================================================================
--- branches/trac241/src/lib/dns/tests/unittest_util.cc (original)
+++ branches/trac241/src/lib/dns/tests/unittest_util.cc Mon Aug  9 18:42:00 2010
@@ -26,7 +26,7 @@
 #include <gtest/gtest.h>
 
 #include <dns/name.h>
-#include "unittest_util.h"
+#include <dns/tests/unittest_util.h>
 
 using namespace std;
 

Modified: branches/trac241/src/lib/exceptions/Makefile.am
==============================================================================
--- branches/trac241/src/lib/exceptions/Makefile.am (original)
+++ branches/trac241/src/lib/exceptions/Makefile.am Mon Aug  9 18:42:00 2010
@@ -1,3 +1,4 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CXXFLAGS=$(B10_CXXFLAGS)
 
 lib_LTLIBRARIES = libexceptions.la
@@ -10,7 +11,7 @@
 TESTS += run_unittests
 run_unittests_SOURCES = run_unittests.cc
 run_unittests_SOURCES += exceptions_unittest.cc
-run_unittests_CPPFLAGS = $(GTEST_INCLUDES)
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(GTEST_LDFLAGS)
 run_unittests_LDADD = .libs/libexceptions.a $(GTEST_LDADD)
 endif

Modified: branches/trac241/src/lib/exceptions/exceptions.cc
==============================================================================
--- branches/trac241/src/lib/exceptions/exceptions.cc (original)
+++ branches/trac241/src/lib/exceptions/exceptions.cc Mon Aug  9 18:42:00 2010
@@ -16,7 +16,7 @@
 
 #include <string>
 
-#include "exceptions.h"
+#include <exceptions/exceptions.h>
 
 using isc::Exception;
 

Modified: branches/trac241/src/lib/exceptions/exceptions.h
==============================================================================
--- branches/trac241/src/lib/exceptions/exceptions.h (original)
+++ branches/trac241/src/lib/exceptions/exceptions.h Mon Aug  9 18:42:00 2010
@@ -104,7 +104,7 @@
 };
 
 ///
-/// \brief A standard DNS module exception that is thrown if a parameter give
+/// \brief A generic exception that is thrown if a parameter given
 /// to a method would refer to or modify out-of-range data.
 ///
 class OutOfRange : public Exception {
@@ -114,7 +114,17 @@
 };
 
 ///
-/// \brief A standard DNS module exception that is thrown when an unexpected
+/// \brief A generic exception that is thrown if a parameter given
+/// to a method is considered invalid in that context.
+///
+class BadValue : public Exception {
+public:
+    BadValue(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+///
+/// \brief A generic exception that is thrown when an unexpected
 /// error condition occurs.
 ///
 class Unexpected : public Exception {

Modified: branches/trac241/src/lib/exceptions/exceptions_unittest.cc
==============================================================================
--- branches/trac241/src/lib/exceptions/exceptions_unittest.cc (original)
+++ branches/trac241/src/lib/exceptions/exceptions_unittest.cc Mon Aug  9 18:42:00 2010
@@ -17,7 +17,7 @@
 #include <stdexcept>
 #include <string>
 
-#include "exceptions.h"
+#include <exceptions/exceptions.h>
 
 #include <gtest/gtest.h>
 

Modified: branches/trac241/src/lib/python/isc/Makefile.am
==============================================================================
--- branches/trac241/src/lib/python/isc/Makefile.am (original)
+++ branches/trac241/src/lib/python/isc/Makefile.am Mon Aug  9 18:42:00 2010
@@ -1,4 +1,4 @@
-SUBDIRS = datasrc cc config # Util
+SUBDIRS = datasrc cc config log # Util
 
 python_PYTHON = __init__.py
 

Modified: branches/trac241/src/lib/python/isc/__init__.py
==============================================================================
--- branches/trac241/src/lib/python/isc/__init__.py (original)
+++ branches/trac241/src/lib/python/isc/__init__.py Mon Aug  9 18:42:00 2010
@@ -1,3 +1,5 @@
 import isc.datasrc
 import isc.cc
 import isc.config
+#import isc.dns
+import isc.log

Modified: branches/trac241/src/lib/python/isc/cc/data.py
==============================================================================
--- branches/trac241/src/lib/python/isc/cc/data.py (original)
+++ branches/trac241/src/lib/python/isc/cc/data.py Mon Aug  9 18:42:00 2010
@@ -20,7 +20,7 @@
 # (int, real, bool, string, list and dict respectively)
 #
 
-import ast
+import json
 
 class DataNotFoundError(Exception): pass
 class DataTypeError(Exception): pass
@@ -134,7 +134,7 @@
     if type(value_str) != str:
         return None
     try:
-        return ast.literal_eval(value_str)
+        return json.loads(value_str)
     except ValueError as ve:
         # simply return the string itself
         return value_str

Modified: branches/trac241/src/lib/python/isc/cc/message.py
==============================================================================
--- branches/trac241/src/lib/python/isc/cc/message.py (original)
+++ branches/trac241/src/lib/python/isc/cc/message.py Mon Aug  9 18:42:00 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/trac241/src/lib/python/isc/cc/tests/Makefile.am
==============================================================================
--- branches/trac241/src/lib/python/isc/cc/tests/Makefile.am (original)
+++ branches/trac241/src/lib/python/isc/cc/tests/Makefile.am Mon Aug  9 18:42:00 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/trac241/src/lib/python/isc/cc/tests/data_test.py
==============================================================================
--- branches/trac241/src/lib/python/isc/cc/tests/data_test.py (original)
+++ branches/trac241/src/lib/python/isc/cc/tests/data_test.py Mon Aug  9 18:42:00 2010
@@ -137,13 +137,13 @@
         
     def test_parse_value_str(self):
         self.assertEqual(data.parse_value_str("1"), 1)
-        self.assertEqual(data.parse_value_str("True"), True)
-        self.assertEqual(data.parse_value_str("None"), None)
+        self.assertEqual(data.parse_value_str("true"), True)
+        self.assertEqual(data.parse_value_str("null"), None)
         self.assertEqual(data.parse_value_str("1.1"), 1.1)
         self.assertEqual(data.parse_value_str("[]"), [])
-        self.assertEqual(data.parse_value_str("[ 1, None, 'asdf' ]"), [ 1, None, "asdf" ])
+        self.assertEqual(data.parse_value_str("[ 1, null, \"asdf\" ]"), [ 1, None, "asdf" ])
         self.assertEqual(data.parse_value_str("{}"), {})
-        self.assertEqual(data.parse_value_str("{ 'a': 'b', 'c': 1 }"), { 'a': 'b', 'c': 1 })
+        self.assertEqual(data.parse_value_str("{ \"a\": \"b\", \"c\": 1 }"), { 'a': 'b', 'c': 1 })
         self.assertEqual(data.parse_value_str("[ a c"), "[ a c")
 
 if __name__ == '__main__':

Modified: branches/trac241/src/lib/python/isc/cc/tests/session_test.py
==============================================================================
--- branches/trac241/src/lib/python/isc/cc/tests/session_test.py (original)
+++ branches/trac241/src/lib/python/isc/cc/tests/session_test.py Mon Aug  9 18:42:00 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/trac241/src/lib/python/isc/config/ccsession.py
==============================================================================
--- branches/trac241/src/lib/python/isc/config/ccsession.py (original)
+++ branches/trac241/src/lib/python/isc/config/ccsession.py Mon Aug  9 18:42:00 2010
@@ -150,6 +150,11 @@
 
         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
            manager, and request the current non-default configuration.
@@ -178,10 +183,10 @@
         if msg and not 'result' in msg:
             answer = None
             try:
+                module_name = env['group']
                 cmd, arg = isc.config.ccsession.parse_command(msg)
                 if cmd == COMMAND_CONFIG_UPDATE:
                     new_config = arg
-                    module_name = env['group']
                     # If the target channel was not this module
                     # it might be in the remote_module_configs
                     if module_name != self._module_name:
@@ -208,10 +213,12 @@
                             isc.cc.data.merge(newc, new_config)
                             self.set_local_config(newc)
                 else:
-                    if self._command_handler:
-                        answer = self._command_handler(cmd, arg)
-                    else:
-                        answer = create_answer(2, self._module_name + " has no command handler")
+                    # ignore commands for 'remote' modules
+                    if module_name == self._module_name:
+                        if self._command_handler:
+                            answer = self._command_handler(cmd, arg)
+                        else:
+                            answer = create_answer(2, self._module_name + " has no command handler")
             except Exception as exc:
                 answer = create_answer(1, str(exc))
             if answer:
@@ -244,7 +251,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 +266,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 +288,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 +325,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 +336,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/trac241/src/lib/python/isc/config/cfgmgr.py
==============================================================================
--- branches/trac241/src/lib/python/isc/config/cfgmgr.py (original)
+++ branches/trac241/src/lib/python/isc/config/cfgmgr.py Mon Aug  9 18:42:00 2010
@@ -22,10 +22,10 @@
 import isc
 import signal
 import ast
-import pprint
 import os
 import copy
 import tempfile
+import json
 from isc.cc import data
 from isc.config import ccsession
 
@@ -67,7 +67,7 @@
         config = ConfigManagerData(data_path, file_name)
         try:
             file = open(config.db_filename, 'r')
-            file_config = ast.literal_eval(file.read())
+            file_config = json.loads(file.read())
             if 'version' in file_config and \
                 file_config['version'] == ConfigManagerData.CONFIG_VERSION:
                 config.data = file_config
@@ -93,9 +93,7 @@
                                                dir=self.data_path,
                                                delete=False)
             filename = file.name
-            pp = pprint.PrettyPrinter(indent=4)
-            s = pp.pformat(self.data)
-            file.write(s)
+            file.write(json.dumps(self.data))
             file.write("\n")
             file.close()
             if output_file_name:
@@ -347,7 +345,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/trac241/src/lib/python/isc/config/config_data.py
==============================================================================
--- branches/trac241/src/lib/python/isc/config/config_data.py (original)
+++ branches/trac241/src/lib/python/isc/config/config_data.py Mon Aug  9 18:42:00 2010
@@ -250,6 +250,11 @@
         """Removes the specification with the given module name. Does nothing if it wasn't there."""
         if module_name in self._specifications:
             del self._specifications[module_name]
+
+    def have_specification(self, module_name):
+        """Returns True if we have a specification for the module with the given name.
+           Returns False if we do not."""
+        return module_name in self._specifications
 
     def get_module_spec(self, module):
         """Returns the ModuleSpec for the module with the given name.

Modified: branches/trac241/src/lib/python/isc/config/module_spec.py
==============================================================================
--- branches/trac241/src/lib/python/isc/config/module_spec.py (original)
+++ branches/trac241/src/lib/python/isc/config/module_spec.py Mon Aug  9 18:42:00 2010
@@ -21,7 +21,8 @@
    set of data against the specification
 """
 
-import ast
+import json
+import sys
 
 import isc.cc.data
 
@@ -37,19 +38,29 @@
 def module_spec_from_file(spec_file, check = True):
     """Returns a ModuleSpec object defined by the file at spec_file.
        If check is True, the contents are verified. If there is an error
-       in those contents, a ModuleSpecError is raised."""
+       in those contents, a ModuleSpecError is raised.
+       A ModuleSpecError is also raised if the file cannot be read, or
+       if it is not valid JSON."""
     module_spec = None
-    if hasattr(spec_file, 'read'):
-        module_spec = ast.literal_eval(spec_file.read(-1))
-    elif type(spec_file) == str:
-        file = open(spec_file)
-        module_spec = ast.literal_eval(file.read(-1))
-        file.close()
-    else:
-        raise ModuleSpecError("spec_file not a str or file-like object")
+    try:
+        if hasattr(spec_file, 'read'):
+            json_str = spec_file.read()
+            module_spec = json.loads(json_str)
+        elif type(spec_file) == str:
+            file = open(spec_file)
+            json_str = file.read()
+            module_spec = json.loads(json_str)
+            file.close()
+        else:
+            raise ModuleSpecError("spec_file not a str or file-like object")
+    except ValueError as ve:
+        raise ModuleSpecError("JSON parse error: " + str(ve))
+    except IOError as ioe:
+        raise ModuleSpecError("JSON read error: " + str(ioe))
+
     if 'module_spec' not in module_spec:
         raise ModuleSpecError("Data definition has no module_spec element")
-        
+
     result = ModuleSpec(module_spec['module_spec'], check)
     return result
 

Modified: branches/trac241/src/lib/python/isc/config/tests/ccsession_test.py
==============================================================================
--- branches/trac241/src/lib/python/isc/config/tests/ccsession_test.py (original)
+++ branches/trac241/src/lib/python/isc/config/tests/ccsession_test.py Mon Aug  9 18:42:00 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,8 +368,43 @@
         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)
+        
+    def test_ignore_command_remote_module(self):
+        # Create a Spec1 module and subscribe to remote config for Spec2
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        mccs.set_command_handler(self.my_command_handler_ok)
+        rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+
+        # remove the 'get config' from the queue
+        self.assertEqual(len(fake_session.message_queue), 1)
+        fake_session.get_message("ConfigManager")
+
+        # check if the command for the module itself is received
+        cmd = isc.config.ccsession.create_command("just_some_command", { 'foo': 'a' })
+        fake_session.group_sendmsg(cmd, 'Spec1')
+        self.assertEqual(len(fake_session.message_queue), 1)
+        mccs.check_command()
+        self.assertEqual(len(fake_session.message_queue), 1)
+        self.assertEqual({'result': [ 0 ]},
+                         fake_session.get_message('Spec1', None))
+
+        # check if the command for the other module is ignored
+        cmd = isc.config.ccsession.create_command("just_some_command", { 'foo': 'a' })
+        fake_session.group_sendmsg(cmd, 'Spec2')
+        self.assertEqual(len(fake_session.message_queue), 1)
+        mccs.check_command()
+        self.assertEqual(len(fake_session.message_queue), 0)
+        
+
 class fakeUIConn():
     def __init__(self):
         self.get_answers = {}

Modified: branches/trac241/src/lib/python/isc/config/tests/cfgmgr_test.py
==============================================================================
--- branches/trac241/src/lib/python/isc/config/tests/cfgmgr_test.py (original)
+++ branches/trac241/src/lib/python/isc/config/tests/cfgmgr_test.py Mon Aug  9 18:42:00 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/trac241/src/lib/python/isc/config/tests/config_data_test.py
==============================================================================
--- branches/trac241/src/lib/python/isc/config/tests/config_data_test.py (original)
+++ branches/trac241/src/lib/python/isc/config/tests/config_data_test.py Mon Aug  9 18:42:00 2010
@@ -267,12 +267,16 @@
         self.assertEqual({}, self.mcd._current_config)
         self.assertEqual({}, self.mcd._local_changes)
 
-    def test_set_specification(self):
+    def test_set_remove_specification(self):
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
-        self.mcd.set_specification(module_spec)
+        self.assertFalse(self.mcd.have_specification(module_spec.get_module_name()))
+        self.mcd.set_specification(module_spec)
+        self.assertTrue(self.mcd.have_specification(module_spec.get_module_name()))
         self.assert_(module_spec.get_module_name() in self.mcd._specifications)
         self.assertEquals(module_spec, self.mcd._specifications[module_spec.get_module_name()])
         self.assertRaises(ConfigDataError, self.mcd.set_specification, "asdf")
+        self.mcd.remove_specification(module_spec.get_module_name())
+        self.assertFalse(self.mcd.have_specification(module_spec.get_module_name()))
 
     def test_get_module_spec(self):
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec1.spec")

Modified: branches/trac241/src/lib/python/isc/config/tests/module_spec_test.py
==============================================================================
--- branches/trac241/src/lib/python/isc/config/tests/module_spec_test.py (original)
+++ branches/trac241/src/lib/python/isc/config/tests/module_spec_test.py Mon Aug  9 18:42:00 2010
@@ -54,6 +54,13 @@
 
     def test_open_bad_file_obj(self):
         self.assertRaises(ModuleSpecError, isc.config.module_spec_from_file, 1)
+        # contains single quotes which json parser does not accept
+        
+        self.assertRaises(ModuleSpecError, isc.config.module_spec_from_file, self.spec_file("spec28.spec"), False)
+        my_spec_file = open(self.spec_file("spec28.spec"))
+        self.assertRaises(ModuleSpecError, isc.config.module_spec_from_file, my_spec_file, False)
+
+        self.assertRaises(ModuleSpecError, isc.config.module_spec_from_file, self.spec_file("does_not_exist"), False)
 
     def test_bad_specfiles(self):
         self.assertRaises(ModuleSpecError, self.read_spec_file, "spec3.spec")

Modified: branches/trac241/src/lib/python/isc/config/tests/unittest_fakesession.py
==============================================================================
--- branches/trac241/src/lib/python/isc/config/tests/unittest_fakesession.py (original)
+++ branches/trac241/src/lib/python/isc/config/tests/unittest_fakesession.py Mon Aug  9 18:42:00 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/trac241/src/lib/python/isc/datasrc/master.py
==============================================================================
--- branches/trac241/src/lib/python/isc/datasrc/master.py (original)
+++ branches/trac241/src/lib/python/isc/datasrc/master.py Mon Aug  9 18:42:00 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/trac241/src/lib/xfr/Makefile.am
==============================================================================
--- branches/trac241/src/lib/xfr/Makefile.am (original)
+++ branches/trac241/src/lib/xfr/Makefile.am Mon Aug  9 18:42:00 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/trac241/src/lib/xfr/fd_share.cc
==============================================================================
--- branches/trac241/src/lib/xfr/fd_share.cc (original)
+++ branches/trac241/src/lib/xfr/fd_share.cc Mon Aug  9 18:42:00 2010
@@ -20,7 +20,8 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
-#include "fd_share.h"
+#include <stdlib.h>             // for malloc and free
+#include <xfr/fd_share.h>
 
 namespace isc {
 namespace xfr {

Modified: branches/trac241/src/lib/xfr/fd_share.h
==============================================================================
--- branches/trac241/src/lib/xfr/fd_share.h (original)
+++ branches/trac241/src/lib/xfr/fd_share.h Mon Aug  9 18:42:00 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/trac241/src/lib/xfr/python_xfr.cc
==============================================================================
--- branches/trac241/src/lib/xfr/python_xfr.cc (original)
+++ branches/trac241/src/lib/xfr/python_xfr.cc Mon Aug  9 18:42:00 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
@@ -23,7 +23,7 @@
 #include <boost/python/copy_const_reference.hpp>
 #include <boost/shared_ptr.hpp>
 
-#include "fd_share.h"
+#include <xfr/fd_share.h>
 
 using namespace isc::xfr;
 using namespace boost::python;

Modified: branches/trac241/src/lib/xfr/xfrout_client.cc
==============================================================================
--- branches/trac241/src/lib/xfr/xfrout_client.cc (original)
+++ branches/trac241/src/lib/xfr/xfrout_client.cc Mon Aug  9 18:42:00 2010
@@ -18,10 +18,12 @@
 #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"
-#include "xfrout_client.h"
+#include <xfr/fd_share.h>
+#include <xfr/xfrout_client.h>
 
 using namespace std;
 using asio::local::stream_protocol;
@@ -52,24 +54,34 @@
 
 void
 XfroutClient::connect() {
-    impl_->socket_.connect(stream_protocol::endpoint(impl_->file_path_));
+    asio::error_code err;
+    impl_->socket_.connect(stream_protocol::endpoint(impl_->file_path_), err);
+    if (err) {
+        isc_throw(XfroutError, "socket connect failed: " << err.message());
+    }
 }
 
 void
 XfroutClient::disconnect() {
-    impl_->socket_.close();
+    asio::error_code err;
+    impl_->socket_.close(err);
+    if (err) {
+        isc_throw(XfroutError, "close socket failed: " << err.message());
+    }
 }
 
 int 
-XfroutClient::sendXfroutRequestInfo(const int tcp_sock, uint8_t* msg_data,
+XfroutClient::sendXfroutRequestInfo(const int tcp_sock,
+                                    const void* const msg_data,
                                     const uint16_t msg_len)
 {
     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.
+    // XXX: this shouldn't be blocking send, even though it's unlikely to
+    // block.
     const uint8_t lenbuf[2] = { msg_len >> 8, msg_len & 0xff };
     if (send(impl_->socket_.native(), lenbuf, sizeof(lenbuf), 0) !=
         sizeof(lenbuf)) {

Modified: branches/trac241/src/lib/xfr/xfrout_client.h
==============================================================================
--- branches/trac241/src/lib/xfr/xfrout_client.h (original)
+++ branches/trac241/src/lib/xfr/xfrout_client.h Mon Aug  9 18:42:00 2010
@@ -34,7 +34,39 @@
         isc::Exception(file, line, what) {}
 };
 
-class XfroutClient {
+/// \brief The AbstractXfroutClient class is an abstract base class that
+/// defines the interfaces of XfroutClient.
+///
+/// The intended primary usage of abstraction is to allow tests for the
+/// user class of XfroutClient without requiring actual communication.
+class AbstractXfroutClient {
+    ///
+    /// \name Constructors, Assignment Operator and Destructor.
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private to make it explicit that this is a
+    /// pure base class.
+    //@{
+private:
+    AbstractXfroutClient(const AbstractXfroutClient& source);
+    AbstractXfroutClient& operator=(const AbstractXfroutClient& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class should
+    /// never be instantiated (except as part of a derived class).
+    AbstractXfroutClient() {}
+public:
+    /// \brief The destructor.
+    virtual ~AbstractXfroutClient() {}
+    //@}
+    virtual void connect() = 0;
+    virtual void disconnect() = 0;
+    virtual int sendXfroutRequestInfo(int tcp_sock, const void* msg_data,
+                                      uint16_t msg_len) = 0;
+};
+
+class XfroutClient : public AbstractXfroutClient {
 public:
     XfroutClient(const std::string& file);
     ~XfroutClient();
@@ -43,10 +75,10 @@
     XfroutClient(const XfroutClient& source);
     XfroutClient& operator=(const XfroutClient& source);
 public:
-    void connect();
-    void disconnect();
-    int sendXfroutRequestInfo(int tcp_sock, uint8_t* msg_data,
-                              uint16_t msg_len);
+    virtual void connect();
+    virtual void disconnect();
+    virtual int sendXfroutRequestInfo(int tcp_sock, const void* msg_data,
+                                      uint16_t msg_len);
 private:
     XfroutClientImpl* impl_;
 };

Modified: branches/trac241/tools/import_boost.sh
==============================================================================
--- branches/trac241/tools/import_boost.sh (original)
+++ branches/trac241/tools/import_boost.sh Mon Aug  9 18:42:00 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