Browse Source

Merge branch 'master' into trac521

Conflicts:
	ChangeLog
	src/bin/bind10/bind10.py.in
	src/bin/bind10/bob.spec
	src/bin/bind10/tests/bind10_test.py.in
Naoki Kambe 14 years ago
parent
commit
ba87650574
100 changed files with 3158 additions and 1188 deletions
  1. 85 1
      ChangeLog
  2. 2 2
      INSTALL
  3. 3 3
      README
  4. 3 3
      configure.ac
  5. 36 40
      doc/guide/bind10-guide.html
  6. 38 16
      doc/guide/bind10-guide.xml
  7. 1 1
      ext/asio/asio/detail/epoll_reactor.hpp
  8. 1 1
      ext/asio/asio/detail/kqueue_reactor.hpp
  9. 1 1
      ext/asio/asio/detail/null_thread.hpp
  10. 4 4
      src/bin/auth/main.cc
  11. 2 0
      src/bin/auth/query.cc
  12. 10 2
      src/bin/auth/tests/query_unittest.cc
  13. 9 1
      src/bin/bind10/bind10.8
  14. 110 32
      src/bin/bind10/bind10.py.in
  15. 30 0
      src/bin/bind10/bind10.xml
  16. 10 0
      src/bin/bind10/bob.spec
  17. 2 1
      src/bin/bind10/tests/Makefile.am
  18. 199 12
      src/bin/bind10/tests/bind10_test.py.in
  19. 13 5
      src/bin/bindctl/bindcmd.py
  20. 10 5
      src/bin/bindctl/bindctl.1
  21. 87 21
      src/bin/bindctl/tests/bindctl_test.py
  22. 17 1
      src/bin/cfgmgr/b10-cfgmgr.8
  23. 18 1
      src/bin/cfgmgr/b10-cfgmgr.py.in
  24. 28 20
      src/bin/cfgmgr/b10-cfgmgr.xml
  25. 65 1
      src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
  26. 0 1
      src/bin/cmdctl/cmdctl.py.in
  27. 2 2
      src/bin/msgq/tests/msgq_test.py
  28. 1 0
      src/bin/resolver/Makefile.am
  29. 51 4
      src/bin/resolver/main.cc
  30. 39 5
      src/bin/resolver/resolver.cc
  31. 32 0
      src/bin/resolver/resolver.h
  32. 2 2
      src/bin/resolver/response_scrubber.h
  33. 1 0
      src/bin/resolver/tests/Makefile.am
  34. 1 0
      src/bin/resolver/tests/resolver_config_unittest.cc
  35. 1 1
      src/bin/tests/process_rename_test.py.in
  36. 0 1
      src/bin/xfrin/xfrin.py.in
  37. 0 1
      src/bin/xfrout/xfrout.py.in
  38. 0 1
      src/bin/zonemgr/zonemgr.py.in
  39. 2 2
      src/lib/Makefile.am
  40. 3 11
      src/lib/asiolink/Makefile.am
  41. 0 1
      src/lib/asiolink/asiolink.h
  42. 3 1
      src/lib/asiolink/dns_lookup.h
  43. 2 2
      src/lib/asiolink/dns_service.h
  44. 1 1
      src/lib/asiolink/io_address.h
  45. 13 0
      src/lib/asiolink/io_endpoint.cc
  46. 3 0
      src/lib/asiolink/io_endpoint.h
  47. 89 64
      src/lib/asiolink/io_fetch.cc
  48. 0 2
      src/lib/asiolink/io_socket.cc
  49. 0 593
      src/lib/asiolink/recursive_query.cc
  50. 23 13
      src/lib/asiolink/tcp_server.cc
  51. 0 3
      src/lib/asiolink/tcp_server.h
  52. 2 2
      src/lib/asiolink/tcp_socket.h
  53. 2 6
      src/lib/asiolink/tests/Makefile.am
  54. 501 0
      src/lib/asiolink/tests/dns_server_unittest.cc
  55. 56 0
      src/lib/asiolink/tests/io_endpoint_unittest.cc
  56. 155 40
      src/lib/asiolink/tests/io_fetch_unittest.cc
  57. 13 14
      src/lib/asiolink/udp_server.cc
  58. 1 0
      src/lib/cache/Makefile.am
  59. 4 0
      src/lib/cache/TODO
  60. 22 7
      src/lib/cache/message_cache.cc
  61. 12 6
      src/lib/cache/message_cache.h
  62. 80 9
      src/lib/cache/message_entry.cc
  63. 42 21
      src/lib/cache/message_entry.h
  64. 80 0
      src/lib/cache/message_utility.cc
  65. 66 0
      src/lib/cache/message_utility.h
  66. 18 9
      src/lib/cache/resolver_cache.cc
  67. 16 7
      src/lib/cache/resolver_cache.h
  68. 4 2
      src/lib/cache/rrset_cache.h
  69. 12 1
      src/lib/cache/tests/Makefile.am
  70. 9 5
      src/lib/cache/tests/message_cache_unittest.cc
  71. 46 18
      src/lib/cache/tests/message_entry_unittest.cc
  72. 242 0
      src/lib/cache/tests/negative_cache_unittest.cc
  73. 1 1
      src/lib/cache/tests/resolver_cache_unittest.cc
  74. 56 0
      src/lib/cache/tests/testdata/message_cname_referral.wire
  75. 57 0
      src/lib/cache/tests/testdata/message_example_com_soa.wire
  76. 31 0
      src/lib/cache/tests/testdata/message_large_ttl.wire
  77. 32 0
      src/lib/cache/tests/testdata/message_nodata_with_soa.wire
  78. 36 0
      src/lib/cache/tests/testdata/message_nxdomain_cname.wire
  79. 25 0
      src/lib/cache/tests/testdata/message_nxdomain_large_ttl.wire
  80. 26 0
      src/lib/cache/tests/testdata/message_nxdomain_no_soa.wire
  81. 55 0
      src/lib/cache/tests/testdata/message_nxdomain_with_soa.wire
  82. 36 0
      src/lib/cache/tests/testdata/message_referral.wire
  83. 7 5
      src/lib/cc/data.h
  84. 1 1
      src/lib/cc/session.h
  85. 11 8
      src/lib/config/module_spec.cc
  86. 4 0
      src/lib/config/module_spec.h
  87. 4 0
      src/lib/config/tests/module_spec_unittests.cc
  88. 1 0
      src/lib/config/tests/testdata/Makefile.am
  89. 11 0
      src/lib/config/tests/testdata/data22_10.data
  90. 55 14
      src/lib/datasrc/data_source.cc
  91. 1 1
      src/lib/datasrc/memory_datasrc.h
  92. 0 1
      src/lib/datasrc/result.h
  93. 1 0
      src/lib/datasrc/tests/Makefile.am
  94. 224 119
      src/lib/datasrc/tests/datasrc_unittest.cc
  95. 0 1
      src/lib/datasrc/tests/memory_datasrc_unittest.cc
  96. 31 3
      src/lib/datasrc/tests/test_datasrc.cc
  97. 0 1
      src/lib/datasrc/zone.h
  98. 1 1
      src/lib/datasrc/zonetable.h
  99. 15 0
      src/lib/dns/buffer.h
  100. 0 0
      src/lib/dns/edns.h

+ 85 - 1
ChangeLog

@@ -8,6 +8,90 @@
 	resending statistics data via bindctl manually.
 	resending statistics data via bindctl manually.
 	(Trac #521, git tbdtbdtbdtbdtbdtbdtbdtbdtbdtbdtbdtbdtbd)
 	(Trac #521, git tbdtbdtbdtbdtbdtbdtbdtbdtbdtbdtbdtbdtbd)
 
 
+  212.  [bug]		naokikambe
+	Fixed that the ModuleCCSession object may group_unsubscribe in the
+	closed CC session in being deleted.
+	(Trac #698, git 0355bddc92f6df66ef50b920edd6ec3b27920d61)
+
+  211.  [func]		shane
+	Implement "--brittle" option, which causes the server to exit
+        if any of BIND 10's processes dies.
+	(Trac #788, git 88c0d241fe05e5ea91b10f046f307177cc2f5bc5)
+
+  210.  [bug]		jerry
+	src/bin/auth: fixed a bug where type ANY queries don't provide
+	additional glue records for ANSWER section.
+	(Trac #699, git 510924ebc57def8085cc0e5413deda990b2abeee)
+
+  209.  [func]		jelte
+	Resolver now uses the NSAS when looking for a nameserver to
+	query for any specific zone. This also includes keeping track of
+	the RTT for that nameserver.
+	(Trac #495, git 76022a7e9f3ff339f0f9f10049aa85e5784d72c5)
+
+  208.  [bug]*		jelte
+	Resolver now answers REFUSED on queries that are not for class IN.
+	This includes the various CH TXT queries, which will be added
+	later.
+	(git 012f9e78dc611c72ea213f9bd6743172e1a2ca20)
+
+  207.  [func]		jelte
+	Resolver now starts listening on localhost:53 if no configuration
+	is set.
+	(Trac #471, git 1960b5becbba05570b9c7adf5129e64338659f07)
+
+  206.  [func]		shane
+	Add the ability to list the running BIND 10 processes using the
+	command channel. To try this, use "Boss show_processes".
+	(Trac #648, git 451bbb67c2b5d544db2f7deca4315165245d2b3b)
+
+  205.	[bug]		jinmei
+	b10-auth, src/lib/datasrc: fixed a bug where b10-auth could return
+	an empty additional section for delegation even if some glue is
+	crucial when it fails to find some other glue records in its data
+	source.
+	(Trac #646, git 6070acd1c5b2f7a61574eda4035b93b40aab3e2b)
+
+  204.	[bug]		jinmei
+	b10-auth, src/lib/datasrc: class ANY queries were not handled
+	correctly in the generic data source (mainly for sqlite3).  It
+	could crash b10-auth in the worst case, and could result in
+	incorrect responses in some other cases.
+	(Trac #80, git c65637dd41c8d94399bd3e3cee965b694b633339)
+
+  203.  [bug]		zhang likun
+	Fix resolver cache memory leak: when cache is destructed, rrset
+	and message entries in it are not destructed properly.
+	(Trac #643, git aba4c4067da0dc63c97c6356dc3137651755ffce)
+
+  202.  [func]    vorner
+	It is possible to specify a different directory where we look for
+	configuration files (by -p) and different configuration file to
+	use (-c).  Also, it is possible to specify the port on which
+	cmdctl should listen (--cmdctl-port).
+	(Trac #615, git 5514dd78f2d61a222f3069fc94723ca33fb3200b)
+
+  201.  [bug]           jerry
+	src/bin/bindctl: bindctl doesn't show traceback on shutdown.
+	(Trac #588, git 662e99ef050d98e86614c4443326568a0b5be437)
+
+  200.  [bug]           Jelte
+	Fixed a bug where incoming TCP connections were not closed.
+	(Trac #589, git 1d88daaa24e8b1ab27f28be876f40a144241e93b)
+
+  199.  [func]           ocean
+	Cache negative responses (NXDOMAIN/NODATA) from authoritative
+	server for recursive resolver.
+	(Trac #493, git f8fb852bc6aef292555063590c361f01cf29e5ca)
+
+  198.	[bug]		jinmei
+	b10-auth, src/lib/datasrc: fixed a bug where hot spot cache failed
+	to reuse cached SOA for negative responses.  Due to this bug
+	b10-auth returned SERVFAIL when it was expected to return a
+	negative response immediately after a specific SOA query for
+	the zone.
+	(Trac #626, git 721a53160c15e8218f6798309befe940b9597ba0)
+
   197.  [bug]		zhang likun
   197.  [bug]		zhang likun
 	Remove expired message and rrset entries when looking up them
 	Remove expired message and rrset entries when looking up them
 	in cache, touch or remove the rrset entry in cache properly
 	in cache, touch or remove the rrset entry in cache properly
@@ -243,7 +327,7 @@ bind10-devel-20110224 released on February 24, 2011
 	timeout_client for sending an answer back to the client
 	timeout_client for sending an answer back to the client
 	timeout_lookup for stopping the resolving
 	timeout_lookup for stopping the resolving
 	(currently 2 and 3 have the same final effect)
 	(currently 2 and 3 have the same final effect)
-	(Trac 489, git 578ea7f4ba94dc0d8a3d39231dad2be118e125a2)
+	(Trac #489, git 578ea7f4ba94dc0d8a3d39231dad2be118e125a2)
 
 
   159.	[func]		smann
   159.	[func]		smann
 	The resolver now has a configurable set of root servers to start
 	The resolver now has a configurable set of root servers to start

+ 2 - 2
INSTALL

@@ -1,5 +1,5 @@
-To build "configure" file:
-    autoreconf
+If using git (not the tarball), build the "configure" file:
+    autoreconf --install
 
 
 To then build from source:
 To then build from source:
     ./configure
     ./configure

+ 3 - 3
README

@@ -15,9 +15,9 @@ five year plan are described here:
 
 
 This release includes the bind10 master process, b10-msgq message
 This release includes the bind10 master process, b10-msgq message
 bus, b10-auth authoritative DNS server (with SQLite3 and in-memory
 bus, b10-auth authoritative DNS server (with SQLite3 and in-memory
-backends), b10-resolver forwarding DNS server, b10-cmdctl remote
-control daemon, b10-cfgmgr configuration manager, b10-xfrin AXFR
-inbound service, b10-xfrout outgoing AXFR service, b10-zonemgr
+backends), b10-resolver recursive or forwarding DNS server, b10-cmdctl
+remote control daemon, b10-cfgmgr configuration manager, b10-xfrin
+AXFR inbound service, b10-xfrout outgoing AXFR service, b10-zonemgr
 secondary manager, b10-stats statistics collection and reporting
 secondary manager, b10-stats statistics collection and reporting
 daemon, and a new libdns++ library for C++ with a python wrapper.
 daemon, and a new libdns++ library for C++ with a python wrapper.
 
 

+ 3 - 3
configure.ac

@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 # Process this file with autoconf to produce a configure script.
 
 
 AC_PREREQ([2.59])
 AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20110224, bind10-dev@isc.org)
+AC_INIT(bind10-devel, 20110322, bind10-dev@isc.org)
 AC_CONFIG_SRCDIR(README)
 AC_CONFIG_SRCDIR(README)
 AM_INIT_AUTOMAKE
 AM_INIT_AUTOMAKE
 AC_CONFIG_HEADERS([config.h])
 AC_CONFIG_HEADERS([config.h])
@@ -663,6 +663,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/python/isc/net/tests/Makefile
                  src/lib/python/isc/net/tests/Makefile
                  src/lib/python/isc/notify/Makefile
                  src/lib/python/isc/notify/Makefile
                  src/lib/python/isc/notify/tests/Makefile
                  src/lib/python/isc/notify/tests/Makefile
+                 src/lib/python/isc/testutils/Makefile
                  src/lib/config/Makefile
                  src/lib/config/Makefile
                  src/lib/config/tests/Makefile
                  src/lib/config/tests/Makefile
                  src/lib/config/tests/testdata/Makefile
                  src/lib/config/tests/testdata/Makefile
@@ -719,9 +720,8 @@ AC_OUTPUT([doc/version.ent
            src/bin/stats/run_b10-stats_stub.sh
            src/bin/stats/run_b10-stats_stub.sh
            src/bin/stats/tests/stats_test
            src/bin/stats/tests/stats_test
            src/bin/bind10/bind10.py
            src/bin/bind10/bind10.py
-           src/bin/bind10/tests/bind10_test
-           src/bin/bind10/tests/bind10_test.py
            src/bin/bind10/run_bind10.sh
            src/bin/bind10/run_bind10.sh
+           src/bin/bind10/tests/bind10_test.py
            src/bin/bindctl/run_bindctl.sh
            src/bin/bindctl/run_bindctl.sh
            src/bin/bindctl/bindctl_main.py
            src/bin/bindctl/bindctl_main.py
            src/bin/bindctl/tests/bindctl_test
            src/bin/bindctl/tests/bindctl_test

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


+ 38 - 16
doc/guide/bind10-guide.xml

@@ -1199,10 +1199,9 @@ TODO
     <title>Incoming Zone Transfers</title>
     <title>Incoming Zone Transfers</title>
 
 
     <para>
     <para>
-      The <command>b10-xfrin</command> process is started by
-      <command>bind10</command>.
-      It can be manually triggered to request an AXFR zone
-      transfer. When received, it is stored in the BIND 10
+      Incoming zones are transferred using the <command>b10-xfrin</command>
+      process which is started by <command>bind10</command>.
+      When received, the zone is stored in the BIND 10
       data store, and its records can be served by
       data store, and its records can be served by
       <command>b10-auth</command>.
       <command>b10-auth</command>.
       In combination with <command>b10-zonemgr</command> (for
       In combination with <command>b10-zonemgr</command> (for
@@ -1213,8 +1212,22 @@ TODO
     <note><simpara>
     <note><simpara>
      The current development release of BIND 10 only supports
      The current development release of BIND 10 only supports
      AXFR. (IXFR is not supported.) 
      AXFR. (IXFR is not supported.) 
+
+<!-- TODO: sqlite3 data source only? -->
+
     </simpara></note>
     </simpara></note>
 
 
+<!-- TODO:
+
+how to tell bind10 you are a secondary?
+
+when will it first attempt to check for new zone? (using REFRESH?)
+what if zonemgr is not running?
+
+what if a NOTIFY is sent?
+
+-->
+
     <para>
     <para>
        To manually trigger a zone transfer to retrieve a remote zone,
        To manually trigger a zone transfer to retrieve a remote zone,
        you may use the <command>bindctl</command> utility.
        you may use the <command>bindctl</command> utility.
@@ -1223,6 +1236,9 @@ TODO
        <screen>&gt; <userinput>Xfrin retransfer zone_name="<option>foo.example.org</option>" master=<option>192.0.2.99</option></userinput></screen>
        <screen>&gt; <userinput>Xfrin retransfer zone_name="<option>foo.example.org</option>" master=<option>192.0.2.99</option></userinput></screen>
     </para>
     </para>
 
 
+<!-- TODO: can that retransfer be used to identify a new zone? -->
+<!-- TODO: what if doesn't exist at that master IP? -->
+
   </chapter>
   </chapter>
 
 
   <chapter id="xfrout">
   <chapter id="xfrout">
@@ -1329,28 +1345,34 @@ what is XfroutClient xfr_client??
 
 
 <!-- TODO: later the above will have some defaults -->
 <!-- TODO: later the above will have some defaults -->
 
 
-    <para>
-      To enable forwarding, the upstream address and port must be
-      configured to forward queries to, such as:
+    <section>
+      <title>Forwarding</title>
 
 
-      <screen>
+      <para>
+
+        To enable forwarding, the upstream address and port must be
+        configured to forward queries to, such as:
+
+        <screen>
 &gt; <userinput>config set Resolver/forward_addresses [{ "address": "<replaceable>192.168.1.1</replaceable>", "port": 53 }]</userinput>
 &gt; <userinput>config set Resolver/forward_addresses [{ "address": "<replaceable>192.168.1.1</replaceable>", "port": 53 }]</userinput>
 &gt; <userinput>config commit</userinput>
 &gt; <userinput>config commit</userinput>
 </screen>
 </screen>
 
 
-      (Replace <replaceable>192.168.1.1</replaceable> to point to your
-      full resolver.)
-    </para>
+        (Replace <replaceable>192.168.1.1</replaceable> to point to your
+        full resolver.)
+      </para>
 
 
-    <para>
-      Normal iterative name service can be re-enabled by clearing the
-      forwarding address(es); for example:
+      <para>
+        Normal iterative name service can be re-enabled by clearing the
+        forwarding address(es); for example:
 
 
-      <screen>
+        <screen>
 &gt; <userinput>config set Resolver/forward_addresses []</userinput>
 &gt; <userinput>config set Resolver/forward_addresses []</userinput>
 &gt; <userinput>config commit</userinput>
 &gt; <userinput>config commit</userinput>
 </screen>
 </screen>
-    </para>
+      </para>
+
+    </section>
 
 
 <!-- TODO: later try this
 <!-- TODO: later try this
 
 

+ 1 - 1
ext/asio/asio/detail/epoll_reactor.hpp

@@ -207,7 +207,7 @@ public:
   // Cancel all operations associated with the given descriptor. The
   // Cancel all operations associated with the given descriptor. The
   // handlers associated with the descriptor will be invoked with the
   // handlers associated with the descriptor will be invoked with the
   // operation_aborted error.
   // operation_aborted error.
-  void cancel_ops(socket_type descriptor, per_descriptor_data& descriptor_data)
+  void cancel_ops(socket_type, per_descriptor_data& descriptor_data)
   {
   {
     mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
     mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
 
 

+ 1 - 1
ext/asio/asio/detail/kqueue_reactor.hpp

@@ -205,7 +205,7 @@ public:
   // Cancel all operations associated with the given descriptor. The
   // Cancel all operations associated with the given descriptor. The
   // handlers associated with the descriptor will be invoked with the
   // handlers associated with the descriptor will be invoked with the
   // operation_aborted error.
   // operation_aborted error.
-  void cancel_ops(socket_type descriptor, per_descriptor_data& descriptor_data)
+  void cancel_ops(socket_type , per_descriptor_data& descriptor_data)
   {
   {
     mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
     mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);
 
 

+ 1 - 1
ext/asio/asio/detail/null_thread.hpp

@@ -40,7 +40,7 @@ class null_thread
 public:
 public:
   // Constructor.
   // Constructor.
   template <typename Function>
   template <typename Function>
-  null_thread(Function f)
+  null_thread(Function )
   {
   {
     asio::system_error e(
     asio::system_error e(
         asio::error::operation_not_supported, "thread");
         asio::error::operation_not_supported, "thread");

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

@@ -163,10 +163,6 @@ main(int argc, char* argv[]) {
                                              my_command_handler);
                                              my_command_handler);
         cout << "[b10-auth] Configuration channel established." << endl;
         cout << "[b10-auth] Configuration channel established." << endl;
 
 
-        if (uid != NULL) {
-            changeUser(uid);
-        }
-
         xfrin_session = new Session(io_service.get_io_service());
         xfrin_session = new Session(io_service.get_io_service());
         cout << "[b10-auth] Xfrin session channel created." << endl;
         cout << "[b10-auth] Xfrin session channel created." << endl;
         xfrin_session->establish(NULL);
         xfrin_session->establish(NULL);
@@ -190,6 +186,10 @@ main(int argc, char* argv[]) {
         configureAuthServer(*auth_server, config_session->getFullConfig());
         configureAuthServer(*auth_server, config_session->getFullConfig());
         auth_server->updateConfig(ElementPtr());
         auth_server->updateConfig(ElementPtr());
 
 
+        if (uid != NULL) {
+            changeUser(uid);
+        }
+
         cout << "[b10-auth] Server started." << endl;
         cout << "[b10-auth] Server started." << endl;
         io_service.run();
         io_service.run();
 
 

+ 2 - 0
src/bin/auth/query.cc

@@ -210,6 +210,8 @@ Query::process() const {
                     // into answer section.
                     // into answer section.
                     BOOST_FOREACH(RRsetPtr rrset, *target) {
                     BOOST_FOREACH(RRsetPtr rrset, *target) {
                         response_.addRRset(Message::SECTION_ANSWER, rrset);
                         response_.addRRset(Message::SECTION_ANSWER, rrset);
+                        // Handle additional for answer section
+                        getAdditional(*result.zone, *rrset.get());
                     }
                     }
                 } else {
                 } else {
                     response_.addRRset(Message::SECTION_ANSWER,
                     response_.addRRset(Message::SECTION_ANSWER,

+ 10 - 2
src/bin/auth/tests/query_unittest.cc

@@ -341,12 +341,20 @@ TEST_F(QueryTest, apexAnyMatch) {
     // in the answer section from the additional.
     // in the answer section from the additional.
     EXPECT_NO_THROW(Query(memory_datasrc, Name("example.com"),
     EXPECT_NO_THROW(Query(memory_datasrc, Name("example.com"),
                           RRType::ANY(), response).process());
                           RRType::ANY(), response).process());
-    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 4, 0, 0,
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 4, 0, 3,
                   "example.com. 3600 IN SOA . . 0 0 0 0 0\n"
                   "example.com. 3600 IN SOA . . 0 0 0 0 0\n"
                   "example.com. 3600 IN NS glue.delegation.example.com.\n"
                   "example.com. 3600 IN NS glue.delegation.example.com.\n"
                   "example.com. 3600 IN NS noglue.example.com.\n"
                   "example.com. 3600 IN NS noglue.example.com.\n"
                   "example.com. 3600 IN NS example.net.\n",
                   "example.com. 3600 IN NS example.net.\n",
-                  NULL, NULL, mock_zone->getOrigin());
+                  NULL, ns_addrs_txt, mock_zone->getOrigin());
+}
+
+TEST_F(QueryTest, mxANYMatch) {
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("mx.example.com"),
+                          RRType::ANY(), response).process());
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 3, 4,
+                  mx_txt, zone_ns_txt,
+                  (string(ns_addrs_txt) + string(www_a_txt)).c_str());
 }
 }
 
 
 TEST_F(QueryTest, glueANYMatch) {
 TEST_F(QueryTest, glueANYMatch) {

+ 9 - 1
src/bin/bind10/bind10.8

@@ -22,7 +22,7 @@
 bind10 \- BIND 10 boss process
 bind10 \- BIND 10 boss process
 .SH "SYNOPSIS"
 .SH "SYNOPSIS"
 .HP \w'\fBbind10\fR\ 'u
 .HP \w'\fBbind10\fR\ 'u
-\fBbind10\fR [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-verbose\fR]
+\fBbind10\fR [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-brittle\fR] [\fB\-\-verbose\fR]
 .SH "DESCRIPTION"
 .SH "DESCRIPTION"
 .PP
 .PP
 The
 The
@@ -66,6 +66,14 @@ or
 \fBbind10\fR\&.
 \fBbind10\fR\&.
 .RE
 .RE
 .PP
 .PP
+\fB\-\-brittle\fR
+.RS 4
+Shutdown if any of the child processes of 
+\fBbind10\fR
+exit\&. This is intended to help developers debug the server, and should
+not be used in production.
+.RE
+.PP
 \fB\-v\fR, \fB\-\-verbose\fR
 \fB\-v\fR, \fB\-\-verbose\fR
 .RS 4
 .RS 4
 Display more about what is going on for
 Display more about what is going on for

+ 110 - 32
src/bin/bind10/bind10.py.in

@@ -139,7 +139,8 @@ class ProcessInfo:
         self.restart_schedule = RestartSchedule()
         self.restart_schedule = RestartSchedule()
         self.uid = uid
         self.uid = uid
         self.username = username
         self.username = username
-        self._spawn()
+        self.process = None
+        self.pid = None
 
 
     def _preexec_work(self):
     def _preexec_work(self):
         """Function used before running a program that needs to run as a
         """Function used before running a program that needs to run as a
@@ -186,6 +187,11 @@ class ProcessInfo:
         self.pid = self.process.pid
         self.pid = self.process.pid
         self.restart_schedule.set_run_start_time()
         self.restart_schedule.set_run_start_time()
 
 
+    # spawn() and respawn() are the same for now, but in the future they
+    # may have different functionality
+    def spawn(self):
+        self._spawn()
+
     def respawn(self):
     def respawn(self):
         self._spawn()
         self._spawn()
 
 
@@ -194,14 +200,21 @@ class CChannelConnectError(Exception): pass
 class BoB:
 class BoB:
     """Boss of BIND class."""
     """Boss of BIND class."""
     
     
-    def __init__(self, msgq_socket_file=None, nocache=False, verbose=False,
-    setuid=None, username=None):
+    def __init__(self, msgq_socket_file=None, data_path=None,
+    config_filename=None, nocache=False, verbose=False, setuid=None,
+    username=None, cmdctl_port=None, brittle=False):
         """
         """
             Initialize the Boss of BIND. This is a singleton (only one can run).
             Initialize the Boss of BIND. This is a singleton (only one can run).
         
         
             The msgq_socket_file specifies the UNIX domain socket file that the
             The msgq_socket_file specifies the UNIX domain socket file that the
             msgq process listens on.  If verbose is True, then the boss reports
             msgq process listens on.  If verbose is True, then the boss reports
             what it is doing.
             what it is doing.
+
+            Data path and config filename are passed trough to config manager
+            (if provided) and specify the config file to be used.
+
+            The cmdctl_port is passed to cmdctl and specify on which port it
+            should listen.
         """
         """
         self.cc_session = None
         self.cc_session = None
         self.ccs = None
         self.ccs = None
@@ -219,6 +232,10 @@ class BoB:
         self.uid = setuid
         self.uid = setuid
         self.username = username
         self.username = username
         self.verbose = verbose
         self.verbose = verbose
+        self.data_path = data_path
+        self.config_filename = config_filename
+        self.cmdctl_port = cmdctl_port
+        self.brittle = brittle
 
 
     def config_handler(self, new_config):
     def config_handler(self, new_config):
         # If this is initial update, don't do anything now, leave it to startup
         # If this is initial update, don't do anything now, leave it to startup
@@ -270,6 +287,14 @@ class BoB:
         answer = isc.config.ccsession.create_answer(0)
         answer = isc.config.ccsession.create_answer(0)
         return answer
         return answer
 
 
+    def get_processes(self):
+        pids = list(self.processes.keys())
+        pids.sort()
+        process_list = [ ]
+        for pid in pids:
+            process_list.append([pid, self.processes[pid].name])
+        return process_list
+
     def command_handler(self, command, args):
     def command_handler(self, command, args):
         if self.verbose:
         if self.verbose:
             sys.stdout.write("[bind10] Boss got command: " + str(command) + "\n")
             sys.stdout.write("[bind10] Boss got command: " + str(command) + "\n")
@@ -289,8 +314,13 @@ class BoB:
                 seq = self.cc_session.group_sendmsg(cmd, 'Stats')
                 seq = self.cc_session.group_sendmsg(cmd, 'Stats')
                 self.cc_session.group_recvmsg(True, seq)
                 self.cc_session.group_recvmsg(True, seq)
                 answer = isc.config.ccsession.create_answer(0)
                 answer = isc.config.ccsession.create_answer(0)
+            elif command == "ping":
+                answer = isc.config.ccsession.create_answer(0, "pong")
+            elif command == "show_processes":
+                answer = isc.config.ccsession. \
+                    create_answer(0, self.get_processes())
             else:
             else:
-                answer = isc.config.ccsession.create_answer(1, 
+                answer = isc.config.ccsession.create_answer(1,
                                                             "Unknown command")
                                                             "Unknown command")
         return answer
         return answer
 
 
@@ -378,6 +408,7 @@ class BoB:
         c_channel = ProcessInfo("b10-msgq", ["b10-msgq"], c_channel_env,
         c_channel = ProcessInfo("b10-msgq", ["b10-msgq"], c_channel_env,
                                 True, not self.verbose, uid=self.uid,
                                 True, not self.verbose, uid=self.uid,
                                 username=self.username)
                                 username=self.username)
+        c_channel.spawn()
         self.processes[c_channel.pid] = c_channel
         self.processes[c_channel.pid] = c_channel
         self.log_started(c_channel.pid)
         self.log_started(c_channel.pid)
 
 
@@ -399,9 +430,15 @@ class BoB:
             Starts the configuration manager process
             Starts the configuration manager process
         """
         """
         self.log_starting("b10-cfgmgr")
         self.log_starting("b10-cfgmgr")
-        bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
+        args = ["b10-cfgmgr"]
+        if self.data_path is not None:
+            args.append("--data-path=" + self.data_path)
+        if self.config_filename is not None:
+            args.append("--config-filename=" + self.config_filename)
+        bind_cfgd = ProcessInfo("b10-cfgmgr", args,
                                 c_channel_env, uid=self.uid,
                                 c_channel_env, uid=self.uid,
                                 username=self.username)
                                 username=self.username)
+        bind_cfgd.spawn()
         self.processes[bind_cfgd.pid] = bind_cfgd
         self.processes[bind_cfgd.pid] = bind_cfgd
         self.log_started(bind_cfgd.pid)
         self.log_started(bind_cfgd.pid)
 
 
@@ -436,6 +473,7 @@ class BoB:
         """
         """
         self.log_starting(name, port, address)
         self.log_starting(name, port, address)
         newproc = ProcessInfo(name, args, c_channel_env)
         newproc = ProcessInfo(name, args, c_channel_env)
+        newproc.spawn()
         self.processes[newproc.pid] = newproc
         self.processes[newproc.pid] = newproc
         self.log_started(newproc.pid)
         self.log_started(newproc.pid)
 
 
@@ -509,8 +547,13 @@ class BoB:
         self.start_simple("b10-stats", c_channel_env)
         self.start_simple("b10-stats", c_channel_env)
 
 
     def start_cmdctl(self, c_channel_env):
     def start_cmdctl(self, c_channel_env):
-        # XXX: we hardcode port 8080
-        self.start_simple("b10-cmdctl", c_channel_env, 8080)
+        """
+            Starts the command control process
+        """
+        args = ["b10-cmdctl"]
+        if self.cmdctl_port is not None:
+            args.append("--port=" + str(self.cmdctl_port))
+        self.start_process("b10-cmdctl", args, c_channel_env, self.cmdctl_port)
 
 
     def start_all_processes(self):
     def start_all_processes(self):
         """
         """
@@ -677,20 +720,22 @@ class BoB:
         if self.verbose:
         if self.verbose:
             sys.stdout.write("[bind10] All processes ended, server done.\n")
             sys.stdout.write("[bind10] All processes ended, server done.\n")
 
 
+    def _get_process_exit_status(self):
+        return os.waitpid(-1, os.WNOHANG)
+
     def reap_children(self):
     def reap_children(self):
         """Check to see if any of our child processes have exited, 
         """Check to see if any of our child processes have exited, 
         and note this for later handling. 
         and note this for later handling. 
         """
         """
         while True:
         while True:
             try:
             try:
-                (pid, exit_status) = os.waitpid(-1, os.WNOHANG)
+                (pid, exit_status) = self._get_process_exit_status()
             except OSError as o:
             except OSError as o:
                 if o.errno == errno.ECHILD: break
                 if o.errno == errno.ECHILD: break
                 # XXX: should be impossible to get any other error here
                 # XXX: should be impossible to get any other error here
                 raise
                 raise
             if pid == 0: break
             if pid == 0: break
             if pid in self.processes:
             if pid in self.processes:
-
                 # One of the processes we know about.  Get information on it.
                 # One of the processes we know about.  Get information on it.
                 proc_info = self.processes.pop(pid)
                 proc_info = self.processes.pop(pid)
                 proc_info.restart_schedule.set_run_stop_time()
                 proc_info.restart_schedule.set_run_stop_time()
@@ -714,6 +759,11 @@ class BoB:
                         sys.stdout.write(
                         sys.stdout.write(
                                  "[bind10] The b10-msgq process died, shutting down.\n")
                                  "[bind10] The b10-msgq process died, shutting down.\n")
                         self.runnable = False
                         self.runnable = False
+
+                # If we're in 'brittle' mode, we want to shutdown after
+                # any process dies.
+                if self.brittle:
+                    self.runnable = False
             else:
             else:
                 sys.stdout.write("[bind10] Unknown child pid %d exited.\n" % pid)
                 sys.stdout.write("[bind10] Unknown child pid %d exited.\n" % pid)
 
 
@@ -794,6 +844,52 @@ def process_rename(option, opt_str, value, parser):
     """Function that renames the process if it is requested by a option."""
     """Function that renames the process if it is requested by a option."""
     isc.util.process.rename(value)
     isc.util.process.rename(value)
 
 
+def parse_args(args=sys.argv[1:], Parser=OptionParser):
+    """
+    Function for parsing command line arguments. Returns the
+    options object from OptionParser.
+    """
+    parser = Parser(version=VERSION)
+    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 authoritative DNS server")
+    parser.add_option("-u", "--user", dest="user", type="string", default=None,
+                      help="Change user after startup (must run as root)")
+    parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
+                      help="display more about what is going on")
+    parser.add_option("--pretty-name", type="string", action="callback",
+                      callback=process_rename,
+                      help="Set the process name (displayed in ps, top, ...)")
+    parser.add_option("-c", "--config-file", action="store",
+                      dest="config_file", default=None,
+                      help="Configuration database filename")
+    parser.add_option("-p", "--data-path", dest="data_path",
+                      help="Directory to search for configuration files",
+                      default=None)
+    parser.add_option("--cmdctl-port", dest="cmdctl_port", type="int",
+                      default=None, help="Port of command control")
+    parser.add_option("--pid-file", dest="pid_file", type="string",
+                      default=None,
+                      help="file to dump the PID of the BIND 10 process")
+    parser.add_option("--brittle", dest="brittle", action="store_true",
+                      help="debugging flag: exit if any component dies")
+
+    (options, args) = parser.parse_args(args)
+
+    if options.cmdctl_port is not None:
+        try:
+            isc.net.parse.port_parse(options.cmdctl_port)
+        except ValueError as e:
+            parser.error(e)
+
+    if args:
+        parser.print_help()
+        sys.exit(1)
+
+    return options
+
 def dump_pid(pid_file):
 def dump_pid(pid_file):
     """
     """
     Dump the PID of the current process to the specified file.  If the given
     Dump the PID of the current process to the specified file.  If the given
@@ -823,33 +919,14 @@ def unlink_pid_file(pid_file):
         if error.errno is not errno.ENOENT:
         if error.errno is not errno.ENOENT:
             raise
             raise
 
 
+
 def main():
 def main():
     global options
     global options
     global boss_of_bind
     global boss_of_bind
     # Enforce line buffering on stdout, even when not a TTY
     # Enforce line buffering on stdout, even when not a TTY
     sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
     sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
 
 
-    # Parse any command-line options.
-    parser = OptionParser(version=VERSION)
-    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 authoritative DNS server")
-    parser.add_option("-u", "--user", dest="user", type="string", default=None,
-                      help="Change user after startup (must run as root)")
-    parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
-                      help="display more about what is going on")
-    parser.add_option("--pretty-name", type="string", action="callback",
-                      callback=process_rename,
-                      help="Set the process name (displayed in ps, top, ...)")
-    parser.add_option("--pid-file", dest="pid_file", type="string",
-                      default=None,
-                      help="file to dump the PID of the BIND 10 process")
-    (options, args) = parser.parse_args()
-    if args:
-        parser.print_help()
-        sys.exit(1)
+    options = parse_args()
 
 
     # Check user ID.
     # Check user ID.
     setuid = None
     setuid = None
@@ -899,8 +976,9 @@ def main():
     signal.signal(signal.SIGPIPE, signal.SIG_IGN)
     signal.signal(signal.SIGPIPE, signal.SIG_IGN)
 
 
     # Go bob!
     # Go bob!
-    boss_of_bind = BoB(options.msgq_socket_file, options.nocache,
-                       options.verbose, setuid, username)
+    boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
+                       options.config_file, options.nocache, options.verbose,
+                       setuid, username, options.cmdctl_port, options.brittle)
     startup_result = boss_of_bind.startup()
     startup_result = boss_of_bind.startup()
     if startup_result:
     if startup_result:
         sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
         sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)

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

@@ -48,6 +48,8 @@
       <arg><option>-n</option></arg>
       <arg><option>-n</option></arg>
       <arg><option>-u <replaceable>user</replaceable></option></arg>
       <arg><option>-u <replaceable>user</replaceable></option></arg>
       <arg><option>-v</option></arg>
       <arg><option>-v</option></arg>
+      <arg><option>-c<replaceable>config-filename</replaceable></option></arg>
+      <arg><option>-p<replaceable>data_path</replaceable></option></arg>
       <arg><option>--msgq-socket-file <replaceable>file</replaceable></option></arg>
       <arg><option>--msgq-socket-file <replaceable>file</replaceable></option></arg>
       <arg><option>--no-cache</option></arg>
       <arg><option>--no-cache</option></arg>
       <arg><option>--user <replaceable>user</replaceable></option></arg>
       <arg><option>--user <replaceable>user</replaceable></option></arg>
@@ -80,6 +82,31 @@
     <para>The arguments are as follows:</para>
     <para>The arguments are as follows:</para>
 
 
     <variablelist>
     <variablelist>
+      <varlistentry>
+        <term>
+          <option>-c</option><replaceable>config-filename</replaceable>,
+          <option>--config-file</option> <replaceable>config-filename</replaceable>
+        </term>
+        <listitem>
+          <para>The configuration filename to use. Can be either absolute or
+          relative to data path. In case it is absolute, value of data path is
+          not considered.</para>
+          <para>Defaults to b10-config.db.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>-p</option><replaceable>data-path</replaceable>,
+          <option>--data-path</option> <replaceable>data-path</replaceable>
+        </term>
+        <listitem>
+          <para>The path where BIND 10 programs look for various data files.
+          Currently only b10-cfgmgr uses it to locate the configuration file,
+          but the usage might be extended for other programs and other types
+          of files.</para>
+        </listitem>
+      </varlistentry>
 
 
       <varlistentry>
       <varlistentry>
         <term><option>-m</option> <replaceable>file</replaceable>,
         <term><option>-m</option> <replaceable>file</replaceable>,
@@ -145,6 +172,9 @@ The default is the basename of ARG 0.
   </refsect1>
   </refsect1>
 
 
 <!--
 <!--
+TODO: configuration section
+-->
+<!--
   <refsect1>
   <refsect1>
     <title>FILES</title>
     <title>FILES</title>
     <para><filename></filename>
     <para><filename></filename>

+ 10 - 0
src/bin/bind10/bob.spec

@@ -26,6 +26,16 @@
         "command_name": "sendstats",
         "command_name": "sendstats",
         "command_description": "Send data to a statistics module at once",
         "command_description": "Send data to a statistics module at once",
         "command_args": []
         "command_args": []
+      },
+      {
+        "command_name": "ping",
+        "command_description": "Ping the boss process",
+        "command_args": []
+      },
+      {
+        "command_name": "show_processes",
+        "command_description": "List the running BIND 10 processes",
+        "command_args": []
       }
       }
     ]
     ]
   }
   }

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

@@ -13,5 +13,6 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	echo Running test: $$pytest ; \
 	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bind10 \
 	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bind10 \
-	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+	BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
+		$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
 	done

+ 199 - 12
src/bin/bind10/tests/bind10_test.py.in

@@ -1,4 +1,19 @@
-from bind10 import ProcessInfo, BoB, dump_pid, unlink_pid_file, _BASETIME
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from bind10 import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file, _BASETIME
 
 
 # XXX: environment tests are currently disabled, due to the preprocessor
 # XXX: environment tests are currently disabled, due to the preprocessor
 #      setup that we have now complicating the environment
 #      setup that we have now complicating the environment
@@ -12,6 +27,8 @@ from isc.net.addr import IPAddr
 import time
 import time
 import isc
 import isc
 
 
+from isc.testutils.parse_args import TestOptParser, OptsError
+
 class TestProcessInfo(unittest.TestCase):
 class TestProcessInfo(unittest.TestCase):
     def setUp(self):
     def setUp(self):
         # redirect stdout to a pipe so we can check that our
         # redirect stdout to a pipe so we can check that our
@@ -32,6 +49,7 @@ class TestProcessInfo(unittest.TestCase):
 
 
     def test_init(self):
     def test_init(self):
         pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
         pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
+        pi.spawn()
         os.dup2(self.old_stdout, sys.stdout.fileno())
         os.dup2(self.old_stdout, sys.stdout.fileno())
         self.assertEqual(pi.name, 'Test Process')
         self.assertEqual(pi.name, 'Test Process')
         self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
         self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
@@ -52,12 +70,14 @@ class TestProcessInfo(unittest.TestCase):
     def test_setting_null_stdout(self):
     def test_setting_null_stdout(self):
         pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ],
         pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ],
                          dev_null_stdout=True)
                          dev_null_stdout=True)
+        pi.spawn()
         os.dup2(self.old_stdout, sys.stdout.fileno())
         os.dup2(self.old_stdout, sys.stdout.fileno())
         self.assertEqual(pi.dev_null_stdout, True)
         self.assertEqual(pi.dev_null_stdout, True)
         self.assertEqual(os.read(self.pipes[0], 100), b"")
         self.assertEqual(os.read(self.pipes[0], 100), b"")
 
 
     def test_respawn(self):
     def test_respawn(self):
         pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
         pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
+        pi.spawn()
         # wait for old process to work...
         # wait for old process to work...
         self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
         self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
         # respawn it
         # respawn it
@@ -129,17 +149,19 @@ class TestBoB(unittest.TestCase):
         self.assertEqual(bob.command_handler("__UNKNOWN__", None),
         self.assertEqual(bob.command_handler("__UNKNOWN__", None),
                          isc.config.ccsession.create_answer(1, "Unknown command"))
                          isc.config.ccsession.create_answer(1, "Unknown command"))
 
 
-# Class for testing the BoB start/stop components routines.
+# Class for testing the BoB without actually starting processes.
+# This is used for testing the start/stop components routines and
+# the BoB commands.
 #
 #
-# Although testing that external processes start is outside the scope
+# Testing that external processes start is outside the scope
 # of the unit test, by overriding the process start methods we can check
 # of the unit test, by overriding the process start methods we can check
 # that the right processes are started depending on the configuration
 # that the right processes are started depending on the configuration
 # options.
 # options.
-class StartStopCheckBob(BoB):
+class MockBob(BoB):
     def __init__(self):
     def __init__(self):
         BoB.__init__(self)
         BoB.__init__(self)
 
 
-# Set flags as to which of the overridden methods has been run.
+        # Set flags as to which of the overridden methods has been run.
         self.msgq = False
         self.msgq = False
         self.cfgmgr = False
         self.cfgmgr = False
         self.ccsession = False
         self.ccsession = False
@@ -151,6 +173,7 @@ class StartStopCheckBob(BoB):
         self.stats = False
         self.stats = False
         self.cmdctl = False
         self.cmdctl = False
         self.c_channel_env = {}
         self.c_channel_env = {}
+        self.processes = { }
 
 
     def read_bind10_config(self):
     def read_bind10_config(self):
         # Configuration options are set directly
         # Configuration options are set directly
@@ -158,65 +181,105 @@ class StartStopCheckBob(BoB):
 
 
     def start_msgq(self, c_channel_env):
     def start_msgq(self, c_channel_env):
         self.msgq = True
         self.msgq = True
+        self.processes[2] = ProcessInfo('b10-msgq', ['/bin/false'])
+        self.processes[2].pid = 2
 
 
     def start_cfgmgr(self, c_channel_env):
     def start_cfgmgr(self, c_channel_env):
         self.cfgmgr = True
         self.cfgmgr = True
+        self.processes[3] = ProcessInfo('b10-cfgmgr', ['/bin/false'])
+        self.processes[3].pid = 3
 
 
     def start_ccsession(self, c_channel_env):
     def start_ccsession(self, c_channel_env):
         self.ccsession = True
         self.ccsession = True
+        self.processes[4] = ProcessInfo('b10-ccsession', ['/bin/false'])
+        self.processes[4].pid = 4
 
 
     def start_auth(self, c_channel_env):
     def start_auth(self, c_channel_env):
         self.auth = True
         self.auth = True
+        self.processes[5] = ProcessInfo('b10-auth', ['/bin/false'])
+        self.processes[5].pid = 5
 
 
     def start_resolver(self, c_channel_env):
     def start_resolver(self, c_channel_env):
         self.resolver = True
         self.resolver = True
+        self.processes[6] = ProcessInfo('b10-resolver', ['/bin/false'])
+        self.processes[6].pid = 6
 
 
     def start_xfrout(self, c_channel_env):
     def start_xfrout(self, c_channel_env):
         self.xfrout = True
         self.xfrout = True
+        self.processes[7] = ProcessInfo('b10-xfrout', ['/bin/false'])
+        self.processes[7].pid = 7
 
 
     def start_xfrin(self, c_channel_env):
     def start_xfrin(self, c_channel_env):
         self.xfrin = True
         self.xfrin = True
+        self.processes[8] = ProcessInfo('b10-xfrin', ['/bin/false'])
+        self.processes[8].pid = 8
 
 
     def start_zonemgr(self, c_channel_env):
     def start_zonemgr(self, c_channel_env):
         self.zonemgr = True
         self.zonemgr = True
+        self.processes[9] = ProcessInfo('b10-zonemgr', ['/bin/false'])
+        self.processes[9].pid = 9
 
 
     def start_stats(self, c_channel_env):
     def start_stats(self, c_channel_env):
         self.stats = True
         self.stats = True
+        self.processes[10] = ProcessInfo('b10-stats', ['/bin/false'])
+        self.processes[10].pid = 10
 
 
     def start_cmdctl(self, c_channel_env):
     def start_cmdctl(self, c_channel_env):
         self.cmdctl = True
         self.cmdctl = True
+        self.processes[11] = ProcessInfo('b10-cmdctl', ['/bin/false'])
+        self.processes[11].pid = 11
 
 
     # We don't really use all of these stop_ methods. But it might turn out
     # We don't really use all of these stop_ methods. But it might turn out
     # someone would add some stop_ method to BoB and we want that one overriden
     # someone would add some stop_ method to BoB and we want that one overriden
     # in case he forgets to update the tests.
     # in case he forgets to update the tests.
     def stop_msgq(self):
     def stop_msgq(self):
+        if self.msgq:
+            del self.processes[2]
         self.msgq = False
         self.msgq = False
 
 
     def stop_cfgmgr(self):
     def stop_cfgmgr(self):
+        if self.cfgmgr:
+            del self.processes[3]
         self.cfgmgr = False
         self.cfgmgr = False
 
 
     def stop_ccsession(self):
     def stop_ccsession(self):
+        if self.ccssession:
+            del self.processes[4]
         self.ccsession = False
         self.ccsession = False
 
 
     def stop_auth(self):
     def stop_auth(self):
+        if self.auth:
+            del self.processes[5]
         self.auth = False
         self.auth = False
 
 
     def stop_resolver(self):
     def stop_resolver(self):
+        if self.resolver:
+            del self.processes[6]
         self.resolver = False
         self.resolver = False
 
 
     def stop_xfrout(self):
     def stop_xfrout(self):
+        if self.xfrout:
+            del self.processes[7]
         self.xfrout = False
         self.xfrout = False
 
 
     def stop_xfrin(self):
     def stop_xfrin(self):
+        if self.xfrin:
+            del self.processes[8]
         self.xfrin = False
         self.xfrin = False
 
 
     def stop_zonemgr(self):
     def stop_zonemgr(self):
+        if self.zonemgr:
+            del self.processes[9]
         self.zonemgr = False
         self.zonemgr = False
 
 
     def stop_stats(self):
     def stop_stats(self):
+        if self.stats:
+            del self.processes[10]
         self.stats = False
         self.stats = False
 
 
     def stop_cmdctl(self):
     def stop_cmdctl(self):
+        if self.cmdctl:
+            del self.processes[11]
         self.cmdctl = False
         self.cmdctl = False
 
 
 class TestStartStopProcessesBob(unittest.TestCase):
 class TestStartStopProcessesBob(unittest.TestCase):
@@ -276,7 +339,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
     # is specified.
     # is specified.
     def test_start_none(self):
     def test_start_none(self):
         # Create BoB and ensure correct initialization
         # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
+        bob = MockBob()
         self.check_preconditions(bob)
         self.check_preconditions(bob)
 
 
         # Start processes and check what was started
         # Start processes and check what was started
@@ -289,7 +352,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
     # Checks the processes started when starting only the auth process
     # Checks the processes started when starting only the auth process
     def test_start_auth(self):
     def test_start_auth(self):
         # Create BoB and ensure correct initialization
         # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
+        bob = MockBob()
         self.check_preconditions(bob)
         self.check_preconditions(bob)
 
 
         # Start processes and check what was started
         # Start processes and check what was started
@@ -303,7 +366,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
     # Checks the processes started when starting only the resolver process
     # Checks the processes started when starting only the resolver process
     def test_start_resolver(self):
     def test_start_resolver(self):
         # Create BoB and ensure correct initialization
         # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
+        bob = MockBob()
         self.check_preconditions(bob)
         self.check_preconditions(bob)
 
 
         # Start processes and check what was started
         # Start processes and check what was started
@@ -317,7 +380,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
     # Checks the processes started when starting both auth and resolver process
     # Checks the processes started when starting both auth and resolver process
     def test_start_both(self):
     def test_start_both(self):
         # Create BoB and ensure correct initialization
         # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
+        bob = MockBob()
         self.check_preconditions(bob)
         self.check_preconditions(bob)
 
 
         # Start processes and check what was started
         # Start processes and check what was started
@@ -335,7 +398,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
         """
         """
 
 
         # Create BoB and ensure correct initialization
         # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
+        bob = MockBob()
         self.check_preconditions(bob)
         self.check_preconditions(bob)
 
 
         # Start processes (nothing much should be started, as in
         # Start processes (nothing much should be started, as in
@@ -400,7 +463,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
         Tests that a process is started only once.
         Tests that a process is started only once.
         """
         """
         # Create BoB and ensure correct initialization
         # Create BoB and ensure correct initialization
-        bob = StartStopCheckBob()
+        bob = MockBob()
         self.check_preconditions(bob)
         self.check_preconditions(bob)
 
 
         # Start processes (both)
         # Start processes (both)
@@ -426,7 +489,7 @@ class TestStartStopProcessesBob(unittest.TestCase):
         Test that processes are not started by the config handler before
         Test that processes are not started by the config handler before
         startup.
         startup.
         """
         """
-        bob = StartStopCheckBob()
+        bob = MockBob()
         self.check_preconditions(bob)
         self.check_preconditions(bob)
 
 
         bob.start_auth = lambda: self.fail("Started auth again")
         bob.start_auth = lambda: self.fail("Started auth again")
@@ -437,6 +500,101 @@ class TestStartStopProcessesBob(unittest.TestCase):
 
 
         bob.config_handler({'start_auth': True, 'start_resolver': True})
         bob.config_handler({'start_auth': True, 'start_resolver': True})
 
 
+class TestBossCmd(unittest.TestCase):
+    def test_ping(self):
+        """
+        Confirm simple ping command works.
+        """
+        bob = MockBob()
+        answer = bob.command_handler("ping", None)
+        self.assertEqual(answer, {'result': [0, 'pong']})
+
+    def test_show_processes(self):
+        """
+        Confirm getting a list of processes works.
+        """
+        bob = MockBob()
+        answer = bob.command_handler("show_processes", None)
+        self.assertEqual(answer, {'result': [0, []]})
+
+    def test_show_processes_started(self):
+        """
+        Confirm getting a list of processes works.
+        """
+        bob = MockBob()
+        bob.start_all_processes()
+        answer = bob.command_handler("show_processes", None)
+        processes = [[2, 'b10-msgq'],
+                     [3, 'b10-cfgmgr'], 
+                     [4, 'b10-ccsession'],
+                     [5, 'b10-auth'],
+                     [7, 'b10-xfrout'],
+                     [8, 'b10-xfrin'], 
+                     [9, 'b10-zonemgr'],
+                     [10, 'b10-stats'], 
+                     [11, 'b10-cmdctl']]
+        self.assertEqual(answer, {'result': [0, processes]})
+
+class TestParseArgs(unittest.TestCase):
+    """
+    This tests parsing of arguments of the bind10 master process.
+    """
+    #TODO: Write tests for the original parsing, bad options, etc.
+    def test_no_opts(self):
+        """
+        Test correct default values when no options are passed.
+        """
+        options = parse_args([], TestOptParser)
+        self.assertEqual(None, options.data_path)
+        self.assertEqual(None, options.config_file)
+        self.assertEqual(None, options.cmdctl_port)
+
+    def test_data_path(self):
+        """
+        Test it can parse the data path.
+        """
+        self.assertRaises(OptsError, parse_args, ['-p'], TestOptParser)
+        self.assertRaises(OptsError, parse_args, ['--data-path'],
+                          TestOptParser)
+        options = parse_args(['-p', '/data/path'], TestOptParser)
+        self.assertEqual('/data/path', options.data_path)
+        options = parse_args(['--data-path=/data/path'], TestOptParser)
+        self.assertEqual('/data/path', options.data_path)
+
+    def test_config_filename(self):
+        """
+        Test it can parse the config switch.
+        """
+        self.assertRaises(OptsError, parse_args, ['-c'], TestOptParser)
+        self.assertRaises(OptsError, parse_args, ['--config-file'],
+                          TestOptParser)
+        options = parse_args(['-c', 'config-file'], TestOptParser)
+        self.assertEqual('config-file', options.config_file)
+        options = parse_args(['--config-file=config-file'], TestOptParser)
+        self.assertEqual('config-file', options.config_file)
+
+    def test_cmdctl_port(self):
+        """
+        Test it can parse the command control port.
+        """
+        self.assertRaises(OptsError, parse_args, ['--cmdctl-port=abc'],
+                                                TestOptParser)
+        self.assertRaises(OptsError, parse_args, ['--cmdctl-port=100000000'],
+                                                TestOptParser)
+        self.assertRaises(OptsError, parse_args, ['--cmdctl-port'],
+                          TestOptParser)
+        options = parse_args(['--cmdctl-port=1234'], TestOptParser)
+        self.assertEqual(1234, options.cmdctl_port)
+
+    def test_brittle(self):
+        """
+        Test we can use the "brittle" flag.
+        """
+        options = parse_args([], TestOptParser)
+        self.assertFalse(options.brittle)
+        options = parse_args(['--brittle'], TestOptParser)
+        self.assertTrue(options.brittle)
+
 class TestPIDFile(unittest.TestCase):
 class TestPIDFile(unittest.TestCase):
     def setUp(self):
     def setUp(self):
         self.pid_file = '@builddir@' + os.sep + 'bind10.pid'
         self.pid_file = '@builddir@' + os.sep + 'bind10.pid'
@@ -484,5 +642,34 @@ class TestPIDFile(unittest.TestCase):
         self.assertRaises(IOError, dump_pid,
         self.assertRaises(IOError, dump_pid,
                           'nonexistent_dir' + os.sep + 'bind10.pid')
                           'nonexistent_dir' + os.sep + 'bind10.pid')
 
 
+class TestBrittle(unittest.TestCase):
+    def test_brittle_disabled(self):
+        bob = MockBob()
+        bob.start_all_processes()
+        bob.runnable = True
+
+        bob.reap_children()
+        self.assertTrue(bob.runnable)
+
+    def simulated_exit(self):
+        ret_val = self.exit_info
+        self.exit_info = (0, 0)
+        return ret_val
+
+    def test_brittle_enabled(self):
+        bob = MockBob()
+        bob.start_all_processes()
+        bob.runnable = True
+
+        bob.brittle = True
+        self.exit_info = (5, 0)
+        bob._get_process_exit_status = self.simulated_exit
+
+        old_stdout = sys.stdout
+        sys.stdout = open("/dev/null", "w")
+        bob.reap_children()
+        sys.stdout = old_stdout
+        self.assertFalse(bob.runnable)
+
 if __name__ == '__main__':
 if __name__ == '__main__':
     unittest.main()
     unittest.main()

+ 13 - 5
src/bin/bindctl/bindcmd.py

@@ -123,14 +123,19 @@ class BindCmdInterpreter(Cmd):
         '''Parse commands from user and send them to cmdctl. '''
         '''Parse commands from user and send them to cmdctl. '''
         try:
         try:
             if not self.login_to_cmdctl():
             if not self.login_to_cmdctl():
-                return 
+                return
 
 
             self.cmdloop()
             self.cmdloop()
+            print('\nExit from bindctl')
         except FailToLogin as err:
         except FailToLogin as err:
             # error already printed when this was raised, ignoring
             # error already printed when this was raised, ignoring
             pass
             pass
         except KeyboardInterrupt:
         except KeyboardInterrupt:
             print('\nExit from bindctl')
             print('\nExit from bindctl')
+        except socket.error as err:
+            print('Failed to send request, the connection is closed')
+        except http.client.CannotSendRequest:
+            print('Can not send request, the connection is busy')
 
 
     def _get_saved_user_info(self, dir, file_name):
     def _get_saved_user_info(self, dir, file_name):
         ''' Read all the available username and password pairs saved in 
         ''' Read all the available username and password pairs saved in 
@@ -192,8 +197,10 @@ class BindCmdInterpreter(Cmd):
                 raise FailToLogin()
                 raise FailToLogin()
 
 
             if response.status == http.client.OK:
             if response.status == http.client.OK:
-                print(data + ' login as ' + row[0] )
-                return True 
+                # Is interactive?
+                if sys.stdin.isatty():
+                    print(data + ' login as ' + row[0])
+                return True
 
 
         count = 0
         count = 0
         print("[TEMP MESSAGE]: username :root  password :bind10")
         print("[TEMP MESSAGE]: username :root  password :bind10")
@@ -273,8 +280,9 @@ class BindCmdInterpreter(Cmd):
         self._update_commands()
         self._update_commands()
 
 
     def precmd(self, line):
     def precmd(self, line):
-        self._update_all_modules_info()
-        return line 
+        if line != 'EOF':
+            self._update_all_modules_info()
+        return line
 
 
     def postcmd(self, stop, line):
     def postcmd(self, stop, line):
         '''Update the prompt after every command, but only if we
         '''Update the prompt after every command, but only if we

+ 10 - 5
src/bin/bindctl/bindctl.1

@@ -22,7 +22,7 @@
 bindctl \- control and configure BIND 10
 bindctl \- control and configure BIND 10
 .SH "SYNOPSIS"
 .SH "SYNOPSIS"
 .HP \w'\fBbindctl\fR\ 'u
 .HP \w'\fBbindctl\fR\ 'u
-\fBbindctl\fR [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-h\fR] [\fB\-c\ \fR\fB\fIfile\fR\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-\-address\ \fR\fB\fIaddress\fR\fR] [\fB\-\-help\fR] [\fB\-\-certificate\-chain\ \fR\fB\fIfile\fR\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-version\fR]
+\fBbindctl\fR [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-h\fR] [\fB\-c\ \fR\fB\fIfile\fR\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-\-address\ \fR\fB\fIaddress\fR\fR] [\fB\-\-help\fR] [\fB\-\-certificate\-chain\ \fR\fB\fIfile\fR\fR] [\fB\-\-csv\-file\-dir\fR\fB\fIfile\fR\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-version\fR]
 .SH "DESCRIPTION"
 .SH "DESCRIPTION"
 .PP
 .PP
 The
 The
@@ -52,6 +52,11 @@ daemon\&. The default is 127\&.0\&.0\&.1\&.
 The PEM formatted server certificate validation chain file\&.
 The PEM formatted server certificate validation chain file\&.
 .RE
 .RE
 .PP
 .PP
+\fB\-\-csv\-file\-dir\fR\fIfile\fR
+.RS 4
+The directory name in which the user/password CSV file is stored (see AUTHENTICATION)\&. By default this option doesn\'t have any value, in which case the "\&.bind10" directory under the user\'s home directory will be used\&.
+.RE
+.PP
 \fB\-h\fR, \fB\-\-help\fR
 \fB\-h\fR, \fB\-\-help\fR
 .RS 4
 .RS 4
 Display command usage\&.
 Display command usage\&.
@@ -85,10 +90,10 @@ Display the version number and exit\&.
 .RE
 .RE
 .SH "AUTHENTICATION"
 .SH "AUTHENTICATION"
 .PP
 .PP
-The tool will authenticate using a username and password\&. On the first successful login, it will save the details to
-~/\&.bind10/default_user\&.csv
-which will be used for later uses of
-\fBbindctl\fR\&.
+The tool will authenticate using a username and password\&. On the first successful login, it will save the details to a comma\-separated\-value (CSV) file which will be used for later uses of
+\fBbindctl\fR\&. The file name is
+default_user\&.csv
+located under the directory specified by the \-\-csv\-file\-dir option\&.
 .SH "USAGE"
 .SH "USAGE"
 .PP
 .PP
 The
 The

+ 87 - 21
src/bin/bindctl/tests/bindctl_test.py

@@ -17,11 +17,16 @@
 import unittest
 import unittest
 import isc.cc.data
 import isc.cc.data
 import os
 import os
+import io
+import sys
+import socket
+import http.client
 import pwd
 import pwd
 import getpass
 import getpass
 from optparse import OptionParser
 from optparse import OptionParser
 from isc.config.config_data import ConfigData, MultiConfigData
 from isc.config.config_data import ConfigData, MultiConfigData
 from isc.config.module_spec import ModuleSpec
 from isc.config.module_spec import ModuleSpec
+from isc.testutils.parse_args import TestOptParser, OptsError
 from bindctl_main import set_bindctl_options
 from bindctl_main import set_bindctl_options
 from bindctl import cmdparse
 from bindctl import cmdparse
 from bindctl import bindcmd
 from bindctl import bindcmd
@@ -275,7 +280,33 @@ class FakeCCSession(MultiConfigData):
                  ]
                  ]
                }
                }
         self.set_specification(ModuleSpec(spec))
         self.set_specification(ModuleSpec(spec))
-    
+
+
+# fake socket
+class FakeSocket():
+    def __init__(self):
+        self.run = True
+
+    def connect(self, to):
+        if not self.run:
+            raise socket.error
+
+    def close(self):
+        self.run = False
+
+    def send(self, data):
+        if not self.run:
+            raise socket.error
+        return len(data)
+
+    def makefile(self, type):
+        return self
+
+    def sendall(self, data):
+        if not self.run:
+            raise socket.error
+        return len(data)
+
 
 
 class TestConfigCommands(unittest.TestCase):
 class TestConfigCommands(unittest.TestCase):
     def setUp(self):
     def setUp(self):
@@ -283,7 +314,47 @@ class TestConfigCommands(unittest.TestCase):
         mod_info = ModuleInfo(name = "foo")
         mod_info = ModuleInfo(name = "foo")
         self.tool.add_module_info(mod_info)
         self.tool.add_module_info(mod_info)
         self.tool.config_data = FakeCCSession()
         self.tool.config_data = FakeCCSession()
-        
+        self.stdout_backup = sys.stdout
+
+    def test_precmd(self):
+        def update_all_modules_info():
+            raise socket.error
+        def precmd(line):
+            self.tool.precmd(line)
+        self.tool._update_all_modules_info = update_all_modules_info
+        # If line is equals to 'EOF', _update_all_modules_info() shouldn't be called
+        precmd('EOF')
+        self.assertRaises(socket.error, precmd, 'continue')
+
+    def test_run(self):
+        def login_to_cmdctl():
+            return True
+        def cmd_loop():
+            self.tool._send_message("/module_spec", None)
+
+        self.tool.login_to_cmdctl = login_to_cmdctl
+        # rewrite cmdloop() to avoid interactive mode
+        self.tool.cmdloop = cmd_loop
+
+        self.tool.conn.sock = FakeSocket()
+        self.tool.conn.sock.close()
+
+        # validate log message for socket.err
+        socket_err_output = io.StringIO()
+        sys.stdout = socket_err_output
+        self.assertRaises(None, self.tool.run())
+        self.assertEqual("Failed to send request, the connection is closed\n",
+                         socket_err_output.getvalue())
+        socket_err_output.close()
+
+        # validate log message for http.client.CannotSendRequest
+        cannot_send_output = io.StringIO()
+        sys.stdout = cannot_send_output
+        self.assertRaises(None, self.tool.run())
+        self.assertEqual("Can not send request, the connection is busy\n",
+                         cannot_send_output.getvalue())
+        cannot_send_output.close()
+
     def test_apply_cfg_command_int(self):
     def test_apply_cfg_command_int(self):
         self.tool.location = '/'
         self.tool.location = '/'
 
 
@@ -332,10 +403,17 @@ class TestConfigCommands(unittest.TestCase):
         # this should raise a TypeError
         # this should raise a TypeError
         cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=\"a\"")
         cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=\"a\"")
         self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
         self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
-        
+
         cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=[1]")
         cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=[1]")
         self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
         self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
 
 
+    def tearDown(self):
+        sys.stdout = self.stdout_backup
+
+class FakeBindCmdInterpreter(bindcmd.BindCmdInterpreter):
+    def __init__(self):
+        pass
+
 class TestBindCmdInterpreter(unittest.TestCase):
 class TestBindCmdInterpreter(unittest.TestCase):
 
 
     def _create_invalid_csv_file(self, csvfilename):
     def _create_invalid_csv_file(self, csvfilename):
@@ -360,35 +438,23 @@ class TestBindCmdInterpreter(unittest.TestCase):
         self.assertEqual(new_csv_dir, custom_cmd.csv_file_dir)
         self.assertEqual(new_csv_dir, custom_cmd.csv_file_dir)
 
 
     def test_get_saved_user_info(self):
     def test_get_saved_user_info(self):
+        old_stdout = sys.stdout
+        sys.stdout = open(os.devnull, 'w')
         cmd = bindcmd.BindCmdInterpreter()
         cmd = bindcmd.BindCmdInterpreter()
         users = cmd._get_saved_user_info('/notexist', 'csv_file.csv')
         users = cmd._get_saved_user_info('/notexist', 'csv_file.csv')
         self.assertEqual([], users)
         self.assertEqual([], users)
-        
+
         csvfilename = 'csv_file.csv'
         csvfilename = 'csv_file.csv'
         self._create_invalid_csv_file(csvfilename)
         self._create_invalid_csv_file(csvfilename)
         users = cmd._get_saved_user_info('./', csvfilename)
         users = cmd._get_saved_user_info('./', csvfilename)
         self.assertEqual([], users)
         self.assertEqual([], users)
         os.remove(csvfilename)
         os.remove(csvfilename)
+        sys.stdout = old_stdout
 
 
 
 
 class TestCommandLineOptions(unittest.TestCase):
 class TestCommandLineOptions(unittest.TestCase):
-    class FakeParserError(Exception):
-        """An exception thrown from FakeOptionParser on parser error.
-        """
-        pass
-
-    class FakeOptionParser(OptionParser):
-        """This fake class emulates the OptionParser class with customized
-        error handling for the convenient of tests.
-        """
-        def __init__(self):
-            OptionParser.__init__(self)
-
-        def error(self, msg):
-            raise TestCommandLineOptions.FakeParserError
-
     def setUp(self):
     def setUp(self):
-        self.parser = self.FakeOptionParser()
+        self.parser = TestOptParser()
         set_bindctl_options(self.parser)
         set_bindctl_options(self.parser)
 
 
     def test_csv_file_dir(self):
     def test_csv_file_dir(self):
@@ -401,7 +467,7 @@ class TestCommandLineOptions(unittest.TestCase):
         self.assertEqual('some_dir', options.csv_file_dir)
         self.assertEqual('some_dir', options.csv_file_dir)
 
 
         # missing option arg; should trigger parser error.
         # missing option arg; should trigger parser error.
-        self.assertRaises(self.FakeParserError, self.parser.parse_args,
+        self.assertRaises(OptsError, self.parser.parse_args,
                           ['--csv-file-dir'])
                           ['--csv-file-dir'])
 
 
 if __name__== "__main__":
 if __name__== "__main__":

+ 17 - 1
src/bin/cfgmgr/b10-cfgmgr.8

@@ -20,6 +20,9 @@
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
 .SH "NAME"
 .SH "NAME"
 b10-cfgmgr \- Configuration manager
 b10-cfgmgr \- Configuration manager
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-cfgmgr\fR\ 'u
+\fBb10\-cfgmgr\fR [\fB\-c\fR\fB\fIconfig\-filename\fR\fR] [\fB\-p\fR\fB\fIdata_path\fR\fR]
 .SH "DESCRIPTION"
 .SH "DESCRIPTION"
 .PP
 .PP
 The
 The
@@ -43,8 +46,21 @@ The daemon may be cleanly stopped by sending the SIGTERM signal to the process\&
 When it exits, it saves its current configuration to
 When it exits, it saves its current configuration to
 /usr/local/var/bind10\-devel/b10\-config\&.db\&.
 /usr/local/var/bind10\-devel/b10\-config\&.db\&.
 
 
+.SH "ARGUMENTS"
 .PP
 .PP
-The daemon has no command line options\&. It ignores any arguments\&.
+The arguments are as follows:
+.PP
+\fB\-c\fR\fIconfig\-filename\fR, \fB\-\-config\-filename\fR \fIconfig\-filename\fR
+.RS 4
+The configuration database filename to use\&. Can be either absolute or relative to data path\&.
+.sp
+Defaults to b10\-config\&.db
+.RE
+.PP
+\fB\-p\fR\fIdata\-path\fR, \fB\-\-data\-path\fR \fIdata\-path\fR
+.RS 4
+The path where BIND 10 looks for files\&. The configuration file is looked for here, if it is relative\&. If it is absolute, the path is ignored\&.
+.RE
 .SH "FILES"
 .SH "FILES"
 .PP
 .PP
 /usr/local/var/bind10\-devel/b10\-config\&.db
 /usr/local/var/bind10\-devel/b10\-config\&.db

+ 18 - 1
src/bin/cfgmgr/b10-cfgmgr.py.in

@@ -22,6 +22,7 @@ from isc.cc import SessionError
 import isc.util.process
 import isc.util.process
 import signal
 import signal
 import os
 import os
+from optparse import OptionParser
 
 
 isc.util.process.rename()
 isc.util.process.rename()
 
 
@@ -41,18 +42,34 @@ if "B10_FROM_SOURCE" in os.environ:
 else:
 else:
     PREFIX = "@prefix@"
     PREFIX = "@prefix@"
     DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX)
     DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX)
+DEFAULT_CONFIG_FILE = "b10-config.db"
 
 
 cm = None
 cm = None
 
 
+def parse_options(args=sys.argv[1:], Parser=OptionParser):
+    parser = Parser()
+    parser.add_option("-p", "--data-path", dest="data_path",
+                      help="Directory to search for configuration files " +
+                      "(default=" + DATA_PATH + ")", default=DATA_PATH)
+    parser.add_option("-c", "--config-filename", dest="config_file",
+                      help="Configuration database filename " +
+                      "(default=" + DEFAULT_CONFIG_FILE + ")",
+                      default=DEFAULT_CONFIG_FILE)
+    (options, args) = parser.parse_args(args)
+    if args:
+        parser.error("No non-option arguments allowed")
+    return options
+
 def signal_handler(signal, frame):
 def signal_handler(signal, frame):
     global cm
     global cm
     if cm:
     if cm:
         cm.running = False
         cm.running = False
 
 
 def main():
 def main():
+    options = parse_options()
     global cm
     global cm
     try:
     try:
-        cm = ConfigManager(DATA_PATH)
+        cm = ConfigManager(options.data_path, options.config_file)
         signal.signal(signal.SIGINT, signal_handler)
         signal.signal(signal.SIGINT, signal_handler)
         signal.signal(signal.SIGTERM, signal_handler)
         signal.signal(signal.SIGTERM, signal_handler)
         cm.read_config()
         cm.read_config()

+ 28 - 20
src/bin/cfgmgr/b10-cfgmgr.xml

@@ -41,16 +41,13 @@
     </copyright>
     </copyright>
   </docinfo>
   </docinfo>
 
 
-<!--
   <refsynopsisdiv>
   <refsynopsisdiv>
     <cmdsynopsis>
     <cmdsynopsis>
-      <command></command>
-      <arg><option></option></arg>
-      <arg choice="opt"></arg>
-      <arg choice="opt"></arg>
+      <command>b10-cfgmgr</command>
+      <arg><option>-c<replaceable>config-filename</replaceable></option></arg>
+      <arg><option>-p<replaceable>data_path</replaceable></option></arg>
     </cmdsynopsis>
     </cmdsynopsis>
   </refsynopsisdiv>
   </refsynopsisdiv>
--->
 
 
   <refsect1>
   <refsect1>
     <title>DESCRIPTION</title>
     <title>DESCRIPTION</title>
@@ -87,30 +84,41 @@
 <!-- TODO: does it periodically save configuration? -->
 <!-- TODO: does it periodically save configuration? -->
     </para>
     </para>
 
 
-    <para>
-      The daemon has no command line options.  It ignores any arguments.
 <!-- TODO: add a verbose or quiet switch so it is not so noisy -->
 <!-- TODO: add a verbose or quiet switch so it is not so noisy -->
-    </para>
   </refsect1>
   </refsect1>
 
 
-<!--
   <refsect1>
   <refsect1>
     <title>ARGUMENTS</title>
     <title>ARGUMENTS</title>
-    <para>
-      <orderedlist numeration="loweralpha">
+
+    <para>The arguments are as follows:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>
+          <option>-c</option><replaceable>config-filename</replaceable>,
+          <option>--config-filename</option> <replaceable>config-filename</replaceable>
+        </term>
         <listitem>
         <listitem>
-          <para>
-          </para>
+          <para>The configuration database filename to use. Can be either
+          absolute or relative to data path.</para>
+          <para>Defaults to b10-config.db</para>
         </listitem>
         </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>-p</option><replaceable>data-path</replaceable>,
+          <option>--data-path</option> <replaceable>data-path</replaceable>
+        </term>
         <listitem>
         <listitem>
-          <para>
-          </para>
+          <para>The path where BIND 10 looks for files. The
+          configuration file is looked for here, if it is relative. If it is
+          absolute, the path is ignored.</para>
         </listitem>
         </listitem>
-      </orderedlist>
-    </para>
-
+      </varlistentry>
+    </variablelist>
   </refsect1>
   </refsect1>
--->
+
   <refsect1>
   <refsect1>
     <title>FILES</title>
     <title>FILES</title>
 <!-- TODO: fix path -->
 <!-- TODO: fix path -->

+ 65 - 1
src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in

@@ -20,9 +20,10 @@
 import unittest
 import unittest
 import os
 import os
 import sys
 import sys
+from isc.testutils.parse_args import OptsError, TestOptParser
 
 
 class MyConfigManager:
 class MyConfigManager:
-    def __init__(self, path):
+    def __init__(self, path, filename):
         self._path = path
         self._path = path
         self.read_config_called = False
         self.read_config_called = False
         self.notify_boss_called = False
         self.notify_boss_called = False
@@ -88,6 +89,69 @@ class TestConfigManagerStartup(unittest.TestCase):
 
 
         sys.modules.pop("b10-cfgmgr")
         sys.modules.pop("b10-cfgmgr")
 
 
+class TestParseArgs(unittest.TestCase):
+    """
+    Test for the parsing of command line arguments. We provide a different
+    array to parse instead.
+    """
+
+    def test_defaults(self):
+        """
+        Test the default values when no options are provided.
+        """
+        # Pass it empty array, not our arguments
+        b = __import__("b10-cfgmgr")
+        parsed = b.parse_options([], TestOptParser)
+        self.assertEqual(b.DATA_PATH, parsed.data_path)
+        self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+
+    def test_wrong_args(self):
+        """
+        Test it fails when we pass invalid option.
+        """
+        b = __import__("b10-cfgmgr")
+        self.assertRaises(OptsError, b.parse_options, ['--wrong-option'],
+                          TestOptParser)
+
+    def test_not_arg(self):
+        """
+        Test it fails when there's an argument that's not option
+        (eg. without -- at the beginning).
+        """
+        b = __import__("b10-cfgmgr")
+        self.assertRaises(OptsError, b.parse_options, ['not-option'],
+                          TestOptParser)
+
+    def test_datapath(self):
+        """
+        Test overwriting the data path.
+        """
+        b = __import__("b10-cfgmgr")
+        parsed = b.parse_options(['--data-path=/path'], TestOptParser)
+        self.assertEqual('/path', parsed.data_path)
+        self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+        parsed = b.parse_options(['-p', '/path'], TestOptParser)
+        self.assertEqual('/path', parsed.data_path)
+        self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+        self.assertRaises(OptsError, b.parse_options, ['-p'], TestOptParser)
+        self.assertRaises(OptsError, b.parse_options, ['--data-path'],
+                          TestOptParser)
+
+    def test_db_filename(self):
+        """
+        Test setting the configuration database file.
+        """
+        b = __import__("b10-cfgmgr")
+        parsed = b.parse_options(['--config-filename=filename'],
+                                 TestOptParser)
+        self.assertEqual(b.DATA_PATH, parsed.data_path)
+        self.assertEqual("filename", parsed.config_file)
+        parsed = b.parse_options(['-c', 'filename'], TestOptParser)
+        self.assertEqual(b.DATA_PATH, parsed.data_path)
+        self.assertEqual("filename", parsed.config_file)
+        self.assertRaises(OptsError, b.parse_options, ['-c'], TestOptParser)
+        self.assertRaises(OptsError, b.parse_options, ['--config-filename'],
+                          TestOptParser)
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':
     unittest.main()
     unittest.main()

+ 0 - 1
src/bin/cmdctl/cmdctl.py.in

@@ -1,7 +1,6 @@
 #!@PYTHON@
 #!@PYTHON@
 
 
 # Copyright (C) 2010  Internet Systems Consortium.
 # Copyright (C) 2010  Internet Systems Consortium.
-# Copyright (C) 2010  CZ NIC
 #
 #
 # Permission to use, copy, modify, and distribute this software for any
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
 # purpose with or without fee is hereby granted, provided that the above

+ 2 - 2
src/bin/msgq/tests/msgq_test.py

@@ -117,7 +117,7 @@ class SendNonblock(unittest.TestCase):
     Tests that the whole thing will not get blocked if someone does not read.
     Tests that the whole thing will not get blocked if someone does not read.
     """
     """
 
 
-    def terminate_check(self, task, timeout = 10):
+    def terminate_check(self, task, timeout=30):
         """
         """
         Runs task in separate process (task is a function) and checks
         Runs task in separate process (task is a function) and checks
         it terminates sooner than timeout.
         it terminates sooner than timeout.
@@ -194,7 +194,7 @@ class SendNonblock(unittest.TestCase):
             length = len(data)
             length = len(data)
             queue_pid = os.fork()
             queue_pid = os.fork()
             if queue_pid == 0:
             if queue_pid == 0:
-                signal.alarm(30)
+                signal.alarm(120)
                 msgq.setup_poller()
                 msgq.setup_poller()
                 msgq.register_socket(queue)
                 msgq.register_socket(queue)
                 msgq.run()
                 msgq.run()

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

@@ -51,6 +51,7 @@ b10_resolver_LDADD += $(top_builddir)/src/lib/log/liblog.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libcache.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libcache.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
 b10_resolver_LDADD += $(top_builddir)/src/bin/auth/change_user.o
 b10_resolver_LDADD += $(top_builddir)/src/bin/auth/change_user.o
 b10_resolver_LDFLAGS = -pthread
 b10_resolver_LDFLAGS = -pthread
 
 

+ 51 - 4
src/bin/resolver/main.cc

@@ -30,6 +30,7 @@
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
 #include <dns/buffer.h>
 #include <dns/buffer.h>
+#include <dns/rcode.h>
 #include <dns/message.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
 #include <dns/messagerenderer.h>
 
 
@@ -45,6 +46,9 @@
 #include <resolver/spec_config.h>
 #include <resolver/spec_config.h>
 #include <resolver/resolver.h>
 #include <resolver/resolver.h>
 
 
+#include <cache/resolver_cache.h>
+#include <nsas/nameserver_address_store.h>
+
 #include <log/dummylog.h>
 #include <log/dummylog.h>
 
 
 using namespace std;
 using namespace std;
@@ -59,7 +63,7 @@ namespace {
 static const string PROGRAM = "Resolver";
 static const string PROGRAM = "Resolver";
 
 
 IOService io_service;
 IOService io_service;
-static Resolver *resolver;
+static boost::shared_ptr<Resolver> resolver;
 
 
 ConstElementPtr
 ConstElementPtr
 my_config_handler(ConstElementPtr new_config) {
 my_config_handler(ConstElementPtr new_config) {
@@ -135,15 +139,59 @@ main(int argc, char* argv[]) {
             specfile = string(RESOLVER_SPECFILE_LOCATION);
             specfile = string(RESOLVER_SPECFILE_LOCATION);
         }
         }
 
 
-        resolver = new Resolver();
+        resolver = boost::shared_ptr<Resolver>(new Resolver());
         dlog("Server created.");
         dlog("Server created.");
 
 
         SimpleCallback* checkin = resolver->getCheckinProvider();
         SimpleCallback* checkin = resolver->getCheckinProvider();
         DNSLookup* lookup = resolver->getDNSLookupProvider();
         DNSLookup* lookup = resolver->getDNSLookupProvider();
         DNSAnswer* answer = resolver->getDNSAnswerProvider();
         DNSAnswer* answer = resolver->getDNSAnswerProvider();
 
 
+        isc::nsas::NameserverAddressStore nsas(resolver);
+        resolver->setNameserverAddressStore(nsas);
+
+        isc::cache::ResolverCache cache;
+        resolver->setCache(cache);
+        
+        // TODO priming query, remove root from direct
+        // Fake a priming query result here (TODO2 how to flag non-expiry?)
+        // propagation to runningquery. And check for forwarder mode?
+        isc::dns::QuestionPtr root_question(new isc::dns::Question(
+                                            isc::dns::Name("."),
+                                            isc::dns::RRClass::IN(),
+                                            isc::dns::RRType::NS()));
+        isc::dns::RRsetPtr root_ns_rrset(new isc::dns::RRset(isc::dns::Name("."), 
+                                         isc::dns::RRClass::IN(),
+                                         isc::dns::RRType::NS(),
+                                         isc::dns::RRTTL(8888)));
+        root_ns_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::NS(),
+                                                             isc::dns::RRClass::IN(),
+                                                             "l.root-servers.net."));
+        isc::dns::RRsetPtr root_a_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"), 
+                                        isc::dns::RRClass::IN(),
+                                        isc::dns::RRType::A(),
+                                        isc::dns::RRTTL(8888)));
+        root_a_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::A(),
+                                                             isc::dns::RRClass::IN(),
+                                                             "199.7.83.42"));
+        isc::dns::RRsetPtr root_aaaa_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"), 
+                                        isc::dns::RRClass::IN(),
+                                        isc::dns::RRType::AAAA(),
+                                        isc::dns::RRTTL(8888)));
+        root_aaaa_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::AAAA(),
+                                                             isc::dns::RRClass::IN(),
+                                                             "2001:500:3::42"));
+        isc::dns::MessagePtr priming_result(new isc::dns::Message(isc::dns::Message::RENDER));
+        priming_result->setRcode(isc::dns::Rcode::NOERROR());
+        priming_result->addQuestion(root_question);
+        priming_result->addRRset(isc::dns::Message::SECTION_ANSWER, root_ns_rrset);
+        priming_result->addRRset(isc::dns::Message::SECTION_ADDITIONAL, root_a_rrset);
+        priming_result->addRRset(isc::dns::Message::SECTION_ADDITIONAL, root_aaaa_rrset);
+        cache.update(*priming_result);
+        cache.update(root_ns_rrset);
+        cache.update(root_a_rrset);
+        cache.update(root_aaaa_rrset);
+        
         DNSService dns_service(io_service, checkin, lookup, answer);
         DNSService dns_service(io_service, checkin, lookup, answer);
-
         resolver->setDNSService(dns_service);
         resolver->setDNSService(dns_service);
         dlog("IOService created.");
         dlog("IOService created.");
 
 
@@ -172,7 +220,6 @@ main(int argc, char* argv[]) {
 
 
     delete config_session;
     delete config_session;
     delete cc_session;
     delete cc_session;
-    delete resolver;
 
 
     return (ret);
     return (ret);
 }
 }

+ 39 - 5
src/bin/resolver/resolver.cc

@@ -41,6 +41,8 @@
 #include <dns/messagerenderer.h>
 #include <dns/messagerenderer.h>
 #include <server_common/portconfig.h>
 #include <server_common/portconfig.h>
 
 
+#include <resolve/recursive_query.h>
+
 #include <log/dummylog.h>
 #include <log/dummylog.h>
 
 
 #include <resolver/resolver.h>
 #include <resolver/resolver.h>
@@ -74,10 +76,15 @@ public:
         queryShutdown();
         queryShutdown();
     }
     }
 
 
-    void querySetup(DNSService& dnss) {
+    void querySetup(DNSService& dnss,
+                    isc::nsas::NameserverAddressStore& nsas,
+                    isc::cache::ResolverCache& cache)
+    {
         assert(!rec_query_); // queryShutdown must be called first
         assert(!rec_query_); // queryShutdown must be called first
         dlog("Query setup");
         dlog("Query setup");
-        rec_query_ = new RecursiveQuery(dnss, upstream_,
+        rec_query_ = new RecursiveQuery(dnss, 
+                                        nsas, cache,
+                                        upstream_,
                                         upstream_root_,
                                         upstream_root_,
                                         query_timeout_,
                                         query_timeout_,
                                         client_timeout_,
                                         client_timeout_,
@@ -129,7 +136,7 @@ public:
             }
             }
         }
         }
     }
     }
-
+    
     void resolve(const isc::dns::QuestionPtr& question,
     void resolve(const isc::dns::QuestionPtr& question,
         const isc::resolve::ResolverInterface::CallbackPtr& callback);
         const isc::resolve::ResolverInterface::CallbackPtr& callback);
 
 
@@ -326,7 +333,8 @@ Resolver::Resolver() :
     impl_(new ResolverImpl()),
     impl_(new ResolverImpl()),
     checkin_(new ConfigCheck(this)),
     checkin_(new ConfigCheck(this)),
     dns_lookup_(new MessageLookup(this)),
     dns_lookup_(new MessageLookup(this)),
-    dns_answer_(new MessageAnswer)
+    dns_answer_(new MessageAnswer),
+    configured_(false)
 {}
 {}
 
 
 Resolver::~Resolver() {
 Resolver::~Resolver() {
@@ -342,6 +350,19 @@ Resolver::setDNSService(asiolink::DNSService& dnss) {
 }
 }
 
 
 void
 void
+Resolver::setNameserverAddressStore(isc::nsas::NameserverAddressStore& nsas)
+{
+    nsas_ = &nsas;
+}
+
+void
+Resolver::setCache(isc::cache::ResolverCache& cache)
+{
+    cache_ = &cache;
+}
+
+
+void
 Resolver::setConfigSession(ModuleCCSession* config_session) {
 Resolver::setConfigSession(ModuleCCSession* config_session) {
     impl_->config_session_ = config_session;
     impl_->config_session_ = config_session;
 }
 }
@@ -436,6 +457,9 @@ Resolver::processMessage(const IOMessage& io_message,
         } else if (qtype == RRType::IXFR()) {
         } else if (qtype == RRType::IXFR()) {
             makeErrorMessage(query_message, answer_message,
             makeErrorMessage(query_message, answer_message,
                              buffer, Rcode::NOTIMP());
                              buffer, Rcode::NOTIMP());
+        } else if (question->getClass() != RRClass::IN()) {
+            makeErrorMessage(query_message, answer_message,
+                             buffer, Rcode::REFUSED());
         } else {
         } else {
             // The RecursiveQuery object will post the "resume" event to the
             // The RecursiveQuery object will post the "resume" event to the
             // DNSServer when an answer arrives, so we don't have to do it now.
             // DNSServer when an answer arrives, so we don't have to do it now.
@@ -528,6 +552,15 @@ Resolver::updateConfig(ConstElementPtr config) {
         if (listenAddressesE) {
         if (listenAddressesE) {
             setListenAddresses(listenAddresses);
             setListenAddresses(listenAddresses);
             need_query_restart = true;
             need_query_restart = true;
+        } else {
+            if (!configured_) {
+                // TODO: ModuleSpec needs getDefault()
+                AddressList initial_addresses;
+                initial_addresses.push_back(AddressPair("127.0.0.1", 53));
+                initial_addresses.push_back(AddressPair("::1", 53));
+                setListenAddresses(initial_addresses);
+                need_query_restart = true;
+            }
         }
         }
         if (forwardAddressesE) {
         if (forwardAddressesE) {
             setForwardAddresses(forwardAddresses);
             setForwardAddresses(forwardAddresses);
@@ -544,8 +577,9 @@ Resolver::updateConfig(ConstElementPtr config) {
 
 
         if (need_query_restart) {
         if (need_query_restart) {
             impl_->queryShutdown();
             impl_->queryShutdown();
-            impl_->querySetup(*dnss_);
+            impl_->querySetup(*dnss_, *nsas_, *cache_);
         }
         }
+        setConfigured();
         return (isc::config::createAnswer());
         return (isc::config::createAnswer());
     } catch (const isc::Exception& error) {
     } catch (const isc::Exception& error) {
         dlog(string("error in config: ") + error.what(),true);
         dlog(string("error in config: ") + error.what(),true);

+ 32 - 0
src/bin/resolver/resolver.h

@@ -24,6 +24,9 @@
 
 
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
 
 
+#include <nsas/nameserver_address_store.h>
+#include <cache/resolver_cache.h>
+
 #include <resolve/resolver_interface.h>
 #include <resolve/resolver_interface.h>
 
 
 class ResolverImpl;
 class ResolverImpl;
@@ -86,10 +89,26 @@ public:
 
 
     /// \brief Assign an ASIO IO Service queue to this Resolver object
     /// \brief Assign an ASIO IO Service queue to this Resolver object
     void setDNSService(asiolink::DNSService& dnss);
     void setDNSService(asiolink::DNSService& dnss);
+    
+    /// \brief Assign a NameserverAddressStore to this Resolver object
+    void setNameserverAddressStore(isc::nsas::NameserverAddressStore &nsas);
+    
+    /// \brief Assign a cache to this Resolver object
+    void setCache(isc::cache::ResolverCache& cache);
 
 
     /// \brief Return this object's ASIO IO Service queue
     /// \brief Return this object's ASIO IO Service queue
     asiolink::DNSService& getDNSService() const { return (*dnss_); }
     asiolink::DNSService& getDNSService() const { return (*dnss_); }
 
 
+    /// \brief Returns this object's NSAS
+    isc::nsas::NameserverAddressStore& getNameserverAddressStore() const {
+        return *nsas_;
+    };
+
+    /// \brief Returns this object's ResolverCache
+    isc::cache::ResolverCache& getResolverCache() const {
+        return *cache_;
+    };
+    
     /// \brief Return pointer to the DNS Lookup callback function
     /// \brief Return pointer to the DNS Lookup callback function
     asiolink::DNSLookup* getDNSLookupProvider() { return (dns_lookup_); }
     asiolink::DNSLookup* getDNSLookupProvider() { return (dns_lookup_); }
 
 
@@ -100,6 +119,13 @@ public:
     asiolink::SimpleCallback* getCheckinProvider() { return (checkin_); }
     asiolink::SimpleCallback* getCheckinProvider() { return (checkin_); }
 
 
     /**
     /**
+     * \brief Tell the Resolver that is has already been configured
+     *        so that it will only set some defaults the first time
+     *        (used by updateConfig() and tests)
+     */
+    void setConfigured() { configured_ = true; };
+
+    /**
      * \brief Specify the list of upstream servers.
      * \brief Specify the list of upstream servers.
      *
      *
      * Specify the list off addresses of upstream servers to forward queries
      * Specify the list off addresses of upstream servers to forward queries
@@ -208,6 +234,12 @@ private:
     asiolink::SimpleCallback* checkin_;
     asiolink::SimpleCallback* checkin_;
     asiolink::DNSLookup* dns_lookup_;
     asiolink::DNSLookup* dns_lookup_;
     asiolink::DNSAnswer* dns_answer_;
     asiolink::DNSAnswer* dns_answer_;
+    isc::nsas::NameserverAddressStore* nsas_;
+    isc::cache::ResolverCache* cache_;
+    // This value is initally false, and will be set to true
+    // when the initial configuration is done (updateConfig
+    // should act a tiny bit different on the very first call)
+    bool configured_;
 };
 };
 
 
 #endif // __RESOLVER_H
 #endif // __RESOLVER_H

+ 2 - 2
src/bin/resolver/response_scrubber.h

@@ -177,7 +177,7 @@
 /// Qu: www.sub.example.com\n
 /// Qu: www.sub.example.com\n
 /// Zo: example.com
 /// Zo: example.com
 ///
 ///
-/// An: <nothing>
+/// An: (nothing)
 ///
 ///
 /// Au(1): sub.example.com NS ns0.sub.example.com\n
 /// Au(1): sub.example.com NS ns0.sub.example.com\n
 /// Au(2): sub.example.com NS ns1.example.net
 /// Au(2): sub.example.com NS ns1.example.net
@@ -312,7 +312,7 @@ public:
     /// QNAME is equal to or in the supplied relationship with the given name.
     /// QNAME is equal to or in the supplied relationship with the given name.
     ///
     ///
     /// \param section Section of the message to be scrubbed.
     /// \param section Section of the message to be scrubbed.
-    /// \param zone Names against which RRsets should be checked.  Note that
+    /// \param names Names against which RRsets should be checked.  Note that
     /// this is a vector of pointers to Name objects; they are assumed to
     /// this is a vector of pointers to Name objects; they are assumed to
     /// independently exist, and the caller retains ownership of them and is
     /// independently exist, and the caller retains ownership of them and is
     /// assumed to destroy them when needed.
     /// assumed to destroy them when needed.

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

@@ -40,6 +40,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
 
 
 # Note the ordering matters: -Wno-... must follow -Wextra (defined in
 # Note the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS
 # B10_CXXFLAGS

+ 1 - 0
src/bin/resolver/tests/resolver_config_unittest.cc

@@ -42,6 +42,7 @@ class ResolverConfig : public ::testing::Test {
             dnss(ios, NULL, NULL, NULL)
             dnss(ios, NULL, NULL, NULL)
         {
         {
             server.setDNSService(dnss);
             server.setDNSService(dnss);
+            server.setConfigured();
         }
         }
         void invalidTest(const string &JSON, const string& name);
         void invalidTest(const string &JSON, const string& name);
 };
 };

+ 1 - 1
src/bin/tests/process_rename_test.py.in

@@ -1,4 +1,4 @@
-# Copyright (C) 2010  CZ NIC
+# Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
 #
 #
 # Permission to use, copy, modify, and distribute this software for any
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
 # purpose with or without fee is hereby granted, provided that the above

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

@@ -1,7 +1,6 @@
 #!@PYTHON@
 #!@PYTHON@
 
 
 # Copyright (C) 2010  Internet Systems Consortium.
 # Copyright (C) 2010  Internet Systems Consortium.
-# Copyright (C) 2010  CZ NIC
 #
 #
 # Permission to use, copy, modify, and distribute this software for any
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
 # purpose with or without fee is hereby granted, provided that the above

+ 0 - 1
src/bin/xfrout/xfrout.py.in

@@ -1,7 +1,6 @@
 #!@PYTHON@
 #!@PYTHON@
 
 
 # Copyright (C) 2010  Internet Systems Consortium.
 # Copyright (C) 2010  Internet Systems Consortium.
-# Copyright (C) 2010  CZ NIC
 #
 #
 # Permission to use, copy, modify, and distribute this software for any
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
 # purpose with or without fee is hereby granted, provided that the above

+ 0 - 1
src/bin/zonemgr/zonemgr.py.in

@@ -1,7 +1,6 @@
 #!@PYTHON@
 #!@PYTHON@
 
 
 # Copyright (C) 2010  Internet Systems Consortium.
 # Copyright (C) 2010  Internet Systems Consortium.
-# Copyright (C) 2010  CZ NIC
 #
 #
 # Permission to use, copy, modify, and distribute this software for any
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
 # purpose with or without fee is hereby granted, provided that the above

+ 2 - 2
src/lib/Makefile.am

@@ -1,2 +1,2 @@
-SUBDIRS = exceptions dns cc config datasrc python xfr bench log \
-          resolve nsas cache asiolink testutils server_common
+SUBDIRS = exceptions dns cc config python xfr bench log asiolink \
+          nsas cache resolve testutils datasrc server_common

+ 3 - 11
src/lib/asiolink/Makefile.am

@@ -27,10 +27,9 @@ libasiolink_la_SOURCES += io_endpoint.cc io_endpoint.h
 libasiolink_la_SOURCES += io_error.h
 libasiolink_la_SOURCES += io_error.h
 libasiolink_la_SOURCES += io_fetch.cc io_fetch.h
 libasiolink_la_SOURCES += io_fetch.cc io_fetch.h
 libasiolink_la_SOURCES += io_message.h
 libasiolink_la_SOURCES += io_message.h
-libasiolink_la_SOURCES += io_service.cc io_service.h
-libasiolink_la_SOURCES += io_socket.cc io_socket.h
 libasiolink_la_SOURCES += qid_gen.cc qid_gen.h
 libasiolink_la_SOURCES += qid_gen.cc qid_gen.h
-libasiolink_la_SOURCES += recursive_query.cc recursive_query.h
+libasiolink_la_SOURCES += io_service.h io_service.cc
+libasiolink_la_SOURCES += io_socket.h io_socket.cc
 libasiolink_la_SOURCES += simple_callback.h
 libasiolink_la_SOURCES += simple_callback.h
 libasiolink_la_SOURCES += tcp_endpoint.h
 libasiolink_la_SOURCES += tcp_endpoint.h
 libasiolink_la_SOURCES += tcp_server.cc tcp_server.h
 libasiolink_la_SOURCES += tcp_server.cc tcp_server.h
@@ -44,16 +43,9 @@ EXTRA_DIST = asiodef.msg
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 # B10_CXXFLAGS)
 libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS)
 libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_GXX
-libasiolink_la_CXXFLAGS += -Wno-unused-parameter
-endif
 if USE_CLANGPP
 if USE_CLANGPP
 # Same for clang++, but we need to turn off -Werror completely.
 # Same for clang++, but we need to turn off -Werror completely.
 libasiolink_la_CXXFLAGS += -Wno-error
 libasiolink_la_CXXFLAGS += -Wno-error
 endif
 endif
 libasiolink_la_CPPFLAGS = $(AM_CPPFLAGS)
 libasiolink_la_CPPFLAGS = $(AM_CPPFLAGS)
-libasiolink_la_LIBADD  = 
-libasiolink_la_LIBADD += $(top_builddir)/src/lib/resolve/libresolve.la
-libasiolink_la_LIBADD += $(top_builddir)/src/lib/cache/libcache.la
-libasiolink_la_LIBADD += $(top_builddir)/src/lib/nsas/libnsas.la
-libasiolink_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
+libasiolink_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la

+ 0 - 1
src/lib/asiolink/asiolink.h

@@ -25,7 +25,6 @@
 #include <asiolink/dns_lookup.h>
 #include <asiolink/dns_lookup.h>
 #include <asiolink/dns_answer.h>
 #include <asiolink/dns_answer.h>
 #include <asiolink/simple_callback.h>
 #include <asiolink/simple_callback.h>
-#include <asiolink/recursive_query.h>
 #include <asiolink/interval_timer.h>
 #include <asiolink/interval_timer.h>
 
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>

+ 3 - 1
src/lib/asiolink/dns_lookup.h

@@ -63,8 +63,10 @@ public:
     ///
     ///
     /// \param io_message The event message to handle
     /// \param io_message The event message to handle
     /// \param message The DNS MessagePtr that needs handling
     /// \param message The DNS MessagePtr that needs handling
+    /// \param answer_message The final answer will be constructed in
+    ///                       this MessagePtr
     /// \param buffer The final answer is put here
     /// \param buffer The final answer is put here
-    /// \param DNSServer DNSServer object to use
+    /// \param server DNSServer object to use
     virtual void operator()(const IOMessage& io_message,
     virtual void operator()(const IOMessage& io_message,
                             isc::dns::MessagePtr message,
                             isc::dns::MessagePtr message,
                             isc::dns::MessagePtr answer_message,
                             isc::dns::MessagePtr answer_message,

+ 2 - 2
src/lib/asiolink/dns_service.h

@@ -66,8 +66,8 @@ public:
     ///
     ///
     /// \param io_service The IOService to work with
     /// \param io_service The IOService to work with
     /// \param port the port to listen on
     /// \param port the port to listen on
-    /// \param ipv4 If true, listen on ipv4 'any'
-    /// \param ipv6 If true, listen on ipv6 'any'
+    /// \param use_ipv4 If true, listen on ipv4 'any'
+    /// \param use_ipv6 If true, listen on ipv6 'any'
     /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
     /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
     /// \param lookup The lookup provider (see \c DNSLookup)
     /// \param lookup The lookup provider (see \c DNSLookup)
     /// \param answer The answer provider (see \c DNSAnswer)
     /// \param answer The answer provider (see \c DNSAnswer)

+ 1 - 1
src/lib/asiolink/io_address.h

@@ -61,7 +61,7 @@ public:
     /// This constructor never throws an exception.
     /// This constructor never throws an exception.
     ///
     ///
     /// \param asio_address The ASIO \c ip::address to be converted.
     /// \param asio_address The ASIO \c ip::address to be converted.
-    IOAddress(const asio::ip::address& asio_adress);
+    IOAddress(const asio::ip::address& asio_address);
     //@}
     //@}
 
 
     /// \brief Convert the address to a string.
     /// \brief Convert the address to a string.

+ 13 - 0
src/lib/asiolink/io_endpoint.cc

@@ -44,4 +44,17 @@ IOEndpoint::create(const int protocol, const IOAddress& address,
               protocol);
               protocol);
 }
 }
 
 
+bool
+IOEndpoint::operator==(const IOEndpoint& other) const {
+    return (getProtocol() == other.getProtocol() &&
+            getPort() == other.getPort() &&
+            getFamily() == other.getFamily() &&
+            getAddress() == other.getAddress());
+}
+
+bool
+IOEndpoint::operator!=(const IOEndpoint& other) const {
+    return (!operator==(other));
+}
+
 }
 }

+ 3 - 0
src/lib/asiolink/io_endpoint.h

@@ -89,6 +89,9 @@ public:
     /// \brief Returns the address family of the endpoint.
     /// \brief Returns the address family of the endpoint.
     virtual short getFamily() const = 0;
     virtual short getFamily() const = 0;
 
 
+    bool operator==(const IOEndpoint& other) const;
+    bool operator!=(const IOEndpoint& other) const;
+
     /// \brief A polymorphic factory of endpoint from address and port.
     /// \brief A polymorphic factory of endpoint from address and port.
     ///
     ///
     /// This method creates a new instance of (a derived class of)
     /// This method creates a new instance of (a derived class of)

+ 89 - 64
src/lib/asiolink/io_fetch.cc

@@ -43,6 +43,9 @@
 #include <asiolink/tcp_socket.h>
 #include <asiolink/tcp_socket.h>
 #include <asiolink/udp_endpoint.h>
 #include <asiolink/udp_endpoint.h>
 #include <asiolink/udp_socket.h>
 #include <asiolink/udp_socket.h>
+#include <asiolink/qid_gen.h>
+
+#include <stdint.h>
 
 
 using namespace asio;
 using namespace asio;
 using namespace isc::dns;
 using namespace isc::dns;
@@ -69,19 +72,20 @@ struct IOFetchData {
     // which is not known until construction of the IOFetch.  Use of a shared
     // which is not known until construction of the IOFetch.  Use of a shared
     // pointer here is merely to ensure deletion when the data object is deleted.
     // pointer here is merely to ensure deletion when the data object is deleted.
     boost::scoped_ptr<IOAsioSocket<IOFetch> > socket;
     boost::scoped_ptr<IOAsioSocket<IOFetch> > socket;
-                                            ///< Socket to use for I/O
-    boost::scoped_ptr<IOEndpoint> remote;   ///< Where the fetch was sent
-    isc::dns::Question          question;   ///< Question to be asked
-    isc::dns::OutputBufferPtr   msgbuf;     ///< Wire buffer for question
-    isc::dns::OutputBufferPtr   received;   ///< Received data put here
-    IOFetch::Callback*          callback;   ///< Called on I/O Completion
-    asio::deadline_timer        timer;      ///< Timer to measure timeouts
-    IOFetch::Protocol           protocol;   ///< Protocol being used
-    size_t                      cumulative; ///< Cumulative received amount
-    size_t                      expected;   ///< Expected amount of data
-    size_t                      offset;     ///< Offset to receive data
-    bool                        stopped;    ///< Have we stopped running?
-    int                         timeout;    ///< Timeout in ms
+                                             ///< Socket to use for I/O
+    boost::scoped_ptr<IOEndpoint> remote_snd;///< Where the fetch is sent
+    boost::scoped_ptr<IOEndpoint> remote_rcv;///< Where the response came from
+    isc::dns::Question          question;    ///< Question to be asked
+    isc::dns::OutputBufferPtr   msgbuf;      ///< Wire buffer for question
+    isc::dns::OutputBufferPtr   received;    ///< Received data put here
+    IOFetch::Callback*          callback;    ///< Called on I/O Completion
+    asio::deadline_timer        timer;       ///< Timer to measure timeouts
+    IOFetch::Protocol           protocol;    ///< Protocol being used
+    size_t                      cumulative;  ///< Cumulative received amount
+    size_t                      expected;    ///< Expected amount of data
+    size_t                      offset;      ///< Offset to receive data
+    bool                        stopped;     ///< Have we stopped running?
+    int                         timeout;     ///< Timeout in ms
 
 
     // In case we need to log an error, the origin of the last asynchronous
     // In case we need to log an error, the origin of the last asynchronous
     // I/O is recorded.  To save time and simplify the code, this is recorded
     // I/O is recorded.  To save time and simplify the code, this is recorded
@@ -91,6 +95,7 @@ struct IOFetchData {
     isc::log::MessageID         origin;     ///< Origin of last asynchronous I/O
     isc::log::MessageID         origin;     ///< Origin of last asynchronous I/O
     uint8_t                     staging[IOFetch::STAGING_LENGTH];
     uint8_t                     staging[IOFetch::STAGING_LENGTH];
                                             ///< Temporary array for received data
                                             ///< Temporary array for received data
+    isc::dns::qid_t             qid;         ///< The QID set in the query
 
 
     /// \brief Constructor
     /// \brief Constructor
     ///
     ///
@@ -121,7 +126,11 @@ struct IOFetchData {
             static_cast<IOAsioSocket<IOFetch>*>(
             static_cast<IOAsioSocket<IOFetch>*>(
                 new TCPSocket<IOFetch>(service))
                 new TCPSocket<IOFetch>(service))
             ),
             ),
-        remote((proto == IOFetch::UDP) ?
+        remote_snd((proto == IOFetch::UDP) ?
+            static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
+            static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
+            ),
+        remote_rcv((proto == IOFetch::UDP) ?
             static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
             static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
             static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
             static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
             ),
             ),
@@ -138,8 +147,21 @@ struct IOFetchData {
         stopped(false),
         stopped(false),
         timeout(wait),
         timeout(wait),
         origin(ASIO_UNKORIGIN),
         origin(ASIO_UNKORIGIN),
-        staging()
+        staging(),
+        qid(QidGenerator::getInstance().generateQid())
     {}
     {}
+
+    // Checks if the response we received was ok;
+    // - data contains the buffer we read, as well as the address
+    // we sent to and the address we received from.
+    // length is provided by the operator() in IOFetch.
+    // Addresses must match, number of octets read must be at least
+    // 2, and the first two octets must match the qid of the message
+    // we sent.
+    bool responseOK() {
+        return (*remote_snd == *remote_rcv && cumulative >= 2 &&
+                readUint16(received->getData()) == qid);
+    }
 };
 };
 
 
 /// IOFetch Constructor - just initialize the private data
 /// IOFetch Constructor - just initialize the private data
@@ -180,7 +202,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
         /// declarations.
         /// declarations.
         {
         {
             Message msg(Message::RENDER);
             Message msg(Message::RENDER);
-            msg.setQid(QidGenerator::getInstance().generateQid());
+            msg.setQid(data_->qid);
             msg.setOpcode(Opcode::QUERY());
             msg.setOpcode(Opcode::QUERY());
             msg.setRcode(Rcode::NOERROR());
             msg.setRcode(Rcode::NOERROR());
             msg.setHeaderFlag(Message::HEADERFLAG_RD);
             msg.setHeaderFlag(Message::HEADERFLAG_RD);
@@ -202,47 +224,50 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
         // is synchronous (i.e. UDP operation) we bypass the yield.
         // is synchronous (i.e. UDP operation) we bypass the yield.
         data_->origin = ASIO_OPENSOCK;
         data_->origin = ASIO_OPENSOCK;
         if (data_->socket->isOpenSynchronous()) {
         if (data_->socket->isOpenSynchronous()) {
-            data_->socket->open(data_->remote.get(), *this);
+            data_->socket->open(data_->remote_snd.get(), *this);
         } else {
         } else {
-            CORO_YIELD data_->socket->open(data_->remote.get(), *this);
+            CORO_YIELD data_->socket->open(data_->remote_snd.get(), *this);
         }
         }
 
 
-        // Begin an asynchronous send, and then yield.  When the send completes,
-        // we will resume immediately after this point.
-        data_->origin = ASIO_SENDSOCK;
-        CORO_YIELD data_->socket->asyncSend(data_->msgbuf->getData(),
-            data_->msgbuf->getLength(), data_->remote.get(), *this);
-
-        // Now receive the response.  Since TCP may not receive the entire
-        // message in one operation, we need to loop until we have received
-        // it. (This can't be done within the asyncReceive() method because
-        // each I/O operation will be done asynchronously and between each one
-        // we need to yield ... and we *really* don't want to set up another
-        // coroutine within that method.)  So after each receive (and yield),
-        // we check if the operation is complete and if not, loop to read again.
-        //
-        // Another concession to TCP is that the amount of is contained in the
-        // first two bytes.  This leads to two problems:
-        //
-        // a) We don't want those bytes in the return buffer.
-        // b) They may not both arrive in the first I/O.
-        //
-        // So... we need to loop until we have at least two bytes, then store
-        // the expected amount of data.  Then we need to loop until we have
-        // received all the data before copying it back to the user's buffer.
-        // And we want to minimise the amount of copying...
-
-        data_->origin = ASIO_RECVSOCK;
-        data_->cumulative = 0;          // No data yet received
-        data_->offset = 0;              // First data into start of buffer
         do {
         do {
-            CORO_YIELD data_->socket->asyncReceive(data_->staging,
-                                                   static_cast<size_t>(STAGING_LENGTH),
-                                                   data_->offset,
-                                                   data_->remote.get(), *this);
-        } while (!data_->socket->processReceivedData(data_->staging, length,
-                                                     data_->cumulative, data_->offset,
-                                                     data_->expected, data_->received));
+            // Begin an asynchronous send, and then yield.  When the send completes,
+            // we will resume immediately after this point.
+            data_->origin = ASIO_SENDSOCK;
+            CORO_YIELD data_->socket->asyncSend(data_->msgbuf->getData(),
+                data_->msgbuf->getLength(), data_->remote_snd.get(), *this);
+    
+            // Now receive the response.  Since TCP may not receive the entire
+            // message in one operation, we need to loop until we have received
+            // it. (This can't be done within the asyncReceive() method because
+            // each I/O operation will be done asynchronously and between each one
+            // we need to yield ... and we *really* don't want to set up another
+            // coroutine within that method.)  So after each receive (and yield),
+            // we check if the operation is complete and if not, loop to read again.
+            //
+            // Another concession to TCP is that the amount of is contained in the
+            // first two bytes.  This leads to two problems:
+            //
+            // a) We don't want those bytes in the return buffer.
+            // b) They may not both arrive in the first I/O.
+            //
+            // So... we need to loop until we have at least two bytes, then store
+            // the expected amount of data.  Then we need to loop until we have
+            // received all the data before copying it back to the user's buffer.
+            // And we want to minimise the amount of copying...
+    
+            data_->origin = ASIO_RECVSOCK;
+            data_->cumulative = 0;          // No data yet received
+            data_->offset = 0;              // First data into start of buffer
+            data_->received->clear();       // Clear the receive buffer
+            do {
+                CORO_YIELD data_->socket->asyncReceive(data_->staging,
+                                                       static_cast<size_t>(STAGING_LENGTH),
+                                                       data_->offset,
+                                                       data_->remote_rcv.get(), *this);
+            } while (!data_->socket->processReceivedData(data_->staging, length,
+                                                         data_->cumulative, data_->offset,
+                                                         data_->expected, data_->received));
+        } while (!data_->responseOK());
 
 
         // Finished with this socket, so close it.  This will not generate an
         // Finished with this socket, so close it.  This will not generate an
         // I/O error, but reset the origin to unknown in case we change this.
         // I/O error, but reset the origin to unknown in case we change this.
@@ -290,16 +315,16 @@ IOFetch::stop(Result result) {
             case TIME_OUT:
             case TIME_OUT:
                 if (logger.isDebugEnabled(1)) {
                 if (logger.isDebugEnabled(1)) {
                     logger.debug(20, ASIO_RECVTMO,
                     logger.debug(20, ASIO_RECVTMO,
-                                 data_->remote->getAddress().toText().c_str(),
-                                 static_cast<int>(data_->remote->getPort()));
+                                 data_->remote_snd->getAddress().toText().c_str(),
+                                 static_cast<int>(data_->remote_snd->getPort()));
                 }
                 }
                 break;
                 break;
 
 
             case SUCCESS:
             case SUCCESS:
                 if (logger.isDebugEnabled(50)) {
                 if (logger.isDebugEnabled(50)) {
                     logger.debug(30, ASIO_FETCHCOMP,
                     logger.debug(30, ASIO_FETCHCOMP,
-                                 data_->remote->getAddress().toText().c_str(),
-                                 static_cast<int>(data_->remote->getPort()));
+                                 data_->remote_rcv->getAddress().toText().c_str(),
+                                 static_cast<int>(data_->remote_rcv->getPort()));
                 }
                 }
                 break;
                 break;
 
 
@@ -308,14 +333,14 @@ IOFetch::stop(Result result) {
                 // allowed but as it is unusual it is logged, but with a lower
                 // allowed but as it is unusual it is logged, but with a lower
                 // debug level than a timeout (which is totally normal).
                 // debug level than a timeout (which is totally normal).
                 logger.debug(1, ASIO_FETCHSTOP,
                 logger.debug(1, ASIO_FETCHSTOP,
-                             data_->remote->getAddress().toText().c_str(),
-                             static_cast<int>(data_->remote->getPort()));
+                             data_->remote_snd->getAddress().toText().c_str(),
+                             static_cast<int>(data_->remote_snd->getPort()));
                 break;
                 break;
 
 
             default:
             default:
                 logger.error(ASIO_UNKRESULT, static_cast<int>(result),
                 logger.error(ASIO_UNKRESULT, static_cast<int>(result),
-                             data_->remote->getAddress().toText().c_str(),
-                             static_cast<int>(data_->remote->getPort()));
+                             data_->remote_snd->getAddress().toText().c_str(),
+                             static_cast<int>(data_->remote_snd->getPort()));
         }
         }
 
 
         // Stop requested, cancel and I/O's on the socket and shut it down,
         // Stop requested, cancel and I/O's on the socket and shut it down,
@@ -345,10 +370,10 @@ void IOFetch::logIOFailure(asio::error_code ec) {
     static const char* PROTOCOL[2] = {"TCP", "UDP"};
     static const char* PROTOCOL[2] = {"TCP", "UDP"};
     logger.error(data_->origin,
     logger.error(data_->origin,
                  ec.value(),
                  ec.value(),
-                 ((data_->remote->getProtocol() == IPPROTO_TCP) ?
+                 ((data_->remote_snd->getProtocol() == IPPROTO_TCP) ?
                      PROTOCOL[0] : PROTOCOL[1]),
                      PROTOCOL[0] : PROTOCOL[1]),
-                 data_->remote->getAddress().toText().c_str(),
-                 static_cast<int>(data_->remote->getPort()));
+                 data_->remote_snd->getAddress().toText().c_str(),
+                 static_cast<int>(data_->remote_snd->getPort()));
 }
 }
 
 
 } // namespace asiolink
 } // namespace asiolink

+ 0 - 2
src/lib/asiolink/io_socket.cc

@@ -1,5 +1,3 @@
-// Copyright (C) 2010  CZ NIC
-// Copyed from other version of auth/asiolink.cc which is:
 // Copyright (C) 2010  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
 // Permission to use, copy, modify, and/or distribute this software for any

+ 0 - 593
src/lib/asiolink/recursive_query.cc

@@ -1,593 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
-
-#include <boost/lexical_cast.hpp>
-#include <boost/bind.hpp>
-
-#include <config.h>
-
-#include <log/dummylog.h>
-
-#include <dns/question.h>
-#include <dns/message.h>
-#include <dns/opcode.h>
-
-#include <resolve/resolve.h>
-#include <cache/resolver_cache.h>
-
-#include <asio.hpp>
-#include <asiolink/dns_service.h>
-#include <asiolink/io_fetch.h>
-#include <asiolink/io_service.h>
-#include <asiolink/recursive_query.h>
-
-using isc::log::dlog;
-using namespace isc::dns;
-
-namespace asiolink {
-
-typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
-
-// Here we do not use the typedef above, as the SunStudio compiler
-// mishandles this in its name mangling, and wouldn't compile.
-// We can probably use a typedef, but need to move it to a central
-// location and use it consistently.
-RecursiveQuery::RecursiveQuery(DNSService& dns_service,
-    const std::vector<std::pair<std::string, uint16_t> >& upstream,
-    const std::vector<std::pair<std::string, uint16_t> >& upstream_root,
-    int query_timeout, int client_timeout, int lookup_timeout,
-    unsigned retries) :
-    dns_service_(dns_service), upstream_(new AddressVector(upstream)),
-    upstream_root_(new AddressVector(upstream_root)),
-    test_server_("", 0),
-    query_timeout_(query_timeout), client_timeout_(client_timeout),
-    lookup_timeout_(lookup_timeout), retries_(retries)
-{}
-
-// Set the test server - only used for unit testing.
-
-void
-RecursiveQuery::setTestServer(const std::string& address, uint16_t port) {
-    dlog("Setting test server to " + address + "(" +
-            boost::lexical_cast<std::string>(port) + ")");
-    test_server_.first = address;
-    test_server_.second = port;
-}
-
-
-namespace {
-
-typedef std::pair<std::string, uint16_t> addr_t;
-
-/*
- * This is a query in progress. When a new query is made, this one holds
- * the context information about it, like how many times we are allowed
- * to retry on failure, what to do when we succeed, etc.
- *
- * Used by RecursiveQuery::sendQuery.
- */
-class RunningQuery : public IOFetch::Callback {
-private:
-    // The io service to handle async calls
-    IOService& io_;
-
-    // Info for (re)sending the query (the question and destination)
-    Question question_;
-
-    // This is where we build and store our final answer
-    MessagePtr answer_message_;
-
-    // currently we use upstream as the current list of NS records
-    // we should differentiate between forwarding and resolving
-    boost::shared_ptr<AddressVector> upstream_;
-
-    // root servers...just copied over to the zone_servers_
-    boost::shared_ptr<AddressVector> upstream_root_;
-
-    // Test server - only used for testing.  This takes precedence over all
-    // other servers if the port is non-zero.
-    std::pair<std::string, uint16_t> test_server_;
-
-    // Buffer to store the result.
-    OutputBufferPtr buffer_;
-
-    // Server to notify when we succeed or fail
-    //shared_ptr<DNSServer> server_;
-    isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
-
-    // Protocol used for the last query.  This is set to IOFetch::UDP when a
-    // new upstream query is initiated, and changed to IOFetch::TCP if a
-    // packet is returned with the TC bit set.  It is stored here to detect the
-    // case of a TCP packet being returned with the TC bit set.
-    IOFetch::Protocol protocol_;
-
-    // To prevent both unreasonably long cname chains and cname loops,
-    // we simply keep a counter of the number of CNAMEs we have
-    // followed so far (and error if it exceeds RESOLVER_MAX_CNAME_CHAIN
-    // from lib/resolve/response_classifier.h)
-    unsigned cname_count_;
-
-    /*
-     * TODO Do something more clever with timeouts. In the long term, some
-     *     computation of average RTT, increase with each retry, etc.
-     */
-    // Timeout information
-    int query_timeout_;
-    unsigned retries_;
-
-    // normal query state
-
-    // Not using NSAS at this moment, so we keep a list
-    // of 'current' zone servers
-    std::vector<addr_t> zone_servers_;
-
-    // Update the question that will be sent to the server
-    void setQuestion(const Question& new_question) {
-        question_ = new_question;
-    }
-
-    // TODO: replace by our wrapper
-    asio::deadline_timer client_timer;
-    asio::deadline_timer lookup_timer;
-
-    size_t queries_out_;
-    
-    // If we timed out ourselves (lookup timeout), stop issuing queries
-    bool done_;
-
-    // If we have a client timeout, we send back an answer, but don't
-    // stop. We use this variable to make sure we don't send another
-    // answer if we do find one later (or if we have a lookup_timeout)
-    bool answer_sent_;
-
-    // Reference to our cache
-    isc::cache::ResolverCache& cache_;
-
-    // perform a single lookup; first we check the cache to see
-    // if we have a response for our query stored already. if
-    // so, call handlerecursiveresponse(), if not, we call send()
-    void doLookup() {
-        dlog("doLookup: try cache");
-        Message cached_message(Message::RENDER);
-        isc::resolve::initResponseMessage(question_, cached_message);
-        if (cache_.lookup(question_.getName(), question_.getType(),
-                          question_.getClass(), cached_message)) {
-            dlog("Message found in cache, returning that");
-            handleRecursiveAnswer(cached_message);
-        } else {
-            send();
-        }
-        
-    }
-
-    // (re)send the query to the server.
-    //
-    // \param protocol Protocol to use for the fetch (default is UDP)
-    void send(IOFetch::Protocol protocol = IOFetch::UDP) {
-        const int uc = upstream_->size();
-        const int zs = zone_servers_.size();
-        protocol_ = protocol;   // Store protocol being used for this
-        buffer_->clear();
-        if (test_server_.second != 0) {
-            dlog("Sending upstream query (" + question_.toText() +
-                 ") to test server at " + test_server_.first);
-            IOFetch query(protocol, io_, question_,
-                test_server_.first,
-                test_server_.second, buffer_, this,
-                query_timeout_);
-            ++queries_out_;
-            io_.get_io_service().post(query);
-        } else if (uc > 0) {
-            int serverIndex = rand() % uc;
-            dlog("Sending upstream query (" + question_.toText() +
-                ") to " + upstream_->at(serverIndex).first);
-            IOFetch query(protocol, io_, question_,
-                upstream_->at(serverIndex).first,
-                upstream_->at(serverIndex).second, buffer_, this,
-                query_timeout_);
-            ++queries_out_;
-            io_.get_io_service().post(query);
-        } else if (zs > 0) {
-            int serverIndex = rand() % zs;
-            dlog("Sending query to zone server (" + question_.toText() +
-                ") to " + zone_servers_.at(serverIndex).first);
-            IOFetch query(protocol, io_, question_,
-                zone_servers_.at(serverIndex).first,
-                zone_servers_.at(serverIndex).second, buffer_, this,
-                query_timeout_);
-            ++queries_out_;
-            io_.get_io_service().post(query);
-        } else {
-            dlog("Error, no upstream servers to send to.");
-        }
-    }
-    
-    // This function is called by operator() if there is an actual
-    // answer from a server and we are in recursive mode
-    // depending on the contents, we go on recursing or return
-    //
-    // Note that the footprint may change as this function may
-    // need to append data to the answer we are building later.
-    //
-    // returns true if we are done (either we have an answer or an
-    //              error message)
-    // returns false if we are not done
-    bool handleRecursiveAnswer(const Message& incoming) {
-        dlog("Handle response");
-        // In case we get a CNAME, we store the target
-        // here (classify() will set it when it walks through
-        // the cname chain to verify it).
-        Name cname_target(question_.getName());
-        
-        isc::resolve::ResponseClassifier::Category category =
-            isc::resolve::ResponseClassifier::classify(
-                question_, incoming, cname_target, cname_count_);
-
-        bool found_ns_address = false;
-            
-        // If the packet is OK, store it in the cache
-        if (!isc::resolve::ResponseClassifier::error(category)) {
-            cache_.update(incoming);
-        }
-
-        switch (category) {
-        case isc::resolve::ResponseClassifier::ANSWER:
-        case isc::resolve::ResponseClassifier::ANSWERCNAME:
-            // Done. copy and return.
-            isc::resolve::copyResponseMessage(incoming, answer_message_);
-            return true;
-            break;
-        case isc::resolve::ResponseClassifier::CNAME:
-            dlog("Response is CNAME!");
-            // (unfinished) CNAME. We set our question_ to the CNAME
-            // target, then start over at the beginning (for now, that
-            // is, we reset our 'current servers' to the root servers).
-            if (cname_count_ >= RESOLVER_MAX_CNAME_CHAIN) {
-                // just give up
-                dlog("CNAME chain too long");
-                isc::resolve::makeErrorMessage(answer_message_,
-                                               Rcode::SERVFAIL());
-                return true;
-            }
-
-            answer_message_->appendSection(Message::SECTION_ANSWER,
-                                           incoming);
-            setZoneServersToRoot();
-
-            question_ = Question(cname_target, question_.getClass(),
-                                 question_.getType());
-
-            dlog("Following CNAME chain to " + question_.toText());
-            doLookup();
-            return false;
-            break;
-        case isc::resolve::ResponseClassifier::NXDOMAIN:
-            // NXDOMAIN, just copy and return.
-            isc::resolve::copyResponseMessage(incoming, answer_message_);
-            return true;
-            break;
-        case isc::resolve::ResponseClassifier::REFERRAL:
-            // Referral. For now we just take the first glue address
-            // we find and continue with that
-            zone_servers_.clear();
-
-            for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_ADDITIONAL);
-                 rrsi != incoming.endSection(Message::SECTION_ADDITIONAL) && !found_ns_address;
-                 rrsi++) {
-                ConstRRsetPtr rrs = *rrsi;
-                if (rrs->getType() == RRType::A()) {
-                    // found address
-                    RdataIteratorPtr rdi = rrs->getRdataIterator();
-                    // just use the first for now
-                    if (!rdi->isLast()) {
-                        std::string addr_str = rdi->getCurrent().toText();
-                        dlog("[XX] first address found: " + addr_str);
-                        // now we have one address, simply
-                        // resend that exact same query
-                        // to that address and yield, when it
-                        // returns, loop again.
-                        
-                        // TODO should use NSAS
-                        zone_servers_.push_back(addr_t(addr_str, 53));
-                        found_ns_address = true;
-                        break;
-                    }
-                }
-            }
-            if (found_ns_address) {
-                // next resolver round
-                // we do NOT use doLookup() here, but send() (i.e. we
-                // skip the cache), since if we had the final answer
-                // instead of a delegation cached, we would have been
-                // there by now.
-                send();
-                return false;
-            } else {
-                dlog("[XX] no ready-made addresses in additional. need nsas.");
-                // TODO this will result in answering with the delegation. oh well
-                isc::resolve::copyResponseMessage(incoming, answer_message_);
-                return true;
-            }
-            break;
-        case isc::resolve::ResponseClassifier::TRUNCATED:
-            // Truncated packet.  If the protocol we used for the last one is
-            // UDP, re-query using TCP.  Otherwise regard it as an error.
-            if (protocol_ == IOFetch::UDP) {
-                dlog("Response truncated, re-querying over TCP");
-                send(IOFetch::TCP);
-                return false;
-            }
-            // Was a TCP query so we have received a packet over TCP with the TC
-            // bit set: drop through to common error processing.
-            // TODO: Can we use what we have received instead of discarding it?
-
-        case isc::resolve::ResponseClassifier::EMPTY:
-        case isc::resolve::ResponseClassifier::EXTRADATA:
-        case isc::resolve::ResponseClassifier::INVNAMCLASS:
-        case isc::resolve::ResponseClassifier::INVTYPE:
-        case isc::resolve::ResponseClassifier::MISMATQUEST:
-        case isc::resolve::ResponseClassifier::MULTICLASS:
-        case isc::resolve::ResponseClassifier::NOTONEQUEST:
-        case isc::resolve::ResponseClassifier::NOTRESPONSE:
-        case isc::resolve::ResponseClassifier::NOTSINGLE:
-        case isc::resolve::ResponseClassifier::OPCODE:
-        case isc::resolve::ResponseClassifier::RCODE:
-
-            // Should we try a different server rather than SERVFAIL?
-            isc::resolve::makeErrorMessage(answer_message_,
-                                           Rcode::SERVFAIL());
-            return true;
-            break;
-        }
-        // should not be reached. assert here?
-        dlog("[FATAL] unreachable code");
-        return true;
-    }
-    
-public:
-    RunningQuery(IOService& io,
-        const Question &question,
-        MessagePtr answer_message,
-        boost::shared_ptr<AddressVector> upstream,
-        boost::shared_ptr<AddressVector> upstream_root,
-        std::pair<std::string, uint16_t>& test_server,
-        OutputBufferPtr buffer,
-        isc::resolve::ResolverInterface::CallbackPtr cb,
-        int query_timeout, int client_timeout, int lookup_timeout,
-        unsigned retries,
-        isc::cache::ResolverCache& cache) :
-        io_(io),
-        question_(question),
-        answer_message_(answer_message),
-        upstream_(upstream),
-        upstream_root_(upstream_root),
-        test_server_(test_server),
-        buffer_(buffer),
-        resolvercallback_(cb),
-        protocol_(IOFetch::UDP),
-        cname_count_(0),
-        query_timeout_(query_timeout),
-        retries_(retries),
-        client_timer(io.get_io_service()),
-        lookup_timer(io.get_io_service()),
-        queries_out_(0),
-        done_(false),
-        answer_sent_(false),
-        cache_(cache)
-    {
-        // Setup the timer to stop trying (lookup_timeout)
-        if (lookup_timeout >= 0) {
-            lookup_timer.expires_from_now(
-                boost::posix_time::milliseconds(lookup_timeout));
-            lookup_timer.async_wait(boost::bind(&RunningQuery::stop, this, false));
-        }
-        
-        // Setup the timer to send an answer (client_timeout)
-        if (client_timeout >= 0) {
-            client_timer.expires_from_now(
-                boost::posix_time::milliseconds(client_timeout));
-            client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
-        }
-        
-        // should use NSAS for root servers
-        // Adding root servers if not a forwarder
-        if (upstream_->empty()) {
-            setZoneServersToRoot();
-        }
-
-        doLookup();
-    }
-
-    void setZoneServersToRoot() {
-        zone_servers_.clear();
-        if (upstream_root_->empty()) { //if no root ips given, use this
-            zone_servers_.push_back(addr_t("192.5.5.241", 53));
-        } else {
-            // copy the list
-            dlog("Size is " + 
-                boost::lexical_cast<std::string>(upstream_root_->size()) + 
-                "\n");
-            for(AddressVector::iterator it = upstream_root_->begin();
-                it < upstream_root_->end(); ++it) {
-            zone_servers_.push_back(addr_t(it->first,it->second));
-            dlog("Put " + zone_servers_.back().first + "into root list\n");
-            }
-        }
-    }
-    virtual void clientTimeout() {
-        // Return a SERVFAIL, but do not stop until
-        // we have an answer or timeout ourselves
-        isc::resolve::makeErrorMessage(answer_message_,
-                                       Rcode::SERVFAIL());
-        if (!answer_sent_) {
-            answer_sent_ = true;
-            resolvercallback_->success(answer_message_);
-        }
-    }
-
-    virtual void stop(bool resume) {
-        // if we cancel our timers, we will still get an event for
-        // that, so we cannot delete ourselves just yet (those events
-        // would be bound to a deleted object)
-        // cancel them one by one, both cancels should get us back
-        // here again.
-        // same goes if we have an outstanding query (can't delete
-        // until that one comes back to us)
-        done_ = true;
-        if (resume && !answer_sent_) {
-            answer_sent_ = true;
-
-            // There are two types of messages we could store in the
-            // cache;
-            // 1. answers to our fetches from authoritative servers,
-            //    exactly as we receive them, and
-            // 2. answers to queries we received from clients, which
-            //    have received additional processing (following CNAME
-            //    chains, for instance)
-            //
-            // Doing only the first would mean we would have to re-do
-            // processing when we get data from our cache, and doing
-            // only the second would miss out on the side-effect of
-            // having nameserver data in our cache.
-            //
-            // So right now we do both. Since the cache (currently)
-            // stores Messages on their question section only, this
-            // does mean that we overwrite the messages we stored in
-            // the previous iteration if we are following a delegation.
-            cache_.update(*answer_message_);
-
-            resolvercallback_->success(answer_message_);
-        } else {
-            resolvercallback_->failure();
-        }
-        if (lookup_timer.cancel() != 0) {
-            return;
-        }
-        if (client_timer.cancel() != 0) {
-            return;
-        }
-        if (queries_out_ > 0) {
-            return;
-        }
-        delete this;
-    }
-
-    // This function is used as callback from DNSQuery.
-    virtual void operator()(IOFetch::Result result) {
-        --queries_out_;
-        if (!done_ && result != IOFetch::TIME_OUT) {
-            // we got an answer
-            Message incoming(Message::PARSE);
-            InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
-            incoming.fromWire(ibuf);
-
-            if (upstream_->size() == 0 &&
-                incoming.getRcode() == Rcode::NOERROR()) {
-                done_ = handleRecursiveAnswer(incoming);
-            } else {
-                isc::resolve::copyResponseMessage(incoming, answer_message_);
-                done_ = true;
-            }
-            
-            if (done_) {
-                stop(true);
-            }
-        } else if (!done_ && retries_--) {
-            // We timed out, but we have some retries, so send again
-            dlog("Timeout, resending query");
-            send();
-        } else {
-            // out of retries, give up for now
-            stop(false);
-        }
-    }
-};
-
-}
-
-void
-RecursiveQuery::resolve(const QuestionPtr& question,
-    const isc::resolve::ResolverInterface::CallbackPtr callback)
-{
-    IOService& io = dns_service_.getIOService();
-
-    MessagePtr answer_message(new Message(Message::RENDER));
-    isc::resolve::initResponseMessage(*question, *answer_message);
-
-    OutputBufferPtr buffer(new OutputBuffer(0));
-
-    dlog("Try out cache first (direct call to resolve)");
-    // First try to see if we have something cached in the messagecache
-    if (cache_.lookup(question->getName(), question->getType(),
-                      question->getClass(), *answer_message)) {
-        dlog("Message found in cache, returning that");
-        // TODO: err, should cache set rcode as well?
-        answer_message->setRcode(Rcode::NOERROR());
-        callback->success(answer_message);
-    } else {
-        dlog("Message not found in cache, starting recursive query");
-        // It will delete itself when it is done
-        new RunningQuery(io, *question, answer_message, upstream_,
-                         upstream_root_, test_server_,
-                         buffer, callback, query_timeout_,
-                         client_timeout_, lookup_timeout_, retries_,
-                         cache_);
-    }
-}
-
-void
-RecursiveQuery::resolve(const Question& question,
-                        MessagePtr answer_message,
-                        OutputBufferPtr buffer,
-                        DNSServer* server)
-{
-    // XXX: eventually we will need to be able to determine whether
-    // the message should be sent via TCP or UDP, or sent initially via
-    // UDP and then fall back to TCP on failure, but for the moment
-    // we're only going to handle UDP.
-    IOService& io = dns_service_.getIOService();
-
-    isc::resolve::ResolverInterface::CallbackPtr crs(
-        new isc::resolve::ResolverCallbackServer(server));
-
-    // TODO: general 'prepareinitialanswer'
-    answer_message->setOpcode(isc::dns::Opcode::QUERY());
-    answer_message->addQuestion(question);
-    
-    // First try to see if we have something cached in the messagecache
-    dlog("Try out cache first (started by incoming event)");
-    if (cache_.lookup(question.getName(), question.getType(),
-                      question.getClass(), *answer_message)) {
-        dlog("Message found in cache, returning that");
-        // TODO: err, should cache set rcode as well?
-        answer_message->setRcode(Rcode::NOERROR());
-        crs->success(answer_message);
-    } else {
-        dlog("Message not found in cache, starting recursive query");
-        // It will delete itself when it is done
-        new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
-                         test_server_,
-                         buffer, crs, query_timeout_, client_timeout_,
-                         lookup_timeout_, retries_, cache_);
-    }
-}
-
-
-
-} // namespace asiolink

+ 23 - 13
src/lib/asiolink/tcp_server.cc

@@ -47,7 +47,7 @@ TCPServer::TCPServer(io_service& io_service,
                      const SimpleCallback* checkin,
                      const SimpleCallback* checkin,
                      const DNSLookup* lookup,
                      const DNSLookup* lookup,
                      const DNSAnswer* answer) :
                      const DNSAnswer* answer) :
-    io_(io_service), done_(false), stopped_by_hand_(false),
+    io_(io_service), done_(false),
     checkin_callback_(checkin), lookup_callback_(lookup),
     checkin_callback_(checkin), lookup_callback_(lookup),
     answer_callback_(answer)
     answer_callback_(answer)
 {
 {
@@ -70,12 +70,6 @@ TCPServer::operator()(error_code ec, size_t length) {
     /// a switch statement, inline variable declarations are not
     /// a switch statement, inline variable declarations are not
     /// permitted.  Certain variables used below can be declared here.
     /// permitted.  Certain variables used below can be declared here.
 
 
-    /// If user has stopped the server, we won't enter the
-    /// coroutine body, just return
-    if (stopped_by_hand_) {
-        return;
-    }
-
     boost::array<const_buffer,2> bufs;
     boost::array<const_buffer,2> bufs;
     OutputBuffer lenbuf(TCP_MESSAGE_LENGTHSIZE);
     OutputBuffer lenbuf(TCP_MESSAGE_LENGTHSIZE);
 
 
@@ -88,6 +82,7 @@ TCPServer::operator()(error_code ec, size_t length) {
             /// try again
             /// try again
             do {
             do {
                 CORO_YIELD acceptor_->async_accept(*socket_, *this);
                 CORO_YIELD acceptor_->async_accept(*socket_, *this);
+
                 // Abort on fatal errors
                 // Abort on fatal errors
                 // TODO: Log error?
                 // TODO: Log error?
                 if (ec) {
                 if (ec) {
@@ -115,6 +110,7 @@ TCPServer::operator()(error_code ec, size_t length) {
         CORO_YIELD async_read(*socket_, asio::buffer(data_.get(),
         CORO_YIELD async_read(*socket_, asio::buffer(data_.get(),
                               TCP_MESSAGE_LENGTHSIZE), *this);
                               TCP_MESSAGE_LENGTHSIZE), *this);
         if (ec) {
         if (ec) {
+            socket_->close();
             CORO_YIELD return;
             CORO_YIELD return;
         }
         }
 
 
@@ -127,9 +123,11 @@ TCPServer::operator()(error_code ec, size_t length) {
         }
         }
 
 
         if (ec) {
         if (ec) {
+            socket_->close();
             CORO_YIELD return;
             CORO_YIELD return;
         }
         }
 
 
+
         // Create an \c IOMessage object to store the query.
         // Create an \c IOMessage object to store the query.
         //
         //
         // (XXX: It would be good to write a factory function
         // (XXX: It would be good to write a factory function
@@ -160,6 +158,7 @@ TCPServer::operator()(error_code ec, size_t length) {
         // If we don't have a DNS Lookup provider, there's no point in
         // If we don't have a DNS Lookup provider, there's no point in
         // continuing; we exit the coroutine permanently.
         // continuing; we exit the coroutine permanently.
         if (lookup_callback_ == NULL) {
         if (lookup_callback_ == NULL) {
+            socket_->close();
             CORO_YIELD return;
             CORO_YIELD return;
         }
         }
 
 
@@ -177,9 +176,15 @@ TCPServer::operator()(error_code ec, size_t length) {
         // The 'done_' flag indicates whether we have an answer
         // The 'done_' flag indicates whether we have an answer
         // to send back.  If not, exit the coroutine permanently.
         // to send back.  If not, exit the coroutine permanently.
         if (!done_) {
         if (!done_) {
+            // TODO: should we keep the connection open for a short time
+            // to see if new requests come in?
+            socket_->close();
             CORO_YIELD return;
             CORO_YIELD return;
         }
         }
 
 
+        if (ec) {
+            CORO_YIELD return;
+        }
         // Call the DNS answer provider to render the answer into
         // Call the DNS answer provider to render the answer into
         // wire format
         // wire format
         (*answer_callback_)(*io_message_, query_message_,
         (*answer_callback_)(*io_message_, query_message_,
@@ -195,6 +200,10 @@ TCPServer::operator()(error_code ec, size_t length) {
         // (though we have nothing further to do, so the coroutine
         // (though we have nothing further to do, so the coroutine
         // will simply exit at that time).
         // will simply exit at that time).
         CORO_YIELD async_write(*socket_, bufs, *this);
         CORO_YIELD async_write(*socket_, bufs, *this);
+
+        // TODO: should we keep the connection open for a short time
+        // to see if new requests come in?
+        socket_->close();
     }
     }
 }
 }
 
 
@@ -207,14 +216,15 @@ TCPServer::asyncLookup() {
 }
 }
 
 
 void TCPServer::stop() {
 void TCPServer::stop() {
-    // server should not be stopped twice
-    if (stopped_by_hand_) {
-        return;
-    }
+    /// we use close instead of cancel, with the same reason
+    /// with udp server stop, refer to the udp server code
 
 
-    stopped_by_hand_ = true;
     acceptor_->close();
     acceptor_->close();
-    socket_->close();
+    // User may stop the server even when it hasn't started to
+    // run, in that that socket_ is empty
+    if (socket_) {
+        socket_->close();
+    }
 }
 }
 /// Post this coroutine on the ASIO service queue so that it will
 /// Post this coroutine on the ASIO service queue so that it will
 /// resume processing where it left off.  The 'done' parameter indicates
 /// resume processing where it left off.  The 'done' parameter indicates

+ 0 - 3
src/lib/asiolink/tcp_server.h

@@ -107,9 +107,6 @@ private:
     size_t bytes_;
     size_t bytes_;
     bool done_;
     bool done_;
 
 
-    // whether user has stopped the server
-    bool stopped_by_hand_;
-
     // Callback functions provided by the caller
     // Callback functions provided by the caller
     const SimpleCallback* checkin_callback_;
     const SimpleCallback* checkin_callback_;
     const DNSLookup* lookup_callback_;
     const DNSLookup* lookup_callback_;

+ 2 - 2
src/lib/asiolink/tcp_socket.h

@@ -255,8 +255,8 @@ TCPSocket<C>::open(const IOEndpoint* endpoint, C& callback) {
 // an exception if this is the case.
 // an exception if this is the case.
 
 
 template <typename C> void
 template <typename C> void
-TCPSocket<C>::asyncSend(const void* data, size_t length, const IOEndpoint*,
-                        C& callback)
+TCPSocket<C>::asyncSend(const void* data, size_t length,
+    const IOEndpoint*, C& callback)
 {
 {
     if (isopen_) {
     if (isopen_) {
 
 

+ 2 - 6
src/lib/asiolink/tests/Makefile.am

@@ -25,12 +25,11 @@ run_unittests_SOURCES += io_fetch_unittest.cc
 run_unittests_SOURCES += io_socket_unittest.cc
 run_unittests_SOURCES += io_socket_unittest.cc
 run_unittests_SOURCES += io_service_unittest.cc
 run_unittests_SOURCES += io_service_unittest.cc
 run_unittests_SOURCES += interval_timer_unittest.cc
 run_unittests_SOURCES += interval_timer_unittest.cc
-run_unittests_SOURCES += recursive_query_unittest.cc
-run_unittests_SOURCES += recursive_query_unittest_2.cc
 run_unittests_SOURCES += tcp_endpoint_unittest.cc
 run_unittests_SOURCES += tcp_endpoint_unittest.cc
 run_unittests_SOURCES += tcp_socket_unittest.cc
 run_unittests_SOURCES += tcp_socket_unittest.cc
 run_unittests_SOURCES += udp_endpoint_unittest.cc
 run_unittests_SOURCES += udp_endpoint_unittest.cc
 run_unittests_SOURCES += udp_socket_unittest.cc
 run_unittests_SOURCES += udp_socket_unittest.cc
+run_unittests_SOURCES += dns_server_unittest.cc
 run_unittests_SOURCES += qid_gen_unittest.cc
 run_unittests_SOURCES += qid_gen_unittest.cc
 
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -39,13 +38,10 @@ run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
-run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
-run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 
 
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) 
 
 
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 # B10_CXXFLAGS)

+ 501 - 0
src/lib/asiolink/tests/dns_server_unittest.cc

@@ -0,0 +1,501 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <gtest/gtest.h>
+
+#include <asio.hpp>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_error.h>
+#include <asiolink/udp_server.h>
+#include <asiolink/tcp_server.h>
+#include <asiolink/dns_answer.h>
+#include <asiolink/dns_lookup.h>
+#include <string>
+#include <csignal>
+#include <unistd.h> //for alarm
+
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+
+/// The following tests focus on stop interface for udp and
+/// tcp server, there are lots of things can be shared to test
+/// both tcp and udp server, so they are in the same unittest
+
+/// The general work flow for dns server, is that wait for user
+/// query, once get one query, we will check the data is valid or
+/// not, if it passed, we will try to loop up the question, then
+/// compose the answer and finally send it back to user. The server
+/// may be stopped at any point during this porcess, so the test strategy
+/// is that we define 5 stop point and stop the server at these
+/// 5 points, to check whether stop is successful
+/// The 5 test points are :
+///   Before the server start to run
+///   After we get the query and check whether it's valid
+///   After we lookup the query
+///   After we compoisite the answer
+///   After user get the final result.
+
+/// The standard about whether we stop the server successfully or not
+/// is based on the fact that if the server is still running, the io
+/// service won't quit since it will wait for some asynchronized event for
+/// server. So if the io service block function run returns we assume
+/// that the server is stopped. To avoid stop interface failure which
+/// will block followed tests, using alarm signal to stop the blocking
+/// io service
+///
+/// The whole test context including one server and one client, and
+/// five stop checkpoints, we call them ServerStopper exclude the first
+/// stop point. Once the unittest fired, the client will send message
+/// to server, and the stopper may stop the server at the checkpoint, then
+/// we check the client get feedback or not. Since there is no DNS logic
+/// involved so the message sending between client and server is plain text
+/// And the valid checker, question lookup and answer composition are dummy.
+
+using namespace asiolink;
+using namespace asio;
+namespace {
+static const std::string server_ip = "127.0.0.1";
+const int server_port = 5553;
+//message client send to udp server, which isn't dns package
+//just for simple testing
+static const std::string query_message("BIND10 is awesome");
+
+// \brief provide capacity to derived class the ability
+// to stop DNSServer at certern point
+class ServerStopper {
+    public:
+        ServerStopper() : server_to_stop_(NULL) {}
+        virtual ~ServerStopper(){}
+
+        void setServerToStop(DNSServer* server) {
+            server_to_stop_ = server;
+        }
+
+        void stopServer() const {
+            if (server_to_stop_) {
+                server_to_stop_->stop();
+            }
+        }
+
+    private:
+        DNSServer* server_to_stop_;
+};
+
+// \brief no check logic at all,just provide a checkpoint to stop the server
+class DummyChecker : public SimpleCallback, public ServerStopper {
+    public:
+        virtual void operator()(const IOMessage&) const {
+            stopServer();
+        }
+};
+
+// \brief no lookup logic at all,just provide a checkpoint to stop the server
+class DummyLookup : public DNSLookup, public ServerStopper {
+    public:
+        void operator()(const IOMessage& io_message,
+                isc::dns::MessagePtr message,
+                isc::dns::MessagePtr answer_message,
+                isc::dns::OutputBufferPtr buffer,
+                DNSServer* server) const {
+            stopServer();
+            server->resume(true);
+        }
+};
+
+// \brief copy the data received from user to the answer part
+//  provide checkpoint to stop server
+class SimpleAnswer : public DNSAnswer, public ServerStopper {
+    public:
+        void operator()(const IOMessage& message,
+                isc::dns::MessagePtr query_message,
+                isc::dns::MessagePtr answer_message,
+                isc::dns::OutputBufferPtr buffer) const
+        {
+            //copy what we get from user
+            buffer->writeData(message.getData(), message.getDataSize());
+            stopServer();
+        }
+
+};
+
+// \brief simple client, send one string to server and wait for response
+//  in case, server stopped and client cann't get response, there is a timer wait
+//  for specified seconds (the value is just a estimate since server process logic is quite
+//  simple, and all the intercommunication is local) then cancel the waiting.
+class SimpleClient : public ServerStopper {
+    public:
+    static const size_t MAX_DATA_LEN = 256;
+    SimpleClient(asio::io_service& service,
+                 unsigned int wait_server_time_out)
+    {
+        wait_for_response_timer_.reset(new deadline_timer(service));
+        received_data_ = new char[MAX_DATA_LEN];
+        received_data_len_ = 0;
+        wait_server_time_out_ = wait_server_time_out;
+    }
+
+    virtual ~SimpleClient() {
+        delete [] received_data_;
+    }
+
+    void setGetFeedbackCallback(boost::function<void()>& func) {
+        get_response_call_back_ = func;
+    }
+
+    virtual void sendDataThenWaitForFeedback(const std::string& data)  = 0;
+    virtual std::string getReceivedData() const = 0;
+
+    void startTimer() {
+        wait_for_response_timer_->cancel();
+        wait_for_response_timer_->
+            expires_from_now(boost::posix_time::
+                             seconds(wait_server_time_out_));
+        wait_for_response_timer_->
+            async_wait(boost::bind(&SimpleClient::stopWaitingforResponse,
+                                   this));
+    }
+
+    void cancelTimer() { wait_for_response_timer_->cancel(); }
+
+    void getResponseCallBack(const asio::error_code& error, size_t
+                             received_bytes)
+    {
+        cancelTimer();
+        if (!error)
+            received_data_len_ = received_bytes;
+        if (!get_response_call_back_.empty()) {
+            get_response_call_back_();
+        }
+        stopServer();
+    }
+
+
+    protected:
+    virtual void stopWaitingforResponse() = 0;
+
+    boost::shared_ptr<deadline_timer> wait_for_response_timer_;
+    char* received_data_;
+    size_t received_data_len_;
+    boost::function<void()> get_response_call_back_;
+    unsigned int wait_server_time_out_;
+};
+
+
+
+class UDPClient : public SimpleClient {
+    public:
+    //After 1 seconds without feedback client will stop wait
+    static const unsigned int server_time_out = 1;
+
+    UDPClient(asio::io_service& service, const ip::udp::endpoint& server) :
+        SimpleClient(service, server_time_out)
+    {
+        server_ = server;
+        socket_.reset(new ip::udp::socket(service));
+        socket_->open(ip::udp::v4());
+    }
+
+
+    void sendDataThenWaitForFeedback(const std::string& data) {
+        received_data_len_ = 0;
+        socket_->send_to(buffer(data.c_str(), data.size() + 1), server_);
+        socket_->async_receive_from(buffer(received_data_, MAX_DATA_LEN),
+                                    received_from_,
+                                    boost::bind(&SimpleClient::
+                                                getResponseCallBack, this, _1,
+                                                _2));
+        startTimer();
+    }
+
+    virtual std::string getReceivedData() const {
+        return (received_data_len_ == 0 ? std::string("") :
+                                std::string(received_data_));
+    }
+
+    private:
+    void stopWaitingforResponse() {
+        socket_->close();
+    }
+
+    boost::shared_ptr<ip::udp::socket> socket_;
+    ip::udp::endpoint server_;
+    ip::udp::endpoint received_from_;
+};
+
+
+class TCPClient : public SimpleClient {
+    public:
+    // after 2 seconds without feedback client will stop wait,
+    // this includes connect, send message and recevice message
+    static const unsigned int server_time_out = 2;
+    TCPClient(asio::io_service& service, const ip::tcp::endpoint& server)
+        : SimpleClient(service, server_time_out)
+    {
+        server_ = server;
+        socket_.reset(new ip::tcp::socket(service));
+        socket_->open(ip::tcp::v4());
+    }
+
+
+    virtual void sendDataThenWaitForFeedback(const std::string &data) {
+        received_data_len_ = 0;
+        data_to_send_ = data;
+        data_to_send_len_ = data.size() + 1;
+        socket_->async_connect(server_, boost::bind(&TCPClient::connectHandler,
+                                                    this, _1));
+        startTimer();
+    }
+
+    virtual std::string getReceivedData() const {
+        return (received_data_len_ == 0 ? std::string("") :
+                                std::string(received_data_ + 2));
+    }
+
+    private:
+    void stopWaitingforResponse() {
+        socket_->close();
+    }
+
+    void connectHandler(const asio::error_code& error) {
+        if (!error) {
+            data_to_send_len_ = htons(data_to_send_len_);
+            socket_->async_send(buffer(&data_to_send_len_, 2),
+                                boost::bind(&TCPClient::sendMessageBodyHandler,
+                                            this, _1, _2));
+        }
+    }
+
+    void sendMessageBodyHandler(const asio::error_code& error,
+                                size_t send_bytes)
+    {
+        if (!error && send_bytes == 2) {
+            socket_->async_send(buffer(data_to_send_.c_str(),
+                                       data_to_send_.size() + 1),
+                    boost::bind(&TCPClient::finishSendHandler, this, _1, _2));
+        }
+    }
+
+    void finishSendHandler(const asio::error_code& error, size_t send_bytes) {
+        if (!error && send_bytes == data_to_send_.size() + 1) {
+            socket_->async_receive(buffer(received_data_, MAX_DATA_LEN),
+                   boost::bind(&SimpleClient::getResponseCallBack, this, _1,
+                               _2));
+        }
+    }
+
+    boost::shared_ptr<ip::tcp::socket> socket_;
+    ip::tcp::endpoint server_;
+    std::string data_to_send_;
+    uint16_t data_to_send_len_;
+};
+
+
+
+// \brief provide the context which including two client and
+// two server, udp client will only communicate with udp server, same for tcp client
+class DNSServerTest : public::testing::Test {
+    protected:
+        void SetUp() {
+            ip::address server_address = ip::address::from_string(server_ip);
+            checker_ = new DummyChecker();
+            lookup_ = new DummyLookup();
+            answer_ = new SimpleAnswer();
+            udp_server_ = new UDPServer(service, server_address, server_port,
+                    checker_, lookup_, answer_);
+            udp_client_ = new UDPClient(service,
+                    ip::udp::endpoint(server_address,
+                        server_port));
+            tcp_server_ = new TCPServer(service, server_address, server_port,
+                    checker_, lookup_, answer_);
+            tcp_client_ = new TCPClient(service,
+                    ip::tcp::endpoint(server_address,
+                        server_port));
+        }
+
+
+        void TearDown() {
+            udp_server_->stop();
+            tcp_server_->stop();
+            delete checker_;
+            delete lookup_;
+            delete answer_;
+            delete udp_server_;
+            delete udp_client_;
+            delete tcp_server_;
+            delete tcp_client_;
+        }
+
+
+        void testStopServerByStopper(DNSServer* server, SimpleClient* client,
+                ServerStopper* stopper)
+        {
+            static const unsigned int io_service_time_out = 5;
+            io_service_is_time_out = false;
+            stopper->setServerToStop(server);
+            (*server)();
+            client->sendDataThenWaitForFeedback(query_message);
+            // Since thread hasn't been introduced into the tool box, using signal
+            // to make sure run function will eventually return even server stop
+            // failed
+            void (*prev_handler)(int) = std::signal(SIGALRM, DNSServerTest::stopIOService);
+            alarm(io_service_time_out);
+            service.run();
+            service.reset();
+            //cancel scheduled alarm
+            alarm(0);
+            std::signal(SIGALRM, prev_handler);
+        }
+
+
+        static void stopIOService(int _no_use_parameter) {
+            io_service_is_time_out = true;
+            service.stop();
+        }
+
+        bool serverStopSucceed() const {
+            return (!io_service_is_time_out);
+        }
+
+        DummyChecker* checker_;
+        DummyLookup*  lookup_;
+        SimpleAnswer* answer_;
+        UDPServer*    udp_server_;
+        UDPClient*    udp_client_;
+        TCPClient*    tcp_client_;
+        TCPServer*    tcp_server_;
+
+        // To access them in signal handle function, the following
+        // variables have to be static.
+        static asio::io_service service;
+        static bool io_service_is_time_out;
+};
+
+bool DNSServerTest::io_service_is_time_out = false;
+asio::io_service DNSServerTest::service;
+
+// Test whether server stopped successfully after client get response
+// client will send query and start to wait for response, once client
+// get response, udp server will be stopped, the io service won't quit
+// if udp server doesn't stop successfully.
+TEST_F(DNSServerTest, stopUDPServerAfterOneQuery) {
+    testStopServerByStopper(udp_server_, udp_client_, udp_client_);
+    EXPECT_EQ(query_message, udp_client_->getReceivedData());
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+// Test whether udp server stopped successfully before server start to serve
+TEST_F(DNSServerTest, stopUDPServerBeforeItStartServing) {
+    udp_server_->stop();
+    testStopServerByStopper(udp_server_, udp_client_, udp_client_);
+    EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+
+// Test whether udp server stopped successfully during message check
+TEST_F(DNSServerTest, stopUDPServerDuringMessageCheck) {
+    testStopServerByStopper(udp_server_, udp_client_, checker_);
+    EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+// Test whether udp server stopped successfully during query lookup
+TEST_F(DNSServerTest, stopUDPServerDuringQueryLookup) {
+    testStopServerByStopper(udp_server_, udp_client_, lookup_);
+    EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+// Test whether udp server stopped successfully during composing answer
+TEST_F(DNSServerTest, stopUDPServerDuringPrepareAnswer) {
+    testStopServerByStopper(udp_server_, udp_client_, answer_);
+    EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+static void stopServerManyTimes(DNSServer *server, unsigned int times) {
+    for (int i = 0; i < times; ++i) {
+        server->stop();
+    }
+}
+
+// Test whether udp server stop interface can be invoked several times without
+// throw any exception
+TEST_F(DNSServerTest, stopUDPServeMoreThanOnce) {
+    ASSERT_NO_THROW({
+        boost::function<void()> stop_server_3_times
+            = boost::bind(stopServerManyTimes, udp_server_, 3);
+        udp_client_->setGetFeedbackCallback(stop_server_3_times);
+        testStopServerByStopper(udp_server_, udp_client_, udp_client_);
+        EXPECT_EQ(query_message, udp_client_->getReceivedData());
+    });
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+
+TEST_F(DNSServerTest, stopTCPServerAfterOneQuery) {
+    testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
+    EXPECT_EQ(query_message, tcp_client_->getReceivedData());
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+
+// Test whether tcp server stopped successfully before server start to serve
+TEST_F(DNSServerTest, stopTCPServerBeforeItStartServing) {
+    tcp_server_->stop();
+    testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
+    EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+
+// Test whether tcp server stopped successfully during message check
+TEST_F(DNSServerTest, stopTCPServerDuringMessageCheck) {
+    testStopServerByStopper(tcp_server_, tcp_client_, checker_);
+    EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+// Test whether tcp server stopped successfully during query lookup
+TEST_F(DNSServerTest, stopTCPServerDuringQueryLookup) {
+    testStopServerByStopper(tcp_server_, tcp_client_, lookup_);
+    EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+// Test whether tcp server stopped successfully during composing answer
+TEST_F(DNSServerTest, stopTCPServerDuringPrepareAnswer) {
+    testStopServerByStopper(tcp_server_, tcp_client_, answer_);
+    EXPECT_EQ(std::string(""), tcp_client_->getReceivedData());
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+
+// Test whether tcp server stop interface can be invoked several times without
+// throw any exception
+TEST_F(DNSServerTest, stopTCPServeMoreThanOnce) {
+    ASSERT_NO_THROW({
+        boost::function<void()> stop_server_3_times
+            = boost::bind(stopServerManyTimes, tcp_server_, 3);
+        tcp_client_->setGetFeedbackCallback(stop_server_3_times);
+        testStopServerByStopper(tcp_server_, tcp_client_, tcp_client_);
+        EXPECT_EQ(query_message, tcp_client_->getReceivedData());
+    });
+    EXPECT_TRUE(serverStopSucceed());
+}
+
+}

+ 56 - 0
src/lib/asiolink/tests/io_endpoint_unittest.cc

@@ -60,6 +60,62 @@ TEST(IOEndpointTest, createTCPv6) {
     EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
     EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
 }
 }
 
 
+TEST(IOEndpointTest, equality) {
+    std::vector<const IOEndpoint *> epv;
+    epv.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1234"), 5303));
+    epv.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1234"), 5303));
+    epv.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1234"), 5304));
+    epv.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1234"), 5304));
+    epv.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1235"), 5303));
+    epv.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1235"), 5303));
+    epv.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1235"), 5304));
+    epv.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1235"), 5304));
+    epv.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"), 5303));
+    epv.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"), 5303));
+    epv.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"), 5304));
+    epv.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"), 5304));
+    epv.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.2"), 5303));
+    epv.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.2"), 5303));
+    epv.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.2"), 5304));
+    epv.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.2"), 5304));
+
+    for (size_t i = 0; i < epv.size(); ++i) {
+        for (size_t j = 0; j < epv.size(); ++j) {
+            if (i != j) {
+                // We use EXPECT_TRUE/FALSE instead of _EQ here, since
+                // _EQ requires there is an operator<< as well
+                EXPECT_FALSE(*epv[i] == *epv[j]);
+                EXPECT_TRUE(*epv[i] != *epv[j]);
+            }
+        }
+    }
+
+    // Create a second array with exactly the same values. We use create()
+    // again to make sure we get different endpoints
+    std::vector<const IOEndpoint *> epv2;
+    epv2.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1234"), 5303));
+    epv2.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1234"), 5303));
+    epv2.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1234"), 5304));
+    epv2.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1234"), 5304));
+    epv2.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1235"), 5303));
+    epv2.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1235"), 5303));
+    epv2.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1235"), 5304));
+    epv2.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1235"), 5304));
+    epv2.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"), 5303));
+    epv2.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"), 5303));
+    epv2.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"), 5304));
+    epv2.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"), 5304));
+    epv2.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.2"), 5303));
+    epv2.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.2"), 5303));
+    epv2.push_back(IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.2"), 5304));
+    epv2.push_back(IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.2"), 5304));
+
+    for (size_t i = 0; i < epv.size(); ++i) {
+        EXPECT_TRUE(*epv[i] == *epv2[i]);
+        EXPECT_FALSE(*epv[i] != *epv2[i]);
+    }
+}
+
 TEST(IOEndpointTest, createIPProto) {
 TEST(IOEndpointTest, createIPProto) {
     EXPECT_THROW(IOEndpoint::create(IPPROTO_IP, IOAddress("192.0.2.1"),
     EXPECT_THROW(IOEndpoint::create(IPPROTO_IP, IOAddress("192.0.2.1"),
                                     53210)->getAddress().toText(),
                                     53210)->getAddress().toText(),

+ 155 - 40
src/lib/asiolink/tests/io_fetch_unittest.cc

@@ -76,6 +76,7 @@ public:
     // response handler methods in this class) receives the question sent by the
     // response handler methods in this class) receives the question sent by the
     // fetch object.
     // fetch object.
     uint8_t         receive_buffer_[MAX_SIZE]; ///< Server receive buffer
     uint8_t         receive_buffer_[MAX_SIZE]; ///< Server receive buffer
+    OutputBufferPtr expected_buffer_;          ///< Data we expect to receive
     vector<uint8_t> send_buffer_;           ///< Server send buffer
     vector<uint8_t> send_buffer_;           ///< Server send buffer
     uint16_t        send_cumulative_;       ///< Data sent so far
     uint16_t        send_cumulative_;       ///< Data sent so far
 
 
@@ -84,6 +85,11 @@ public:
     string          test_data_;             ///< Large string - here for convenience
     string          test_data_;             ///< Large string - here for convenience
     bool            debug_;                 ///< true to enable debug output
     bool            debug_;                 ///< true to enable debug output
     size_t          tcp_send_size_;         ///< Max size of TCP send
     size_t          tcp_send_size_;         ///< Max size of TCP send
+    uint8_t         qid_0;                  ///< First octet of qid
+    uint8_t         qid_1;                  ///< Second octet of qid
+
+    bool            tcp_short_send_;        ///< If set to true, we do not send
+                                            ///  all data in the tcp response
 
 
     /// \brief Constructor
     /// \brief Constructor
     IOFetchTest() :
     IOFetchTest() :
@@ -102,12 +108,16 @@ public:
         cumulative_(0),
         cumulative_(0),
         timer_(service_.get_io_service()),
         timer_(service_.get_io_service()),
         receive_buffer_(),
         receive_buffer_(),
+        expected_buffer_(new OutputBuffer(512)),
         send_buffer_(),
         send_buffer_(),
         send_cumulative_(0),
         send_cumulative_(0),
         return_data_(""),
         return_data_(""),
         test_data_(""),
         test_data_(""),
         debug_(DEBUG),
         debug_(DEBUG),
-        tcp_send_size_(0)
+        tcp_send_size_(0),
+        qid_0(0),
+        qid_1(0),
+        tcp_short_send_(false)
     {
     {
         // Construct the data buffer for question we expect to receive.
         // Construct the data buffer for question we expect to receive.
         Message msg(Message::RENDER);
         Message msg(Message::RENDER);
@@ -118,6 +128,8 @@ public:
         msg.addQuestion(question_);
         msg.addQuestion(question_);
         MessageRenderer renderer(*msgbuf_);
         MessageRenderer renderer(*msgbuf_);
         msg.toWire(renderer);
         msg.toWire(renderer);
+        MessageRenderer renderer2(*expected_buffer_);
+        msg.toWire(renderer2);
 
 
         // Initialize the test data to be returned: tests will return a
         // Initialize the test data to be returned: tests will return a
         // substring of this data. (It's convenient to have this as a member of
         // substring of this data. (It's convenient to have this as a member of
@@ -144,9 +156,14 @@ public:
     /// \param socket Socket to use to send the answer
     /// \param socket Socket to use to send the answer
     /// \param ec ASIO error code, completion code of asynchronous I/O issued
     /// \param ec ASIO error code, completion code of asynchronous I/O issued
     ///        by the "server" to receive data.
     ///        by the "server" to receive data.
+    /// \param bad_qid If set to true, the QID in the response will be mangled
+    /// \param second_send If set to true, (and bad_qid is too), after the
+    ///        mangled qid response has been sent, a second packet will be
+    ///        sent with the correct QID.
     /// \param length Amount of data received.
     /// \param length Amount of data received.
     void udpReceiveHandler(udp::endpoint* remote, udp::socket* socket,
     void udpReceiveHandler(udp::endpoint* remote, udp::socket* socket,
-                    error_code ec = error_code(), size_t length = 0) {
+                    error_code ec = error_code(), size_t length = 0,
+                    bool bad_qid = false, bool second_send = false) {
         if (debug_) {
         if (debug_) {
             cout << "udpReceiveHandler(): error = " << ec.value() <<
             cout << "udpReceiveHandler(): error = " << ec.value() <<
                     ", length = " << length << endl;
                     ", length = " << length << endl;
@@ -155,6 +172,8 @@ public:
         // The QID in the incoming data is random so set it to 0 for the
         // The QID in the incoming data is random so set it to 0 for the
         // data comparison check. (It is set to 0 in the buffer containing
         // data comparison check. (It is set to 0 in the buffer containing
         // the expected data.)
         // the expected data.)
+        qid_0 = receive_buffer_[0];
+        qid_1 = receive_buffer_[1];
         receive_buffer_[0] = receive_buffer_[1] = 0;
         receive_buffer_[0] = receive_buffer_[1] = 0;
 
 
         // Check that length of the received data and the expected data are
         // Check that length of the received data and the expected data are
@@ -164,10 +183,23 @@ public:
         static_cast<const uint8_t*>(msgbuf_->getData())));
         static_cast<const uint8_t*>(msgbuf_->getData())));
 
 
         // Return a message back to the IOFetch object.
         // Return a message back to the IOFetch object.
-        socket->send_to(asio::buffer(return_data_.c_str(), return_data_.size()),
-                                     *remote);
+        if (!bad_qid) {
+            expected_buffer_->writeUint8At(qid_0, 0);
+            expected_buffer_->writeUint8At(qid_1, 1);
+        } else {
+            expected_buffer_->writeUint8At(qid_0 + 1, 0);
+            expected_buffer_->writeUint8At(qid_1 + 1, 1);
+        }
+        socket->send_to(asio::buffer(expected_buffer_->getData(), length), *remote);
+
+        if (bad_qid && second_send) {
+            expected_buffer_->writeUint8At(qid_0, 0);
+            expected_buffer_->writeUint8At(qid_1, 1);
+            socket->send_to(asio::buffer(expected_buffer_->getData(),
+                            expected_buffer_->getLength()), *remote);
+        }
         if (debug_) {
         if (debug_) {
-            cout << "udpReceiveHandler(): returned " << return_data_.size() <<
+            cout << "udpReceiveHandler(): returned " << expected_buffer_->getLength() <<
                     " bytes to the client" << endl;
                     " bytes to the client" << endl;
         }
         }
     }
     }
@@ -249,18 +281,25 @@ public:
         // field the QID in the received buffer is in the third and fourth
         // field the QID in the received buffer is in the third and fourth
         // bytes.
         // bytes.
         EXPECT_EQ(msgbuf_->getLength() + 2, cumulative_);
         EXPECT_EQ(msgbuf_->getLength() + 2, cumulative_);
+        qid_0 = receive_buffer_[2];
+        qid_1 = receive_buffer_[3];
+
         receive_buffer_[2] = receive_buffer_[3] = 0;
         receive_buffer_[2] = receive_buffer_[3] = 0;
         EXPECT_TRUE(equal((receive_buffer_ + 2), (receive_buffer_ + cumulative_ - 2),
         EXPECT_TRUE(equal((receive_buffer_ + 2), (receive_buffer_ + cumulative_ - 2),
             static_cast<const uint8_t*>(msgbuf_->getData())));
             static_cast<const uint8_t*>(msgbuf_->getData())));
 
 
         // ... and return a message back.  This has to be preceded by a two-byte
         // ... and return a message back.  This has to be preceded by a two-byte
         // count field.
         // count field.
+
         send_buffer_.clear();
         send_buffer_.clear();
         send_buffer_.push_back(0);
         send_buffer_.push_back(0);
         send_buffer_.push_back(0);
         send_buffer_.push_back(0);
         writeUint16(return_data_.size(), &send_buffer_[0]);
         writeUint16(return_data_.size(), &send_buffer_[0]);
         copy(return_data_.begin(), return_data_.end(), back_inserter(send_buffer_));
         copy(return_data_.begin(), return_data_.end(), back_inserter(send_buffer_));
-
+        if (return_data_.size() >= 2) {
+            send_buffer_[2] = qid_0;
+            send_buffer_[3] = qid_1;
+        }
         // Send the data.  This is done in multiple writes with a delay between
         // Send the data.  This is done in multiple writes with a delay between
         // each to check that the reassembly of TCP packets from fragments works.
         // each to check that the reassembly of TCP packets from fragments works.
         send_cumulative_ = 0;
         send_cumulative_ = 0;
@@ -298,10 +337,21 @@ public:
             amount = min(tcp_send_size_,
             amount = min(tcp_send_size_,
                         (send_buffer_.size() - send_cumulative_));
                         (send_buffer_.size() - send_cumulative_));
         }
         }
-        if (debug_) {
-            cout << "tcpSendData(): sending " << amount << " bytes" << endl;
-        }
 
 
+        // This is for the short send test; reduce the actual amount of
+        // data we send
+        if (tcp_short_send_) {
+            if (debug_) {
+                cout << "tcpSendData(): sending incomplete data (" <<
+                        (amount - 1) << " of " << amount << " bytes)" <<
+                        endl;
+            }
+            --amount;
+        } else {
+            if (debug_) {
+                cout << "tcpSendData(): sending " << amount << " bytes" << endl;
+            }
+        }
 
 
         // ... and send it.  The amount sent is also passed as the first
         // ... and send it.  The amount sent is also passed as the first
         // argument of the send callback, as a check.
         // argument of the send callback, as a check.
@@ -373,10 +423,23 @@ public:
         // when one of the "servers" in this class has sent back return_data_.
         // when one of the "servers" in this class has sent back return_data_.
         // Check the data is as expected/
         // Check the data is as expected/
         if (expected_ == IOFetch::SUCCESS) {
         if (expected_ == IOFetch::SUCCESS) {
-            EXPECT_EQ(return_data_.size(), result_buff_->getLength());
-
-            const uint8_t* start = static_cast<const uint8_t*>(result_buff_->getData());
-            EXPECT_TRUE(equal(return_data_.begin(), return_data_.end(), start));
+            // In the case of UDP, we actually send back a real looking packet
+            // in the case of TCP, we send back a 'random' string
+            if (protocol_ == IOFetch::UDP) {
+                EXPECT_EQ(expected_buffer_->getLength(), result_buff_->getLength());
+                EXPECT_EQ(0, memcmp(expected_buffer_->getData(), result_buff_->getData(),
+                          expected_buffer_->getLength()));
+            } else {
+                EXPECT_EQ(return_data_.size(), result_buff_->getLength());
+                // Overwrite the random qid with our own data for the
+                // comparison to succeed
+                if (result_buff_->getLength() >= 2) {
+                    result_buff_->writeUint8At(return_data_[0], 0);
+                    result_buff_->writeUint8At(return_data_[1], 1);
+                }
+                const uint8_t* start = static_cast<const uint8_t*>(result_buff_->getData());
+                EXPECT_TRUE(equal(return_data_.begin(), return_data_.end(), start));
+            }
         }
         }
 
 
         // ... and cause the run loop to exit.
         // ... and cause the run loop to exit.
@@ -452,13 +515,20 @@ public:
     /// Send a query to the server then receives a response.
     /// Send a query to the server then receives a response.
     ///
     ///
     /// \param Test data to return to client
     /// \param Test data to return to client
-    void tcpSendReturnTest(const std::string& return_data) {
+    /// \param short_send If true, do not send all data
+    ///                   (should result in timeout)
+    void tcpSendReturnTest(const std::string& return_data, bool short_send = false) {
         if (debug_) {
         if (debug_) {
             cout << "tcpSendReturnTest(): data size = " << return_data.size() << endl;
             cout << "tcpSendReturnTest(): data size = " << return_data.size() << endl;
         }
         }
         return_data_ = return_data;
         return_data_ = return_data;
         protocol_ = IOFetch::TCP;
         protocol_ = IOFetch::TCP;
-        expected_ = IOFetch::SUCCESS;
+        if (short_send) {
+            tcp_short_send_ = true;
+            expected_ = IOFetch::TIME_OUT;
+        } else {
+            expected_ = IOFetch::SUCCESS;
+        }
 
 
         // Socket into which the connection will be accepted.
         // Socket into which the connection will be accepted.
         tcp::socket socket(service_.get_io_service());
         tcp::socket socket(service_.get_io_service());
@@ -481,6 +551,39 @@ public:
         // Tidy up
         // Tidy up
         socket.close();
         socket.close();
     }
     }
+
+    /// Perform a send/receive test over UDP
+    ///
+    /// \param bad_qid If true, do the test where the QID is mangled
+    ///                in the response
+    /// \param second_send If true, do the test where the QID is
+    ///                    mangled in the response, but a second
+    ///                    (correct) packet is used
+    void udpSendReturnTest(bool bad_qid, bool second_send) {
+        protocol_ = IOFetch::UDP;
+
+        // Set up the server.
+        udp::socket socket(service_.get_io_service(), udp::v4());
+        socket.set_option(socket_base::reuse_address(true));
+        socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
+        return_data_ = "Message returned to the client";
+
+        udp::endpoint remote;
+        socket.async_receive_from(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
+            remote,
+            boost::bind(&IOFetchTest::udpReceiveHandler, this, &remote, &socket,
+                        _1, _2, bad_qid, second_send));
+        service_.get_io_service().post(udp_fetch_);
+        if (debug_) {
+            cout << "udpSendReceive: async_receive_from posted, waiting for callback" <<
+                    endl;
+        }
+        service_.run();
+
+        socket.close();
+
+        EXPECT_TRUE(run_);;
+    }
 };
 };
 
 
 // Check the protocol
 // Check the protocol
@@ -507,28 +610,25 @@ TEST_F(IOFetchTest, UdpTimeout) {
 // UDP SendReceive test.  Set up a UDP server then ports a UDP fetch object.
 // UDP SendReceive test.  Set up a UDP server then ports a UDP fetch object.
 // This will send question_ to the server and receive the answer back from it.
 // This will send question_ to the server and receive the answer back from it.
 TEST_F(IOFetchTest, UdpSendReceive) {
 TEST_F(IOFetchTest, UdpSendReceive) {
-    protocol_ = IOFetch::UDP;
     expected_ = IOFetch::SUCCESS;
     expected_ = IOFetch::SUCCESS;
 
 
-    // Set up the server.
-    udp::socket socket(service_.get_io_service(), udp::v4());
-    socket.set_option(socket_base::reuse_address(true));
-    socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
-    return_data_ = "Message returned to the client";
-
-    udp::endpoint remote;
-    socket.async_receive_from(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
-        remote,
-        boost::bind(&IOFetchTest::udpReceiveHandler, this, &remote, &socket,
-                    _1, _2));
-    service_.get_io_service().post(udp_fetch_);
-    if (debug_) {
-        cout << "udpSendReceive: async_receive_from posted, waiting for callback" <<
-                endl;
-    }
-    service_.run();
+    udpSendReturnTest(false, false);
 
 
-    socket.close();
+    EXPECT_TRUE(run_);;
+}
+
+TEST_F(IOFetchTest, UdpSendReceiveBadQid) {
+    expected_ = IOFetch::TIME_OUT;
+
+    udpSendReturnTest(true, false);
+
+    EXPECT_TRUE(run_);;
+}
+
+TEST_F(IOFetchTest, UdpSendReceiveBadQidResend) {
+    expected_ = IOFetch::SUCCESS;
+
+    udpSendReturnTest(true, true);
 
 
     EXPECT_TRUE(run_);;
     EXPECT_TRUE(run_);;
 }
 }
@@ -547,18 +647,20 @@ TEST_F(IOFetchTest, TcpTimeout) {
     timeoutTest(IOFetch::TCP, tcp_fetch_);
     timeoutTest(IOFetch::TCP, tcp_fetch_);
 }
 }
 
 
-// Test with values at or near 0, then at or near the chunk size (16 and 32
+// Test with values at or near 2, then at or near the chunk size (16 and 32
 // bytes, the sizes of the first two packets) then up to 65535.  These are done
 // bytes, the sizes of the first two packets) then up to 65535.  These are done
 // in separate tests because in practice a new IOFetch is created for each
 // in separate tests because in practice a new IOFetch is created for each
 // query/response exchange and we don't want to confuse matters in the test
 // query/response exchange and we don't want to confuse matters in the test
 // by running the test with an IOFetch that has already done one exchange.
 // by running the test with an IOFetch that has already done one exchange.
-
-TEST_F(IOFetchTest, TcpSendReceive0) {
-    tcpSendReturnTest(test_data_.substr(0, 0));
+//
+// Don't do 0 or 1; the server would not accept the packet
+// (since the length is too short to check the qid)
+TEST_F(IOFetchTest, TcpSendReceive2) {
+    tcpSendReturnTest(test_data_.substr(0, 2));
 }
 }
 
 
-TEST_F(IOFetchTest, TcpSendReceive1) {
-    tcpSendReturnTest(test_data_.substr(0, 1));
+TEST_F(IOFetchTest, TcpSendReceive3) {
+    tcpSendReturnTest(test_data_.substr(0, 3));
 }
 }
 
 
 TEST_F(IOFetchTest, TcpSendReceive15) {
 TEST_F(IOFetchTest, TcpSendReceive15) {
@@ -605,4 +707,17 @@ TEST_F(IOFetchTest, TcpSendReceive65535) {
     tcpSendReturnTest(test_data_.substr(0, 65535));
     tcpSendReturnTest(test_data_.substr(0, 65535));
 }
 }
 
 
+TEST_F(IOFetchTest, TcpSendReceive2ShortSend) {
+    tcpSendReturnTest(test_data_.substr(0, 2), true);
+}
+
+TEST_F(IOFetchTest, TcpSendReceive15ShortSend) {
+    tcpSendReturnTest(test_data_.substr(0, 15), true);
+}
+
+TEST_F(IOFetchTest, TcpSendReceive8192ShortSend) {
+    tcpSendReturnTest(test_data_.substr(0, 8192), true);
+}
+
+
 } // namespace asiolink
 } // namespace asiolink

+ 13 - 14
src/lib/asiolink/udp_server.cc

@@ -24,6 +24,7 @@
 #include <log/dummylog.h>
 #include <log/dummylog.h>
 
 
 #include <asio.hpp>
 #include <asio.hpp>
+#include <asio/error.hpp>
 #include <asiolink/dummy_io_cb.h>
 #include <asiolink/dummy_io_cb.h>
 #include <asiolink/udp_endpoint.h>
 #include <asiolink/udp_endpoint.h>
 #include <asiolink/udp_server.h>
 #include <asiolink/udp_server.h>
@@ -56,7 +57,7 @@ struct UDPServer::Data {
      */
      */
     Data(io_service& io_service, const ip::address& addr, const uint16_t port,
     Data(io_service& io_service, const ip::address& addr, const uint16_t port,
         SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
         SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
-        io_(io_service), done_(false), stopped_by_hand_(false),
+        io_(io_service), done_(false),
         checkin_callback_(checkin),lookup_callback_(lookup),
         checkin_callback_(checkin),lookup_callback_(lookup),
         answer_callback_(answer)
         answer_callback_(answer)
     {
     {
@@ -80,7 +81,6 @@ struct UDPServer::Data {
      */
      */
     Data(const Data& other) :
     Data(const Data& other) :
         io_(other.io_), socket_(other.socket_), done_(false),
         io_(other.io_), socket_(other.socket_), done_(false),
-        stopped_by_hand_(false),
         checkin_callback_(other.checkin_callback_),
         checkin_callback_(other.checkin_callback_),
         lookup_callback_(other.lookup_callback_),
         lookup_callback_(other.lookup_callback_),
         answer_callback_(other.answer_callback_)
         answer_callback_(other.answer_callback_)
@@ -144,8 +144,6 @@ struct UDPServer::Data {
     size_t bytes_;
     size_t bytes_;
     bool done_;
     bool done_;
 
 
-    //whether user explicitly stop the server
-    bool stopped_by_hand_;
 
 
     // Callback functions provided by the caller
     // Callback functions provided by the caller
     const SimpleCallback* checkin_callback_;
     const SimpleCallback* checkin_callback_;
@@ -174,12 +172,6 @@ UDPServer::operator()(error_code ec, size_t length) {
     /// a switch statement, inline variable declarations are not
     /// a switch statement, inline variable declarations are not
     /// permitted.  Certain variables used below can be declared here.
     /// permitted.  Certain variables used below can be declared here.
 
 
-    /// if user stopped the server, we won't enter the coroutine body
-    /// just return
-    if (data_->stopped_by_hand_) {
-        return;
-    }
-
     CORO_REENTER (this) {
     CORO_REENTER (this) {
         do {
         do {
             /*
             /*
@@ -196,7 +188,9 @@ UDPServer::operator()(error_code ec, size_t length) {
                 CORO_YIELD data_->socket_->async_receive_from(
                 CORO_YIELD data_->socket_->async_receive_from(
                     buffer(data_->data_.get(), MAX_LENGTH), *data_->sender_,
                     buffer(data_->data_.get(), MAX_LENGTH), *data_->sender_,
                     *this);
                     *this);
+
                 // Abort on fatal errors
                 // Abort on fatal errors
+                // TODO: add log
                 if (ec) {
                 if (ec) {
                     using namespace asio::error;
                     using namespace asio::error;
                     if (ec.value() != would_block && ec.value() != try_again &&
                     if (ec.value() != would_block && ec.value() != try_again &&
@@ -204,6 +198,7 @@ UDPServer::operator()(error_code ec, size_t length) {
                         return;
                         return;
                     }
                     }
                 }
                 }
+
             } while (ec || length == 0);
             } while (ec || length == 0);
 
 
             data_->bytes_ = length;
             data_->bytes_ = length;
@@ -298,10 +293,14 @@ UDPServer::asyncLookup() {
 /// Stop the UDPServer
 /// Stop the UDPServer
 void
 void
 UDPServer::stop() {
 UDPServer::stop() {
-    //server should not be stopped twice
-    if (data_->stopped_by_hand_)
-        return;
-    data_->stopped_by_hand_ = true;
+    /// Using close instead of cancel, because cancel
+    /// will only cancel the asynchornized event already submitted
+    /// to io service, the events post to io service after
+    /// cancel still can be scheduled by io service, if
+    /// the socket is cloesed, all the asynchronized event
+    /// for it won't be scheduled by io service not matter it is
+    /// submit to io serice before or after close call. And we will
+    //. get bad_descriptor error
     data_->socket_->close();
     data_->socket_->close();
 }
 }
 
 

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

@@ -29,5 +29,6 @@ libcache_la_SOURCES  += rrset_entry.h rrset_entry.cc
 libcache_la_SOURCES  += cache_entry_key.h cache_entry_key.cc
 libcache_la_SOURCES  += cache_entry_key.h cache_entry_key.cc
 libcache_la_SOURCES  += rrset_copy.h rrset_copy.cc
 libcache_la_SOURCES  += rrset_copy.h rrset_copy.cc
 libcache_la_SOURCES  += local_zone_data.h local_zone_data.cc
 libcache_la_SOURCES  += local_zone_data.h local_zone_data.cc
+libcache_la_SOURCES  += message_utility.h message_utility.cc
 
 
 CLEANFILES = *.gcno *.gcda
 CLEANFILES = *.gcno *.gcda

+ 4 - 0
src/lib/cache/TODO

@@ -11,4 +11,8 @@
   to expire.
   to expire.
 * When the rrset beging updated is an NS rrset, NSAS should be updated
 * When the rrset beging updated is an NS rrset, NSAS should be updated
   together.
   together.
+* Share the NXDOMAIN info between different type queries. current implementation
+  can only cache for the type that user quired, for example, if user query A 
+  record of a.example. and the server replied with NXDOMAIN, this should be
+  cached for all the types queries of a.example.
 
 

+ 22 - 7
src/lib/cache/message_cache.cc

@@ -18,25 +18,34 @@
 #include <nsas/hash_table.h>
 #include <nsas/hash_table.h>
 #include <nsas/hash_deleter.h>
 #include <nsas/hash_deleter.h>
 #include "message_cache.h"
 #include "message_cache.h"
+#include "message_utility.h"
 #include "cache_entry_key.h"
 #include "cache_entry_key.h"
 
 
+namespace isc {
+namespace cache {
+
 using namespace isc::nsas;
 using namespace isc::nsas;
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace std;
 using namespace std;
+using namespace MessageUtility;
 
 
-namespace isc {
-namespace cache {
-
-MessageCache::MessageCache(boost::shared_ptr<RRsetCache> rrset_cache,
-    uint32_t cache_size, uint16_t message_class):
+MessageCache::MessageCache(const RRsetCachePtr& rrset_cache,
+                           uint32_t cache_size, uint16_t message_class,
+                           const RRsetCachePtr& negative_soa_cache):
     message_class_(message_class),
     message_class_(message_class),
     rrset_cache_(rrset_cache),
     rrset_cache_(rrset_cache),
+    negative_soa_cache_(negative_soa_cache),
     message_table_(new NsasEntryCompare<MessageEntry>, cache_size),
     message_table_(new NsasEntryCompare<MessageEntry>, cache_size),
     message_lru_((3 * cache_size),
     message_lru_((3 * cache_size),
                   new HashDeleter<MessageEntry>(message_table_))
                   new HashDeleter<MessageEntry>(message_table_))
 {
 {
 }
 }
 
 
+MessageCache::~MessageCache() {
+    // Destroy all the message entries in the cache.
+    message_lru_.clear();
+}
+
 bool
 bool
 MessageCache::lookup(const isc::dns::Name& qname,
 MessageCache::lookup(const isc::dns::Name& qname,
                      const isc::dns::RRType& qtype,
                      const isc::dns::RRType& qtype,
@@ -63,8 +72,13 @@ MessageCache::lookup(const isc::dns::Name& qname,
 
 
 bool
 bool
 MessageCache::update(const Message& msg) {
 MessageCache::update(const Message& msg) {
+    if (!canMessageBeCached(msg)){
+        return (false);
+    }
+
     QuestionIterator iter = msg.beginQuestion();
     QuestionIterator iter = msg.beginQuestion();
-    std::string entry_name = genCacheEntryName((*iter)->getName(), (*iter)->getType());
+    std::string entry_name = genCacheEntryName((*iter)->getName(),
+                                               (*iter)->getType());
     HashKey entry_key = HashKey(entry_name, RRClass(message_class_));
     HashKey entry_key = HashKey(entry_name, RRClass(message_class_));
 
 
     // The simplest way to update is removing the old message entry directly.
     // The simplest way to update is removing the old message entry directly.
@@ -77,7 +91,8 @@ MessageCache::update(const Message& msg) {
         message_lru_.remove(old_msg_entry);
         message_lru_.remove(old_msg_entry);
     }
     }
 
 
-    MessageEntryPtr msg_entry(new MessageEntry(msg, rrset_cache_));
+    MessageEntryPtr msg_entry(new MessageEntry(msg, rrset_cache_,
+                                               negative_soa_cache_));
     message_lru_.add(msg_entry);
     message_lru_.add(msg_entry);
     return (message_table_.add(msg_entry, entry_key, true));
     return (message_table_.add(msg_entry, entry_key, true));
 }
 }

+ 12 - 6
src/lib/cache/message_cache.h

@@ -21,12 +21,11 @@
 #include "message_entry.h"
 #include "message_entry.h"
 #include <nsas/hash_table.h>
 #include <nsas/hash_table.h>
 #include <nsas/lru_list.h>
 #include <nsas/lru_list.h>
+#include "rrset_cache.h"
 
 
 namespace isc {
 namespace isc {
 namespace cache {
 namespace cache {
 
 
-class RRsetCache;
-
 /// \brief Message Cache
 /// \brief Message Cache
 /// The object of MessageCache represents the cache for class-specific
 /// The object of MessageCache represents the cache for class-specific
 /// messages.
 /// messages.
@@ -37,12 +36,18 @@ private:
     MessageCache(const MessageCache& source);
     MessageCache(const MessageCache& source);
     MessageCache& operator=(const MessageCache& source);
     MessageCache& operator=(const MessageCache& source);
 public:
 public:
+    /// \param rrset_cache The cache that stores the RRsets that the
+    ///        message entry will points to
     /// \param cache_size The size of message cache.
     /// \param cache_size The size of message cache.
-    MessageCache(boost::shared_ptr<RRsetCache> rrset_cache_,
-                 uint32_t cache_size, uint16_t message_class);
+    /// \param message_class The class of the message cache
+    /// \param negative_soa_cache The cache that stores the SOA record
+    ///        that comes from negative response message
+    MessageCache(const RRsetCachePtr& rrset_cache,
+                 uint32_t cache_size, uint16_t message_class,
+                 const RRsetCachePtr& negative_soa_cache);
 
 
     /// \brief Destructor function
     /// \brief Destructor function
-    virtual ~MessageCache() {}
+    virtual ~MessageCache();
 
 
     /// \brief Look up message in cache.
     /// \brief Look up message in cache.
     /// \param message generated response message if the message entry
     /// \param message generated response message if the message entry
@@ -84,7 +89,8 @@ protected:
     // Make these variants be protected for easy unittest.
     // Make these variants be protected for easy unittest.
 protected:
 protected:
     uint16_t message_class_; // The class of the message cache.
     uint16_t message_class_; // The class of the message cache.
-    boost::shared_ptr<RRsetCache> rrset_cache_;
+    RRsetCachePtr rrset_cache_;
+    RRsetCachePtr negative_soa_cache_;
     isc::nsas::HashTable<MessageEntry> message_table_;
     isc::nsas::HashTable<MessageEntry> message_table_;
     isc::nsas::LruList<MessageEntry> message_lru_;
     isc::nsas::LruList<MessageEntry> message_lru_;
 };
 };

+ 80 - 9
src/lib/cache/message_entry.cc

@@ -18,6 +18,7 @@
 #include <dns/message.h>
 #include <dns/message.h>
 #include <nsas/nsas_entry.h>
 #include <nsas/nsas_entry.h>
 #include "message_entry.h"
 #include "message_entry.h"
+#include "message_utility.h"
 #include "rrset_cache.h"
 #include "rrset_cache.h"
 
 
 using namespace isc::dns;
 using namespace isc::dns;
@@ -56,9 +57,27 @@ namespace cache {
 
 
 static uint32_t MAX_UINT32 = numeric_limits<uint32_t>::max();
 static uint32_t MAX_UINT32 = numeric_limits<uint32_t>::max();
 
 
+// As with caching positive responses it is sensible for a resolver to
+// limit for how long it will cache a negative response as the protocol
+// supports caching for up to 68 years.  Such a limit should not be
+// greater than that applied to positive answers and preferably be
+// tunable.  Values of one to three hours have been found to work well
+// and would make sensible a default.  Values exceeding one day have
+// been found to be problematic. (sec 5, RFC2308)
+// The default value is 3 hourse (10800 seconds)
+// TODO:Give an option to let user configure
+static uint32_t MAX_NEGATIVE_CACHE_TTL = 10800;
+
+// Sets the maximum time for which the server will cache ordinary (positive) answers. The
+// default is one week (7 days = 604800 seconds)
+// TODO:Give an option to let user configure
+static uint32_t MAX_NORMAL_CACHE_TTL = 604800;
+
 MessageEntry::MessageEntry(const isc::dns::Message& msg,
 MessageEntry::MessageEntry(const isc::dns::Message& msg,
-                           boost::shared_ptr<RRsetCache> rrset_cache):
+                           const RRsetCachePtr& rrset_cache,
+                           const RRsetCachePtr& negative_soa_cache):
     rrset_cache_(rrset_cache),
     rrset_cache_(rrset_cache),
+    negative_soa_cache_(negative_soa_cache),
     headerflag_aa_(false),
     headerflag_aa_(false),
     headerflag_tc_(false)
     headerflag_tc_(false)
 {
 {
@@ -74,7 +93,8 @@ MessageEntry::getRRsetEntries(vector<RRsetEntryPtr>& rrset_entry_vec,
     uint16_t entry_count = answer_count_ + authority_count_ + additional_count_;
     uint16_t entry_count = answer_count_ + authority_count_ + additional_count_;
     rrset_entry_vec.reserve(rrset_entry_vec.size() + entry_count);
     rrset_entry_vec.reserve(rrset_entry_vec.size() + entry_count);
     for (int index = 0; index < entry_count; ++index) {
     for (int index = 0; index < entry_count; ++index) {
-        RRsetEntryPtr rrset_entry = rrset_cache_->lookup(rrsets_[index].name_,
+        RRsetCache* rrset_cache = rrsets_[index].cache_;
+        RRsetEntryPtr rrset_entry = rrset_cache->lookup(rrsets_[index].name_,
                                                         rrsets_[index].type_);
                                                         rrsets_[index].type_);
         if (rrset_entry && time_now < rrset_entry->getExpireTime()) {
         if (rrset_entry && time_now < rrset_entry->getExpireTime()) {
             rrset_entry_vec.push_back(rrset_entry);
             rrset_entry_vec.push_back(rrset_entry);
@@ -104,8 +124,9 @@ MessageEntry::addRRset(isc::dns::Message& message,
         end_index = start_index + additional_count_;
         end_index = start_index + additional_count_;
     }
     }
 
 
-    for(uint16_t index = start_index; index < end_index; ++index) {
-        message.addRRset(section, rrset_entry_vec[index]->getRRset(), dnssec_need);
+    for (uint16_t index = start_index; index < end_index; ++index) {
+        message.addRRset(section, rrset_entry_vec[index]->getRRset(),
+                         dnssec_need);
     }
     }
 }
 }
 
 
@@ -127,7 +148,9 @@ MessageEntry::genMessage(const time_t& time_now,
         // Begin message generation. We don't need to add question
         // Begin message generation. We don't need to add question
         // section, since it has been included in the message.
         // section, since it has been included in the message.
         // Set cached header flags.
         // Set cached header flags.
-        msg.setHeaderFlag(Message::HEADERFLAG_AA, headerflag_aa_);
+        // The AA flag bit should be cleared because this is a response from
+        // resolver cache
+        msg.setHeaderFlag(Message::HEADERFLAG_AA, false);
         msg.setHeaderFlag(Message::HEADERFLAG_TC, headerflag_tc_);
         msg.setHeaderFlag(Message::HEADERFLAG_TC, headerflag_tc_);
 
 
         bool dnssec_need = msg.getEDNS().get();
         bool dnssec_need = msg.getEDNS().get();
@@ -233,7 +256,8 @@ MessageEntry::parseSection(const isc::dns::Message& msg,
         RRsetPtr rrset_ptr = *iter;
         RRsetPtr rrset_ptr = *iter;
         RRsetTrustLevel level = getRRsetTrustLevel(msg, rrset_ptr, section);
         RRsetTrustLevel level = getRRsetTrustLevel(msg, rrset_ptr, section);
         RRsetEntryPtr rrset_entry = rrset_cache_->update(*rrset_ptr, level);
         RRsetEntryPtr rrset_entry = rrset_cache_->update(*rrset_ptr, level);
-        rrsets_.push_back(RRsetRef(rrset_ptr->getName(), rrset_ptr->getType()));
+        rrsets_.push_back(RRsetRef(rrset_ptr->getName(), rrset_ptr->getType(),
+                          rrset_cache_.get()));
 
 
         uint32_t rrset_ttl = rrset_entry->getTTL();
         uint32_t rrset_ttl = rrset_entry->getTTL();
         if (smaller_ttl > rrset_ttl) {
         if (smaller_ttl > rrset_ttl) {
@@ -247,6 +271,37 @@ MessageEntry::parseSection(const isc::dns::Message& msg,
 }
 }
 
 
 void
 void
+MessageEntry::parseNegativeResponseAuthoritySection(const isc::dns::Message& msg,
+        uint32_t& min_ttl,
+        uint16_t& rrset_count)
+{
+    uint16_t count = 0;
+    for (RRsetIterator iter = msg.beginSection(Message::SECTION_AUTHORITY);
+            iter != msg.endSection(Message::SECTION_AUTHORITY);
+            ++iter) {
+        RRsetPtr rrset_ptr = *iter;
+        RRsetTrustLevel level = getRRsetTrustLevel(msg, rrset_ptr,
+                                                   Message::SECTION_AUTHORITY);
+        boost::shared_ptr<RRsetCache> rrset_cache_ptr = rrset_cache_;
+        if (rrset_ptr->getType() == RRType::SOA()) {
+            rrset_cache_ptr = negative_soa_cache_;
+        }
+
+        RRsetEntryPtr rrset_entry = rrset_cache_ptr->update(*rrset_ptr, level);
+        rrsets_.push_back(RRsetRef(rrset_ptr->getName(),
+                                   rrset_ptr->getType(),
+                                   rrset_cache_ptr.get()));
+        uint32_t rrset_ttl = rrset_entry->getTTL();
+        if (min_ttl > rrset_ttl) {
+            min_ttl = rrset_ttl;
+        }
+        ++count;
+    }
+
+    rrset_count = count;
+}
+
+void
 MessageEntry::initMessageEntry(const isc::dns::Message& msg) {
 MessageEntry::initMessageEntry(const isc::dns::Message& msg) {
     //TODO better way to cache the header flags?
     //TODO better way to cache the header flags?
     headerflag_aa_ = msg.getHeaderFlag(Message::HEADERFLAG_AA);
     headerflag_aa_ = msg.getHeaderFlag(Message::HEADERFLAG_AA);
@@ -261,14 +316,30 @@ MessageEntry::initMessageEntry(const isc::dns::Message& msg) {
     query_class_ = (*iter)->getClass().getCode();
     query_class_ = (*iter)->getClass().getCode();
 
 
     uint32_t min_ttl = MAX_UINT32;
     uint32_t min_ttl = MAX_UINT32;
+
+    bool isNegativeResponse = MessageUtility::isNegativeResponse(msg);
+
     parseSection(msg, Message::SECTION_ANSWER, min_ttl, answer_count_);
     parseSection(msg, Message::SECTION_ANSWER, min_ttl, answer_count_);
-    parseSection(msg, Message::SECTION_AUTHORITY, min_ttl, authority_count_);
+    if (!isNegativeResponse) {
+        parseSection(msg, Message::SECTION_AUTHORITY, min_ttl, authority_count_);
+    } else {
+        parseNegativeResponseAuthoritySection(msg, min_ttl, authority_count_);
+    }
     parseSection(msg, Message::SECTION_ADDITIONAL, min_ttl, additional_count_);
     parseSection(msg, Message::SECTION_ADDITIONAL, min_ttl, additional_count_);
 
 
+    // Limit the ttl to a prset max-value
+    if (!isNegativeResponse) {
+        if (min_ttl > MAX_NORMAL_CACHE_TTL) {
+            min_ttl = MAX_NORMAL_CACHE_TTL;
+        }
+    } else {
+        if (min_ttl > MAX_NEGATIVE_CACHE_TTL) {
+            min_ttl = MAX_NEGATIVE_CACHE_TTL;
+        }
+    }
+
     expire_time_ = time(NULL) + min_ttl;
     expire_time_ = time(NULL) + min_ttl;
 }
 }
 
 
 } // namespace cache
 } // namespace cache
 } // namespace isc
 } // namespace isc
-
-

+ 42 - 21
src/lib/cache/message_entry.h

@@ -19,33 +19,15 @@
 #include <dns/message.h>
 #include <dns/message.h>
 #include <dns/rrset.h>
 #include <dns/rrset.h>
 #include <nsas/nsas_entry.h>
 #include <nsas/nsas_entry.h>
+#include "rrset_cache.h"
 #include "rrset_entry.h"
 #include "rrset_entry.h"
 
 
-
 using namespace isc::nsas;
 using namespace isc::nsas;
 
 
 namespace isc {
 namespace isc {
 namespace cache {
 namespace cache {
 
 
 class RRsetEntry;
 class RRsetEntry;
-class RRsetCache;
-
-/// \brief Information to refer an RRset.
-///
-/// There is no class information here, since the rrsets are cached in
-/// the class-specific rrset cache.
-struct RRsetRef{
-    /// \brief Constructor
-    ///
-    /// \param name The Name for the RRset
-    /// \param type the RRType for the RRrset
-    RRsetRef(const isc::dns::Name& name, const isc::dns::RRType& type):
-            name_(name), type_(type)
-    {}
-
-    isc::dns::Name name_; // Name of rrset.
-    isc::dns::RRType type_; // Type of rrset.
-};
 
 
 /// \brief Message Entry
 /// \brief Message Entry
 ///
 ///
@@ -56,6 +38,27 @@ class MessageEntry : public NsasEntry<MessageEntry> {
 private:
 private:
     MessageEntry(const MessageEntry& source);
     MessageEntry(const MessageEntry& source);
     MessageEntry& operator=(const MessageEntry& source);
     MessageEntry& operator=(const MessageEntry& source);
+
+    /// \brief Information to refer an RRset.
+    ///
+    /// There is no class information here, since the rrsets are cached in
+    /// the class-specific rrset cache.
+    struct RRsetRef{
+        /// \brief Constructor
+        ///
+        /// \param name The Name for the RRset
+        /// \param type The RRType for the RRrset
+        /// \param cache Which cache the RRset is stored in
+        RRsetRef(const isc::dns::Name& name, const isc::dns::RRType& type,
+                RRsetCache* cache):
+                name_(name), type_(type), cache_(cache)
+        {}
+
+        isc::dns::Name name_; // Name of rrset.
+        isc::dns::RRType type_; // Type of rrset.
+        RRsetCache* cache_; //Which cache the RRset is stored
+    };
+
 public:
 public:
 
 
     /// \brief Initialize the message entry object with one dns
     /// \brief Initialize the message entry object with one dns
@@ -66,8 +69,14 @@ public:
     ///        since some new rrset entries may be inserted into
     ///        since some new rrset entries may be inserted into
     ///        rrset cache, or the existed rrset entries need
     ///        rrset cache, or the existed rrset entries need
     ///        to be updated.
     ///        to be updated.
+    /// \param negative_soa_cache the pointer of RRsetCache. This
+    ///        cache is used only for storing SOA rrset from negative
+    ///        response (NXDOMAIN or NOERROR_NODATA)
     MessageEntry(const isc::dns::Message& message,
     MessageEntry(const isc::dns::Message& message,
-                 boost::shared_ptr<RRsetCache> rrset_cache);
+                 const RRsetCachePtr& rrset_cache,
+                 const RRsetCachePtr& negative_soa_cache);
+
+    ~MessageEntry() { delete hash_key_ptr_; };
 
 
     /// \brief generate one dns message according
     /// \brief generate one dns message according
     ///        the rrsets information of the message.
     ///        the rrsets information of the message.
@@ -115,6 +124,16 @@ protected:
                       uint32_t& smaller_ttl,
                       uint32_t& smaller_ttl,
                       uint16_t& rrset_count);
                       uint16_t& rrset_count);
 
 
+    /// \brief Parse the RRsets in the authority section of
+    ///        negative response. The SOA RRset need to be located and
+    ///        stored in a seperate cache
+    /// \param msg The message to parse the RRsets from
+    /// \param min_ttl Get the minimum ttl of rrset in the authority section
+    /// \param rrset_count the rrset count of the authority section
+    void parseNegativeResponseAuthoritySection(const isc::dns::Message& msg,
+            uint32_t& min_ttl,
+            uint16_t& rrset_count);
+
     /// \brief Get RRset Trustworthiness
     /// \brief Get RRset Trustworthiness
     ///        The algorithm refers to RFC2181 section 5.4.1
     ///        The algorithm refers to RFC2181 section 5.4.1
     ///        Only the rrset can be updated by the rrsets
     ///        Only the rrset can be updated by the rrsets
@@ -159,7 +178,9 @@ private:
     HashKey* hash_key_ptr_;  // the key for messag entry in hash table.
     HashKey* hash_key_ptr_;  // the key for messag entry in hash table.
 
 
     std::vector<RRsetRef> rrsets_;
     std::vector<RRsetRef> rrsets_;
-    boost::shared_ptr<RRsetCache> rrset_cache_;
+    RRsetCachePtr rrset_cache_; //Normal rrset cache
+    // SOA rrset from negative response
+    RRsetCachePtr negative_soa_cache_;
 
 
     std::string query_name_; // query name of the message.
     std::string query_name_; // query name of the message.
     uint16_t query_class_; // query class of the message.
     uint16_t query_class_; // query class of the message.

+ 80 - 0
src/lib/cache/message_utility.cc

@@ -0,0 +1,80 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include "message_utility.h"
+#include <dns/rcode.h>
+
+using namespace isc::dns;
+
+namespace isc {
+namespace cache {
+namespace MessageUtility{
+
+bool
+hasTheRecordInAuthoritySection(const isc::dns::Message& msg,
+                               const isc::dns::RRType& type)
+{
+    // isc::dns::Message provide one function hasRRset() should be used to
+    // determine whether the given section has an RRset matching the given
+    // name and type, but currently it is not const-qualified and cannot be
+    // used here
+    // TODO: use hasRRset() function when it is const qualified
+    for (RRsetIterator iter = msg.beginSection(Message::SECTION_AUTHORITY);
+            iter != msg.endSection(Message::SECTION_AUTHORITY);
+            ++iter) {
+        RRsetPtr rrset_ptr = *iter;
+        if (rrset_ptr->getType() == type) {
+            return (true);
+        }
+    }
+    return (false);
+}
+
+bool
+isNegativeResponse(const isc::dns::Message& msg) {
+    if (msg.getRcode() == Rcode::NXDOMAIN()) {
+        return (true);
+    } else if (msg.getRcode() == Rcode::NOERROR()) {
+        // no data in the answer section
+        if (msg.getRRCount(Message::SECTION_ANSWER) == 0) {
+            // NODATA type 1/ type 2 (ref sec2.2 of RFC2308)
+            if (hasTheRecordInAuthoritySection(msg, RRType::SOA())) {
+                return (true);
+            } else if (!hasTheRecordInAuthoritySection(msg, RRType::NS())) {
+                // NODATA type 3 (sec2.2 of RFC2308)
+                return (true);
+            }
+        }
+    }
+
+    return (false);
+}
+
+bool
+canMessageBeCached(const isc::dns::Message& msg) {
+    // If the message is a negative response, but no SOA record is found in
+    // the authority section, the message cannot be cached
+    if (isNegativeResponse(msg) &&
+        !hasTheRecordInAuthoritySection(msg, RRType::SOA())){
+        return (false);
+    }
+
+    return (true);
+}
+
+} // namespace MessageUtility
+} // namespace cache
+} // namespace isc

+ 66 - 0
src/lib/cache/message_utility.h

@@ -0,0 +1,66 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __MESSAGE_UTILITY_H
+#define __MESSAGE_UTILITY_H
+
+#include <dns/message.h>
+
+namespace isc {
+namespace cache {
+
+/// \brief Some utility functions to extract info from message
+///
+/// We need to check the message before cache it, for example, if no SOA
+/// record is found in the Authority section of NXDOMAIN response, the
+/// message cannot be cached
+namespace MessageUtility{
+
+/// \brief Check whether there is some type of record in
+///        Authority section
+///
+/// \param msg The response message to be checked
+/// \param type The RR type that need to check
+bool hasTheRecordInAuthoritySection(const isc::dns::Message& msg,
+                                    const isc::dns::RRType& type);
+
+/// \brief Check whetehr the message is a negative response
+///        (NXDOMAIN or NOERROR_NODATA)
+///
+/// \param msg The response message
+bool isNegativeResponse(const isc::dns::Message& msg);
+
+/// \brief Check whether the message can be cached
+///        Negative responses without SOA records SHOULD NOT be cached as there
+///        is no way to prevent the negative responses looping forever between a
+///        pair of servers even with a short TTL.
+///        Despite the DNS forming a tree of servers, with various mis-
+///        configurations it is possible to form a loop in the query graph, e.g.
+///        two servers listing each other as forwarders, various lame server
+///        configurations.  Without a TTL count down a cache negative response
+///        when received by the next server would have its TTL reset.  This
+///        negative indication could then live forever circulating between the
+///        servers involved. (Sec 5, RFC2308)
+///
+/// \param msg The response message
+bool canMessageBeCached(const isc::dns::Message& msg);
+
+} // namespace MessageUtility
+} // namespace cache
+} // namespace isc
+
+
+#endif//__MESSAGE_UTILITY_H

+ 18 - 9
src/lib/cache/resolver_cache.cc

@@ -32,12 +32,17 @@ ResolverClassCache::ResolverClassCache(const RRClass& cache_class) :
     local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(cache_class_.getCode()));
     local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(cache_class_.getCode()));
     rrsets_cache_ = RRsetCachePtr(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE,
     rrsets_cache_ = RRsetCachePtr(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE,
                                                  cache_class_.getCode()));
                                                  cache_class_.getCode()));
+    // SOA rrset cache from negative response
+    negative_soa_cache_ = RRsetCachePtr(new RRsetCache(NEGATIVE_RRSET_CACHE_DEFAULT_SIZE,
+                                                       cache_class_.getCode()));
+
     messages_cache_ = MessageCachePtr(new MessageCache(rrsets_cache_,
     messages_cache_ = MessageCachePtr(new MessageCache(rrsets_cache_,
                                       MESSAGE_CACHE_DEFAULT_SIZE,
                                       MESSAGE_CACHE_DEFAULT_SIZE,
-                                      cache_class_.getCode()));
+                                      cache_class_.getCode(),
+                                      negative_soa_cache_));
 }
 }
 
 
-ResolverClassCache::ResolverClassCache(CacheSizeInfo cache_info) :
+ResolverClassCache::ResolverClassCache(const CacheSizeInfo& cache_info) :
     cache_class_(cache_info.cclass)
     cache_class_(cache_info.cclass)
 {
 {
     uint16_t klass = cache_class_.getCode();
     uint16_t klass = cache_class_.getCode();
@@ -45,14 +50,18 @@ ResolverClassCache::ResolverClassCache(CacheSizeInfo cache_info) :
     local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(klass));
     local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(klass));
     rrsets_cache_ = RRsetCachePtr(new
     rrsets_cache_ = RRsetCachePtr(new
                         RRsetCache(cache_info.rrset_cache_size, klass));
                         RRsetCache(cache_info.rrset_cache_size, klass));
+    // SOA rrset cache from negative response
+    negative_soa_cache_ = RRsetCachePtr(new RRsetCache(cache_info.rrset_cache_size,
+                                                       klass));
+
     messages_cache_ = MessageCachePtr(new MessageCache(rrsets_cache_,
     messages_cache_ = MessageCachePtr(new MessageCache(rrsets_cache_,
                                       cache_info.message_cache_size,
                                       cache_info.message_cache_size,
-                                      klass));
+                                      klass, negative_soa_cache_));
 }
 }
 
 
 const RRClass&
 const RRClass&
 ResolverClassCache::getClass() const {
 ResolverClassCache::getClass() const {
-    return cache_class_;
+    return (cache_class_);
 }
 }
 
 
 bool
 bool
@@ -104,7 +113,7 @@ ResolverClassCache::update(const isc::dns::Message& msg) {
 }
 }
 
 
 bool
 bool
-ResolverClassCache::updateRRsetCache(const isc::dns::ConstRRsetPtr rrset_ptr,
+ResolverClassCache::updateRRsetCache(const isc::dns::ConstRRsetPtr& rrset_ptr,
                                 RRsetCachePtr rrset_cache_ptr)
                                 RRsetCachePtr rrset_cache_ptr)
 {
 {
     RRsetTrustLevel level;
     RRsetTrustLevel level;
@@ -120,7 +129,7 @@ ResolverClassCache::updateRRsetCache(const isc::dns::ConstRRsetPtr rrset_ptr,
 }
 }
 
 
 bool
 bool
-ResolverClassCache::update(const isc::dns::ConstRRsetPtr rrset_ptr) {
+ResolverClassCache::update(const isc::dns::ConstRRsetPtr& rrset_ptr) {
     // First update local zone, then update rrset cache.
     // First update local zone, then update rrset cache.
     local_zone_data_->update((*rrset_ptr.get()));
     local_zone_data_->update((*rrset_ptr.get()));
     updateRRsetCache(rrset_ptr, rrsets_cache_);
     updateRRsetCache(rrset_ptr, rrsets_cache_);
@@ -209,7 +218,7 @@ ResolverCache::update(const isc::dns::Message& msg) {
 }
 }
 
 
 bool
 bool
-ResolverCache::update(const isc::dns::ConstRRsetPtr rrset_ptr) {
+ResolverCache::update(const isc::dns::ConstRRsetPtr& rrset_ptr) {
     ResolverClassCache* cc = getClassCache(rrset_ptr->getClass());
     ResolverClassCache* cc = getClassCache(rrset_ptr->getClass());
     if (cc) {
     if (cc) {
         return (cc->update(rrset_ptr));
         return (cc->update(rrset_ptr));
@@ -232,10 +241,10 @@ ResolverClassCache*
 ResolverCache::getClassCache(const isc::dns::RRClass& cache_class) const {
 ResolverCache::getClassCache(const isc::dns::RRClass& cache_class) const {
     for (int i = 0; i < class_caches_.size(); ++i) {
     for (int i = 0; i < class_caches_.size(); ++i) {
         if (class_caches_[i]->getClass() == cache_class) {
         if (class_caches_[i]->getClass() == cache_class) {
-            return class_caches_[i];
+            return (class_caches_[i]);
         }
         }
     }
     }
-    return NULL;
+    return (NULL);
 }
 }
 
 
 } // namespace cache
 } // namespace cache

+ 16 - 7
src/lib/cache/resolver_cache.h

@@ -32,6 +32,7 @@ class RRsetCache;
 //TODO a better proper default cache size
 //TODO a better proper default cache size
 #define MESSAGE_CACHE_DEFAULT_SIZE 10000
 #define MESSAGE_CACHE_DEFAULT_SIZE 10000
 #define RRSET_CACHE_DEFAULT_SIZE   20000
 #define RRSET_CACHE_DEFAULT_SIZE   20000
+#define NEGATIVE_RRSET_CACHE_DEFAULT_SIZE   10000
 
 
 /// \brief Cache Size Information.
 /// \brief Cache Size Information.
 ///
 ///
@@ -44,7 +45,7 @@ public:
     /// \param cls The RRClass code
     /// \param cls The RRClass code
     /// \param msg_cache_size The size for the message cache
     /// \param msg_cache_size The size for the message cache
     /// \param rst_cache_size The size for the RRset cache
     /// \param rst_cache_size The size for the RRset cache
-    CacheSizeInfo(const isc::dns::RRClass& cls, 
+    CacheSizeInfo(const isc::dns::RRClass& cls,
                   uint32_t msg_cache_size,
                   uint32_t msg_cache_size,
                   uint32_t rst_cache_size):
                   uint32_t rst_cache_size):
                     cclass(cls),
                     cclass(cls),
@@ -87,7 +88,7 @@ public:
     /// \brief Construct Function.
     /// \brief Construct Function.
     /// \param caches_size cache size information for each
     /// \param caches_size cache size information for each
     ///        messages/rrsets of different classes.
     ///        messages/rrsets of different classes.
-    ResolverClassCache(CacheSizeInfo cache_info);
+    ResolverClassCache(const CacheSizeInfo& cache_info);
 
 
     /// \name Lookup Interfaces
     /// \name Lookup Interfaces
     //@{
     //@{
@@ -132,6 +133,11 @@ public:
     /// \note the function doesn't do any message validation check,
     /// \note the function doesn't do any message validation check,
     ///       the user should make sure the message is valid, and of
     ///       the user should make sure the message is valid, and of
     ///       the right class
     ///       the right class
+    /// TODO: Share the NXDOMAIN info between different type queries
+    ///       current implementation can only cache for the type that
+    ///       user quired, for example, if user query A record of
+    ///       a.example. and the server replied with NXDOMAIN, this
+    ///       should be cached for all the types queries of a.example.
     bool update(const isc::dns::Message& msg);
     bool update(const isc::dns::Message& msg);
 
 
     /// \brief Update the rrset in the cache with the new one.
     /// \brief Update the rrset in the cache with the new one.
@@ -149,13 +155,13 @@ public:
     ///
     ///
     /// \note The class of the RRset must have been checked. It is not
     /// \note The class of the RRset must have been checked. It is not
     /// here.
     /// here.
-    bool update(const isc::dns::ConstRRsetPtr rrset_ptr);
+    bool update(const isc::dns::ConstRRsetPtr& rrset_ptr);
 
 
     /// \brief Get the RRClass this cache is for
     /// \brief Get the RRClass this cache is for
     ///
     ///
     /// \return The RRClass of this cache
     /// \return The RRClass of this cache
     const isc::dns::RRClass& getClass() const;
     const isc::dns::RRClass& getClass() const;
-    
+
 private:
 private:
     /// \brief Update rrset cache.
     /// \brief Update rrset cache.
     ///
     ///
@@ -165,7 +171,7 @@ private:
     /// \return return true if the rrset is updated in the rrset cache,
     /// \return return true if the rrset is updated in the rrset cache,
     ///         or else return false if failed.
     ///         or else return false if failed.
     /// \param rrset_cache_ptr The rrset cache need to be updated.
     /// \param rrset_cache_ptr The rrset cache need to be updated.
-    bool updateRRsetCache(const isc::dns::ConstRRsetPtr rrset_ptr,
+    bool updateRRsetCache(const isc::dns::ConstRRsetPtr& rrset_ptr,
                           RRsetCachePtr rrset_cache_ptr);
                           RRsetCachePtr rrset_cache_ptr);
 
 
     /// \brief Class this cache is for.
     /// \brief Class this cache is for.
@@ -181,10 +187,13 @@ private:
     /// Cache for rrsets in local zones, rrsets
     /// Cache for rrsets in local zones, rrsets
     /// in it never expire.
     /// in it never expire.
     LocalZoneDataPtr local_zone_data_;
     LocalZoneDataPtr local_zone_data_;
+    //@}
 
 
     /// \brief cache the rrsets parsed from the received message.
     /// \brief cache the rrsets parsed from the received message.
     RRsetCachePtr rrsets_cache_;
     RRsetCachePtr rrsets_cache_;
-    //@}
+
+    /// \brief cache the SOA rrset parsed from the negative response message.
+    RRsetCachePtr negative_soa_cache_;
 };
 };
 
 
 class ResolverCache {
 class ResolverCache {
@@ -289,7 +298,7 @@ public:
     ///
     ///
     /// \overload
     /// \overload
     ///
     ///
-    bool update(const isc::dns::ConstRRsetPtr rrset_ptr);
+    bool update(const isc::dns::ConstRRsetPtr& rrset_ptr);
 
 
     /// \name Cache Serialization
     /// \name Cache Serialization
     //@{
     //@{

+ 4 - 2
src/lib/cache/rrset_cache.h

@@ -40,12 +40,14 @@ private:
     RRsetCache(const RRsetCache&);
     RRsetCache(const RRsetCache&);
     RRsetCache& operator=(const RRsetCache&);
     RRsetCache& operator=(const RRsetCache&);
 public:
 public:
-    /// \brief Constructor
+    /// \brief Constructor and Destructor
     ///
     ///
     /// \param cache_size the size of rrset cache.
     /// \param cache_size the size of rrset cache.
     /// \param rrset_class the class of rrset cache.
     /// \param rrset_class the class of rrset cache.
     RRsetCache(uint32_t cache_size, uint16_t rrset_class);
     RRsetCache(uint32_t cache_size, uint16_t rrset_class);
-    virtual ~RRsetCache() {}
+    virtual ~RRsetCache() {
+        rrset_lru_.clear(); // Clear the rrset entries in the list.
+    }
     //@}
     //@}
 
 
     /// \brief Look up rrset in cache.
     /// \brief Look up rrset in cache.

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

@@ -38,6 +38,7 @@ run_unittests_SOURCES  += message_cache_unittest.cc
 run_unittests_SOURCES  += message_entry_unittest.cc
 run_unittests_SOURCES  += message_entry_unittest.cc
 run_unittests_SOURCES  += local_zone_data_unittest.cc
 run_unittests_SOURCES  += local_zone_data_unittest.cc
 run_unittests_SOURCES  += resolver_cache_unittest.cc
 run_unittests_SOURCES  += resolver_cache_unittest.cc
+run_unittests_SOURCES  += negative_cache_unittest.cc
 run_unittests_SOURCES  += cache_test_messagefromfile.h
 run_unittests_SOURCES  += cache_test_messagefromfile.h
 run_unittests_SOURCES  += cache_test_sectioncount.h
 run_unittests_SOURCES  += cache_test_sectioncount.h
 
 
@@ -53,12 +54,15 @@ endif
 run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 noinst_PROGRAMS = $(TESTS)
 
 
-EXTRA_DIST = testdata/message_fromWire1
+EXTRA_DIST = testdata/message_cname_referral.wire
+EXTRA_DIST += testdata/message_example_com_soa.wire
+EXTRA_DIST += testdata/message_fromWire1
 EXTRA_DIST += testdata/message_fromWire2
 EXTRA_DIST += testdata/message_fromWire2
 EXTRA_DIST += testdata/message_fromWire3
 EXTRA_DIST += testdata/message_fromWire3
 EXTRA_DIST += testdata/message_fromWire4
 EXTRA_DIST += testdata/message_fromWire4
@@ -67,3 +71,10 @@ EXTRA_DIST += testdata/message_fromWire6
 EXTRA_DIST += testdata/message_fromWire7
 EXTRA_DIST += testdata/message_fromWire7
 EXTRA_DIST += testdata/message_fromWire8
 EXTRA_DIST += testdata/message_fromWire8
 EXTRA_DIST += testdata/message_fromWire9
 EXTRA_DIST += testdata/message_fromWire9
+EXTRA_DIST += testdata/message_large_ttl.wire
+EXTRA_DIST += testdata/message_nodata_with_soa.wire
+EXTRA_DIST += testdata/message_nxdomain_cname.wire
+EXTRA_DIST += testdata/message_nxdomain_large_ttl.wire
+EXTRA_DIST += testdata/message_nxdomain_no_soa.wire
+EXTRA_DIST += testdata/message_nxdomain_with_soa.wire
+EXTRA_DIST += testdata/message_referral.wire

+ 9 - 5
src/lib/cache/tests/message_cache_unittest.cc

@@ -33,9 +33,10 @@ namespace {
 /// its internals.
 /// its internals.
 class DerivedMessageCache: public MessageCache {
 class DerivedMessageCache: public MessageCache {
 public:
 public:
-    DerivedMessageCache(boost::shared_ptr<RRsetCache> rrset_cache_,
-                        uint32_t cache_size, uint16_t message_class):
-        MessageCache(rrset_cache_, cache_size, message_class)
+    DerivedMessageCache(const RRsetCachePtr& rrset_cache,
+                        uint32_t cache_size, uint16_t message_class,
+                        const RRsetCachePtr& negative_soa_cache):
+        MessageCache(rrset_cache, cache_size, message_class, negative_soa_cache)
     {}
     {}
 
 
     uint16_t messages_count() {
     uint16_t messages_count() {
@@ -70,13 +71,16 @@ public:
     {
     {
         uint16_t class_ = RRClass::IN().getCode();
         uint16_t class_ = RRClass::IN().getCode();
         rrset_cache_.reset(new DerivedRRsetCache(RRSET_CACHE_DEFAULT_SIZE, class_));
         rrset_cache_.reset(new DerivedRRsetCache(RRSET_CACHE_DEFAULT_SIZE, class_));
+        negative_soa_cache_.reset(new RRsetCache(NEGATIVE_RRSET_CACHE_DEFAULT_SIZE, class_));
         // Set the message cache size to 1, make it easy for unittest.
         // Set the message cache size to 1, make it easy for unittest.
-        message_cache_.reset(new DerivedMessageCache(rrset_cache_, 1, class_ ));
+        message_cache_.reset(new DerivedMessageCache(rrset_cache_, 1, class_,
+                                                     negative_soa_cache_));
     }
     }
 
 
 protected:
 protected:
     boost::shared_ptr<DerivedMessageCache> message_cache_;
     boost::shared_ptr<DerivedMessageCache> message_cache_;
     boost::shared_ptr<DerivedRRsetCache> rrset_cache_;
     boost::shared_ptr<DerivedRRsetCache> rrset_cache_;
+    RRsetCachePtr negative_soa_cache_;
     Message message_parse;
     Message message_parse;
     Message message_render;
     Message message_render;
 };
 };
@@ -134,7 +138,7 @@ TEST_F(MessageCacheTest, testUpdate) {
     EXPECT_TRUE(message_cache_->update(new_msg));
     EXPECT_TRUE(message_cache_->update(new_msg));
     Message new_msg_render(Message::RENDER);
     Message new_msg_render(Message::RENDER);
     EXPECT_TRUE(message_cache_->lookup(qname, RRType::SOA(), new_msg_render));
     EXPECT_TRUE(message_cache_->lookup(qname, RRType::SOA(), new_msg_render));
-    EXPECT_TRUE(new_msg_render.getHeaderFlag(Message::HEADERFLAG_AA));
+    EXPECT_FALSE(new_msg_render.getHeaderFlag(Message::HEADERFLAG_AA));
 }
 }
 
 
 TEST_F(MessageCacheTest, testCacheLruBehavior) {
 TEST_F(MessageCacheTest, testCacheLruBehavior) {

+ 46 - 18
src/lib/cache/tests/message_entry_unittest.cc

@@ -1,5 +1,3 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
 // copyright notice and this permission notice appear in all copies.
 // copyright notice and this permission notice appear in all copies.
@@ -38,14 +36,15 @@ namespace {
 class DerivedMessageEntry: public MessageEntry {
 class DerivedMessageEntry: public MessageEntry {
 public:
 public:
     DerivedMessageEntry(const isc::dns::Message& message,
     DerivedMessageEntry(const isc::dns::Message& message,
-                        boost::shared_ptr<RRsetCache> rrset_cache_):
-             MessageEntry(message, rrset_cache_)
+                        const RRsetCachePtr& rrset_cache_,
+                        const RRsetCachePtr& negative_soa_cache_):
+             MessageEntry(message, rrset_cache_, negative_soa_cache_)
     {}
     {}
 
 
-    /// \brief Wrap the protected function so that it can be tested.   
+    /// \brief Wrap the protected function so that it can be tested.
     void parseSectionForTest(const Message& msg,
     void parseSectionForTest(const Message& msg,
                            const Message::Section& section,
                            const Message::Section& section,
-                           uint32_t& smaller_ttl, 
+                           uint32_t& smaller_ttl,
                            uint16_t& rrset_count)
                            uint16_t& rrset_count)
     {
     {
         parseSection(msg, section, smaller_ttl, rrset_count);
         parseSection(msg, section, smaller_ttl, rrset_count);
@@ -75,18 +74,20 @@ public:
                         message_render(Message::RENDER)
                         message_render(Message::RENDER)
     {
     {
         rrset_cache_.reset(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE, class_));
         rrset_cache_.reset(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE, class_));
+        negative_soa_cache_.reset(new RRsetCache(NEGATIVE_RRSET_CACHE_DEFAULT_SIZE, class_));
     }
     }
 
 
 protected:
 protected:
     uint16_t class_;
     uint16_t class_;
     RRsetCachePtr rrset_cache_;
     RRsetCachePtr rrset_cache_;
+    RRsetCachePtr negative_soa_cache_;
     Message message_parse;
     Message message_parse;
     Message message_render;
     Message message_render;
 };
 };
 
 
 TEST_F(MessageEntryTest, testParseRRset) {
 TEST_F(MessageEntryTest, testParseRRset) {
     messageFromFile(message_parse, "message_fromWire3");
     messageFromFile(message_parse, "message_fromWire3");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     uint32_t ttl = MAX_UINT32;
     uint32_t ttl = MAX_UINT32;
     uint16_t rrset_count = 0;
     uint16_t rrset_count = 0;
     message_entry.parseSectionForTest(message_parse, Message::SECTION_ANSWER, ttl, rrset_count);
     message_entry.parseSectionForTest(message_parse, Message::SECTION_ANSWER, ttl, rrset_count);
@@ -106,7 +107,7 @@ TEST_F(MessageEntryTest, testParseRRset) {
 
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_AA) {
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_AA) {
     messageFromFile(message_parse, "message_fromWire3");
     messageFromFile(message_parse, "message_fromWire3");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
 
 
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
@@ -129,7 +130,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_AA) {
 
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_NONAA) {
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_NONAA) {
     messageFromFile(message_parse, "message_fromWire4");
     messageFromFile(message_parse, "message_fromWire4");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
                                                                     *rrset_iter,
                                                                     *rrset_iter,
@@ -151,7 +152,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_NONAA) {
 
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME) {
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME) {
     messageFromFile(message_parse, "message_fromWire5");
     messageFromFile(message_parse, "message_fromWire5");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
                                                                     *rrset_iter,
                                                                     *rrset_iter,
@@ -167,7 +168,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME) {
 
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME_and_DNAME) {
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME_and_DNAME) {
     messageFromFile(message_parse, "message_fromWire7");
     messageFromFile(message_parse, "message_fromWire7");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
                                                                     *rrset_iter,
                                                                     *rrset_iter,
@@ -186,7 +187,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME_and_DNAME) {
 
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME_and_CNAME) {
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME_and_CNAME) {
     messageFromFile(message_parse, "message_fromWire8");
     messageFromFile(message_parse, "message_fromWire8");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
                                                                     *rrset_iter,
                                                                     *rrset_iter,
@@ -214,7 +215,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME_and_CNAME) {
 
 
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME) {
 TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME) {
     messageFromFile(message_parse, "message_fromWire6");
     messageFromFile(message_parse, "message_fromWire6");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
     RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
                                                                     *rrset_iter,
                                                                     *rrset_iter,
@@ -239,7 +240,7 @@ TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME) {
 // is right
 // is right
 TEST_F(MessageEntryTest, testInitMessageEntry) {
 TEST_F(MessageEntryTest, testInitMessageEntry) {
     messageFromFile(message_parse, "message_fromWire3");
     messageFromFile(message_parse, "message_fromWire3");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     time_t expire_time = message_entry.getExpireTime();
     time_t expire_time = message_entry.getExpireTime();
     // 1 second should be enough to do the compare
     // 1 second should be enough to do the compare
     EXPECT_TRUE((time(NULL) + 10801) > expire_time);
     EXPECT_TRUE((time(NULL) + 10801) > expire_time);
@@ -247,7 +248,7 @@ TEST_F(MessageEntryTest, testInitMessageEntry) {
 
 
 TEST_F(MessageEntryTest, testGetRRsetEntries) {
 TEST_F(MessageEntryTest, testGetRRsetEntries) {
     messageFromFile(message_parse, "message_fromWire3");
     messageFromFile(message_parse, "message_fromWire3");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     vector<RRsetEntryPtr> vec;
     vector<RRsetEntryPtr> vec;
 
 
     // the time is bigger than the smallest expire time of
     // the time is bigger than the smallest expire time of
@@ -258,15 +259,14 @@ TEST_F(MessageEntryTest, testGetRRsetEntries) {
 
 
 TEST_F(MessageEntryTest, testGenMessage) {
 TEST_F(MessageEntryTest, testGenMessage) {
     messageFromFile(message_parse, "message_fromWire3");
     messageFromFile(message_parse, "message_fromWire3");
-    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
     time_t expire_time = message_entry.getExpireTime();
     time_t expire_time = message_entry.getExpireTime();
 
 
     Message msg(Message::RENDER);
     Message msg(Message::RENDER);
     EXPECT_FALSE(message_entry.genMessage(expire_time + 2, msg));
     EXPECT_FALSE(message_entry.genMessage(expire_time + 2, msg));
     message_entry.genMessage(time(NULL), msg);
     message_entry.genMessage(time(NULL), msg);
     // Check whether the generated message is same with cached one.
     // Check whether the generated message is same with cached one.
-
-    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
+    EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
     EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_TC));
     EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_TC));
     EXPECT_EQ(1, sectionRRsetCount(msg, Message::SECTION_ANSWER));
     EXPECT_EQ(1, sectionRRsetCount(msg, Message::SECTION_ANSWER));
     EXPECT_EQ(1, sectionRRsetCount(msg, Message::SECTION_AUTHORITY));
     EXPECT_EQ(1, sectionRRsetCount(msg, Message::SECTION_AUTHORITY));
@@ -278,4 +278,32 @@ TEST_F(MessageEntryTest, testGenMessage) {
     EXPECT_EQ(7, msg.getRRCount(Message::SECTION_ADDITIONAL));
     EXPECT_EQ(7, msg.getRRCount(Message::SECTION_ADDITIONAL));
 }
 }
 
 
+TEST_F(MessageEntryTest, testMaxTTL) {
+    messageFromFile(message_parse, "message_large_ttl.wire");
+
+    // The ttl of rrset from Answer and Authority sections are both 604801 seconds
+    RRsetIterator iter = message_parse.beginSection(Message::SECTION_ANSWER);
+    EXPECT_EQ(604801, (*iter)->getTTL().getValue());
+    iter = message_parse.beginSection(Message::SECTION_AUTHORITY);
+    EXPECT_EQ(604801, (*iter)->getTTL().getValue());
+
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
+
+    // The ttl is limited to 604800 seconds (7days)
+    EXPECT_EQ(time(NULL) + 604800, message_entry.getExpireTime());
+}
+
+TEST_F(MessageEntryTest, testMaxNegativeTTL) {
+    messageFromFile(message_parse, "message_nxdomain_large_ttl.wire");
+
+    // The ttl of rrset Authority sections are 10801 seconds
+    RRsetIterator iter = message_parse.beginSection(Message::SECTION_AUTHORITY);
+    EXPECT_EQ(10801, (*iter)->getTTL().getValue());
+
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_, negative_soa_cache_);
+
+    // The ttl is limited to 10800 seconds (3 hours)
+    EXPECT_EQ(time(NULL) + 10800, message_entry.getExpireTime());
+}
+
 }   // namespace
 }   // namespace

+ 242 - 0
src/lib/cache/tests/negative_cache_unittest.cc

@@ -0,0 +1,242 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+#include <config.h>
+#include <string>
+#include <gtest/gtest.h>
+#include <dns/rrset.h>
+#include <dns/rcode.h>
+#include "resolver_cache.h"
+#include "cache_test_messagefromfile.h"
+
+using namespace isc::cache;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+class NegativeCacheTest: public testing::Test{
+public:
+    NegativeCacheTest() {
+        vector<CacheSizeInfo> vec;
+        CacheSizeInfo class_in(RRClass::IN(), 100, 200);
+        vec.push_back(class_in);
+        cache = new ResolverCache(vec);
+    }
+
+    ~NegativeCacheTest() {
+        delete cache;
+    }
+
+    ResolverCache *cache;
+};
+
+TEST_F(NegativeCacheTest, testNXDOMAIN){
+    // NXDOMAIN response for nonexist.example.com
+    Message msg_nxdomain(Message::PARSE);
+    messageFromFile(msg_nxdomain, "message_nxdomain_with_soa.wire");
+    cache->update(msg_nxdomain);
+
+    msg_nxdomain.makeResponse();
+
+    Name non_exist_qname("nonexist.example.com.");
+    EXPECT_TRUE(cache->lookup(non_exist_qname, RRType::A(), RRClass::IN(), msg_nxdomain));
+
+    RRsetIterator iter = msg_nxdomain.beginSection(Message::SECTION_AUTHORITY);
+    RRsetPtr rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of SOA record
+    const RRTTL& nxdomain_ttl1 = rrset_ptr->getTTL();
+    EXPECT_EQ(nxdomain_ttl1.getValue(), 86400);
+
+    // SOA response for example.com
+    Message msg_example_com_soa(Message::PARSE);
+    messageFromFile(msg_example_com_soa, "message_example_com_soa.wire");
+    cache->update(msg_example_com_soa);
+
+    msg_example_com_soa.makeResponse();
+    Name soa_qname("example.com.");
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa));
+
+    iter = msg_example_com_soa.beginSection(Message::SECTION_ANSWER);
+    rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of SOA record in answer section
+    const RRTTL& soa_ttl = rrset_ptr->getTTL();
+    EXPECT_EQ(soa_ttl.getValue(), 172800);
+
+    sleep(1);
+
+    // Query nonexist.example.com again
+    Message msg_nxdomain2(Message::PARSE);
+    messageFromFile(msg_nxdomain2, "message_nxdomain_with_soa.wire");
+    msg_nxdomain2.makeResponse();
+
+    EXPECT_TRUE(cache->lookup(non_exist_qname, RRType::A(), RRClass::IN(), msg_nxdomain2));
+    iter = msg_nxdomain2.beginSection(Message::SECTION_AUTHORITY);
+    rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of negative response SOA record
+    const RRTTL& nxdomain_ttl2 = rrset_ptr->getTTL();
+    EXPECT_TRUE(86398 <= nxdomain_ttl2.getValue() && nxdomain_ttl2.getValue() <= 86399);
+    // No RRset in ANSWER section
+    EXPECT_TRUE(msg_nxdomain2.getRRCount(Message::SECTION_ANSWER) == 0);
+    // Check that only one SOA record exist in AUTHORITY section
+    EXPECT_TRUE(msg_nxdomain2.getRRCount(Message::SECTION_AUTHORITY) == 1);
+    iter = msg_nxdomain2.beginSection(Message::SECTION_AUTHORITY);
+    rrset_ptr = *iter;
+    EXPECT_TRUE(rrset_ptr->getType() == RRType::SOA());
+
+    // Check the normal SOA cache again
+    Message msg_example_com_soa2(Message::PARSE);
+    messageFromFile(msg_example_com_soa2, "message_example_com_soa.wire");
+    msg_example_com_soa2.makeResponse();
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa2));
+
+    iter = msg_example_com_soa2.beginSection(Message::SECTION_ANSWER);
+    rrset_ptr = *iter;
+    const RRTTL& soa_ttl2 = rrset_ptr->getTTL();
+    // The TTL should equal to the TTL of SOA record in answer section
+    EXPECT_TRUE(172798 <= soa_ttl2.getValue() && soa_ttl2.getValue() <= 172799);
+}
+
+TEST_F(NegativeCacheTest, testNXDOMAINWithoutSOA){
+    // NXDOMAIN response for nonexist.example.com
+    Message msg_nxdomain(Message::PARSE);
+    messageFromFile(msg_nxdomain, "message_nxdomain_no_soa.wire");
+    cache->update(msg_nxdomain);
+
+    msg_nxdomain.makeResponse();
+
+    Name non_exist_qname("nonexist.example.com.");
+    // The message should not be cached
+    EXPECT_FALSE(cache->lookup(non_exist_qname, RRType::A(), RRClass::IN(), msg_nxdomain));
+}
+
+TEST_F(NegativeCacheTest, testNXDOMAINCname){
+    // a.example.org points to b.example.org
+    // b.example.org points to c.example.org
+    // c.example.org does not exist
+    Message msg_nxdomain_cname(Message::PARSE);
+    messageFromFile(msg_nxdomain_cname, "message_nxdomain_cname.wire");
+    cache->update(msg_nxdomain_cname);
+
+    msg_nxdomain_cname.makeResponse();
+
+    Name a_example_org("a.example.org.");
+    // The message should be cached
+    EXPECT_TRUE(cache->lookup(a_example_org, RRType::A(), RRClass::IN(), msg_nxdomain_cname));
+
+    EXPECT_EQ(msg_nxdomain_cname.getRcode().getCode(), Rcode::NXDOMAIN().getCode());
+
+    // It should include 2 CNAME records in Answer section
+    EXPECT_TRUE(msg_nxdomain_cname.getRRCount(Message::SECTION_ANSWER) == 2);
+    RRsetIterator iter = msg_nxdomain_cname.beginSection(Message::SECTION_ANSWER);
+    EXPECT_TRUE((*iter)->getType() == RRType::CNAME());
+    ++iter;
+    EXPECT_TRUE((*iter)->getType() == RRType::CNAME());
+
+    // It should include 1 SOA record in Authority section
+    EXPECT_TRUE(msg_nxdomain_cname.getRRCount(Message::SECTION_AUTHORITY) == 1);
+    iter = msg_nxdomain_cname.beginSection(Message::SECTION_AUTHORITY);
+    EXPECT_TRUE((*iter)->getType() == RRType::SOA());
+
+    const RRTTL& soa_ttl = (*iter)->getTTL();
+    EXPECT_EQ(soa_ttl.getValue(), 600);
+}
+
+TEST_F(NegativeCacheTest, testNoerrorNodata){
+    // NODATA/NOERROR response for MX type query of example.com
+    Message msg_nodata(Message::PARSE);
+    messageFromFile(msg_nodata, "message_nodata_with_soa.wire");
+    cache->update(msg_nodata);
+
+    msg_nodata.makeResponse();
+
+    Name example_dot_com("example.com.");
+    EXPECT_TRUE(cache->lookup(example_dot_com, RRType::MX(), RRClass::IN(), msg_nodata));
+
+    RRsetIterator iter = msg_nodata.beginSection(Message::SECTION_AUTHORITY);
+    RRsetPtr rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of SOA record
+    const RRTTL& nodata_ttl1 = rrset_ptr->getTTL();
+    EXPECT_EQ(nodata_ttl1.getValue(), 86400);
+
+
+    // Normal SOA response for example.com
+    Message msg_example_com_soa(Message::PARSE);
+    messageFromFile(msg_example_com_soa, "message_example_com_soa.wire");
+    cache->update(msg_example_com_soa);
+
+    msg_example_com_soa.makeResponse();
+    Name soa_qname("example.com.");
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa));
+
+    iter = msg_example_com_soa.beginSection(Message::SECTION_ANSWER);
+    rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of SOA record in answer section
+    const RRTTL& soa_ttl = rrset_ptr->getTTL();
+    EXPECT_EQ(soa_ttl.getValue(), 172800);
+
+    // Query MX record of example.com again
+    Message msg_nodata2(Message::PARSE);
+    messageFromFile(msg_nodata2, "message_nodata_with_soa.wire");
+    msg_nodata2.makeResponse();
+
+    sleep(1);
+
+    EXPECT_TRUE(cache->lookup(example_dot_com, RRType::MX(), RRClass::IN(), msg_nodata2));
+
+    // No answer
+    EXPECT_EQ(msg_nodata2.getRRCount(Message::SECTION_ANSWER), 0);
+    // One SOA record in authority section
+    EXPECT_EQ(msg_nodata2.getRRCount(Message::SECTION_AUTHORITY), 1);
+
+    iter = msg_nodata2.beginSection(Message::SECTION_AUTHORITY);
+    rrset_ptr = *iter;
+
+    // The TTL should equal to the TTL of negative response SOA record and counted down
+    const RRTTL& nodata_ttl2 = rrset_ptr->getTTL();
+    EXPECT_TRUE(86398 <= nodata_ttl2.getValue() && nodata_ttl2.getValue() <= 86399);
+}
+
+TEST_F(NegativeCacheTest, testReferralResponse){
+    // CNAME exist, but it points to out of zone data, so the server give some reference data
+    Message msg_cname_referral(Message::PARSE);
+    messageFromFile(msg_cname_referral, "message_cname_referral.wire");
+    cache->update(msg_cname_referral);
+
+    msg_cname_referral.makeResponse();
+
+    Name x_example_org("x.example.org.");
+    EXPECT_TRUE(cache->lookup(x_example_org, RRType::A(), RRClass::IN(), msg_cname_referral));
+
+    // The Rcode should be NOERROR
+    EXPECT_EQ(msg_cname_referral.getRcode().getCode(), Rcode::NOERROR().getCode());
+
+    // One CNAME record in Answer section
+    EXPECT_EQ(msg_cname_referral.getRRCount(Message::SECTION_ANSWER), 1);
+    RRsetIterator iter = msg_cname_referral.beginSection(Message::SECTION_ANSWER);
+    EXPECT_EQ((*iter)->getType(), RRType::CNAME());
+
+    // 13 NS records in Authority section
+    EXPECT_EQ(msg_cname_referral.getRRCount(Message::SECTION_AUTHORITY), 13);
+    iter = msg_cname_referral.beginSection(Message::SECTION_AUTHORITY);
+    EXPECT_EQ((*iter)->getType(), RRType::NS());
+}
+
+}

+ 1 - 1
src/lib/cache/tests/resolver_cache_unittest.cc

@@ -53,7 +53,7 @@ TEST_F(ResolverCacheTest, testUpdateMessage) {
 
 
     msg.makeResponse();
     msg.makeResponse();
     EXPECT_TRUE(cache->lookup(qname, RRType::SOA(), RRClass::IN(), msg));
     EXPECT_TRUE(cache->lookup(qname, RRType::SOA(), RRClass::IN(), msg));
-    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
+    EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
 
 
     // Test whether the old message can be updated
     // Test whether the old message can be updated
     Message new_msg(Message::PARSE);
     Message new_msg(Message::PARSE);

+ 56 - 0
src/lib/cache/tests/testdata/message_cname_referral.wire

@@ -0,0 +1,56 @@
+#
+# Request A record for x.example.org, the CNAME record exist for x.example.org
+# it poinst to x.example.net, but the server has no idea whether x.example.net exist
+# so it give some NS records for reference
+#
+# Transaction ID: 0xaf71
+# Flags: 0x8480 (Standard query response, No error)
+af71 8480
+# Questions: 1
+# Answer RRs: 1
+# Authority RRs: 13
+# Additional RRs: 0
+00 01 00 01 00 0d 00 00
+##
+## query
+##
+# x.example.org: type A, class IN
+##
+## Answer
+##
+# x.example.org: type CNAME, class IN, cname x.example.net
+# TTL: 360s
+01 78 07 65 78 61 6d 70 6c 65 03 6f 72 67 00 00 01 00 01
+c0 0c 00 05 00 01 00 00 0e 10 00 0f 01 78 07 65 78
+61 6d 70 6c 65 03 6e 65 74 00
+##
+## Authority
+##
+# TTL:518400
+# <Root>: type NS, class IN, ns G.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns E.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns J.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns L.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns H.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns I.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns K.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns M.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns F.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns B.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns C.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns D.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns A.ROOT-SERVERS.net
+00 00 02 00 01 00
+07 e9 00 00 11 01 47 0c 52 4f 4f 54 2d 53 45 52
+56 45 52 53 c0 35 00 00 02 00 01 00 07 e9 00 00
+04 01 45 c0 47 00 00 02 00 01 00 07 e9 00 00 04
+01 4a c0 47 00 00 02 00 01 00 07 e9 00 00 04 01
+4c c0 47 00 00 02 00 01 00 07 e9 00 00 04 01 48
+c0 47 00 00 02 00 01 00 07 e9 00 00 04 01 49 c0
+47 00 00 02 00 01 00 07 e9 00 00 04 01 4b c0 47
+00 00 02 00 01 00 07 e9 00 00 04 01 4d c0 47 00
+00 02 00 01 00 07 e9 00 00 04 01 46 c0 47 00 00
+02 00 01 00 07 e9 00 00 04 01 42 c0 47 00 00 02
+00 01 00 07 e9 00 00 04 01 43 c0 47 00 00 02 00
+01 00 07 e9 00 00 04 01 44 c0 47 00 00 02 00 01
+00 07 e9 00 00 04 01 41 c0 47

+ 57 - 0
src/lib/cache/tests/testdata/message_example_com_soa.wire

@@ -0,0 +1,57 @@
+#
+# SOA request response for example.com 
+#
+# Transaction ID: 0x7f36
+# Flags: 0x8400 (Standard query response, No error)
+7f 36 84 00
+# Questions: 1
+00 01
+# Answer RRs: 1
+00 01
+# Authority RRs: 2
+00 02
+# Additional RRs: 0
+00 00
+##
+## Query
+##
+# Name: example.com
+07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# Type: SOA (Start of zone of authority)
+00 06
+# Class: IN (0x0001)
+00 01
+##
+## Answers
+##
+# Name: example.com
+c0 0c
+# Type: SOA (Start of zone of authority)
+00 06
+# Class: IN (0x0001)
+00 01
+# Time to live: 2 days (172800s)
+00 02 a3 00
+# Data length: 49
+00 31
+# Primary name server: dns1.icann.org
+04 64 6e 73 31 05 69 63 61 6e 6e 03 6f 72 67 00
+# Responsible authority's mailbox: hostmaster.icann.org
+0a 68 6f 73 74 6d 61 73 74 65 72 c0 2e
+# Serial number: 2010072301
+77 cf 44 ed
+# Refresh interval: 2 hours
+00 00 1c 20
+# Retry interval: 1 hour
+00 00 0e 10
+# Expiration limit: 14 days
+00 12 75 00
+# Minimum TTL: 1 day
+00 01 51 80
+##
+## Authoritative nameservers
+##
+# example.com: type NS, class IN, ns a.iana-servers.net
+c0 0c 00 02 00 01 00 02 a3 00 00 14 01 61 0c 69 61 6e 61 2d 73 65 72 76 65 72 73 03 6e 65 74 00
+# example.com: type NS, class IN, ns b.iana-servers.net
+c0 0c 00 02 00 01 00 02 a3 00 00 04 01 62 c0 68

+ 31 - 0
src/lib/cache/tests/testdata/message_large_ttl.wire

@@ -0,0 +1,31 @@
+#
+# A response that the TTL is quite large(> 7days)
+#
+##
+## header
+##
+# Transaction ID: 0x0d1f
+# Flags: 0x8580 (Standard query response, No error)
+0d1f 8580
+# Questions: 1
+# Answer RRs: 1
+# Authority RRs: 3
+# Additional RRs: 3
+00 01 00 01 00 01 00 00
+##
+## Query
+##
+# test.example.org: type A, class IN
+04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 6f 72 67 00 00 01 00 01
+##
+## Answer
+##
+# test.example.org: type A, class IN, addr 127.0.0.1
+# TTL: 7 days, 1 second (604801 seconds)
+c0 0c 00 01 00 01 00 09 3a 81 00 04 7f 00 00 01
+##
+## Authority
+##
+# example.org: type NS, class IN, ns ns1.example.org
+# TTL: 7 days, 1 second (604801 seconds)
+c0 11 00 02 00 01 00 09 3a 81 00 06 03 6e 73 31 c0 11

+ 32 - 0
src/lib/cache/tests/testdata/message_nodata_with_soa.wire

@@ -0,0 +1,32 @@
+#
+# NOERROR/NODATA response with SOA record
+#
+##
+## header
+##
+#Transaction ID: 0x0284
+#Flags: 0x8500 (Standard query response, No error)
+0284 8500
+#Question:1
+00 01
+#Answer RRs:0
+00 00
+#Authority RRs:1
+00 01
+#Additional RRs:0
+00 00
+##
+## Queries
+##
+# example.com: type MX, class IN
+07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 0f 00 01
+##
+## Authoritative nameservers
+##
+# example.com: type SOA, class IN, mname dns1.icann.org
+# TTL:86400
+c0 0c 00
+06 00 01 00 01 51 80 00 31 04 64 6e 73 31 05 69
+63 61 6e 6e 03 6f 72 67 00 0a 68 6f 73 74 6d 61
+73 74 65 72 c0 2e 77 cf 44 ed 00 00 1c 20 00 00
+0e 10 00 12 75 00 00 01 51 80

+ 36 - 0
src/lib/cache/tests/testdata/message_nxdomain_cname.wire

@@ -0,0 +1,36 @@
+#
+# NXDOMAIN response
+# The cname type of a.example.org exist, it points to b.example.org
+# b.example.org points to c.example.org
+# but c.example.org does not exist
+#
+##
+## header
+##
+# Transaction ID: 0xc2aa
+# Flags: 0x8583 (Standard query response, No such name)
+c2aa 8583
+# Questions: 1
+# Answer RRs: 2
+# Authority RRs: 1
+# dditional RRs: 0
+00 01 00 02 00 01 00 00
+##
+## Queries
+##
+# a.example.org: type A, class IN
+01 61 07 65 78 61 6d 70 6c 65 03 6f 72 67 00 00 01 00 01
+##
+## Answers
+##
+# a.example.org: type CNAME, class IN, cname b.example.org
+c0 0c 00 05 00 01 00 00 0e 10 00 04 01 62 c0 0e
+# b.example.org: type CNAME, class IN, cname c.example.org
+c0 2b 00 05 00 01 00 00 0e 10 00 04 01 63 c0 0e
+##
+## Authority
+##
+# example.org: type SOA, class IN, mname ns1.example.org
+c0 0e 00 06 00 01 00 00 02 58 00 22 03 6e 73 31 c0
+0e 05 61 64 6d 69 6e c0 0e 00 00 04 d2 00 00 0e
+10 00 00 07 08 00 24 ea 00 00 00 02 58

+ 25 - 0
src/lib/cache/tests/testdata/message_nxdomain_large_ttl.wire

@@ -0,0 +1,25 @@
+#
+# Negative response (NXDOMAIN) with large TTL (3hours + 1second)
+#
+##
+## Header
+##
+# Transaction ID: 0xb1fe
+# Flags: 0x8583 (Standard query response, No such name)
+b1fe 8583
+# Questions: 1
+# Authority RRs: 1
+00 01 00 00 00 01 00 00
+##
+## Query
+##
+# c.example.org: type A, class IN
+01 63 07 65 78 61 6d 70 6c 65 03 6f 72 67 00 00 01 00 01
+##
+## Authority
+##
+# example.org: type SOA, class IN, mname ns1.example.org
+# TTL: 3 Hourse, 1 second (10801seconds)
+c0 0e 00 06 00 01 00 00 2a 31 00 22 03 6e 73 31 c0
+0e 05 61 64 6d 69 6e c0 0e 00 00 04 d2 00 00 0e
+10 00 00 07 08 00 24 ea 00 00 00 2a 31

+ 26 - 0
src/lib/cache/tests/testdata/message_nxdomain_no_soa.wire

@@ -0,0 +1,26 @@
+#
+# NXDOMAIN response with SOA record
+#
+##
+## Header
+##
+# ID = 0x3da0
+# QR = 1 (response), Opcode = 0, AA = 1, RCODE=3 (NXDOMAIN)
+3da0 8403
+# Question : 1
+00 01
+# Answer : 0
+00 00
+# Authority : 0
+00 00
+# Additional : 0
+00 00
+##
+## Query
+##
+#(4) n  o  n  e  x  i  s  t (7) e  x  a  m  p  l  e (3) c  o  m (0)
+  08 6e 6f 6e 65 78 69 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# Type:A
+00 01
+# class: IN
+00 01

+ 55 - 0
src/lib/cache/tests/testdata/message_nxdomain_with_soa.wire

@@ -0,0 +1,55 @@
+#
+# NXDOMAIN response with SOA record
+#
+##
+## Header
+##
+# ID = 0x3da0
+# QR = 1 (response), Opcode = 0, AA = 1, RCODE=3 (NXDOMAIN)
+3da0 8403
+# Question : 1
+00 01
+# Answer : 0
+00 00
+# Authority : 1
+00 01
+# Additional : 0
+00 00
+##
+## Query
+##
+#(4) n  o  n  e  x  i  s  t (7) e  x  a  m  p  l  e (3) c  o  m (0)
+  08 6e 6f 6e 65 78 69 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# Type:A
+00 01
+# class: IN
+00 01
+##
+## Authority
+## 
+# name: example.com
+c0 15
+# Type:SOA
+00 06
+# Class: IN
+00 01
+# TTL: 86400
+00 01 51 80
+# Data Length: 49
+00 31
+# Name Server:
+#(4) d  n  s  1 (5) i   c a  n  n (3) o  r  g (0)
+  04 64 6e 73 31 05 69 63 61 6e 6e 03 6f 72 67 00
+# MX: 
+# (10) h  o   s  t  m  a  s  t  e  r .icann.org.
+  0a   68 6f 73 74 6d 61 73 74 65 72 c0 37
+# Serial Number:2010072301
+77 cf 44 ed
+# Refresh Interval:2 hours
+00 00 1c 20
+# Retry Interval: 1 hour
+00 00 0e 10
+# Expiration: 14 days
+00 12 75 00
+# Minimum TTL 1 day
+00 01 51 80

+ 36 - 0
src/lib/cache/tests/testdata/message_referral.wire

@@ -0,0 +1,36 @@
+#
+# Query x.example.net to nameservr of example.org
+# It will just give some referral info
+#
+#
+# Transaction ID: 0x8b61
+# Flags: 0x8080 (Standard query response, No error)
+8b61 8080
+# Questions: 1
+# Authority RRs: 13
+00 01 00 00 00 0d 00 00
+##
+## Query
+##
+# x.example.net: type A, class IN
+01 78 07 65 78 61 6d 70 6c 65 03 6e 65 74 00 00 01 00 01
+##
+## Authority
+##
+# <Root>: type NS, class IN, ns B.ROOT-SERVERS.net
+# <Root>: type NS, class IN, ns M.ROOT-SERVERS.net
+# ...
+# <Root>: type NS, class IN, ns H.ROOT-SERVERS.net
+00 00 02 00 01 00 07 e9 00 00 11 01 42 0c 52 4f 4f
+54 2d 53 45 52 56 45 52 53 c0 16 00 00 02 00 01
+00 07 e9 00 00 04 01 4d c0 2c 00 00 02 00 01 00
+07 e9 00 00 04 01 44 c0 2c 00 00 02 00 01 00 07
+e9 00 00 04 01 4c c0 2c 00 00 02 00 01 00 07 e9
+00 00 04 01 4b c0 2c 00 00 02 00 01 00 07 e9 00
+00 04 01 43 c0 2c 00 00 02 00 01 00 07 e9 00 00
+04 01 41 c0 2c 00 00 02 00 01 00 07 e9 00 00 04
+01 49 c0 2c 00 00 02 00 01 00 07 e9 00 00 04 01
+45 c0 2c 00 00 02 00 01 00 07 e9 00 00 04 01 46
+c0 2c 00 00 02 00 01 00 07 e9 00 00 04 01 4a c0
+2c 00 00 02 00 01 00 07 e9 00 00 04 01 47 c0 2c
+00 00 02 00 01 00 07 e9 00 00 04 01 48 c0 2c

+ 7 - 5
src/lib/cc/data.h

@@ -222,6 +222,7 @@ public:
 
 
     /// Sets the ElementPtr at the given key
     /// Sets the ElementPtr at the given key
     /// \param name The key of the Element to set
     /// \param name The key of the Element to set
+    /// \param element The ElementPtr to set at the given key.
     virtual void set(const std::string& name, ConstElementPtr element);
     virtual void set(const std::string& name, ConstElementPtr element);
 
 
     /// Remove the ElementPtr at the given key
     /// Remove the ElementPtr at the given key
@@ -315,10 +316,11 @@ public:
     /// Creates an Element from the given input stream, where we keep
     /// Creates an Element from the given input stream, where we keep
     /// track of the location in the stream for error reporting.
     /// track of the location in the stream for error reporting.
     ///
     ///
-    /// \param in The string to parse the element from
+    /// \param in The string to parse the element from.
+    /// \param file The input file name.
     /// \param line A reference to the int where the function keeps
     /// \param line A reference to the int where the function keeps
     /// track of the current line.
     /// track of the current line.
-    /// \param line A reference to the int where the function keeps
+    /// \param pos A reference to the int where the function keeps
     /// track of the current position within the current line.
     /// track of the current position within the current line.
     /// \return An ElementPtr that contains the element(s) specified
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
     /// in the given input stream.
@@ -548,18 +550,18 @@ void merge(ElementPtr element, ConstElementPtr other);
 ///
 ///
 /// \brief Insert the Element as a string into stream.
 /// \brief Insert the Element as a string into stream.
 ///
 ///
-/// This method converts the \c ElemetPtr into a string with
+/// This method converts the \c ElementPtr into a string with
 /// \c Element::str() and inserts it into the
 /// \c Element::str() and inserts it into the
 /// output stream \c out.
 /// output stream \c out.
 ///
 ///
 /// This function overloads the global operator<< to behave as described in
 /// This function overloads the global operator<< to behave as described in
 /// ostream::operator<< but applied to \c ElementPtr objects.
 /// ostream::operator<< but applied to \c ElementPtr objects.
 ///
 ///
-/// \param os A \c std::ostream object on which the insertion operation is
+/// \param out A \c std::ostream object on which the insertion operation is
 /// performed.
 /// performed.
 /// \param e The \c ElementPtr object to insert.
 /// \param e The \c ElementPtr object to insert.
 /// \return A reference to the same \c std::ostream object referenced by
 /// \return A reference to the same \c std::ostream object referenced by
-/// parameter \c os after the insertion operation.
+/// parameter \c out after the insertion operation.
 std::ostream& operator<<(std::ostream& out, const Element& e);
 std::ostream& operator<<(std::ostream& out, const Element& e);
 
 
 bool operator==(const Element& a, const Element& b);
 bool operator==(const Element& a, const Element& b);

+ 1 - 1
src/lib/cc/session.h

@@ -99,7 +99,7 @@ namespace isc {
             /// \brief Sets the default timeout for blocking reads
             /// \brief Sets the default timeout for blocking reads
             ///        in this session to the given number of milliseconds
             ///        in this session to the given number of milliseconds
             /// \param milliseconds the timeout for blocking reads in
             /// \param milliseconds the timeout for blocking reads in
-            ///        milliseconds, if this is set to 0, reads will block
+            ///        milliseconds; if this is set to 0, reads will block
             ///        forever.
             ///        forever.
             virtual void setTimeout(size_t milliseconds) = 0;
             virtual void setTimeout(size_t milliseconds) = 0;
 
 

+ 11 - 8
src/lib/config/module_spec.cc

@@ -372,15 +372,18 @@ ModuleSpec::validateSpecList(ConstElementPtr spec, ConstElementPtr data,
     
     
     BOOST_FOREACH(maptype m, data->mapValue()) {
     BOOST_FOREACH(maptype m, data->mapValue()) {
         bool found = false;
         bool found = false;
-        BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
-            if (cur_spec_el->get("item_name")->stringValue().compare(m.first) == 0) {
-                found = true;
+        // Ignore 'version' as a config element
+        if (m.first.compare("version") != 0) {
+            BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
+                if (cur_spec_el->get("item_name")->stringValue().compare(m.first) == 0) {
+                    found = true;
+                }
             }
             }
-        }
-        if (!found) {
-            validated = false;
-            if (errors) {
-                errors->add(Element::create("Unknown item " + m.first));
+            if (!found) {
+                validated = false;
+                if (errors) {
+                    errors->add(Element::create("Unknown item " + m.first));
+                }
             }
             }
         }
         }
     }
     }

+ 4 - 0
src/lib/config/module_spec.h

@@ -53,6 +53,8 @@ namespace isc { namespace config {
         /// Create a \c ModuleSpec instance with the given data as
         /// Create a \c ModuleSpec instance with the given data as
         /// the specification
         /// the specification
         /// \param e The Element containing the data specification
         /// \param e The Element containing the data specification
+        /// \param check If false, the module specification in the file
+        /// is not checked to be of the correct form.
         explicit ModuleSpec(isc::data::ConstElementPtr e,
         explicit ModuleSpec(isc::data::ConstElementPtr e,
                             const bool check = true)
                             const bool check = true)
             throw(ModuleSpecError);
             throw(ModuleSpecError);
@@ -86,6 +88,8 @@ namespace isc { namespace config {
         // configuration specification
         // configuration specification
         /// Validates the given configuration data for this specification.
         /// Validates the given configuration data for this specification.
         /// \param data The base \c Element of the data to check
         /// \param data The base \c Element of the data to check
+        /// \param full If true, all non-optional configuration parameters
+        /// must be specified.
         /// \return true if the data conforms to the specification,
         /// \return true if the data conforms to the specification,
         /// false otherwise.
         /// false otherwise.
         bool validateConfig(isc::data::ConstElementPtr data,
         bool validateConfig(isc::data::ConstElementPtr data,

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

@@ -162,6 +162,10 @@ TEST(ModuleSpec, DataValidation) {
     EXPECT_FALSE(dataTest(dd, "data22_8.data"));
     EXPECT_FALSE(dataTest(dd, "data22_8.data"));
     EXPECT_FALSE(dataTest(dd, "data22_9.data"));
     EXPECT_FALSE(dataTest(dd, "data22_9.data"));
 
 
+    // Test if "version" is allowed in config data
+    // (same data as 22_7, but added "version")
+    EXPECT_TRUE(dataTest(dd, "data22_10.data"));
+
     ElementPtr errors = Element::createList();
     ElementPtr errors = Element::createList();
     EXPECT_FALSE(dataTestWithErrors(dd, "data22_8.data", errors));
     EXPECT_FALSE(dataTestWithErrors(dd, "data22_8.data", errors));
     EXPECT_EQ("[ \"Type mismatch\" ]", errors->str());
     EXPECT_EQ("[ \"Type mismatch\" ]", errors->str());

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

@@ -21,6 +21,7 @@ EXTRA_DIST += data22_6.data
 EXTRA_DIST += data22_7.data
 EXTRA_DIST += data22_7.data
 EXTRA_DIST += data22_8.data
 EXTRA_DIST += data22_8.data
 EXTRA_DIST += data22_9.data
 EXTRA_DIST += data22_9.data
+EXTRA_DIST += data22_10.data
 EXTRA_DIST += spec1.spec
 EXTRA_DIST += spec1.spec
 EXTRA_DIST += spec2.spec
 EXTRA_DIST += spec2.spec
 EXTRA_DIST += spec3.spec
 EXTRA_DIST += spec3.spec

+ 11 - 0
src/lib/config/tests/testdata/data22_10.data

@@ -0,0 +1,11 @@
+{
+    "version": 123,
+    "value1": 1,
+    "value2": 2.3,
+    "value3": true,
+    "value4": "foo",
+    "value5": [ 1, 2, 3 ],
+    "value6": { "v61": "bar", "v62": true },
+    "value8": [ { "a": "d" }, { "a": "e" } ],
+    "value9": { "v91": "hi", "v92": { "v92a": "Hi", "v92b": 3 } }
+}

+ 55 - 14
src/lib/datasrc/data_source.cc

@@ -48,6 +48,28 @@ using namespace std;
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 using namespace isc::dns::rdata;
 
 
+namespace {
+
+struct MatchRRsetForType {
+    MatchRRsetForType(const RRType rrtype) : rrtype_(rrtype) {}
+    bool operator()(RRsetPtr rrset) {
+        return (rrset->getType() == rrtype_);
+    }
+    const RRType rrtype_;
+};
+
+// This is a helper to retrieve a specified RR type of RRset from RRsetList.
+// In our case the data source search logic should ensure that the class is
+// valid.  We use this find logic of our own so that we can support both
+// specific RR class queries (normal case) and class ANY queries.
+RRsetPtr
+findRRsetFromList(RRsetList& list, const RRType rrtype) {
+    RRsetList::iterator it(find_if(list.begin(), list.end(),
+                                   MatchRRsetForType(rrtype)));
+    return (it != list.end() ? *it : RRsetPtr());
+}
+}
+
 namespace isc {
 namespace isc {
 namespace datasrc {
 namespace datasrc {
 
 
@@ -129,7 +151,7 @@ synthesizeCname(QueryTaskPtr task, RRsetPtr rrset, RRsetList& target) {
     const generic::DNAME& dname = dynamic_cast<const generic::DNAME&>(rd);
     const generic::DNAME& dname = dynamic_cast<const generic::DNAME&>(rd);
     const Name& dname_target(dname.getDname());
     const Name& dname_target(dname.getDname());
 
 
-    RRsetPtr cname(new RRset(task->qname, task->qclass, RRType::CNAME(),
+    RRsetPtr cname(new RRset(task->qname, rrset->getClass(), RRType::CNAME(),
                              rrset->getTTL()));
                              rrset->getTTL()));
 
 
     const int qnlen = task->qname.getLabelCount();
     const int qnlen = task->qname.getLabelCount();
@@ -189,6 +211,19 @@ checkCache(QueryTask& task, RRsetList& target) {
                 rrsets.addRRset(rrset);
                 rrsets.addRRset(rrset);
                 target.append(rrsets);
                 target.append(rrsets);
             }
             }
+
+            // Reset the referral flag and treat CNAME as "not found".
+            // This emulates the behavior of the sqlite3 data source.
+            // XXX: this is not ideal in that the responsibility for handling
+            // operation specific cases is spread over various classes at
+            // different abstraction levels.  For longer terms we should
+            // revisit the whole datasource/query design, and clarify this
+            // point better.
+            flags &= ~DataSrc::REFERRAL;
+            if ((flags & DataSrc::CNAME_FOUND) != 0) {
+                flags &= ~DataSrc::CNAME_FOUND;
+                flags |= DataSrc::TYPE_NOT_FOUND;
+            }
             task.flags = flags;
             task.flags = flags;
             return (true);
             return (true);
         }
         }
@@ -556,17 +591,17 @@ hasDelegation(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo) {
         // Found a referral while getting answer data;
         // Found a referral while getting answer data;
         // send a delegation.
         // send a delegation.
         if (found) {
         if (found) {
-            RRsetPtr r = ref.findRRset(RRType::DNAME(), q.qclass());
+            RRsetPtr r = findRRsetFromList(ref, RRType::DNAME());
             if (r != NULL) {
             if (r != NULL) {
                 RRsetList syn;
                 RRsetList syn;
                 addToMessage(q, Message::SECTION_ANSWER, r);
                 addToMessage(q, Message::SECTION_ANSWER, r);
                 q.message().setHeaderFlag(Message::HEADERFLAG_AA);
                 q.message().setHeaderFlag(Message::HEADERFLAG_AA);
                 synthesizeCname(task, r, syn);
                 synthesizeCname(task, r, syn);
                 if (syn.size() == 1) {
                 if (syn.size() == 1) {
-                    addToMessage(q, Message::SECTION_ANSWER,
-                                 syn.findRRset(RRType::CNAME(), q.qclass()));
-                    chaseCname(q, task, syn.findRRset(RRType::CNAME(),
-                                                      q.qclass()));
+                    RRsetPtr cname_rrset = findRRsetFromList(syn,
+                                                             RRType::CNAME());
+                    addToMessage(q, Message::SECTION_ANSWER, cname_rrset);
+                    chaseCname(q, task, cname_rrset);
                     return (true);
                     return (true);
                 }
                 }
             }
             }
@@ -599,7 +634,7 @@ addSOA(Query& q, ZoneInfo& zoneinfo) {
     }
     }
 
 
     addToMessage(q, Message::SECTION_AUTHORITY,
     addToMessage(q, Message::SECTION_AUTHORITY,
-                 soa.findRRset(RRType::SOA(), q.qclass()));
+                 findRRsetFromList(soa, RRType::SOA()));
     return (DataSrc::SUCCESS);
     return (DataSrc::SUCCESS);
 }
 }
 
 
@@ -611,7 +646,7 @@ addNSEC(Query& q, const Name& name, ZoneInfo& zoneinfo) {
     RETERR(doQueryTask(newtask, zoneinfo, nsec));
     RETERR(doQueryTask(newtask, zoneinfo, nsec));
     if (newtask.flags == 0) {
     if (newtask.flags == 0) {
         addToMessage(q, Message::SECTION_AUTHORITY,
         addToMessage(q, Message::SECTION_AUTHORITY,
-                     nsec.findRRset(RRType::NSEC(), q.qclass()));
+                     findRRsetFromList(nsec, RRType::NSEC()));
     }
     }
 
 
     return (DataSrc::SUCCESS);
     return (DataSrc::SUCCESS);
@@ -815,7 +850,7 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
         // match the qname), and then continue as if this were a normal
         // match the qname), and then continue as if this were a normal
         // answer: if a CNAME, chase the target, otherwise add authority.
         // answer: if a CNAME, chase the target, otherwise add authority.
         if (cname) {
         if (cname) {
-            RRsetPtr rrset = wild.findRRset(RRType::CNAME(), q.qclass());
+            RRsetPtr rrset = findRRsetFromList(wild, RRType::CNAME());
             if (rrset != NULL) {
             if (rrset != NULL) {
                 rrset->setName(task->qname);
                 rrset->setName(task->qname);
                 addToMessage(q, Message::SECTION_ANSWER, rrset);
                 addToMessage(q, Message::SECTION_ANSWER, rrset);
@@ -910,7 +945,7 @@ DataSrc::doQuery(Query& q) {
              ((task->qtype == RRType::NSEC() ||
              ((task->qtype == RRType::NSEC() ||
                task->qtype == RRType::DS() ||
                task->qtype == RRType::DS() ||
                task->qtype == RRType::DNAME()) &&
                task->qtype == RRType::DNAME()) &&
-              data.findRRset(task->qtype, task->qclass)))) {
+              findRRsetFromList(data, task->qtype)))) {
             task->flags &= ~REFERRAL;
             task->flags &= ~REFERRAL;
         }
         }
 
 
@@ -935,9 +970,8 @@ DataSrc::doQuery(Query& q) {
                     // Add the NS records for the enclosing zone to
                     // Add the NS records for the enclosing zone to
                     // the authority section.
                     // the authority section.
                     RRsetList auth;
                     RRsetList auth;
-                    const DataSrc* ds = zoneinfo.getDataSource();
-                    if (!refQuery(q, Name(*zonename), zoneinfo, auth)  ||
-                        !auth.findRRset(RRType::NS(), ds->getClass())) {
+                    if (!refQuery(q, Name(*zonename), zoneinfo, auth) ||
+                        !findRRsetFromList(auth, RRType::NS())) {
                         isc_throw(DataSourceError,
                         isc_throw(DataSourceError,
                                   "NS RR not found in " << *zonename << "/" <<
                                   "NS RR not found in " << *zonename << "/" <<
                                   q.qclass());
                                   q.qclass());
@@ -970,7 +1004,7 @@ DataSrc::doQuery(Query& q) {
         } else if ((task->flags & CNAME_FOUND) != 0) {
         } else if ((task->flags & CNAME_FOUND) != 0) {
             // The qname node contains a CNAME.  Add a new task to the
             // The qname node contains a CNAME.  Add a new task to the
             // queue to look up its target.
             // queue to look up its target.
-            RRsetPtr rrset = data.findRRset(RRType::CNAME(), q.qclass());
+            RRsetPtr rrset = findRRsetFromList(data, RRType::CNAME());
             if (rrset != NULL) {
             if (rrset != NULL) {
                 addToMessage(q, task->section, rrset);
                 addToMessage(q, task->section, rrset);
                 chaseCname(q, task, rrset);
                 chaseCname(q, task, rrset);
@@ -1000,6 +1034,13 @@ DataSrc::doQuery(Query& q) {
             continue;
             continue;
         } else if ((task->flags & (NAME_NOT_FOUND|TYPE_NOT_FOUND)) != 0) {
         } else if ((task->flags & (NAME_NOT_FOUND|TYPE_NOT_FOUND)) != 0) {
             // No data found at this qname/qtype.
             // No data found at this qname/qtype.
+
+            // If we were looking for additional data, we should simply
+            // ignore this result.
+            if (task->state == QueryTask::GETADDITIONAL) {
+                continue;
+            }
+
             // If we were looking for answer data, not additional,
             // If we were looking for answer data, not additional,
             // and the name was not found, we need to find out whether
             // and the name was not found, we need to find out whether
             // there are any relevant wildcards.
             // there are any relevant wildcards.

+ 1 - 1
src/lib/datasrc/memory_datasrc.h

@@ -289,7 +289,7 @@ public:
     ///   - \c result::PARTIALMATCH: A zone whose origin is a
     ///   - \c result::PARTIALMATCH: A zone whose origin is a
     //    super domain of \c name is found (but there is no exact match)
     //    super domain of \c name is found (but there is no exact match)
     ///   - \c result::NOTFOUND: For all other cases.
     ///   - \c result::NOTFOUND: For all other cases.
-    /// - \c zone: A <Boost> shared pointer to the found \c Zone object if one
+    /// - \c zone: A "Boost" shared pointer to the found \c Zone object if one
     //  is found; otherwise \c NULL.
     //  is found; otherwise \c NULL.
     ///
     ///
     /// This method never throws an exception.
     /// This method never throws an exception.

+ 0 - 1
src/lib/datasrc/result.h

@@ -1,4 +1,3 @@
-// Copyright (C) 2010  CZ NIC
 // Copyright (C) 2010  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
 // Permission to use, copy, modify, and/or distribute this software for any

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

@@ -31,6 +31,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD += $(SQLITE_LIBS)
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
 run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la

+ 224 - 119
src/lib/datasrc/tests/datasrc_unittest.cc

@@ -38,6 +38,7 @@
 #include <datasrc/sqlite3_datasrc.h>
 #include <datasrc/sqlite3_datasrc.h>
 #include <datasrc/static_datasrc.h>
 #include <datasrc/static_datasrc.h>
 
 
+#include <testutils/dnsmessage_test.h>
 #include <dns/tests/unittest_util.h>
 #include <dns/tests/unittest_util.h>
 #include <datasrc/tests/test_datasrc.h>
 #include <datasrc/tests/test_datasrc.h>
 
 
@@ -47,6 +48,7 @@ using namespace isc::dns;
 using namespace isc::dns::rdata;
 using namespace isc::dns::rdata;
 using namespace isc::datasrc;
 using namespace isc::datasrc;
 using namespace isc::data;
 using namespace isc::data;
+using namespace isc::testutils;
 
 
 namespace {
 namespace {
 ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
 ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
@@ -54,7 +56,9 @@ ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
 
 
 class DataSrcTest : public ::testing::Test {
 class DataSrcTest : public ::testing::Test {
 protected:
 protected:
-    DataSrcTest() : obuffer(0), renderer(obuffer), msg(Message::PARSE) {
+    DataSrcTest() : obuffer(0), renderer(obuffer), msg(Message::PARSE),
+                    opcodeval(Opcode::QUERY().getCode()), qid(0)
+    {
         DataSrcPtr sql3_source = DataSrcPtr(new Sqlite3DataSrc); 
         DataSrcPtr sql3_source = DataSrcPtr(new Sqlite3DataSrc); 
         sql3_source->init(SQLITE_DBFILE_EXAMPLE);
         sql3_source->init(SQLITE_DBFILE_EXAMPLE);
         DataSrcPtr test_source = DataSrcPtr(new TestDataSrc);
         DataSrcPtr test_source = DataSrcPtr(new TestDataSrc);
@@ -66,54 +70,46 @@ protected:
     }
     }
     void QueryCommon(const RRClass& qclass);
     void QueryCommon(const RRClass& qclass);
     void createAndProcessQuery(const Name& qname, const RRClass& qclass,
     void createAndProcessQuery(const Name& qname, const RRClass& qclass,
-                               const RRType& qtype);
+                               const RRType& qtype, bool need_dnssec);
 
 
     HotCache cache;
     HotCache cache;
     MetaDataSrc meta_source;
     MetaDataSrc meta_source;
     OutputBuffer obuffer;
     OutputBuffer obuffer;
     MessageRenderer renderer;
     MessageRenderer renderer;
     Message msg;
     Message msg;
+    const uint16_t opcodeval;
+    qid_t qid;
 };
 };
 
 
 void
 void
-performQuery(DataSrc& data_source, HotCache& cache, Message& message) {
+performQuery(DataSrc& data_source, HotCache& cache, Message& message,
+             bool need_dnssec = true)
+{
     message.setHeaderFlag(Message::HEADERFLAG_AA);
     message.setHeaderFlag(Message::HEADERFLAG_AA);
     message.setRcode(Rcode::NOERROR());
     message.setRcode(Rcode::NOERROR());
-    Query q(message, cache, true);
+    Query q(message, cache, need_dnssec);
     data_source.doQuery(q);
     data_source.doQuery(q);
 }
 }
 
 
 void
 void
 DataSrcTest::createAndProcessQuery(const Name& qname, const RRClass& qclass,
 DataSrcTest::createAndProcessQuery(const Name& qname, const RRClass& qclass,
-                                   const RRType& qtype)
+                                   const RRType& qtype,
+                                   bool need_dnssec = true)
 {
 {
     msg.makeResponse();
     msg.makeResponse();
     msg.setOpcode(Opcode::QUERY());
     msg.setOpcode(Opcode::QUERY());
     msg.addQuestion(Question(qname, qclass, qtype));
     msg.addQuestion(Question(qname, qclass, qtype));
     msg.setHeaderFlag(Message::HEADERFLAG_RD);
     msg.setHeaderFlag(Message::HEADERFLAG_RD);
-    performQuery(meta_source, cache, msg);
-}
-
-void
-headerCheck(const Message& message, const Rcode& rcode, const bool qrflag,
-            const bool aaflag, const bool rdflag, const unsigned int ancount,
-            const unsigned int nscount, const unsigned int arcount)
-{
-    EXPECT_EQ(rcode, message.getRcode());
-    EXPECT_EQ(qrflag, message.getHeaderFlag(Message::HEADERFLAG_QR));
-    EXPECT_EQ(aaflag, message.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_EQ(rdflag, message.getHeaderFlag(Message::HEADERFLAG_RD));
-
-    EXPECT_EQ(ancount, message.getRRCount(Message::SECTION_ANSWER));
-    EXPECT_EQ(nscount, message.getRRCount(Message::SECTION_AUTHORITY));
-    EXPECT_EQ(arcount, message.getRRCount(Message::SECTION_ADDITIONAL));
+    qid = msg.getQid();
+    performQuery(meta_source, cache, msg, need_dnssec);
 }
 }
 
 
 void
 void
 DataSrcTest::QueryCommon(const RRClass& qclass) {
 DataSrcTest::QueryCommon(const RRClass& qclass) {
     createAndProcessQuery(Name("www.example.com"), qclass, RRType::A());
     createAndProcessQuery(Name("www.example.com"), qclass, RRType::A());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -163,12 +159,8 @@ TEST_F(DataSrcTest, Query) {
 // should be the same as "NxZone".
 // should be the same as "NxZone".
 TEST_F(DataSrcTest, QueryClassMismatch) {
 TEST_F(DataSrcTest, QueryClassMismatch) {
     createAndProcessQuery(Name("www.example.com"), RRClass::CH(), RRType::A());
     createAndProcessQuery(Name("www.example.com"), RRClass::CH(), RRType::A());
-    headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
-
-    EXPECT_EQ(Rcode::REFUSED(), msg.getRcode());
-    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_QR));
-    EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_RD));
+    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval, QR_FLAG | RD_FLAG,
+                1, 0, 0, 0);
 }
 }
 
 
 // Query class of any should match the first data source.
 // Query class of any should match the first data source.
@@ -176,10 +168,64 @@ TEST_F(DataSrcTest, QueryClassAny) {
     QueryCommon(RRClass::ANY());
     QueryCommon(RRClass::ANY());
 }
 }
 
 
+TEST_F(DataSrcTest, queryClassAnyNegative) {
+    // There was a bug where Class ANY query triggered a crash due to NULL
+    // pointer dereference.  This test checks that condition.
+
+    // NXDOMAIN case
+    createAndProcessQuery(Name("notexistent.example.com"), RRClass::ANY(),
+                          RRType::A());
+    headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
+
+    // NXRRSET case
+    msg.clear(Message::PARSE);
+    createAndProcessQuery(Name("www.example.com"), RRClass::ANY(),
+                          RRType::TXT());
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 4, 0);
+}
+
+TEST_F(DataSrcTest, queryClassAnyDNAME) {
+    // Class ANY query that would match a DNAME.  Everything including the
+    // synthesized CNAME should be the same as the response to class IN query.
+    createAndProcessQuery(Name("www.dname.example.com"), RRClass::ANY(),
+                          RRType::A(), false);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 3, 3, 3);
+    rrsetsCheck("dname.example.com. 3600 IN DNAME sql1.example.com.\n"
+                "www.dname.example.com. 3600 IN CNAME www.sql1.example.com.\n"
+                "www.sql1.example.com. 3600 IN A 192.0.2.2\n",
+                msg.beginSection(Message::SECTION_ANSWER),
+                msg.endSection(Message::SECTION_ANSWER));
+
+    // Also check the case of explicit DNAME query.
+    msg.clear(Message::PARSE);
+    createAndProcessQuery(Name("dname.example.com"), RRClass::ANY(),
+                          RRType::DNAME(), false);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 1, 3, 3);
+    rrsetsCheck("dname.example.com. 3600 IN DNAME sql1.example.com.\n",
+                msg.beginSection(Message::SECTION_ANSWER),
+                msg.endSection(Message::SECTION_ANSWER));
+}
+
+TEST_F(DataSrcTest, queryClassAnyCNAME) {
+    // Similar test for CNAME
+    createAndProcessQuery(Name("foo.example.com"), RRClass::ANY(),
+                          RRType::A(), false);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 1, 0, 0);
+    rrsetsCheck("foo.example.com. 3600 IN CNAME cnametest.example.net.\n",
+                msg.beginSection(Message::SECTION_ANSWER),
+                msg.endSection(Message::SECTION_ANSWER));
+}
+
 TEST_F(DataSrcTest, NSQuery) {
 TEST_F(DataSrcTest, NSQuery) {
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::NS());
                           RRType::NS());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 0, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -201,7 +247,8 @@ TEST_F(DataSrcTest, NSQuery) {
 TEST_F(DataSrcTest, DuplicateQuery) {
 TEST_F(DataSrcTest, DuplicateQuery) {
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::NS());
                           RRType::NS());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 0, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -221,7 +268,8 @@ TEST_F(DataSrcTest, DuplicateQuery) {
     msg.clear(Message::PARSE);
     msg.clear(Message::PARSE);
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::NS());
                           RRType::NS());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 0, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
 
 
     rit = msg.beginSection(Message::SECTION_ANSWER);
     rit = msg.beginSection(Message::SECTION_ANSWER);
     rrset = *rit;
     rrset = *rit;
@@ -242,7 +290,8 @@ TEST_F(DataSrcTest, DuplicateQuery) {
 TEST_F(DataSrcTest, DNSKEYQuery) {
 TEST_F(DataSrcTest, DNSKEYQuery) {
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::DNSKEY());
                           RRType::DNSKEY());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -257,7 +306,8 @@ TEST_F(DataSrcTest, DNSKEYQuery) {
 TEST_F(DataSrcTest, DNSKEYDuplicateQuery) {
 TEST_F(DataSrcTest, DNSKEYDuplicateQuery) {
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::DNSKEY());
                           RRType::DNSKEY());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -279,7 +329,8 @@ TEST_F(DataSrcTest, NxRRset) {
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
     createAndProcessQuery(Name("example.com"), RRClass::IN(),
                           RRType::PTR());
                           RRType::PTR());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 4, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 4, 0);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -291,7 +342,8 @@ TEST_F(DataSrcTest, Nxdomain) {
     createAndProcessQuery(Name("glork.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("glork.example.com"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
 
 
-    headerCheck(msg, Rcode::NXDOMAIN(), true, true, true, 0, 6, 0);
+    headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -301,11 +353,46 @@ TEST_F(DataSrcTest, Nxdomain) {
     // XXX: check for other authority section answers
     // XXX: check for other authority section answers
 }
 }
 
 
+TEST_F(DataSrcTest, NxdomainAfterSOAQuery) {
+    // There was a bug where once SOA RR is stored in the hot spot cache
+    // subsequent negative search fails due to "missing SOA".  This test
+    // checks that situation.
+
+    // First, run the scenario with disabling the cache.
+    cache.setEnabled(false);
+    createAndProcessQuery(Name("example.com"), RRClass::IN(),
+                          RRType::SOA());
+    msg.clear(Message::PARSE);
+    createAndProcessQuery(Name("notexistent.example.com"), RRClass::IN(),
+                          RRType::A());
+    {
+        SCOPED_TRACE("NXDOMAIN after SOA, without hot spot cache");
+        headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+                    QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
+    }
+
+    // Then enable the cache and perform the same queries.  This should
+    // produce the same result.
+    cache.setEnabled(true);
+    msg.clear(Message::PARSE);
+    createAndProcessQuery(Name("example.com"), RRClass::IN(),
+                          RRType::SOA());
+    msg.clear(Message::PARSE);
+    createAndProcessQuery(Name("notexistent.example.com"), RRClass::IN(),
+                        RRType::A());
+    {
+        SCOPED_TRACE("NXDOMAIN after SOA, without hot spot cache");
+        headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+                    QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
+    }
+}
+
 TEST_F(DataSrcTest, NxZone) {
 TEST_F(DataSrcTest, NxZone) {
     createAndProcessQuery(Name("spork.example"), RRClass::IN(),
     createAndProcessQuery(Name("spork.example"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
 
 
-    headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
+    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 0, 0);
 
 
     EXPECT_EQ(Rcode::REFUSED(), msg.getRcode());
     EXPECT_EQ(Rcode::REFUSED(), msg.getRcode());
     EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_QR));
     EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_QR));
@@ -317,7 +404,8 @@ TEST_F(DataSrcTest, Wildcard) {
     createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 6, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 6, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -369,7 +457,8 @@ TEST_F(DataSrcTest, WildcardNodata) {
     // returns NOERROR
     // returns NOERROR
     createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
                           RRType::AAAA());
                           RRType::AAAA());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 2, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 2, 0);
 }
 }
 
 
 TEST_F(DataSrcTest, DISABLED_WildcardAgainstMultiLabel) {
 TEST_F(DataSrcTest, DISABLED_WildcardAgainstMultiLabel) {
@@ -377,72 +466,42 @@ TEST_F(DataSrcTest, DISABLED_WildcardAgainstMultiLabel) {
     // a single label), and it should result in NXDOMAIN.
     // a single label), and it should result in NXDOMAIN.
     createAndProcessQuery(Name("www.xxx.wild.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("www.xxx.wild.example.com"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
-    headerCheck(msg, Rcode::NXDOMAIN(), true, true, true, 0, 1, 0);
+    headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
 }
 }
 
 
 TEST_F(DataSrcTest, WildcardCname) {
 TEST_F(DataSrcTest, WildcardCname) {
     // Check that wildcard answers containing CNAMES are followed
     // Check that wildcard answers containing CNAMES are followed
-    // correctly
-    createAndProcessQuery(Name("www.wild2.example.com"), RRClass::IN(),
-                          RRType::A());
-
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 6, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("www.wild2.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::CNAME(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("www.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    ++rit;
-    ++rit;
-    rrset = *rit;
-    EXPECT_EQ(Name("www.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    rrset = *rit;
-    EXPECT_EQ(Name("*.wild2.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NSEC(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-    ++rit;
-    ++rit;
-
-    rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    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());
-
-    rit = msg.beginSection(Message::SECTION_ADDITIONAL);
-    rrset = *rit;
-    EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
+    // correctly.  It should result in the same response for both
+    // class IN and ANY queries.
+    const RRClass classes[2] = { RRClass::IN(), RRClass::ANY() };
+
+    for (int i = 0; i < sizeof(classes) / sizeof(classes[0]); ++i) {
+        SCOPED_TRACE("Wildcard + CNAME test for class " + classes[i].toText());
+
+        msg.clear(Message::PARSE);
+
+        createAndProcessQuery(Name("www.wild2.example.com"), classes[i],
+                              RRType::A(), false);
+
+        headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                    QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 3, 3);
+
+        rrsetsCheck("www.wild2.example.com. 3600 IN CNAME www.example.com\n"
+                    "www.example.com. 3600 IN A 192.0.2.1\n",
+                    msg.beginSection(Message::SECTION_ANSWER),
+                    msg.endSection(Message::SECTION_ANSWER));
+        rrsetsCheck("example.com. 3600 IN NS dns01.example.com.\n"
+                    "example.com. 3600 IN NS dns02.example.com.\n"
+                    "example.com. 3600 IN NS dns03.example.com.",
+                    msg.beginSection(Message::SECTION_AUTHORITY),
+                    msg.endSection(Message::SECTION_AUTHORITY));
+        rrsetsCheck("dns01.example.com. 3600 IN A 192.0.2.1\n"
+                    "dns02.example.com. 3600 IN A 192.0.2.2\n"
+                    "dns03.example.com. 3600 IN A 192.0.2.3",
+                    msg.beginSection(Message::SECTION_ADDITIONAL),
+                    msg.endSection(Message::SECTION_ADDITIONAL));
+    }
 }
 }
 
 
 TEST_F(DataSrcTest, WildcardCnameNodata) {
 TEST_F(DataSrcTest, WildcardCnameNodata) {
@@ -450,7 +509,8 @@ TEST_F(DataSrcTest, WildcardCnameNodata) {
     // data of this type.
     // data of this type.
     createAndProcessQuery(Name("www.wild2.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("www.wild2.example.com"), RRClass::IN(),
                           RRType::AAAA());
                           RRType::AAAA());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 4, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 0);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -481,7 +541,8 @@ TEST_F(DataSrcTest, WildcardCnameNxdomain) {
     // A wildcard containing a CNAME whose target does not exist
     // A wildcard containing a CNAME whose target does not exist
     createAndProcessQuery(Name("www.wild3.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("www.wild3.example.com"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 6, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 6, 0);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -518,7 +579,8 @@ TEST_F(DataSrcTest, AuthDelegation) {
     createAndProcessQuery(Name("www.sql1.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("www.sql1.example.com"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -562,7 +624,8 @@ TEST_F(DataSrcTest, Dname) {
     createAndProcessQuery(Name("www.dname.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("www.dname.example.com"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 5, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 5, 4, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -610,14 +673,16 @@ TEST_F(DataSrcTest, DnameExact) {
     // confuse delegation processing.
     // confuse delegation processing.
     createAndProcessQuery(Name("dname2.foo.example.org"), RRClass::IN(),
     createAndProcessQuery(Name("dname2.foo.example.org"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 1, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
 }
 }
 
 
 TEST_F(DataSrcTest, Cname) {
 TEST_F(DataSrcTest, Cname) {
     createAndProcessQuery(Name("foo.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("foo.example.com"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 0, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 0, 0);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -626,7 +691,7 @@ TEST_F(DataSrcTest, Cname) {
     EXPECT_EQ(RRClass::IN(), rrset->getClass());
     EXPECT_EQ(RRClass::IN(), rrset->getClass());
 
 
     RdataIteratorPtr it = rrset->getRdataIterator();
     RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("cnametest.flame.org.", it->getCurrent().toText());
+    EXPECT_EQ("cnametest.example.net.", it->getCurrent().toText());
     it->next();
     it->next();
     EXPECT_TRUE(it->isLast());
     EXPECT_TRUE(it->isLast());
 }
 }
@@ -635,7 +700,8 @@ TEST_F(DataSrcTest, CnameInt) {
     createAndProcessQuery(Name("cname-int.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("cname-int.example.com"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -661,7 +727,8 @@ TEST_F(DataSrcTest, CnameExt) {
     createAndProcessQuery(Name("cname-ext.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("cname-ext.example.com"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -685,7 +752,8 @@ TEST_F(DataSrcTest, Delegation) {
     createAndProcessQuery(Name("www.subzone.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("www.subzone.example.com"), RRClass::IN(),
                           RRType::A());
                           RRType::A());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, false, true, 0, 5, 2);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 5, 2);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -714,7 +782,8 @@ TEST_F(DataSrcTest, NSDelegation) {
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
                           RRType::NS());
                           RRType::NS());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, false, true, 0, 5, 2);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 5, 2);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -750,7 +819,8 @@ TEST_F(DataSrcTest, NSECZonecut) {
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
                           RRType::NSEC());
                           RRType::NSEC());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 2, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -778,7 +848,8 @@ TEST_F(DataSrcTest, DNAMEZonecut) {
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
                           RRType::DNAME());
                           RRType::DNAME());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, false, true, 0, 5, 2);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 5, 2);
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
     EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
     EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
@@ -806,7 +877,8 @@ TEST_F(DataSrcTest, DS) {
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
     createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
                           RRType::DS());
                           RRType::DS());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 3, 4, 6);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 3, 4, 6);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
@@ -847,7 +919,8 @@ TEST_F(DataSrcTest, NSECZonecutOfNonsecureZone) {
     createAndProcessQuery(Name("sub.example.org"), RRClass::IN(),
     createAndProcessQuery(Name("sub.example.org"), RRClass::IN(),
                           RRType::NSEC());
                           RRType::NSEC());
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, false, true, 0, 1, 1);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 1, 1);
 
 
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     ConstRRsetPtr rrset = *rit;
     ConstRRsetPtr rrset = *rit;
@@ -879,7 +952,8 @@ TEST_F(DataSrcTest, NSECZonecutOfNonsecureZone) {
 TEST_F(DataSrcTest, RootDSQuery1) {
 TEST_F(DataSrcTest, RootDSQuery1) {
     EXPECT_NO_THROW(createAndProcessQuery(Name("."), RRClass::IN(),
     EXPECT_NO_THROW(createAndProcessQuery(Name("."), RRClass::IN(),
                                           RRType::DS()));
                                           RRType::DS()));
-    headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
+    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 0, 0);
 }
 }
 
 
 // The same, but when we have the root zone
 // The same, but when we have the root zone
@@ -898,7 +972,8 @@ TEST_F(DataSrcTest, RootDSQuery2) {
     // Make the query
     // Make the query
     EXPECT_NO_THROW(performQuery(*sql3_source, cache, msg));
     EXPECT_NO_THROW(performQuery(*sql3_source, cache, msg));
 
 
-    headerCheck(msg, Rcode::NOERROR(), true, true, true, 0, 1, 0);
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
 }
 }
 
 
 TEST_F(DataSrcTest, DSQueryFromCache) {
 TEST_F(DataSrcTest, DSQueryFromCache) {
@@ -916,7 +991,8 @@ TEST_F(DataSrcTest, DSQueryFromCache) {
 
 
     // returning refused is probably a bad behavior, but it's a different
     // returning refused is probably a bad behavior, but it's a different
     // issue -- see Trac Ticket #306.
     // issue -- see Trac Ticket #306.
-    headerCheck(msg, Rcode::REFUSED(), true, false, true, 0, 0, 0);
+    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 0, 0);
 }
 }
 
 
 // Non-existent name in the "static" data source.  The purpose of this test
 // Non-existent name in the "static" data source.  The purpose of this test
@@ -925,7 +1001,8 @@ TEST_F(DataSrcTest, DSQueryFromCache) {
 TEST_F(DataSrcTest, StaticNxDomain) {
 TEST_F(DataSrcTest, StaticNxDomain) {
     createAndProcessQuery(Name("www.version.bind"), RRClass::CH(),
     createAndProcessQuery(Name("www.version.bind"), RRClass::CH(),
                           RRType::TXT());
                           RRType::TXT());
-    headerCheck(msg, Rcode::NXDOMAIN(), true, true, true, 0, 1, 0);
+    headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
+                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset = *rit;
     RRsetPtr rrset = *rit;
     EXPECT_EQ(Name("version.bind"), rrset->getName());
     EXPECT_EQ(Name("version.bind"), rrset->getName());
@@ -973,6 +1050,34 @@ TEST_F(DataSrcTest, noSOAZone) {
                  DataSourceError);
                  DataSourceError);
 }
 }
 
 
+TEST_F(DataSrcTest, apexCNAMEZone) {
+    // The query name doesn't exist in the best matching zone,
+    // and there's a CNAME at the apex (which is bogus), so query handling
+    // will fail due to missing SOA.
+    EXPECT_THROW(createAndProcessQuery(Name("notexist.apexcname.example"),
+                                       RRClass::IN(), RRType::A()),
+                 DataSourceError);
+}
+
+TEST_F(DataSrcTest, incompleteGlue) {
+    // One of the NS names belong to a different zone (which is still
+    // authoritative), and the glue is missing in that zone.  We should
+    // still return the existent glue.
+    // (nons.example is also broken in that it doesn't have apex NS, but
+    // that doesn't matter for this test)
+    createAndProcessQuery(Name("www.incompletechild.nons.example"),
+                          RRClass::IN(), RRType::A());
+    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
+                QR_FLAG | RD_FLAG, 1, 0, 2, 1);
+    rrsetsCheck("incompletechild.nons.example. 3600 IN NS ns.incompletechild.nons.example.\n"
+                "incompletechild.nons.example. 3600 IN NS nx.nosoa.example.",
+                msg.beginSection(Message::SECTION_AUTHORITY),
+                msg.endSection(Message::SECTION_AUTHORITY));
+    rrsetsCheck("ns.incompletechild.nons.example. 3600 IN A 192.0.2.1",
+                msg.beginSection(Message::SECTION_ADDITIONAL),
+                msg.endSection(Message::SECTION_ADDITIONAL));
+}
+
 // currently fails
 // currently fails
 TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
 TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
     // qname has the possible max length (255 octets).  it matches a DNAME,
     // qname has the possible max length (255 octets).  it matches a DNAME,

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

@@ -1,5 +1,4 @@
 // Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
 // Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-// Copyright (C) 2011  CZ NIC
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above

+ 31 - 3
src/lib/datasrc/tests/test_datasrc.cc

@@ -154,7 +154,7 @@ const struct RRData example_com_records[] = {
     {"*.wild3.example.com", "RRSIG", "NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="},
     {"*.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
-    {"foo.example.com", "CNAME", "cnametest.flame.org"},
+    {"foo.example.com", "CNAME", "cnametest.example.net"},
     {"foo.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. DSqkLnsh0gCeCPVW/Q8viy9GNP+KHmFGfWqyVG1S6koBtGN/VQQ16M4PHZ9Zssmf/JcDVJNIhAChHPE2WJiaPCNGTprsaUshf1Q2vMPVnkrJKgDY8SVRYMptmT8eaT0gGri4KhqRoFpMT5OYfesybwDgfhFSQQAh6ps3bIUsy4o="},
     {"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", "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="},
     {"foo.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. RTQwlSqui6StUYye1KCSOEr1d3irndWFqHBpwP7g7n+w8EDXJ8I7lYgwzHvlQt6BLAxe5fUDi7ct8M5hXvsm7FoWPZ5wXH+2/eJUCYxIw4vezKMkMwBP6M/YkJ2CMqY8DppYf60QaLDONQAr7AcK/naSyioeI5h6eaoVitUDMso="},
@@ -199,6 +199,7 @@ const struct RRData example_com_records[] = {
 
 
     {NULL, NULL, NULL}
     {NULL, NULL, NULL}
 };
 };
+
 const struct RRData example_com_glue_records[] = {
 const struct RRData example_com_glue_records[] = {
     {"ns1.subzone.example.com", "A", "192.0.2.1"},
     {"ns1.subzone.example.com", "A", "192.0.2.1"},
     {"ns2.subzone.example.com", "A", "192.0.2.2"},
     {"ns2.subzone.example.com", "A", "192.0.2.2"},
@@ -247,6 +248,20 @@ const struct RRData nons_example_records[] = {
      "1234 3600 1800 2419200 7200"},
      "1234 3600 1800 2419200 7200"},
     {"www.nons.example", "A", "192.0.2.1"},
     {"www.nons.example", "A", "192.0.2.1"},
     {"ns.nons.example", "A", "192.0.2.2"},
     {"ns.nons.example", "A", "192.0.2.2"},
+
+    // One of the NS names is intentionally non existent in the zone it belongs
+    // to.  This delegation is used to see if we still return the NS and the
+    // existent glue.
+    // (These are not relevant to test the case for the "no NS" case.  We use
+    // this zone to minimize the number of test zones)
+    {"incompletechild.nons.example", "NS", "ns.incompletechild.nons.example"},
+    {"incompletechild.nons.example", "NS", "nx.nosoa.example"},
+
+    {NULL, NULL, NULL}
+};
+
+const struct RRData nons_example_glue_records[] = {
+    {"ns.incompletechild.nons.example", "A", "192.0.2.1"},
     {NULL, NULL, NULL}
     {NULL, NULL, NULL}
 };
 };
 
 
@@ -273,6 +288,18 @@ const struct RRData nosoa_example_records[] = {
 };
 };
 
 
 //
 //
+// zone data for apexcname.example.
+//
+const struct RRData apexcname_example_records[] = {
+    {"apexcname.example", "CNAME", "canonical.apexcname.example"},
+    {"canonical.apexcname.example", "SOA",
+     "master.apexcname.example "
+     "admin.apexcname.example. 1234 3600 1800 2419200 7200"},
+    {NULL, NULL, NULL}
+};
+
+
+//
 // empty data set, for convenience.
 // empty data set, for convenience.
 //
 //
 const struct RRData empty_records[] = {
 const struct RRData empty_records[] = {
@@ -286,9 +313,10 @@ const struct ZoneData zone_data[] = {
     { "example.com", "IN", example_com_records, example_com_glue_records },
     { "example.com", "IN", example_com_records, example_com_glue_records },
     { "sql1.example.com", "IN", sql1_example_com_records, empty_records },
     { "sql1.example.com", "IN", sql1_example_com_records, empty_records },
     { "loop.example", "IN", loop_example_records, empty_records },
     { "loop.example", "IN", loop_example_records, empty_records },
-    { "nons.example", "IN", nons_example_records, empty_records },
+    { "nons.example", "IN", nons_example_records, nons_example_glue_records },
     { "nons-dname.example", "IN", nonsdname_example_records, empty_records },
     { "nons-dname.example", "IN", nonsdname_example_records, empty_records },
-    { "nosoa.example", "IN", nosoa_example_records, empty_records }
+    { "nosoa.example", "IN", nosoa_example_records, empty_records },
+    { "apexcname.example", "IN", nosoa_example_records, empty_records }
 };
 };
 const size_t NUM_ZONES = sizeof(zone_data) / sizeof(zone_data[0]);
 const size_t NUM_ZONES = sizeof(zone_data) / sizeof(zone_data[0]);
 
 

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

@@ -1,4 +1,3 @@
-// Copyright (C) 2010  CZ NIC
 // Copyright (C) 2010  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
 // Permission to use, copy, modify, and/or distribute this software for any

+ 1 - 1
src/lib/datasrc/zonetable.h

@@ -107,7 +107,7 @@ public:
     ///   - \c result::PARTIALMATCH: A zone whose origin is a
     ///   - \c result::PARTIALMATCH: A zone whose origin is a
     ///    super domain of \c name is found (but there is no exact match)
     ///    super domain of \c name is found (but there is no exact match)
     ///   - \c result::NOTFOUND: For all other cases.
     ///   - \c result::NOTFOUND: For all other cases.
-    /// - \c zone: A <Boost> shared pointer to the found \c Zone object if one
+    /// - \c zone: A "Boost" shared pointer to the found \c Zone object if one
     ///  is found; otherwise \c NULL.
     ///  is found; otherwise \c NULL.
     ///
     ///
     /// This method never throws an exception.
     /// This method never throws an exception.

+ 15 - 0
src/lib/dns/buffer.h

@@ -356,6 +356,21 @@ public:
     /// \param data The 8-bit integer to be written into the buffer.
     /// \param data The 8-bit integer to be written into the buffer.
     void writeUint8(uint8_t data) { data_.push_back(data); }
     void writeUint8(uint8_t data) { data_.push_back(data); }
 
 
+    /// \brief Write an unsigned 8-bit integer into the buffer.
+    ///
+    /// The position must be lower than the size of the buffer,
+    /// otherwise an exception of class \c isc::dns::InvalidBufferPosition
+    /// will be thrown.
+    ///
+    /// \param data The 8-bit integer to be written into the buffer.
+    /// \param pos The position in the buffer to write the data.
+    void writeUint8At(uint8_t data, size_t pos) {
+        if (pos + sizeof(data) > data_.size()) {
+            isc_throw(InvalidBufferPosition, "write at invalid position");
+        }
+        data_[pos] = data;
+    }
+
     /// \brief Write an unsigned 16-bit integer in host byte order into the
     /// \brief Write an unsigned 16-bit integer in host byte order into the
     /// buffer in network byte order.
     /// buffer in network byte order.
     ///
     ///

+ 0 - 0
src/lib/dns/edns.h


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