Browse Source

[trac558] Merge branch 'master' into trac558

Conflicts:
	configure.ac
	src/lib/log/Makefile.am
Stephen Morris 14 years ago
parent
commit
8661db658e
100 changed files with 6563 additions and 2610 deletions
  1. 31 0
      ChangeLog
  2. 1 0
      Makefile.am
  3. 54 42
      configure.ac
  4. 1 1
      doc/Doxyfile
  5. 0 0
      ext/coroutine/coroutine.h
  6. 0 1
      src/bin/auth/Makefile.am
  7. 9 3
      src/bin/auth/auth_srv.cc
  8. 2 1
      src/bin/auth/auth_srv.h
  9. 6 1
      src/bin/auth/config.cc
  10. 50 9
      src/bin/auth/query.cc
  11. 1 1
      src/bin/auth/tests/command_unittest.cc
  12. 4 0
      src/bin/auth/tests/config_unittest.cc
  13. 162 13
      src/bin/auth/tests/query_unittest.cc
  14. 12 30
      src/bin/bind10/bind10.py.in
  15. 1 1
      src/bin/msgq/tests/msgq_test.py
  16. 0 1
      src/bin/resolver/Makefile.am
  17. 0 1
      src/bin/resolver/resolver.cc
  18. 9 2
      src/bin/resolver/resolver.h
  19. 1 1
      src/bin/resolver/response_scrubber.h
  20. 2 4
      src/bin/resolver/tests/Makefile.am
  21. 3 3
      src/bin/resolver/tests/response_scrubber_unittest.cc
  22. 1 1
      src/lib/Makefile.am
  23. 19 9
      src/lib/asiolink/Makefile.am
  24. 0 756
      src/lib/asiolink/asiolink.cc
  25. 12 641
      src/lib/asiolink/asiolink.h
  26. 73 0
      src/lib/asiolink/dns_answer.h
  27. 81 0
      src/lib/asiolink/dns_lookup.h
  28. 152 0
      src/lib/asiolink/dns_server.h
  29. 192 0
      src/lib/asiolink/dns_service.cc
  30. 106 0
      src/lib/asiolink/dns_service.h
  31. 0 1
      src/lib/asiolink/internal/Makefile.am
  32. 0 37
      src/lib/asiolink/internal/tests/Makefile.am
  33. 0 253
      src/lib/asiolink/internal/udpdns.h
  34. 136 0
      src/lib/asiolink/interval_timer.cc
  35. 133 0
      src/lib/asiolink/interval_timer.h
  36. 0 0
      src/lib/asiolink/io_address.cc
  37. 0 0
      src/lib/asiolink/io_address.h
  38. 5 3
      src/lib/asiolink/ioendpoint.cc
  39. 1 1
      src/lib/asiolink/ioendpoint.h
  40. 2 2
      src/lib/asiolink/iomessage.h
  41. 97 0
      src/lib/asiolink/io_service.cc
  42. 77 0
      src/lib/asiolink/io_service.h
  43. 1 1
      src/lib/asiolink/iosocket.cc
  44. 0 0
      src/lib/asiolink/io_socket.h
  45. 457 0
      src/lib/asiolink/recursive_query.cc
  46. 113 0
      src/lib/asiolink/recursive_query.h
  47. 71 0
      src/lib/asiolink/simple_callback.h
  48. 98 0
      src/lib/asiolink/tcp_endpoint.h
  49. 14 14
      src/lib/asiolink/tcpdns.cc
  50. 9 114
      src/lib/asiolink/internal/tcpdns.h
  51. 52 0
      src/lib/asiolink/tcp_socket.h
  52. 11 1
      src/lib/asiolink/tests/Makefile.am
  53. 292 0
      src/lib/asiolink/tests/interval_timer_unittest.cc
  54. 115 0
      src/lib/asiolink/tests/io_service_unittest.cc
  55. 57 0
      src/lib/asiolink/tests/ioaddress_unittest.cc
  56. 67 0
      src/lib/asiolink/tests/ioendpoint_unittest.cc
  57. 29 0
      src/lib/asiolink/tests/iosocket_unittest.cc
  58. 75 501
      src/lib/asiolink/tests/asiolink_unittest.cc
  59. 2 2
      src/lib/asiolink/internal/tests/udpdns_unittest.cc
  60. 89 0
      src/lib/asiolink/udp_endpoint.h
  61. 189 0
      src/lib/asiolink/udp_query.cc
  62. 88 0
      src/lib/asiolink/udp_query.h
  63. 9 157
      src/lib/asiolink/udpdns.cc
  64. 102 0
      src/lib/asiolink/udp_server.h
  65. 48 0
      src/lib/asiolink/udp_socket.h
  66. 33 0
      src/lib/cache/Makefile.am
  67. 14 0
      src/lib/cache/TODO
  68. 44 0
      src/lib/cache/cache_entry_key.cc
  69. 56 0
      src/lib/cache/cache_entry_key.h
  70. 58 0
      src/lib/cache/local_zone_data.cc
  71. 66 0
      src/lib/cache/local_zone_data.h
  72. 97 0
      src/lib/cache/message_cache.cc
  73. 95 0
      src/lib/cache/message_cache.h
  74. 251 0
      src/lib/cache/message_entry.cc
  75. 180 0
      src/lib/cache/message_entry.h
  76. 246 0
      src/lib/cache/resolver_cache.cc
  77. 331 0
      src/lib/cache/resolver_cache.h
  78. 106 0
      src/lib/cache/rrset_cache.cc
  79. 108 0
      src/lib/cache/rrset_cache.h
  80. 40 0
      src/lib/cache/rrset_copy.cc
  81. 44 0
      src/lib/cache/rrset_copy.h
  82. 68 0
      src/lib/cache/rrset_entry.cc
  83. 137 0
      src/lib/cache/rrset_entry.h
  84. 66 0
      src/lib/cache/tests/Makefile.am
  85. 40 0
      src/lib/cache/tests/cache_test_messagefromfile.h
  86. 45 0
      src/lib/cache/tests/cache_test_sectioncount.h
  87. 65 0
      src/lib/cache/tests/local_zone_data_unittest.cc
  88. 98 0
      src/lib/cache/tests/message_cache_unittest.cc
  89. 237 0
      src/lib/cache/tests/message_entry_unittest.cc
  90. 138 0
      src/lib/cache/tests/resolver_cache_unittest.cc
  91. 84 0
      src/lib/cache/tests/rrset_cache_unittest.cc
  92. 107 0
      src/lib/cache/tests/rrset_entry_unittest.cc
  93. 9 1
      src/lib/asiolink/internal/tests/run_unittests.cc
  94. 22 0
      src/lib/cache/tests/testdata/message_fromWire1
  95. 22 0
      src/lib/cache/tests/testdata/message_fromWire2
  96. 76 0
      src/lib/cache/tests/testdata/message_fromWire3
  97. 80 0
      src/lib/cache/tests/testdata/message_fromWire4
  98. 36 0
      src/lib/cache/tests/testdata/message_fromWire5
  99. 40 0
      src/lib/cache/tests/testdata/message_fromWire6
  100. 0 0
      src/lib/datasrc/memory_datasrc.cc

+ 31 - 0
ChangeLog

@@ -1,3 +1,34 @@
+  169.  [func]      zhang likun, jelte
+	Added a basic implementation for a resolver cache (though not
+	used yet).
+	(Trac #449, git 8aa3b2246ae095bbe7f855fd11656ae3bdb98986)
+
+  168.  [bug]       vorner
+	Boss no longer has the -f argument, which was undocumented and stayed as
+	a relict of previous versions, currently causing only strange behaviour.
+	(Trac #572, git 17f237478961005707d649a661cc72a4a0d612d4)
+
+  167.  [bug]           naokikambe
+	Fixed failure of termination of msgq_test.py with python3 coverage(3.3.1)
+	(Trac #573, git 0e6a18e12f61cc482e07078776234f32605312e5)
+
+  166.  [func]      jelte
+	The resolver now sends back a SERVFAIL when there is a client
+	timeout (timeout_client config setting), but it will not stop
+	resolving (until there is a lookup timeout or a result).
+	(Trac #497 and #489, git af0e5cd93bebb27cb5c4457f7759d12c8bf953a6)
+
+  165.  [func]      jelte
+	The resolver now handles CNAMEs, it will follow them, and include
+	them in the answer. The maximum length of CNAME chains that is
+	supported is 16.
+	(Trac #497, git af0e5cd93bebb27cb5c4457f7759d12c8bf953a6)
+
+  164.  [bug]           y-aharen
+	IntervalTimer: Modified the interface to accept interval in
+	milliseconds. It shortens the time of the tests of IntervalTimer.
+	(Trac #452, git c9f6acc81e24c4b8f0eb351123dc7b43f64e0914)
+
   163.  [func]      vorner
   163.  [func]      vorner
 	The pimpl design pattern is used in UDPServer, with a shared pointer. This
 	The pimpl design pattern is used in UDPServer, with a shared pointer. This
 	makes it smaller to copy (which is done a lot as a sideeffect of being
 	makes it smaller to copy (which is done a lot as a sideeffect of being

+ 1 - 0
Makefile.am

@@ -282,3 +282,4 @@ EXTRA_DIST += ext/asio/asio/is_write_buffered.hpp
 EXTRA_DIST += ext/asio/asio/buffered_read_stream_fwd.hpp
 EXTRA_DIST += ext/asio/asio/buffered_read_stream_fwd.hpp
 EXTRA_DIST += ext/asio/asio/socket_acceptor_service.hpp
 EXTRA_DIST += ext/asio/asio/socket_acceptor_service.hpp
 EXTRA_DIST += ext/asio/asio.hpp
 EXTRA_DIST += ext/asio/asio.hpp
+EXTRA_DIST += ext/coroutine/coroutine.h

+ 54 - 42
configure.ac

@@ -366,44 +366,53 @@ AC_SUBST(USE_LCOV)
 # Configure log4cxx header and library path
 # Configure log4cxx header and library path
 #
 #
 # If explicitly specified, use it.
 # If explicitly specified, use it.
-#
-# AC_ARG_WITH([log4cxx],
-#   AC_HELP_STRING([--with-log4cxx=PATH],
-#     [specify directory where log4cxx is installed]),
-#   [
-#    log4cxx_include_path="${withval}/include";
-#    log4cxx_library_path="${withval}/lib"
-#   ])
-#
-# # If not specified, try some common paths.  These default to
-# # /usr/include and /usr/lib if not found
-#
-# if test -z "$with_log4cxx"; then
-# 	log4cxxdirs="/usr/local /usr/pkg /opt /opt/local"
-# 	for d in $log4cxxdirs
-# 	do
-# 		if test -d $d/include/log4cxx; then
-# 			log4cxx_include_path=$d/include
-# 			log4cxx_library_path=$d/lib
-# 			break
-# 		fi
-# 	done
-# fi
-#
-# CPPFLAGS_SAVES="$CPPFLAGS"
-# if test "${log4cxx_include_path}" ; then
-# 	LOG4CXX_INCLUDES="-I${log4cxx_include_path}"
-# 	CPPFLAGS="$CPPFLAGS $LOG4CXX_INCLUDES"
-# fi
-# AC_CHECK_HEADER([log4cxx/logger.h],, AC_MSG_ERROR([Missing log4cxx header files.]))
-# CPPFLAGS="$CPPFLAGS_SAVES"
-# AC_SUBST(LOG4CXX_INCLUDES)
-#
-# LOG4CXX_LDFLAGS="-llog4cxx";
-# if test "${log4cxx_library_path}"; then
-#     LOG4CXX_LDFLAGS="-L${log4cxx_library_path} -llog4cxx"
-# fi
-# AC_SUBST(LOG4CXX_LDFLAGS)
+
+AC_ARG_WITH([log4cxx],
+  AC_HELP_STRING([--with-log4cxx=PATH],
+    [specify directory where log4cxx is installed]),
+  [
+   log4cxx_include_path="${withval}/include";
+   log4cxx_library_path="${withval}/lib"
+  ])
+
+# This is an urgent fix to avoid regression due to log4cxx on some
+# platforms.  It should be cleaned up with a better fix.
+if test "X$with_log4cxx" != "Xno"; then
+
+# If not specified, try some common paths.  These default to
+# /usr/include and /usr/lib if not found
+
+if test -z "$with_log4cxx"; then
+	log4cxxdirs="/usr/local /usr/pkg /opt /opt/local"
+	for d in $log4cxxdirs
+	do
+		if test -d $d/include/log4cxx; then
+			log4cxx_include_path=$d/include
+			log4cxx_library_path=$d/lib
+			break
+		fi
+	done
+fi
+
+CPPFLAGS_SAVES="$CPPFLAGS"
+if test "${log4cxx_include_path}" ; then
+	LOG4CXX_INCLUDES="-I${log4cxx_include_path}"
+	CPPFLAGS="$CPPFLAGS $LOG4CXX_INCLUDES"
+fi
+AC_CHECK_HEADER([log4cxx/logger.h],, AC_MSG_ERROR([Missing log4cxx header files.]))
+CPPFLAGS="$CPPFLAGS_SAVES"
+AC_SUBST(LOG4CXX_INCLUDES)
+
+LOG4CXX_LDFLAGS="-llog4cxx";
+if test "${log4cxx_library_path}"; then
+    LOG4CXX_LDFLAGS="-L${log4cxx_library_path} -llog4cxx"
+fi
+AC_SUBST(LOG4CXX_LDFLAGS)
+
+# The following two lines are part of the urgent fix, and should be cleaned
+# up with a better fix.
+fi
+AM_CONDITIONAL(USE_LOG4CXX, test "X${with_log4cxx}" != "Xno")
 
 
 #
 #
 # Configure Boost header path
 # Configure Boost header path
@@ -564,6 +573,9 @@ AC_SUBST(MULTITHREADING_FLAG)
 #
 #
 CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/ext/asio"
 CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/ext/asio"
 #
 #
+# Use our 'coroutine' header from ext
+CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/ext/coroutine"
+#
 # Disable threads: Currently we don't use them.
 # Disable threads: Currently we don't use them.
 CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_THREADS=1"
 CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_THREADS=1"
 #
 #
@@ -657,8 +669,6 @@ AC_CONFIG_FILES([Makefile
                  src/lib/Makefile
                  src/lib/Makefile
                  src/lib/asiolink/Makefile
                  src/lib/asiolink/Makefile
                  src/lib/asiolink/tests/Makefile
                  src/lib/asiolink/tests/Makefile
-                 src/lib/asiolink/internal/Makefile
-                 src/lib/asiolink/internal/tests/Makefile
                  src/lib/bench/Makefile
                  src/lib/bench/Makefile
                  src/lib/bench/example/Makefile
                  src/lib/bench/example/Makefile
                  src/lib/bench/tests/Makefile
                  src/lib/bench/tests/Makefile
@@ -702,6 +712,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/testutils/testdata/Makefile
                  src/lib/testutils/testdata/Makefile
                  src/lib/nsas/Makefile
                  src/lib/nsas/Makefile
                  src/lib/nsas/tests/Makefile
                  src/lib/nsas/tests/Makefile
+                 src/lib/cache/Makefile
+                 src/lib/cache/tests/Makefile
                ])
                ])
 AC_OUTPUT([doc/version.ent
 AC_OUTPUT([doc/version.ent
            src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/cfgmgr/b10-cfgmgr.py
@@ -809,8 +821,8 @@ dnl includes too
                  ${PYTHON_LDFLAGS}
                  ${PYTHON_LDFLAGS}
                  ${PYTHON_LIB}
                  ${PYTHON_LIB}
   Boost:         ${BOOST_INCLUDES}
   Boost:         ${BOOST_INCLUDES}
-dnl  log4cxx:       ${LOG4CXX_INCLUDES}
-dnl                 ${LOG4CXX_LDFLAGS}
+  log4cxx:       ${LOG4CXX_INCLUDES}
+                 ${LOG4CXX_LDFLAGS}
   SQLite:        $SQLITE_CFLAGS
   SQLite:        $SQLITE_CFLAGS
                  $SQLITE_LIBS
                  $SQLITE_LIBS
 
 

+ 1 - 1
doc/Doxyfile

@@ -568,7 +568,7 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 # with spaces.
 
 
-INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas ../src/lib/testutils
+INPUT                  = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas ../src/lib/testutils ../src/lib/cache
 
 
 # This tag can be used to specify the character encoding of the source files
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

src/lib/asiolink/internal/coroutine.h → ext/coroutine/coroutine.h


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

@@ -51,7 +51,6 @@ b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
-b10_auth_LDADD += $(top_builddir)/src/lib/log/liblog.la
 b10_auth_LDADD += $(SQLITE_LIBS)
 b10_auth_LDADD += $(SQLITE_LIBS)
 
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir

+ 9 - 3
src/bin/auth/auth_srv.cc

@@ -354,7 +354,7 @@ AuthSrv::setMemoryDataSrc(const isc::dns::RRClass& rrclass,
 
 
 uint32_t
 uint32_t
 AuthSrv::getStatisticsTimerInterval() const {
 AuthSrv::getStatisticsTimerInterval() const {
-    return (impl_->statistics_timer_.getInterval());
+    return (impl_->statistics_timer_.getInterval() / 1000);
 }
 }
 
 
 void
 void
@@ -362,11 +362,17 @@ AuthSrv::setStatisticsTimerInterval(uint32_t interval) {
     if (interval == impl_->statistics_timer_.getInterval()) {
     if (interval == impl_->statistics_timer_.getInterval()) {
         return;
         return;
     }
     }
+    if (interval > 86400) {
+        // It can't occur since the value is checked in
+        // statisticsIntervalConfig::build().
+        isc_throw(InvalidParameter, "Too long interval: " << interval);
+    }
     if (interval == 0) {
     if (interval == 0) {
         impl_->statistics_timer_.cancel();
         impl_->statistics_timer_.cancel();
     } else {
     } else {
-        impl_->statistics_timer_.setupTimer(
-            boost::bind(&AuthSrv::submitStatistics, this), interval);
+        impl_->statistics_timer_.setup(boost::bind(&AuthSrv::submitStatistics,
+                                                   this),
+                                       interval * 1000);
     }
     }
     if (impl_->verbose_mode_) {
     if (impl_->verbose_mode_) {
         if (interval == 0) {
         if (interval == 0) {

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

@@ -318,7 +318,8 @@ public:
     /// If the specified value is non 0, the \c AuthSrv object will submit
     /// If the specified value is non 0, the \c AuthSrv object will submit
     /// its statistics to the statistics module every \c interval seconds.
     /// its statistics to the statistics module every \c interval seconds.
     /// If it's 0, and \c AuthSrv currently submits statistics, the submission
     /// If it's 0, and \c AuthSrv currently submits statistics, the submission
-    /// will be disabled.
+    /// will be disabled. \c interval must be equal to or shorter than 86400
+    /// seconds (1 day).
     ///
     ///
     /// This method should normally not throw an exception; however, its
     /// This method should normally not throw an exception; however, its
     /// underlying library routines may involve resource allocation, and
     /// underlying library routines may involve resource allocation, and

+ 6 - 1
src/bin/auth/config.cc

@@ -179,9 +179,14 @@ public:
     virtual void build(ConstElementPtr config_value) {
     virtual void build(ConstElementPtr config_value) {
         const int32_t config_interval = config_value->intValue();
         const int32_t config_interval = config_value->intValue();
         if (config_interval < 0) {
         if (config_interval < 0) {
-            isc_throw(AuthConfigError, "negative statistics-interval value: "
+            isc_throw(AuthConfigError, "Negative statistics interval value: "
                       << config_interval);
                       << config_interval);
         }
         }
+        if (config_interval > 86400) {
+            isc_throw(AuthConfigError, "Statistics interval value "
+                      << config_interval
+                      << " must be equal to or shorter than 86400");
+        }
         interval_ = config_interval;
         interval_ = config_interval;
     }
     }
     virtual void commit() {
     virtual void commit() {

+ 50 - 9
src/bin/auth/query.cc

@@ -141,13 +141,56 @@ Query::process() const {
 
 
     // Found a zone which is the nearest ancestor to QNAME, set the AA bit
     // Found a zone which is the nearest ancestor to QNAME, set the AA bit
     response_.setHeaderFlag(Message::HEADERFLAG_AA);
     response_.setHeaderFlag(Message::HEADERFLAG_AA);
+    response_.setRcode(Rcode::NOERROR());
     while (keep_doing) {
     while (keep_doing) {
         keep_doing = false;
         keep_doing = false;
         std::auto_ptr<RRsetList> target(qtype_is_any ? new RRsetList : NULL);
         std::auto_ptr<RRsetList> target(qtype_is_any ? new RRsetList : NULL);
-        Zone::FindResult db_result =
-            result.zone->find(qname_, qtype_, target.get());
+        const Zone::FindResult db_result(result.zone->find(qname_, qtype_,
+            target.get()));
 
 
         switch (db_result.code) {
         switch (db_result.code) {
+            case Zone::DNAME: {
+                // First, put the dname into the answer
+                response_.addRRset(Message::SECTION_ANSWER,
+                    boost::const_pointer_cast<RRset>(db_result.rrset));
+                /*
+                 * Empty DNAME should never get in, as it is impossible to
+                 * create one in master file.
+                 *
+                 * FIXME: Other way to prevent this should be done
+                 */
+                assert(db_result.rrset->getRdataCount() > 0);
+                // Get the data of DNAME
+                const rdata::generic::DNAME& dname(
+                    dynamic_cast<const rdata::generic::DNAME&>(
+                    db_result.rrset->getRdataIterator()->getCurrent()));
+                // The yet unmatched prefix dname
+                const Name prefix(qname_.split(0, qname_.getLabelCount() -
+                    db_result.rrset->getName().getLabelCount()));
+                // If we put it together, will it be too long?
+                // (The prefix contains trailing ., which will be removed
+                if (prefix.getLength() - Name::ROOT_NAME().getLength() +
+                    dname.getDname().getLength() > Name::MAX_WIRE) {
+                    /*
+                     * In case the synthesized name is too long, section 4.1
+                     * of RFC 2672 mandates we return YXDOMAIN.
+                     */
+                    response_.setRcode(Rcode::YXDOMAIN());
+                    return;
+                }
+                // The new CNAME we are creating (it will be unsigned even
+                // with DNSSEC, the DNAME is signed and it can be validated
+                // by that)
+                RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
+                    RRType::CNAME(), db_result.rrset->getTTL()));
+                // Construct the new target by replacing the end
+                cname->addRdata(rdata::generic::CNAME(qname_.split(0,
+                    qname_.getLabelCount() -
+                    db_result.rrset->getName().getLabelCount()).
+                    concatenate(dname.getDname())));
+                response_.addRRset(Message::SECTION_ANSWER, cname);
+                break;
+            }
             case Zone::CNAME:
             case Zone::CNAME:
                 /*
                 /*
                  * We don't do chaining yet. Therefore handling a CNAME is
                  * We don't do chaining yet. Therefore handling a CNAME is
@@ -155,10 +198,13 @@ Query::process() const {
                  * what we expected. It means no exceptions in ANY or NS
                  * what we expected. It means no exceptions in ANY or NS
                  * on the origin (though CNAME in origin is probably
                  * on the origin (though CNAME in origin is probably
                  * forbidden anyway).
                  * forbidden anyway).
+                 *
+                 * So, just put it there.
                  */
                  */
-                // No break; here, fall trough.
+                response_.addRRset(Message::SECTION_ANSWER,
+                    boost::const_pointer_cast<RRset>(db_result.rrset));
+                break;
             case Zone::SUCCESS:
             case Zone::SUCCESS:
-                response_.setRcode(Rcode::NOERROR());
                 if (qtype_is_any) {
                 if (qtype_is_any) {
                     // If quety type is ANY, insert all RRs under the domain
                     // If quety type is ANY, insert all RRs under the domain
                     // into answer section.
                     // into answer section.
@@ -184,7 +230,6 @@ Query::process() const {
                 break;
                 break;
             case Zone::DELEGATION:
             case Zone::DELEGATION:
                 response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
                 response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
-                response_.setRcode(Rcode::NOERROR());
                 response_.addRRset(Message::SECTION_AUTHORITY,
                 response_.addRRset(Message::SECTION_AUTHORITY,
                     boost::const_pointer_cast<RRset>(db_result.rrset));
                     boost::const_pointer_cast<RRset>(db_result.rrset));
                 getAdditional(*result.zone, *db_result.rrset);
                 getAdditional(*result.zone, *db_result.rrset);
@@ -196,12 +241,8 @@ Query::process() const {
                 break;
                 break;
             case Zone::NXRRSET:
             case Zone::NXRRSET:
                 // Just empty answer with SOA in authority section
                 // Just empty answer with SOA in authority section
-                response_.setRcode(Rcode::NOERROR());
                 putSOA(*result.zone);
                 putSOA(*result.zone);
                 break;
                 break;
-            case Zone::DNAME:
-                // TODO : replace qname, continue lookup
-                break;
         }
         }
     }
     }
 }
 }

+ 1 - 1
src/bin/auth/tests/command_unittest.cc

@@ -98,7 +98,7 @@ AuthConmmandTest::stopServer() {
 
 
 TEST_F(AuthConmmandTest, shutdown) {
 TEST_F(AuthConmmandTest, shutdown) {
     asiolink::IntervalTimer itimer(server.getIOService());
     asiolink::IntervalTimer itimer(server.getIOService());
-    itimer.setupTimer(boost::bind(&AuthConmmandTest::stopServer, this), 1);
+    itimer.setup(boost::bind(&AuthConmmandTest::stopServer, this), 1);
     server.getIOService().run();
     server.getIOService().run();
     EXPECT_EQ(0, rcode);
     EXPECT_EQ(0, rcode);
 }
 }

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

@@ -365,5 +365,9 @@ TEST_F(StatisticsIntervalConfigTest, badInterval) {
     EXPECT_THROW(parser->build(Element::fromJSON("2.5")),
     EXPECT_THROW(parser->build(Element::fromJSON("2.5")),
                  isc::data::TypeError);
                  isc::data::TypeError);
     EXPECT_THROW(parser->build(Element::fromJSON("-1")), AuthConfigError);
     EXPECT_THROW(parser->build(Element::fromJSON("-1")), AuthConfigError);
+    // bounds check: interval value must be equal to or shorter than
+    // 86400 seconds (1 day)
+    EXPECT_NO_THROW(parser->build(Element::fromJSON("86400")));
+    EXPECT_THROW(parser->build(Element::fromJSON("86401")), AuthConfigError);
 }
 }
 }
 }

+ 162 - 13
src/bin/auth/tests/query_unittest.cc

@@ -75,6 +75,17 @@ const char* const cname_nxdom_txt =
 // CNAME Leading out of zone
 // CNAME Leading out of zone
 const char* const cname_out_txt =
 const char* const cname_out_txt =
     "cnameout.example.com. 3600 IN CNAME www.example.org.\n";
     "cnameout.example.com. 3600 IN CNAME www.example.org.\n";
+// The DNAME to do tests against
+const char* const dname_txt =
+    "dname.example.com. 3600 IN DNAME "
+    "somethinglong.dnametarget.example.com.\n";
+// Some data at the dname node (allowed by RFC 2672)
+const char* const dname_a_txt =
+    "dname.example.com. 3600 IN A 192.0.2.5\n";
+// This is not inside the zone, this is created at runtime
+const char* const synthetized_cname_txt =
+    "www.dname.example.com. 3600 IN CNAME "
+    "www.somethinglong.dnametarget.example.com.\n";
 // The rest of data won't be referenced from the test cases.
 // The rest of data won't be referenced from the test cases.
 const char* const other_zone_rrs =
 const char* const other_zone_rrs =
     "cnamemailer.example.com. 3600 IN CNAME www.example.com.\n"
     "cnamemailer.example.com. 3600 IN CNAME www.example.com.\n"
@@ -88,13 +99,16 @@ const char* const other_zone_rrs =
 // behavior.
 // behavior.
 // For simplicity, most names are assumed to be "in zone"; there's only
 // For simplicity, most names are assumed to be "in zone"; there's only
 // one zone cut at the point of name "delegation.example.com".
 // one zone cut at the point of name "delegation.example.com".
-// It doesn't handle empty non terminal nodes (if we need to test such cases
-// find() should have specialized code for it).
+// Another special name is "dname.example.com".  Query names under this name
+// will result in DNAME.
+// This mock zone doesn't handle empty non terminal nodes (if we need to test
+// such cases find() should have specialized code for it).
 class MockZone : public Zone {
 class MockZone : public Zone {
 public:
 public:
     MockZone() :
     MockZone() :
         origin_(Name("example.com")),
         origin_(Name("example.com")),
         delegation_name_("delegation.example.com"),
         delegation_name_("delegation.example.com"),
+        dname_name_("dname.example.com"),
         has_SOA_(true),
         has_SOA_(true),
         has_apex_NS_(true),
         has_apex_NS_(true),
         rrclass_(RRClass::IN())
         rrclass_(RRClass::IN())
@@ -102,7 +116,8 @@ public:
         stringstream zone_stream;
         stringstream zone_stream;
         zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt <<
         zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt <<
             delegation_txt << mx_txt << www_a_txt << cname_txt <<
             delegation_txt << mx_txt << www_a_txt << cname_txt <<
-            cname_nxdom_txt << cname_out_txt << other_zone_rrs;
+            cname_nxdom_txt << cname_out_txt << dname_txt << dname_a_txt <<
+            other_zone_rrs;
 
 
         masterLoad(zone_stream, origin_, rrclass_,
         masterLoad(zone_stream, origin_, rrclass_,
                    boost::bind(&MockZone::loadRRset, this, _1));
                    boost::bind(&MockZone::loadRRset, this, _1));
@@ -131,14 +146,20 @@ private:
         if (rrset->getName() == delegation_name_ &&
         if (rrset->getName() == delegation_name_ &&
             rrset->getType() == RRType::NS()) {
             rrset->getType() == RRType::NS()) {
             delegation_rrset_ = rrset;
             delegation_rrset_ = rrset;
+        } else if (rrset->getName() == dname_name_ &&
+            rrset->getType() == RRType::DNAME()) {
+            dname_rrset_ = rrset;
         }
         }
     }
     }
 
 
     const Name origin_;
     const Name origin_;
+    // Names where we delegate somewhere else
     const Name delegation_name_;
     const Name delegation_name_;
+    const Name dname_name_;
     bool has_SOA_;
     bool has_SOA_;
     bool has_apex_NS_;
     bool has_apex_NS_;
     ConstRRsetPtr delegation_rrset_;
     ConstRRsetPtr delegation_rrset_;
+    ConstRRsetPtr dname_rrset_;
     const RRClass rrclass_;
     const RRClass rrclass_;
 };
 };
 
 
@@ -160,6 +181,10 @@ MockZone::find(const Name& name, const RRType& type,
          name.compare(delegation_name_).getRelation() ==
          name.compare(delegation_name_).getRelation() ==
          NameComparisonResult::SUBDOMAIN)) {
          NameComparisonResult::SUBDOMAIN)) {
         return (FindResult(DELEGATION, delegation_rrset_));
         return (FindResult(DELEGATION, delegation_rrset_));
+    // And under DNAME
+    } else if (name.compare(dname_name_).getRelation() ==
+        NameComparisonResult::SUBDOMAIN) {
+        return (FindResult(DNAME, dname_rrset_));
     }
     }
 
 
     // normal cases.  names are searched for only per exact-match basis
     // normal cases.  names are searched for only per exact-match basis
@@ -176,8 +201,7 @@ MockZone::find(const Name& name, const RRType& type,
         // If not found but we have a target, fill it with all RRsets here
         // If not found but we have a target, fill it with all RRsets here
         if (!found_domain->second.empty() && target != NULL) {
         if (!found_domain->second.empty() && target != NULL) {
             for (found_rrset = found_domain->second.begin();
             for (found_rrset = found_domain->second.begin();
-                 found_rrset != found_domain->second.end(); found_rrset++)
-            {
+                 found_rrset != found_domain->second.end(); found_rrset++) {
                 // Insert RRs under the domain name into target
                 // Insert RRs under the domain name into target
                 target->addRRset(
                 target->addRRset(
                     boost::const_pointer_cast<RRset>(found_rrset->second));
                     boost::const_pointer_cast<RRset>(found_rrset->second));
@@ -443,8 +467,8 @@ TEST_F(QueryTest, CNAME) {
     Query(memory_datasrc, Name("cname.example.com"), RRType::A(),
     Query(memory_datasrc, Name("cname.example.com"), RRType::A(),
         response).process();
         response).process();
 
 
-    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
-        cname_txt, zone_ns_txt, ns_addrs_txt);
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
+        cname_txt, NULL, NULL);
 }
 }
 
 
 TEST_F(QueryTest, explicitCNAME) {
 TEST_F(QueryTest, explicitCNAME) {
@@ -465,8 +489,8 @@ TEST_F(QueryTest, CNAME_NX_RRSET) {
     Query(memory_datasrc, Name("cname.example.com"), RRType::TXT(),
     Query(memory_datasrc, Name("cname.example.com"), RRType::TXT(),
         response).process();
         response).process();
 
 
-    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
-        cname_txt, zone_ns_txt, ns_addrs_txt);
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
+        cname_txt, NULL, NULL);
 }
 }
 
 
 TEST_F(QueryTest, explicitCNAME_NX_RRSET) {
 TEST_F(QueryTest, explicitCNAME_NX_RRSET) {
@@ -488,8 +512,8 @@ TEST_F(QueryTest, CNAME_NX_DOMAIN) {
     Query(memory_datasrc, Name("cnamenxdom.example.com"), RRType::A(),
     Query(memory_datasrc, Name("cnamenxdom.example.com"), RRType::A(),
         response).process();
         response).process();
 
 
-    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
-        cname_nxdom_txt, zone_ns_txt, ns_addrs_txt);
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
+        cname_nxdom_txt, NULL, NULL);
 }
 }
 
 
 TEST_F(QueryTest, explicitCNAME_NX_DOMAIN) {
 TEST_F(QueryTest, explicitCNAME_NX_DOMAIN) {
@@ -513,8 +537,8 @@ TEST_F(QueryTest, CNAME_OUT) {
     Query(memory_datasrc, Name("cnameout.example.com"), RRType::A(),
     Query(memory_datasrc, Name("cnameout.example.com"), RRType::A(),
         response).process();
         response).process();
 
 
-    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
-        cname_out_txt, zone_ns_txt, ns_addrs_txt);
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
+        cname_out_txt, NULL, NULL);
 }
 }
 
 
 TEST_F(QueryTest, explicitCNAME_OUT) {
 TEST_F(QueryTest, explicitCNAME_OUT) {
@@ -526,4 +550,129 @@ TEST_F(QueryTest, explicitCNAME_OUT) {
         cname_out_txt, zone_ns_txt, ns_addrs_txt);
         cname_out_txt, zone_ns_txt, ns_addrs_txt);
 }
 }
 
 
+/*
+ * Test a query under a domain with DNAME. We should get a synthetized CNAME
+ * as well as the DNAME.
+ *
+ * TODO: Once we have CNAME chaining, check it works with synthetized CNAMEs
+ * as well. This includes tests pointing inside the zone, outside the zone,
+ * pointing to NXRRSET and NXDOMAIN cases (similarly as with CNAME).
+ */
+TEST_F(QueryTest, DNAME) {
+    Query(memory_datasrc, Name("www.dname.example.com"), RRType::A(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
+        (string(dname_txt) + synthetized_cname_txt).c_str(),
+        NULL, NULL);
+}
+
+/*
+ * Ask an ANY query below a DNAME. Should return the DNAME and synthetized
+ * CNAME.
+ *
+ * ANY is handled specially sometimes. We check it is not the case with
+ * DNAME.
+ */
+TEST_F(QueryTest, DNAME_ANY) {
+    Query(memory_datasrc, Name("www.dname.example.com"), RRType::ANY(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
+        (string(dname_txt) + synthetized_cname_txt).c_str(), NULL, NULL);
+}
+
+// Test when we ask for DNAME explicitly, it does no synthetizing.
+TEST_F(QueryTest, explicitDNAME) {
+    Query(memory_datasrc, Name("dname.example.com"), RRType::DNAME(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        dname_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+/*
+ * Request a RRset at the domain with DNAME. It should not synthetize
+ * the CNAME, it should return the RRset.
+ */
+TEST_F(QueryTest, DNAME_A) {
+    Query(memory_datasrc, Name("dname.example.com"), RRType::A(),
+        response).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+        dname_a_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+/*
+ * Request a RRset at the domain with DNAME that is not there (NXRRSET).
+ * It should not synthetize the CNAME.
+ */
+TEST_F(QueryTest, DNAME_NX_RRSET) {
+    EXPECT_NO_THROW(Query(memory_datasrc, Name("dname.example.com"),
+        RRType::TXT(), response).process());
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
+        NULL, soa_txt, NULL, mock_zone->getOrigin());
+}
+
+/*
+ * Constructing the CNAME will result in a name that is too long. This,
+ * however, should not throw (and crash the server), but respond with
+ * YXDOMAIN.
+ */
+TEST_F(QueryTest, LongDNAME) {
+    // A name that is as long as it can be
+    Name longname(
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "dname.example.com.");
+    EXPECT_NO_THROW(Query(memory_datasrc, longname, RRType::A(),
+        response).process());
+
+    responseCheck(response, Rcode::YXDOMAIN(), AA_FLAG, 1, 0, 0,
+        dname_txt, NULL, NULL);
+}
+
+/*
+ * Constructing the CNAME will result in a name of maximal length.
+ * This tests that we don't reject valid one by some kind of off by
+ * one mistake.
+ */
+TEST_F(QueryTest, MaxLenDNAME) {
+    Name longname(
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
+        "dname.example.com.");
+    EXPECT_NO_THROW(Query(memory_datasrc, longname, RRType::A(),
+        response).process());
+
+    // Check the answer is OK
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
+        NULL, NULL, NULL);
+
+    // Check that the CNAME has the maximal length.
+    bool ok(false);
+    for (RRsetIterator i(response.beginSection(Message::SECTION_ANSWER));
+        i != response.endSection(Message::SECTION_ANSWER); ++ i) {
+        if ((*i)->getType() == RRType::CNAME()) {
+            ok = true;
+            RdataIteratorPtr ci((*i)->getRdataIterator());
+            ASSERT_FALSE(ci->isLast()) << "The CNAME is empty";
+            /*
+             * Does anybody have a clue why, if the Name::MAX_WIRE is put
+             * directly inside ASSERT_EQ, it fails to link and complains
+             * it is unresolved external?
+             */
+            const size_t max_len(Name::MAX_WIRE);
+            ASSERT_EQ(max_len, dynamic_cast<const rdata::generic::CNAME&>(
+                ci->getCurrent()).getCname().getLength());
+        }
+    }
+    EXPECT_TRUE(ok) << "The synthetized CNAME not found";
+}
+
 }
 }

+ 12 - 30
src/bin/bind10/bind10.py.in

@@ -195,8 +195,7 @@ class BoB:
     """Boss of BIND class."""
     """Boss of BIND class."""
     
     
     def __init__(self, msgq_socket_file=None, dns_port=5300, address=None,
     def __init__(self, msgq_socket_file=None, dns_port=5300, address=None,
-                 forward=None, nocache=False, verbose=False, setuid=None,
-                 username=None):
+                 nocache=False, verbose=False, setuid=None, username=None):
         """
         """
             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).
         
         
@@ -206,11 +205,6 @@ class BoB:
         """
         """
         self.address = address
         self.address = address
         self.dns_port = dns_port
         self.dns_port = dns_port
-        self.forward = forward
-        if forward:
-            self.resolver = True
-        else:
-            self.resolver = False
         self.cc_session = None
         self.cc_session = None
         self.ccs = None
         self.ccs = None
         self.cfg_start_auth = True
         self.cfg_start_auth = True
@@ -422,26 +416,19 @@ class BoB:
         """
         """
             Start the Authoritative server
             Start the Authoritative server
         """
         """
-        # XXX: this must be read from the configuration manager in the future
-        if self.resolver:
-            dns_prog = 'b10-resolver'
-        else:
-            dns_prog = 'b10-auth'
-        dnsargs = [dns_prog]
-        if not self.resolver:
-            # The resolver uses configuration manager for these
-            dnsargs += ['-p', str(self.dns_port)]
-            if self.address:
-                dnsargs += ['-a', str(self.address)]
-            if self.nocache:
-                dnsargs += ['-n']
+        authargs = ['b10-auth']
+        authargs += ['-p', str(self.dns_port)]
+        if self.address:
+            authargs += ['-a', str(self.address)]
+        if self.nocache:
+            authargs += ['-n']
         if self.uid:
         if self.uid:
-            dnsargs += ['-u', str(self.uid)]
+            authargs += ['-u', str(self.uid)]
         if self.verbose:
         if self.verbose:
-            dnsargs += ['-v']
+            authargs += ['-v']
 
 
         # ... and start
         # ... and start
-        self.start_process("b10-auth", dnsargs, c_channel_env,
+        self.start_process("b10-auth", authargs, c_channel_env,
             self.dns_port, self.address)
             self.dns_port, self.address)
 
 
     def start_resolver(self, c_channel_env):
     def start_resolver(self, c_channel_env):
@@ -739,8 +726,6 @@ def check_addr(option, opt_str, value, parser):
     try:
     try:
         if opt_str in ['-a', '--address']:
         if opt_str in ['-a', '--address']:
             parser.values.address = isc.net.parse.addr_parse(value)
             parser.values.address = isc.net.parse.addr_parse(value)
-        elif opt_str in ['-f', '--forward']:
-            parser.values.forward = isc.net.parse.addr_parse(value)
         else:
         else:
             raise OptionValueError("Unknown option " + opt_str)
             raise OptionValueError("Unknown option " + opt_str)
     except ValueError:
     except ValueError:
@@ -761,9 +746,6 @@ def main():
     parser.add_option("-a", "--address", dest="address", type="string",
     parser.add_option("-a", "--address", dest="address", type="string",
                       action="callback", callback=check_addr, default=None,
                       action="callback", callback=check_addr, default=None,
                       help="address the DNS server will use (default: listen on all addresses)")
                       help="address the DNS server will use (default: listen on all addresses)")
-    parser.add_option("-f", "--forward", dest="forward", type="string",
-                      action="callback", callback=check_addr, default=None,
-                      help="nameserver to which DNS queries should be forwarded")
     parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
     parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
                       type="string", default=None,
                       type="string", default=None,
                       help="UNIX domain socket file the b10-msgq daemon will use")
                       help="UNIX domain socket file the b10-msgq daemon will use")
@@ -833,8 +815,8 @@ def main():
 
 
     # Go bob!
     # Go bob!
     boss_of_bind = BoB(options.msgq_socket_file, options.dns_port,
     boss_of_bind = BoB(options.msgq_socket_file, options.dns_port,
-                       options.address, options.forward, options.nocache,
-                       options.verbose, setuid, username)
+                       options.address, options.nocache, options.verbose,
+                       setuid, username)
     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)

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

@@ -132,7 +132,7 @@ class SendNonblock(unittest.TestCase):
             task()
             task()
             # If we got here, then everything worked well and in time
             # If we got here, then everything worked well and in time
             # In that case, we terminate successfully
             # In that case, we terminate successfully
-            sys.exit()
+            sys.exit(0)	# needs exit code
         else:
         else:
             (pid, status) = os.waitpid(task_pid, 0)
             (pid, status) = os.waitpid(task_pid, 0)
             self.assertEqual(0, status,
             self.assertEqual(0, status,

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

@@ -37,7 +37,6 @@ spec_config.h: spec_config.h.pre
 BUILT_SOURCES = spec_config.h 
 BUILT_SOURCES = spec_config.h 
 pkglibexec_PROGRAMS = b10-resolver
 pkglibexec_PROGRAMS = b10-resolver
 b10_resolver_SOURCES = resolver.cc resolver.h
 b10_resolver_SOURCES = resolver.cc resolver.h
-b10_resolver_SOURCES += response_classifier.cc response_classifier.h
 b10_resolver_SOURCES += response_scrubber.cc response_scrubber.h
 b10_resolver_SOURCES += response_scrubber.cc response_scrubber.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/change_user.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/change_user.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/common.h
 b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/common.h

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

@@ -21,7 +21,6 @@
 #include <cassert>
 #include <cassert>
 
 
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
-#include <asiolink/ioaddress.h>
 
 
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>

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

@@ -65,7 +65,10 @@ public:
     /// send the reply.
     /// send the reply.
     ///
     ///
     /// \param io_message The raw message received
     /// \param io_message The raw message received
-    /// \param message Pointer to the \c Message object
+    /// \param query_message Pointer to the query Message object we
+    /// received from the client
+    /// \param answer_message Pointer to the anwer Message object we
+    /// shall return to the client
     /// \param buffer Pointer to an \c OutputBuffer for the resposne
     /// \param buffer Pointer to an \c OutputBuffer for the resposne
     /// \param server Pointer to the \c DNSServer
     /// \param server Pointer to the \c DNSServer
     void processMessage(const asiolink::IOMessage& io_message,
     void processMessage(const asiolink::IOMessage& io_message,
@@ -146,7 +149,11 @@ public:
      * \short Set options related to timeouts.
      * \short Set options related to timeouts.
      *
      *
      * This sets the time of timeout and number of retries.
      * This sets the time of timeout and number of retries.
-     * \param timeout The time in milliseconds. The value -1 disables timeouts.
+     * \param query_timeout The timeout we use for queries we send
+     * \param client_timeout The timeout at which point we send back a
+     * SERVFAIL (while continuing to resolve the query)
+     * \param lookup_timeout The timeout at which point we give up and
+     * stop.
      * \param retries The number of retries (0 means try the first time only,
      * \param retries The number of retries (0 means try the first time only,
      *     do not retry).
      *     do not retry).
      */
      */

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

@@ -243,7 +243,7 @@
 /// scrubbed.
 /// scrubbed.
 
 
 #include <config.h>
 #include <config.h>
-#include <asiolink/ioendpoint.h>
+#include <asiolink/io_endpoint.h>
 #include <dns/message.h>
 #include <dns/message.h>
 #include <dns/name.h>
 #include <dns/name.h>
 
 

+ 2 - 4
src/bin/resolver/tests/Makefile.am

@@ -19,11 +19,9 @@ TESTS += run_unittests
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += ../resolver.h ../resolver.cc
 run_unittests_SOURCES += ../resolver.h ../resolver.cc
-run_unittests_SOURCES += ../response_classifier.h ../response_classifier.cc
 run_unittests_SOURCES += ../response_scrubber.h ../response_scrubber.cc
 run_unittests_SOURCES += ../response_scrubber.h ../response_scrubber.cc
 run_unittests_SOURCES += resolver_unittest.cc
 run_unittests_SOURCES += resolver_unittest.cc
 run_unittests_SOURCES += resolver_config_unittest.cc
 run_unittests_SOURCES += resolver_config_unittest.cc
-run_unittests_SOURCES += response_classifier_unittest.cc
 run_unittests_SOURCES += response_scrubber_unittest.cc
 run_unittests_SOURCES += response_scrubber_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -31,8 +29,8 @@ 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/testutils/libtestutils.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/datasrc/libdatasrc.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/config/libcfgclient.la
 run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la

+ 3 - 3
src/bin/resolver/tests/response_scrubber_unittest.cc

@@ -21,8 +21,8 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
-#include <asiolink/ioendpoint.h>
-#include <asiolink/ioaddress.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_address.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
 
 
 #include <dns/name.h>
 #include <dns/name.h>
@@ -539,4 +539,4 @@ TEST_F(ResponseScrubberTest, All) {
     EXPECT_TRUE(mptr->hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns3));
     EXPECT_TRUE(mptr->hasRRset(Message::SECTION_ADDITIONAL, rrs_in_a_ns3));
 
 
 }
 }
-} // Anonymous namespace
+} // Anonymous namespace

+ 1 - 1
src/lib/Makefile.am

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

+ 19 - 9
src/lib/asiolink/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = . tests internal
+SUBDIRS = . tests
 
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -12,14 +12,24 @@ CLEANFILES = *.gcno *.gcda
 # have some code fragments that would hit gcc's unused-parameter warning,
 # have some code fragments that would hit gcc's unused-parameter warning,
 # which would make the build fail with -Werror (our default setting).
 # which would make the build fail with -Werror (our default setting).
 lib_LTLIBRARIES = libasiolink.la
 lib_LTLIBRARIES = libasiolink.la
-libasiolink_la_SOURCES = asiolink.cc asiolink.h
-libasiolink_la_SOURCES += iosocket.cc iosocket.h
-libasiolink_la_SOURCES += iomessage.h
-libasiolink_la_SOURCES += ioaddress.cc ioaddress.h
-libasiolink_la_SOURCES += ioendpoint.cc ioendpoint.h
-libasiolink_la_SOURCES += udpdns.cc internal/udpdns.h
-libasiolink_la_SOURCES += tcpdns.cc internal/tcpdns.h
-libasiolink_la_SOURCES += internal/coroutine.h
+libasiolink_la_SOURCES = asiolink.h
+libasiolink_la_SOURCES += io_service.cc io_service.h
+libasiolink_la_SOURCES += dns_service.cc dns_service.h
+libasiolink_la_SOURCES += dns_server.h
+libasiolink_la_SOURCES += dns_lookup.h
+libasiolink_la_SOURCES += dns_answer.h
+libasiolink_la_SOURCES += simple_callback.h
+libasiolink_la_SOURCES += interval_timer.h interval_timer.cc
+libasiolink_la_SOURCES += recursive_query.h recursive_query.cc
+libasiolink_la_SOURCES += io_socket.cc io_socket.h
+libasiolink_la_SOURCES += io_message.h
+libasiolink_la_SOURCES += io_address.cc io_address.h
+libasiolink_la_SOURCES += io_endpoint.cc io_endpoint.h
+libasiolink_la_SOURCES += udp_endpoint.h udp_socket.h
+libasiolink_la_SOURCES += udp_server.h udp_server.cc
+libasiolink_la_SOURCES += udp_query.h udp_query.cc
+libasiolink_la_SOURCES += tcp_endpoint.h tcp_socket.h
+libasiolink_la_SOURCES += tcp_server.h tcp_server.cc
 # 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)

+ 0 - 756
src/lib/asiolink/asiolink.cc

@@ -1,756 +0,0 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// 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 <cstdlib> // For rand(), temporary until better forwarding is done
-
-#include <unistd.h>             // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include <vector>
-#include <asio.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/bind.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-
-#include <boost/shared_ptr.hpp>
-
-#include <dns/buffer.h>
-#include <dns/message.h>
-#include <dns/rcode.h>
-#include <dns/opcode.h>
-
-#include <asiolink/asiolink.h>
-#include <asiolink/internal/tcpdns.h>
-#include <asiolink/internal/udpdns.h>
-
-#include <resolve/resolve.h>
-
-#include <log/dummylog.h>
-
-using namespace asio;
-using asio::ip::udp;
-using asio::ip::tcp;
-
-using namespace std;
-using namespace isc::dns;
-using isc::log::dlog;
-using namespace boost;
-
-// Is this something we can use in libdns++?
-namespace {
-    class SectionInserter {
-    public:
-        SectionInserter(MessagePtr message, const Message::Section sect) :
-            message_(message), section_(sect)
-        {}
-        void operator()(const RRsetPtr rrset) {
-            message_->addRRset(section_, rrset, true);
-        }
-        MessagePtr message_;
-        const Message::Section section_;
-    };
-
-
-    /// \brief Copies the parts relevant for a DNS answer to the
-    /// target message
-    ///
-    /// This adds all the RRsets in the answer, authority and
-    /// additional sections to the target, as well as the response
-    /// code
-    void copyAnswerMessage(const Message& source, MessagePtr target) {
-        target->setRcode(source.getRcode());
-
-        for_each(source.beginSection(Message::SECTION_ANSWER),
-                 source.endSection(Message::SECTION_ANSWER),
-                 SectionInserter(target, Message::SECTION_ANSWER));
-        for_each(source.beginSection(Message::SECTION_AUTHORITY),
-                 source.endSection(Message::SECTION_AUTHORITY),
-                 SectionInserter(target, Message::SECTION_AUTHORITY));
-        for_each(source.beginSection(Message::SECTION_ADDITIONAL),
-                 source.endSection(Message::SECTION_ADDITIONAL),
-                 SectionInserter(target, Message::SECTION_ADDITIONAL));
-    }
-}
-
-namespace asiolink {
-
-typedef pair<string, uint16_t> addr_t;
-
-class IOServiceImpl {
-private:
-    IOServiceImpl(const IOService& source);
-    IOServiceImpl& operator=(const IOService& source);
-public:
-    /// \brief The constructor
-    IOServiceImpl() :
-        io_service_(),
-        work_(io_service_)
-    {};
-    /// \brief The destructor.
-    ~IOServiceImpl() {};
-    //@}
-
-    /// \brief Start the underlying event loop.
-    ///
-    /// This method does not return control to the caller until
-    /// the \c stop() method is called via some handler.
-    void run() { io_service_.run(); };
-
-    /// \brief Run the underlying event loop for a single event.
-    ///
-    /// This method return control to the caller as soon as the
-    /// first handler has completed.  (If no handlers are ready when
-    /// it is run, it will block until one is.)
-    void run_one() { io_service_.run_one();} ;
-
-    /// \brief Stop the underlying event loop.
-    ///
-    /// This will return the control to the caller of the \c run() method.
-    void stop() { io_service_.stop();} ;
-
-    /// \brief Return the native \c io_service object used in this wrapper.
-    ///
-    /// This is a short term work around to support other BIND 10 modules
-    /// that share the same \c io_service with the authoritative server.
-    /// It will eventually be removed once the wrapper interface is
-    /// generalized.
-    asio::io_service& get_io_service() { return io_service_; };
-private:
-    asio::io_service io_service_;
-    asio::io_service::work work_;
-};
-
-IOService::IOService() {
-    io_impl_ = new IOServiceImpl();
-}
-
-IOService::~IOService() {
-    delete io_impl_;
-}
-
-void
-IOService::run() {
-    io_impl_->run();
-}
-
-void
-IOService::run_one() {
-    io_impl_->run_one();
-}
-
-void
-IOService::stop() {
-    io_impl_->stop();
-}
-
-asio::io_service&
-IOService::get_io_service() {
-    return (io_impl_->get_io_service());
-}
-
-class DNSServiceImpl {
-public:
-    DNSServiceImpl(IOService& io_service, const char& port,
-                  const ip::address* v4addr, const ip::address* v6addr,
-                  SimpleCallback* checkin, DNSLookup* lookup,
-                  DNSAnswer* answer);
-
-    IOService& io_service_;
-
-    typedef boost::shared_ptr<UDPServer> UDPServerPtr;
-    typedef boost::shared_ptr<TCPServer> TCPServerPtr;
-    typedef boost::shared_ptr<DNSServer> DNSServerPtr;
-    vector<DNSServerPtr> servers_;
-    SimpleCallback *checkin_;
-    DNSLookup *lookup_;
-    DNSAnswer *answer_;
-
-    void addServer(uint16_t port, const ip::address& address) {
-        try {
-            dlog(std::string("Initialize TCP server at ") + address.to_string() + ":" + boost::lexical_cast<string>(port));
-            TCPServerPtr tcpServer(new TCPServer(io_service_.get_io_service(),
-                address, port, checkin_, lookup_, answer_));
-            (*tcpServer)();
-            servers_.push_back(tcpServer);
-            dlog(std::string("Initialize UDP server at ") + address.to_string() + ":" + boost::lexical_cast<string>(port));
-            UDPServerPtr udpServer(new UDPServer(io_service_.get_io_service(),
-                address, port, checkin_, lookup_, answer_));
-            (*udpServer)();
-            servers_.push_back(udpServer);
-        }
-        catch (const asio::system_error& err) {
-            // We need to catch and convert any ASIO level exceptions.
-            // This can happen for unavailable address, binding a privilege port
-            // without the privilege, etc.
-            isc_throw(IOError, "Failed to initialize network servers: " <<
-                      err.what());
-        }
-    }
-    void addServer(const char& port, const ip::address& address) {
-        uint16_t portnum;
-        try {
-            // XXX: SunStudio with stlport4 doesn't reject some invalid
-            // representation such as "-1" by lexical_cast<uint16_t>, so
-            // we convert it into a signed integer of a larger size and perform
-            // range check ourselves.
-            const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
-            if (portnum32 < 0 || portnum32 > 65535) {
-                isc_throw(IOError, "Invalid port number '" << &port);
-            }
-            portnum = portnum32;
-        } catch (const boost::bad_lexical_cast& ex) {
-            isc_throw(IOError, "Invalid port number '" << &port << "': " <<
-                      ex.what());
-        }
-        addServer(portnum, address);
-    }
-};
-
-DNSServiceImpl::DNSServiceImpl(IOService& io_service,
-                               const char& port,
-                               const ip::address* const v4addr,
-                               const ip::address* const v6addr,
-                               SimpleCallback* checkin,
-                               DNSLookup* lookup,
-                               DNSAnswer* answer) :
-    io_service_(io_service),
-    checkin_(checkin),
-    lookup_(lookup),
-    answer_(answer)
-{
-
-    if (v4addr) {
-        addServer(port, *v4addr);
-    }
-    if (v6addr) {
-        addServer(port, *v6addr);
-    }
-}
-
-DNSService::DNSService(IOService& io_service,
-                       const char& port, const char& address,
-                       SimpleCallback* checkin,
-                       DNSLookup* lookup,
-                       DNSAnswer* answer) :
-    impl_(new DNSServiceImpl(io_service, port, NULL, NULL, checkin, lookup,
-        answer)), io_service_(io_service)
-{
-    addServer(port, &address);
-}
-
-DNSService::DNSService(IOService& io_service,
-                       const char& port,
-                       const bool use_ipv4, const bool use_ipv6,
-                       SimpleCallback* checkin,
-                       DNSLookup* lookup,
-                       DNSAnswer* answer) :
-    impl_(NULL), io_service_(io_service)
-{
-    const ip::address v4addr_any = ip::address(ip::address_v4::any());
-    const ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL; 
-    const ip::address v6addr_any = ip::address(ip::address_v6::any());
-    const ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
-    impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer);
-}
-
-DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
-    DNSLookup* lookup, DNSAnswer *answer) :
-    impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
-        answer)), io_service_(io_service)
-{
-}
-
-DNSService::~DNSService() {
-    delete impl_;
-}
-
-namespace {
-
-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)),
-    query_timeout_(query_timeout), client_timeout_(client_timeout),
-    lookup_timeout_(lookup_timeout), retries_(retries)
-{}
-
-namespace {
-
-ip::address
-convertAddr(const string& address) {
-    error_code err;
-    ip::address addr = ip::address::from_string(address, err);
-    if (err) {
-        isc_throw(IOError, "Invalid IP address '" << &address << "': "
-            << err.message());
-    }
-    return (addr);
-}
-
-}
-
-void
-DNSService::addServer(const char& port, const string& address) {
-    impl_->addServer(port, convertAddr(address));
-}
-
-void
-DNSService::addServer(uint16_t port, const string& address) {
-    impl_->addServer(port, convertAddr(address));
-}
-
-void
-DNSService::clearServers() {
-    // FIXME: This does not work, it does not close the socket.
-    // How is it done?
-    impl_->servers_.clear();
-}
-
-namespace {
-
-/*
- * 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 UDPQuery::Callback {
-private:
-    // The io service to handle async calls
-    asio::io_service& 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
-    shared_ptr<AddressVector> upstream_;
-
-    // root servers...just copied over to the zone_servers_
-    shared_ptr<AddressVector> upstream_root_;
-
-    // Buffer to store the result.
-    OutputBufferPtr buffer_;
-
-    // Server to notify when we succeed or fail
-    //shared_ptr<DNSServer> server_;
-    isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
-
-    /*
-     * 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
-    vector<addr_t> zone_servers_;
-
-    // Update the question that will be sent to the server
-    void setQuestion(const Question& new_question) {
-        question_ = new_question;
-    }
-
-    deadline_timer client_timer;
-    deadline_timer lookup_timer;
-
-    size_t queries_out_;
-
-    // If we timed out ourselves (lookup timeout), stop issuing queries
-    bool done_;
-
-    // (re)send the query to the server.
-    void send() {
-        const int uc = upstream_->size();
-        const int zs = zone_servers_.size();
-        buffer_->clear();
-        if (uc > 0) {
-            int serverIndex = rand() % uc;
-            dlog("Sending upstream query (" + question_.toText() +
-                ") to " + upstream_->at(serverIndex).first);
-            UDPQuery query(io_, question_,
-                upstream_->at(serverIndex).first,
-                upstream_->at(serverIndex).second, buffer_, this,
-                query_timeout_);
-            ++queries_out_;
-            io_.post(query);
-        } else if (zs > 0) {
-            int serverIndex = rand() % zs;
-            dlog("Sending query to zone server (" + question_.toText() +
-                ") to " + zone_servers_.at(serverIndex).first);
-            UDPQuery query(io_, question_,
-                zone_servers_.at(serverIndex).first,
-                zone_servers_.at(serverIndex).second, buffer_, this,
-                query_timeout_);
-            ++queries_out_;
-            io_.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
-    // returns false if we are not done
-    bool handleRecursiveAnswer(const Message& incoming) {
-        if (incoming.getRRCount(Message::SECTION_ANSWER) > 0) {
-            dlog("Got final result, copying answer.");
-            copyAnswerMessage(incoming, answer_message_);
-            return true;
-        } else {
-            dlog("Got delegation, continuing");
-            // ok we need to do some more processing.
-            // the ns list should contain all nameservers
-            // while the additional may contain addresses for
-            // them.
-            // this needs to tie into NSAS of course
-            // for this very first mockup, hope there is an
-            // address in additional and just use that
-
-            // send query to the addresses in the delegation
-            bool found_ns_address = false;
-            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.
-                        
-                        // should use NSAS
-                        zone_servers_.push_back(addr_t(addr_str, 53));
-                        found_ns_address = true;
-                    }
-                }
-            }
-            if (found_ns_address) {
-                // next resolver round
-                send();
-                return false;
-            } else {
-                dlog("[XX] no ready-made addresses in additional. need nsas.");
-                // this will result in answering with the delegation. oh well
-                copyAnswerMessage(incoming, answer_message_);
-                return true;
-            }
-        }
-    }
-    
-
-public:
-    RunningQuery(asio::io_service& io, const Question &question,
-        MessagePtr answer_message, shared_ptr<AddressVector> upstream,
-        shared_ptr<AddressVector> upstream_root,
-        OutputBufferPtr buffer,
-        isc::resolve::ResolverInterface::CallbackPtr cb,
-        int query_timeout, int client_timeout, int lookup_timeout,
-        unsigned retries) :
-        io_(io),
-        question_(question),
-        answer_message_(answer_message),
-        upstream_(upstream),
-        upstream_root_(upstream_root),
-        buffer_(buffer),
-        resolvercallback_(cb),
-        query_timeout_(query_timeout),
-        retries_(retries),
-        client_timer(io),
-        lookup_timer(io),
-        queries_out_(0),
-        done_(false)
-    {
-        // 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()) {
-            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<string>(upstream_root_->size()) + 
-                    "\n");
-              //Use BOOST_FOREACH here? Is it faster?
-              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");
-              }
-            }
-        }
-
-        send();
-    }
-
-    virtual void clientTimeout() {
-        // right now, just stop (should make SERVFAIL and send that
-        // back, but not stop)
-        stop(false);
-    }
-
-    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) {
-            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()(UDPQuery::Result result) {
-        // XXX is this the place for TCP retry?
-        --queries_out_;
-        if (!done_ && result != UDPQuery::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 {
-                copyAnswerMessage(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 isc::dns::QuestionPtr& question,
-    const isc::resolve::ResolverInterface::CallbackPtr callback)
-{
-    asio::io_service& io = dns_service_.get_io_service();
-
-    MessagePtr answer_message(new Message(Message::RENDER));
-    OutputBufferPtr buffer(new OutputBuffer(0));
-    
-    // It will delete itself when it is done
-    new RunningQuery(io, *question, answer_message, upstream_,
-                     upstream_root_, buffer, callback, query_timeout_,
-                     client_timeout_, lookup_timeout_, retries_);
-}
-
-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.
-    asio::io_service& io = dns_service_.get_io_service();
-
-    isc::resolve::ResolverInterface::CallbackPtr crs(
-        new isc::resolve::ResolverCallbackServer(server));
-    
-    // It will delete itself when it is done
-    new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
-                         buffer, crs, query_timeout_, client_timeout_,
-                         lookup_timeout_, retries_);
-}
-
-class IntervalTimerImpl {
-private:
-    // prohibit copy
-    IntervalTimerImpl(const IntervalTimerImpl& source);
-    IntervalTimerImpl& operator=(const IntervalTimerImpl& source);
-public:
-    IntervalTimerImpl(IOService& io_service);
-    ~IntervalTimerImpl();
-    void setupTimer(const IntervalTimer::Callback& cbfunc,
-                    const uint32_t interval);
-    void callback(const asio::error_code& error);
-    void cancel() {
-        timer_.cancel();
-        interval_ = 0;
-    }
-    uint32_t getInterval() const { return (interval_); }
-private:
-    // a function to update timer_ when it expires
-    void updateTimer();
-    // a function to call back when timer_ expires
-    IntervalTimer::Callback cbfunc_;
-    // interval in seconds
-    uint32_t interval_;
-    // asio timer
-    asio::deadline_timer timer_;
-};
-
-IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
-    interval_(0), timer_(io_service.get_io_service())
-{}
-
-IntervalTimerImpl::~IntervalTimerImpl()
-{}
-
-void
-IntervalTimerImpl::setupTimer(const IntervalTimer::Callback& cbfunc,
-                              const uint32_t interval)
-{
-    // Interval should not be 0.
-    if (interval == 0) {
-        isc_throw(isc::BadValue, "Interval should not be 0");
-    }
-    // Call back function should not be empty.
-    if (cbfunc.empty()) {
-        isc_throw(isc::InvalidParameter, "Callback function is empty");
-    }
-    cbfunc_ = cbfunc;
-    interval_ = interval;
-    // Set initial expire time.
-    // At this point the timer is not running yet and will not expire.
-    // After calling IOService::run(), the timer will expire.
-    updateTimer();
-    return;
-}
-
-void
-IntervalTimerImpl::updateTimer() {
-    if (interval_ == 0) {
-        // timer has been canceled.  Do nothing.
-        return;
-    }
-    try {
-        // Update expire time to (current time + interval_).
-        timer_.expires_from_now(boost::posix_time::seconds(interval_));
-    } catch (const asio::system_error& e) {
-        isc_throw(isc::Unexpected, "Failed to update timer");
-    }
-    // Reset timer.
-    timer_.async_wait(boost::bind(&IntervalTimerImpl::callback, this, _1));
-}
-
-void
-IntervalTimerImpl::callback(const asio::error_code& cancelled) {
-    // Do not call cbfunc_ in case the timer was cancelled.
-    // The timer will be canelled in the destructor of asio::deadline_timer.
-    if (!cancelled) {
-        cbfunc_();
-        // Set next expire time.
-        updateTimer();
-    }
-}
-
-IntervalTimer::IntervalTimer(IOService& io_service) {
-    impl_ = new IntervalTimerImpl(io_service);
-}
-
-IntervalTimer::~IntervalTimer() {
-    delete impl_;
-}
-
-void
-IntervalTimer::setupTimer(const Callback& cbfunc, const uint32_t interval) {
-    return (impl_->setupTimer(cbfunc, interval));
-}
-
-void
-IntervalTimer::cancel() {
-    impl_->cancel();
-}
-
-uint32_t
-IntervalTimer::getInterval() const {
-    return (impl_->getInterval());
-}
-
-}

+ 12 - 641
src/lib/asiolink/asiolink.h

@@ -18,34 +18,20 @@
 // IMPORTANT NOTE: only very few ASIO headers files can be included in
 // IMPORTANT NOTE: only very few ASIO headers files can be included in
 // this file.  In particular, asio.hpp should never be included here.
 // this file.  In particular, asio.hpp should never be included here.
 // See the description of the namespace below.
 // See the description of the namespace below.
-#include <unistd.h>             // for some network system calls
-#include <asio/ip/address.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/function.hpp>
 
 
-#include <functional>
-#include <string>
-#include <vector>
-#include <utility>
+#include <asiolink/io_service.h>
+#include <asiolink/dns_service.h>
+#include <asiolink/dns_server.h>
+#include <asiolink/dns_lookup.h>
+#include <asiolink/dns_answer.h>
+#include <asiolink/simple_callback.h>
+#include <asiolink/recursive_query.h>
+#include <asiolink/interval_timer.h>
 
 
-#include <dns/buffer.h>
-#include <dns/message.h>
-#include <dns/question.h>
-#include <dns/rcode.h>
-
-#include <exceptions/exceptions.h>
-
-#include <asiolink/ioaddress.h>
-#include <asiolink/ioendpoint.h>
-#include <asiolink/iomessage.h>
-#include <asiolink/iosocket.h>
-
-#include <resolve/resolver_interface.h>
-
-namespace asio {
-// forward declaration for IOService::get_io_service() below
-class io_service;
-}
+#include <asiolink/io_address.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_message.h>
+#include <asiolink/io_socket.h>
 
 
 /// \namespace asiolink
 /// \namespace asiolink
 /// \brief A wrapper interface for the ASIO library.
 /// \brief A wrapper interface for the ASIO library.
@@ -98,9 +84,6 @@ class io_service;
 /// http://think-async.com/Asio/asio-1.3.1/doc/asio/reference/asio_handler_allocate.html
 /// http://think-async.com/Asio/asio-1.3.1/doc/asio/reference/asio_handler_allocate.html
 
 
 namespace asiolink {
 namespace asiolink {
-class DNSServiceImpl;
-struct IOServiceImpl;
-struct IntervalTimerImpl;
 
 
 
 
 /// \brief An exception that is thrown if an error occurs within the IO
 /// \brief An exception that is thrown if an error occurs within the IO
@@ -112,618 +95,6 @@ public:
         isc::Exception(file, line, what) {}
         isc::Exception(file, line, what) {}
 };
 };
 
 
-/// \brief Forward declarations for classes used below
-class SimpleCallback;
-class DNSLookup;
-class DNSAnswer;
-
-/// \brief The \c IOService class is a wrapper for the ASIO \c io_service
-/// class.
-///
-class IOService {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    IOService(const IOService& source);
-    IOService& operator=(const IOService& source);
-public:
-    /// \brief The constructor
-    IOService();
-    /// \brief The destructor.
-    ~IOService();
-    //@}
-
-    /// \brief Start the underlying event loop.
-    ///
-    /// This method does not return control to the caller until
-    /// the \c stop() method is called via some handler.
-    void run();
-
-    /// \brief Run the underlying event loop for a single event.
-    ///
-    /// This method return control to the caller as soon as the
-    /// first handler has completed.  (If no handlers are ready when
-    /// it is run, it will block until one is.)
-    void run_one();
-
-    /// \brief Stop the underlying event loop.
-    ///
-    /// This will return the control to the caller of the \c run() method.
-    void stop();
-
-    /// \brief Return the native \c io_service object used in this wrapper.
-    ///
-    /// This is a short term work around to support other BIND 10 modules
-    /// that share the same \c io_service with the authoritative server.
-    /// It will eventually be removed once the wrapper interface is
-    /// generalized.
-    asio::io_service& get_io_service();
-
-private:
-    IOServiceImpl* io_impl_;
-};
-
-///
-/// DNSService is the service that handles DNS queries and answers with
-/// a given IOService. This class is mainly intended to hold all the
-/// logic that is shared between the authoritative and the recursive
-/// server implementations. As such, it handles asio, including config
-/// updates (through the 'Checkinprovider'), and listening sockets.
-/// 
-class DNSService {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    DNSService(const DNSService& source);
-    DNSService& operator=(const DNSService& source);
-
-public:
-    /// \brief The constructor with a specific IP address and port on which
-    /// the services listen on.
-    ///
-    /// \param io_service The IOService to work with
-    /// \param port the port to listen on
-    /// \param address the IP address to listen on
-    /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
-    /// \param lookup The lookup provider (see \c DNSLookup)
-    /// \param answer The answer provider (see \c DNSAnswer)
-    DNSService(IOService& io_service, const char& port,
-               const char& address, SimpleCallback* checkin,
-               DNSLookup* lookup, DNSAnswer* answer);
-    /// \brief The constructor with a specific port on which the services
-    /// listen on.
-    ///
-    /// It effectively listens on "any" IPv4 and/or IPv6 addresses.
-    /// IPv4/IPv6 services will be available if and only if \c use_ipv4
-    /// or \c use_ipv6 is \c true, respectively.
-    ///
-    /// \param io_service The IOService to work with
-    /// \param port the port to listen on
-    /// \param ipv4 If true, listen on ipv4 'any'
-    /// \param ipv6 If true, listen on ipv6 'any'
-    /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
-    /// \param lookup The lookup provider (see \c DNSLookup)
-    /// \param answer The answer provider (see \c DNSAnswer)
-    DNSService(IOService& io_service, const char& port,
-               const bool use_ipv4, const bool use_ipv6,
-               SimpleCallback* checkin, DNSLookup* lookup,
-               DNSAnswer* answer);
-    /// \brief The constructor without any servers.
-    ///
-    /// Use addServer() to add some servers.
-    DNSService(IOService& io_service, SimpleCallback* checkin,
-               DNSLookup* lookup, DNSAnswer* answer);
-    /// \brief The destructor.
-    ~DNSService();
-    //@}
-
-    /// \brief Add another server to the service
-    void addServer(uint16_t port, const std::string &address);
-    void addServer(const char &port, const std::string &address);
-    /// \brief Remove all servers from the service
-    void clearServers();
-
-    /// \brief Return the native \c io_service object used in this wrapper.
-    ///
-    /// This is a short term work around to support other BIND 10 modules
-    /// that share the same \c io_service with the authoritative server.
-    /// It will eventually be removed once the wrapper interface is
-    /// generalized.
-    asio::io_service& get_io_service() { return io_service_.get_io_service(); }
-private:
-    DNSServiceImpl* impl_;
-    IOService& io_service_;
-};
-
-/// \brief The \c DNSServer class is a wrapper (and base class) for
-/// classes which provide DNS server functionality.
-/// 
-/// The classes derived from this one, \c TCPServer and \c UDPServer,
-/// act as the interface layer between clients sending queries, and
-/// functions defined elsewhere that provide answers to those queries.
-/// Those functions are described in more detail below under
-/// \c SimpleCallback, \c DNSLookup, and \c DNSAnswer.
-///
-/// Notes to developers:
-/// When constructed, this class (and its derived classes) will have its
-/// "self_" member set to point to "this".  Objects of this class (as
-/// instantiated through a base class) are sometimes passed by
-/// reference (as this superclass); calls to methods in the base
-/// class are then rerouted via this pointer to methods in the derived
-/// class.  This allows code from outside asiolink, with no specific
-/// knowledge of \c TCPServer or \c UDPServer, to access their methods.
-///
-/// This class is both assignable and copy-constructable.  Its subclasses
-/// use the "stackless coroutine" pattern, meaning that it will copy itself
-/// when "forking", and that instances will be posted as ASIO handler
-/// objects, which are always copied.
-///
-/// Because these objects are frequently copied, it is recommended 
-/// that derived classes be kept small to reduce copy overhead.
-class DNSServer {
-protected: 
-    ///
-    /// \name Constructors and destructors
-    ///
-    /// This is intentionally defined as \c protected, as this base class
-    /// should never be instantiated except as part of a derived class.
-    //@{
-    DNSServer() : self_(this) {}
-public:
-    /// \brief The destructor
-    virtual ~DNSServer() {}
-    //@}
-
-    ///
-    /// \name Class methods
-    ///
-    /// These methods all make their calls indirectly via the "self_"
-    /// pointer, ensuring that the functions ultimately invoked will be
-    /// the ones in the derived class.  This makes it possible to pass
-    /// instances of derived classes as references to this base class
-    /// without losing access to derived class data.
-    /// 
-    //@{
-    /// \brief The funtion operator
-    virtual void operator()(asio::error_code ec = asio::error_code(),
-                            size_t length = 0)
-    {
-        (*self_)(ec, length);
-    }
-
-    /// \brief Resume processing of the server coroutine after an 
-    /// asynchronous call (e.g., to the DNS Lookup provider) has completed.
-    ///
-    /// \param done If true, this signals the system there is an answer
-    ///             to return.
-    virtual void resume(const bool done) { self_->resume(done); }
-
-    /// \brief Indicate whether the server is able to send an answer
-    /// to a query.
-    /// 
-    /// This is presently used only for testing purposes.
-    virtual bool hasAnswer() { return (self_->hasAnswer()); }
-
-    /// \brief Returns the current value of the 'coroutine' object
-    ///
-    /// This is a temporary method, intended to be used for debugging
-    /// purposes during development and removed later.  It allows
-    /// callers from outside the coroutine object to retrieve information
-    /// about its current state.
-    ///
-    /// \return The value of the 'coroutine' object
-    virtual int value() { return (self_->value()); }
-
-    /// \brief Returns a pointer to a clone of this DNSServer object.
-    ///
-    /// When a \c DNSServer object is copied or assigned, the result will
-    /// normally be another \c DNSServer object containing a copy
-    /// of the original "self_" pointer.  Calling clone() guarantees
-    /// that the underlying object is also correctly copied.
-    ///
-    /// \return A deep copy of this DNSServer object
-    virtual DNSServer* clone() { return (self_->clone()); }
-    //@}
-
-protected:
-    /// \brief Lookup handler object.
-    ///
-    /// This is a protected class; it can only be instantiated
-    /// from within a derived class of \c DNSServer.
-    ///
-    /// A server object that has received a query creates an instance
-    /// of this class and scheudles it on the ASIO service queue
-    /// using asio::io_service::post().  When the handler executes, it
-    /// calls the asyncLookup() method in the server object to start a
-    /// DNS lookup.  When the lookup is complete, the server object is
-    /// scheduled to resume, again using io_service::post().
-    ///
-    /// Note that the calling object is copied into the handler object,
-    /// not referenced.  This is because, once the calling object yields
-    /// control to the handler, it falls out of scope and may disappear
-    template <typename T>
-    class AsyncLookup {
-    public:
-        AsyncLookup(T& caller) : caller_(caller) {}
-        void operator()() { caller_.asyncLookup(); }
-    private:
-        T caller_;
-    };
-
-    /// \brief Carries out a DNS lookup.
-    ///
-    /// This function calls the \c DNSLookup object specified by the
-    /// DNS server when the \c IOService was created, passing along
-    /// the details of the query and a pointer back to the current
-    /// server object.  It is called asynchronously via the AsyncLookup
-    /// handler class.
-    virtual void asyncLookup() { self_->asyncLookup(); }
-
-private:
-    DNSServer* self_;
-};
-
-/// \brief The \c DNSLookup class is an abstract base class for a DNS
-/// Lookup provider function.
-///
-/// Specific derived class implementations are hidden within the
-/// implementation.  Instances of the derived classes can be called
-/// as functions via the operator() interface.  Pointers to these
-/// instances can then be provided to the \c IOService class
-/// via its constructor.
-///
-/// A DNS Lookup provider function obtains the data needed to answer
-/// a DNS query (e.g., from authoritative data source, cache, or upstream
-/// query).  After it has run, the OutputBuffer object passed to it
-/// should contain the answer to the query, in an internal representation.
-class DNSLookup {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    DNSLookup(const DNSLookup& source);
-    DNSLookup& operator=(const DNSLookup& source);
-protected:
-    /// \brief The default constructor.
-    ///
-    /// This is intentionally defined as \c protected as this base class
-    /// should never be instantiated (except as part of a derived class).
-    DNSLookup() : self_(this) {}
-public:
-    /// \brief The destructor
-    virtual ~DNSLookup() {}
-    //@}
-    /// \brief The function operator
-    ///
-    /// This makes its call indirectly via the "self" pointer, ensuring
-    /// that the function ultimately invoked will be the one in the derived
-    /// class.
-    ///
-    /// \param io_message The event message to handle
-    /// \param message The DNS MessagePtr that needs handling
-    /// \param buffer The final answer is put here
-    /// \param DNSServer DNSServer object to use
-    virtual void operator()(const IOMessage& io_message,
-                            isc::dns::MessagePtr message,
-                            isc::dns::MessagePtr answer_message,
-                            isc::dns::OutputBufferPtr buffer,
-                            DNSServer* server) const
-    {
-        (*self_)(io_message, message, answer_message, buffer, server);
-    }
-private:
-    DNSLookup* self_;
-};
-
-/// \brief The \c DNSAnswer class is an abstract base class for a DNS
-/// Answer provider function.
-///
-/// Specific derived class implementations are hidden within the
-/// implementation.  Instances of the derived classes can be called
-/// as functions via the operator() interface.  Pointers to these
-/// instances can then be provided to the \c IOService class
-/// via its constructor.
-///
-/// A DNS Answer provider function takes answer data that has been obtained
-/// from a DNS Lookup provider functon and readies it to be sent to the
-/// client.  After it has run, the OutputBuffer object passed to it should
-/// contain the answer to the query rendered into wire format.
-class DNSAnswer {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    DNSAnswer(const DNSAnswer& source);
-    DNSAnswer& operator=(const DNSAnswer& source);
-protected:
-    /// \brief The default constructor.
-    ///
-    /// This is intentionally defined as \c protected as this base class
-    /// should never be instantiated (except as part of a derived class).
-    DNSAnswer() {}
-public:
-    /// \brief The destructor
-    virtual ~DNSAnswer() {}
-    //@}
-    /// \brief The function operator
-    ///
-    /// This makes its call indirectly via the "self" pointer, ensuring
-    /// that the function ultimately invoked will be the one in the derived
-    /// class.
-    ///
-    /// \param io_message The event message to handle
-    /// \param message The DNS MessagePtr that needs handling
-    /// \param buffer The result is put here
-    virtual void operator()(const IOMessage& io_message,
-                            isc::dns::MessagePtr message,
-                            isc::dns::MessagePtr answer_message,
-                            isc::dns::OutputBufferPtr buffer) const = 0;
-};
-
-/// \brief The \c SimpleCallback class is an abstract base class for a
-/// simple callback function with the signature:
-///
-/// void simpleCallback(const IOMessage& io_message) const;
-///
-/// Specific derived class implementations are hidden within the
-/// implementation.  Instances of the derived classes can be called
-/// as functions via the operator() interface.  Pointers to these
-/// instances can then be provided to the \c IOService class
-/// via its constructor.
-///
-/// The \c SimpleCallback is expected to be used for basic, generic
-/// tasks such as checking for configuration changes.  It may also be
-/// used for testing purposes.
-class SimpleCallback {
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    SimpleCallback(const SimpleCallback& source);
-    SimpleCallback& operator=(const SimpleCallback& source);
-protected:
-    /// \brief The default constructor.
-    ///
-    /// This is intentionally defined as \c protected as this base class
-    /// should never be instantiated (except as part of a derived class).
-    SimpleCallback() : self_(this) {}
-public:
-    /// \brief The destructor
-    virtual ~SimpleCallback() {}
-    /// \brief The function operator
-    //@}
-    ///
-    /// This makes its call indirectly via the "self" pointer, ensuring
-    /// that the function ultimately invoked will be the one in the derived
-    /// class.
-    ///
-    /// \param io_message The event message to handle
-    virtual void operator()(const IOMessage& io_message) const {
-        (*self_)(io_message);
-    }
-private:
-    SimpleCallback* self_;
-};
-
-/// \brief The \c RecursiveQuery class provides a layer of abstraction around
-/// the ASIO code that carries out an upstream query.
-///
-/// This design is very preliminary; currently it is only capable of
-/// handling simple forward requests to a single resolver.
-class RecursiveQuery {
-    ///
-    /// \name Constructors
-    ///
-    //@{
-public:
-    /// \brief Constructor
-    ///
-    /// This is currently the only way to construct \c RecursiveQuery
-    /// object. If the addresses of the forward nameservers is specified,
-    /// and every upstream query will be sent to one random address, and
-    /// the result sent back directly. If not, it will do full resolving.
-    ///
-    /// \param dns_service The DNS Service to perform the recursive
-    ///        query on.
-    /// \param upstream Addresses and ports of the upstream servers
-    ///        to forward queries to.
-    /// \param upstream_root Addresses and ports of the root servers
-    ///        to use when resolving.
-    /// \param timeout How long to timeout the query, in ms
-    ///     -1 means never timeout (but do not use that).
-    ///     TODO: This should be computed somehow dynamically in future
-    /// \param retries how many times we try again (0 means just send and
-    ///     and return if it returs).
-    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 = 2000,
-                   int client_timeout = 4000,
-                   int lookup_timeout = 30000,
-                   unsigned retries = 3);
-    //@}
-
-    /// \brief Initiate resolving
-    /// 
-    /// When sendQuery() is called, a (set of) message(s) is sent
-    /// asynchronously. If upstream servers are set, one is chosen
-    /// and the response (if any) from that server will be returned.
-    ///
-    /// If not upstream is set, a root server is chosen from the
-    /// root_servers, and the RunningQuery shall do a full resolve
-    /// (i.e. if the answer is a delegation, it will be followed, etc.)
-    /// until there is an answer or an error.
-    ///
-    /// When there is a response or an error and we give up, the given
-    /// CallbackPtr object shall be called (with either success() or
-    /// failure(). See ResolverInterface::Callback for more information.
-    ///
-    /// \param question The question being answered <qname/qclass/qtype>
-    /// \param callback Callback object. See
-    ///        \c ResolverInterface::Callback for more information
-    void resolve(const isc::dns::QuestionPtr& question,
-                 const isc::resolve::ResolverInterface::CallbackPtr callback);
-
-
-    /// \brief Initiates resolving for the given question.
-    ///
-    /// This actually calls the previous sendQuery() with a default
-    /// callback object, which calls resume() on the given DNSServer
-    /// object.
-    ///
-    /// \param question The question being answered <qname/qclass/qtype>
-    /// \param answer_message An output Message into which the final response will be copied
-    /// \param buffer An output buffer into which the intermediate responses will be copied
-    /// \param server A pointer to the \c DNSServer object handling the client
-    void resolve(const isc::dns::Question& question,
-                 isc::dns::MessagePtr answer_message,
-                 isc::dns::OutputBufferPtr buffer,
-                 DNSServer* server);
-private:
-    DNSService& dns_service_;
-    boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
-        upstream_;
-    boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
-        upstream_root_;
-    int query_timeout_;
-    int client_timeout_;
-    int lookup_timeout_;
-    unsigned retries_;
-};
-
-/// \brief The \c IntervalTimer class is a wrapper for the ASIO
-/// \c asio::deadline_timer class.
-///
-/// This class is implemented to use \c asio::deadline_timer as
-/// interval timer.
-///
-/// \c setupTimer() sets a timer to expire on (now + interval) and
-/// a call back function.
-///
-/// \c IntervalTimerImpl::callback() is called by the timer when
-/// it expires.
-///
-/// The function calls the call back function set by \c setupTimer()
-/// and updates the timer to expire in (now + interval) seconds.
-/// The type of call back function is \c void(void).
-/// 
-/// The call back function will not be called if the instance of this
-/// class is destructed before the timer is expired.
-///
-/// Note: Destruction of an instance of this class while call back
-/// is pending causes throwing an exception from \c IOService.
-///
-/// Sample code:
-/// \code
-///  void function_to_call_back() {
-///      // this function will be called periodically
-///  }
-///  int interval_in_seconds = 1;
-///  IOService io_service;
-///
-///  IntervalTimer intervalTimer(io_service);
-///  intervalTimer.setupTimer(function_to_call_back, interval_in_seconds);
-///  io_service.run();
-/// \endcode
-///
-class IntervalTimer {
-public:
-    /// \name The type of timer callback function
-    typedef boost::function<void()> Callback;
-
-    ///
-    /// \name Constructors and Destructor
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private, making this class non-copyable.
-    //@{
-private:
-    IntervalTimer(const IntervalTimer& source);
-    IntervalTimer& operator=(const IntervalTimer& source);
-public:
-    /// \brief The constructor with \c IOService.
-    ///
-    /// This constructor may throw a standard exception if
-    /// memory allocation fails inside the method.
-    /// This constructor may also throw \c asio::system_error.
-    ///
-    /// \param io_service A reference to an instance of IOService
-    ///
-    IntervalTimer(IOService& io_service);
-
-    /// \brief The destructor.
-    ///
-    /// This destructor never throws an exception.
-    ///
-    /// On the destruction of this class the timer will be canceled
-    /// inside \c asio::deadline_timer.
-    ///
-    ~IntervalTimer();
-    //@}
-
-    /// \brief Register timer callback function and interval.
-    ///
-    /// This function sets callback function and interval in seconds.
-    /// Timer will actually start after calling \c IOService::run().
-    ///
-    /// \param cbfunc A reference to a function \c void(void) to call back
-    /// when the timer is expired (should not be an empty functor)
-    /// \param interval Interval in seconds (greater than 0)
-    ///
-    /// Note: IntervalTimer will not pass \c asio::error_code to
-    /// call back function. In case the timer is cancelled, the function
-    /// will not be called.
-    ///
-    /// \throw isc::InvalidParameter cbfunc is empty
-    /// \throw isc::BadValue interval is 0
-    /// \throw isc::Unexpected ASIO library error
-    ///
-    void setupTimer(const Callback& cbfunc, const uint32_t interval);
-
-    /// Cancel the timer.
-    ///
-    /// If the timer has been set up, this method cancels any asynchronous
-    /// events waiting on the timer and stops the timer itself.
-    /// If the timer has already been canceled, this method effectively does
-    /// nothing.
-    ///
-    /// This method never throws an exception.
-    void cancel();
-
-    /// Return the timer interval.
-    ///
-    /// This method returns the timer interval in seconds if it's running;
-    /// if the timer has been canceled it returns 0.
-    ///
-    /// This method never throws an exception.
-    ///
-    /// Note: We may want to change the granularity of the timer to
-    /// milliseconds or even finer.  If and when this happens the semantics
-    /// of the return value of this method will be changed accordingly.
-    uint32_t getInterval() const;
-
-private:
-    IntervalTimerImpl* impl_;
-};
 
 
 }      // asiolink
 }      // asiolink
 #endif // __ASIOLINK_H
 #endif // __ASIOLINK_H

+ 73 - 0
src/lib/asiolink/dns_answer.h

@@ -0,0 +1,73 @@
+// 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.
+
+#ifndef __ASIOLINK_DNS_ANSWER_H
+#define __ASIOLINK_DNS_ANSWER_H 1
+
+#include <asiolink/io_message.h>
+
+namespace asiolink {
+
+/// \brief The \c DNSAnswer class is an abstract base class for a DNS
+/// Answer provider function.
+///
+/// Specific derived class implementations are hidden within the
+/// implementation.  Instances of the derived classes can be called
+/// as functions via the operator() interface.  Pointers to these
+/// instances can then be provided to the \c IOService class
+/// via its constructor.
+///
+/// A DNS Answer provider function takes answer data that has been obtained
+/// from a DNS Lookup provider functon and readies it to be sent to the
+/// client.  After it has run, the OutputBuffer object passed to it should
+/// contain the answer to the query rendered into wire format.
+class DNSAnswer {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    DNSAnswer(const DNSAnswer& source);
+    DNSAnswer& operator=(const DNSAnswer& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class
+    /// should never be instantiated (except as part of a derived class).
+    DNSAnswer() {}
+public:
+    /// \brief The destructor
+    virtual ~DNSAnswer() {}
+    //@}
+    /// \brief The function operator
+    ///
+    /// This makes its call indirectly via the "self" pointer, ensuring
+    /// that the function ultimately invoked will be the one in the derived
+    /// class.
+    ///
+    /// \param io_message The event message to handle
+    /// \param query_message The DNS MessagePtr of the original query
+    /// \param answer_message The DNS MessagePtr of the answer we are
+    /// building
+    /// \param buffer Intermediate data results are put here
+    virtual void operator()(const IOMessage& io_message,
+                            isc::dns::MessagePtr query_message,
+                            isc::dns::MessagePtr answer_message,
+                            isc::dns::OutputBufferPtr buffer) const = 0;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_DNS_ANSWER_H

+ 81 - 0
src/lib/asiolink/dns_lookup.h

@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef __ASIOLINK_DNS_LOOKUP_H
+#define __ASIOLINK_DNS_LOOKUP_H 1
+
+#include <asiolink/io_message.h>
+#include <asiolink/dns_server.h>
+#include <dns/buffer.h>
+#include <dns/message.h>
+
+namespace asiolink {
+
+/// \brief The \c DNSLookup class is an abstract base class for a DNS
+/// Lookup provider function.
+///
+/// Specific derived class implementations are hidden within the
+/// implementation.  Instances of the derived classes can be called
+/// as functions via the operator() interface.  Pointers to these
+/// instances can then be provided to the \c IOService class
+/// via its constructor.
+///
+/// A DNS Lookup provider function obtains the data needed to answer
+/// a DNS query (e.g., from authoritative data source, cache, or upstream
+/// query).  After it has run, the OutputBuffer object passed to it
+/// should contain the answer to the query, in an internal representation.
+class DNSLookup {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    DNSLookup(const DNSLookup& source);
+    DNSLookup& operator=(const DNSLookup& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class
+    /// should never be instantiated (except as part of a derived class).
+    DNSLookup() : self_(this) {}
+public:
+    /// \brief The destructor
+    virtual ~DNSLookup() {}
+    //@}
+    /// \brief The function operator
+    ///
+    /// This makes its call indirectly via the "self" pointer, ensuring
+    /// that the function ultimately invoked will be the one in the derived
+    /// class.
+    ///
+    /// \param io_message The event message to handle
+    /// \param message The DNS MessagePtr that needs handling
+    /// \param buffer The final answer is put here
+    /// \param DNSServer DNSServer object to use
+    virtual void operator()(const IOMessage& io_message,
+                            isc::dns::MessagePtr message,
+                            isc::dns::MessagePtr answer_message,
+                            isc::dns::OutputBufferPtr buffer,
+                            DNSServer* server) const
+    {
+        (*self_)(io_message, message, answer_message, buffer, server);
+    }
+private:
+    DNSLookup* self_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_DNS_LOOKUP_H

+ 152 - 0
src/lib/asiolink/dns_server.h

@@ -0,0 +1,152 @@
+// 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.
+
+#ifndef __ASIOLINK_DNS_SERVER_H
+#define __ASIOLINK_DNS_SERVER_H 1
+
+#include <asiolink/io_message.h>
+
+namespace asiolink {
+
+/// \brief The \c DNSServer class is a wrapper (and base class) for
+/// classes which provide DNS server functionality.
+/// 
+/// The classes derived from this one, \c TCPServer and \c UDPServer,
+/// act as the interface layer between clients sending queries, and
+/// functions defined elsewhere that provide answers to those queries.
+/// Those functions are described in more detail below under
+/// \c SimpleCallback, \c DNSLookup, and \c DNSAnswer.
+///
+/// Notes to developers:
+/// When constructed, this class (and its derived classes) will have its
+/// "self_" member set to point to "this".  Objects of this class (as
+/// instantiated through a base class) are sometimes passed by
+/// reference (as this superclass); calls to methods in the base
+/// class are then rerouted via this pointer to methods in the derived
+/// class.  This allows code from outside asiolink, with no specific
+/// knowledge of \c TCPServer or \c UDPServer, to access their methods.
+///
+/// This class is both assignable and copy-constructable.  Its subclasses
+/// use the "stackless coroutine" pattern, meaning that it will copy itself
+/// when "forking", and that instances will be posted as ASIO handler
+/// objects, which are always copied.
+///
+/// Because these objects are frequently copied, it is recommended 
+/// that derived classes be kept small to reduce copy overhead.
+class DNSServer {
+protected: 
+    ///
+    /// \name Constructors and destructors
+    ///
+    /// This is intentionally defined as \c protected, as this base class
+    /// should never be instantiated except as part of a derived class.
+    //@{
+    DNSServer() : self_(this) {}
+public:
+    /// \brief The destructor
+    virtual ~DNSServer() {}
+    //@}
+
+    ///
+    /// \name Class methods
+    ///
+    /// These methods all make their calls indirectly via the "self_"
+    /// pointer, ensuring that the functions ultimately invoked will be
+    /// the ones in the derived class.  This makes it possible to pass
+    /// instances of derived classes as references to this base class
+    /// without losing access to derived class data.
+    /// 
+    //@{
+    /// \brief The funtion operator
+    virtual void operator()(asio::error_code ec = asio::error_code(),
+                            size_t length = 0)
+    {
+        (*self_)(ec, length);
+    }
+
+    /// \brief Resume processing of the server coroutine after an 
+    /// asynchronous call (e.g., to the DNS Lookup provider) has completed.
+    ///
+    /// \param done If true, this signals the system there is an answer
+    ///             to return.
+    virtual void resume(const bool done) { self_->resume(done); }
+
+    /// \brief Indicate whether the server is able to send an answer
+    /// to a query.
+    /// 
+    /// This is presently used only for testing purposes.
+    virtual bool hasAnswer() { return (self_->hasAnswer()); }
+
+    /// \brief Returns the current value of the 'coroutine' object
+    ///
+    /// This is a temporary method, intended to be used for debugging
+    /// purposes during development and removed later.  It allows
+    /// callers from outside the coroutine object to retrieve information
+    /// about its current state.
+    ///
+    /// \return The value of the 'coroutine' object
+    virtual int value() { return (self_->value()); }
+
+    /// \brief Returns a pointer to a clone of this DNSServer object.
+    ///
+    /// When a \c DNSServer object is copied or assigned, the result will
+    /// normally be another \c DNSServer object containing a copy
+    /// of the original "self_" pointer.  Calling clone() guarantees
+    /// that the underlying object is also correctly copied.
+    ///
+    /// \return A deep copy of this DNSServer object
+    virtual DNSServer* clone() { return (self_->clone()); }
+    //@}
+
+protected:
+    /// \brief Lookup handler object.
+    ///
+    /// This is a protected class; it can only be instantiated
+    /// from within a derived class of \c DNSServer.
+    ///
+    /// A server object that has received a query creates an instance
+    /// of this class and scheudles it on the ASIO service queue
+    /// using asio::io_service::post().  When the handler executes, it
+    /// calls the asyncLookup() method in the server object to start a
+    /// DNS lookup.  When the lookup is complete, the server object is
+    /// scheduled to resume, again using io_service::post().
+    ///
+    /// Note that the calling object is copied into the handler object,
+    /// not referenced.  This is because, once the calling object yields
+    /// control to the handler, it falls out of scope and may disappear
+    template <typename T>
+    class AsyncLookup {
+    public:
+        AsyncLookup(T& caller) : caller_(caller) {}
+        void operator()() { caller_.asyncLookup(); }
+    private:
+        T caller_;
+    };
+
+    /// \brief Carries out a DNS lookup.
+    ///
+    /// This function calls the \c DNSLookup object specified by the
+    /// DNS server when the \c IOService was created, passing along
+    /// the details of the query and a pointer back to the current
+    /// server object.  It is called asynchronously via the AsyncLookup
+    /// handler class.
+    virtual void asyncLookup() { self_->asyncLookup(); }
+
+private:
+    DNSServer* self_;
+};
+
+
+}      // asiolink
+#endif // __ASIOLINK_DNS_SERVER_H

+ 192 - 0
src/lib/asiolink/dns_service.cc

@@ -0,0 +1,192 @@
+// 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>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_server.h>
+#include <asiolink/udp_server.h>
+
+#include <log/dummylog.h>
+
+#include <boost/lexical_cast.hpp>
+
+using isc::log::dlog;
+
+namespace asiolink {
+
+class SimpleCallback;
+class DNSLookup;
+class DNSAnswer;
+
+namespace {
+
+asio::ip::address
+convertAddr(const std::string& address) {
+    asio::error_code err;
+    asio::ip::address addr = asio::ip::address::from_string(address, err);
+    if (err) {
+        isc_throw(IOError, "Invalid IP address '" << &address << "': "
+            << err.message());
+    }
+    return (addr);
+}
+
+}
+
+
+class DNSServiceImpl {
+public:
+    DNSServiceImpl(IOService& io_service, const char& port,
+                  const asio::ip::address* v4addr,
+                  const asio::ip::address* v6addr,
+                  SimpleCallback* checkin, DNSLookup* lookup,
+                  DNSAnswer* answer);
+
+    IOService& io_service_;
+
+    typedef boost::shared_ptr<UDPServer> UDPServerPtr;
+    typedef boost::shared_ptr<TCPServer> TCPServerPtr;
+    typedef boost::shared_ptr<DNSServer> DNSServerPtr;
+    std::vector<DNSServerPtr> servers_;
+    SimpleCallback *checkin_;
+    DNSLookup *lookup_;
+    DNSAnswer *answer_;
+
+    void addServer(uint16_t port, const asio::ip::address& address) {
+        try {
+            dlog(std::string("Initialize TCP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
+            TCPServerPtr tcpServer(new TCPServer(io_service_.get_io_service(),
+                address, port, checkin_, lookup_, answer_));
+            (*tcpServer)();
+            servers_.push_back(tcpServer);
+            dlog(std::string("Initialize UDP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
+            UDPServerPtr udpServer(new UDPServer(io_service_.get_io_service(),
+                address, port, checkin_, lookup_, answer_));
+            (*udpServer)();
+            servers_.push_back(udpServer);
+        }
+        catch (const asio::system_error& err) {
+            // We need to catch and convert any ASIO level exceptions.
+            // This can happen for unavailable address, binding a privilege port
+            // without the privilege, etc.
+            isc_throw(IOError, "Failed to initialize network servers: " <<
+                      err.what());
+        }
+    }
+    void addServer(const char& port, const asio::ip::address& address) {
+        uint16_t portnum;
+        try {
+            // XXX: SunStudio with stlport4 doesn't reject some invalid
+            // representation such as "-1" by lexical_cast<uint16_t>, so
+            // we convert it into a signed integer of a larger size and perform
+            // range check ourselves.
+            const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
+            if (portnum32 < 0 || portnum32 > 65535) {
+                isc_throw(IOError, "Invalid port number '" << &port);
+            }
+            portnum = portnum32;
+        } catch (const boost::bad_lexical_cast& ex) {
+            isc_throw(IOError, "Invalid port number '" << &port << "': " <<
+                      ex.what());
+        }
+        addServer(portnum, address);
+    }
+};
+
+DNSServiceImpl::DNSServiceImpl(IOService& io_service,
+                               const char& port,
+                               const asio::ip::address* const v4addr,
+                               const asio::ip::address* const v6addr,
+                               SimpleCallback* checkin,
+                               DNSLookup* lookup,
+                               DNSAnswer* answer) :
+    io_service_(io_service),
+    checkin_(checkin),
+    lookup_(lookup),
+    answer_(answer)
+{
+
+    if (v4addr) {
+        addServer(port, *v4addr);
+    }
+    if (v6addr) {
+        addServer(port, *v6addr);
+    }
+}
+
+DNSService::DNSService(IOService& io_service,
+                       const char& port, const char& address,
+                       SimpleCallback* checkin,
+                       DNSLookup* lookup,
+                       DNSAnswer* answer) :
+    impl_(new DNSServiceImpl(io_service, port, NULL, NULL, checkin, lookup,
+        answer)), io_service_(io_service)
+{
+    addServer(port, &address);
+}
+
+DNSService::DNSService(IOService& io_service,
+                       const char& port,
+                       const bool use_ipv4, const bool use_ipv6,
+                       SimpleCallback* checkin,
+                       DNSLookup* lookup,
+                       DNSAnswer* answer) :
+    impl_(NULL), io_service_(io_service)
+{
+    const asio::ip::address v4addr_any =
+        asio::ip::address(asio::ip::address_v4::any());
+    const asio::ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL; 
+    const asio::ip::address v6addr_any =
+        asio::ip::address(asio::ip::address_v6::any());
+    const asio::ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
+    impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer);
+}
+
+DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
+    DNSLookup* lookup, DNSAnswer *answer) :
+    impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
+        answer)), io_service_(io_service)
+{
+}
+
+DNSService::~DNSService() {
+    delete impl_;
+}
+
+void
+DNSService::addServer(const char& port, const std::string& address) {
+    impl_->addServer(port, convertAddr(address));
+}
+
+void
+DNSService::addServer(uint16_t port, const std::string& address) {
+    impl_->addServer(port, convertAddr(address));
+}
+
+void
+DNSService::clearServers() {
+    // FIXME: This does not work, it does not close the socket.
+    // How is it done?
+    impl_->servers_.clear();
+}
+
+
+
+} // namespace asiolink

+ 106 - 0
src/lib/asiolink/dns_service.h

@@ -0,0 +1,106 @@
+// 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.
+
+#ifndef __ASIOLINK_DNS_SERVICE_H
+#define __ASIOLINK_DNS_SERVICE_H 1
+
+#include <resolve/resolver_interface.h>
+
+#include <asiolink/io_service.h>
+
+namespace asiolink {
+
+class SimpleCallback;
+class DNSLookup;
+class DNSAnswer;
+class DNSServiceImpl;
+
+///
+/// DNSService is the service that handles DNS queries and answers with
+/// a given IOService. This class is mainly intended to hold all the
+/// logic that is shared between the authoritative and the recursive
+/// server implementations. As such, it handles asio, including config
+/// updates (through the 'Checkinprovider'), and listening sockets.
+/// 
+class DNSService {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    DNSService(const DNSService& source);
+    DNSService& operator=(const DNSService& source);
+
+public:
+    /// \brief The constructor with a specific IP address and port on which
+    /// the services listen on.
+    ///
+    /// \param io_service The IOService to work with
+    /// \param port the port to listen on
+    /// \param address the IP address to listen on
+    /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
+    /// \param lookup The lookup provider (see \c DNSLookup)
+    /// \param answer The answer provider (see \c DNSAnswer)
+    DNSService(IOService& io_service, const char& port,
+               const char& address, SimpleCallback* checkin,
+               DNSLookup* lookup, DNSAnswer* answer);
+    /// \brief The constructor with a specific port on which the services
+    /// listen on.
+    ///
+    /// It effectively listens on "any" IPv4 and/or IPv6 addresses.
+    /// IPv4/IPv6 services will be available if and only if \c use_ipv4
+    /// or \c use_ipv6 is \c true, respectively.
+    ///
+    /// \param io_service The IOService to work with
+    /// \param port the port to listen on
+    /// \param ipv4 If true, listen on ipv4 'any'
+    /// \param ipv6 If true, listen on ipv6 'any'
+    /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
+    /// \param lookup The lookup provider (see \c DNSLookup)
+    /// \param answer The answer provider (see \c DNSAnswer)
+    DNSService(IOService& io_service, const char& port,
+               const bool use_ipv4, const bool use_ipv6,
+               SimpleCallback* checkin, DNSLookup* lookup,
+               DNSAnswer* answer);
+    /// \brief The constructor without any servers.
+    ///
+    /// Use addServer() to add some servers.
+    DNSService(IOService& io_service, SimpleCallback* checkin,
+               DNSLookup* lookup, DNSAnswer* answer);
+    /// \brief The destructor.
+    ~DNSService();
+    //@}
+
+    /// \brief Add another server to the service
+    void addServer(uint16_t port, const std::string &address);
+    void addServer(const char &port, const std::string &address);
+    /// \brief Remove all servers from the service
+    void clearServers();
+
+    /// \brief Return the native \c io_service object used in this wrapper.
+    ///
+    /// This is a short term work around to support other BIND 10 modules
+    /// that share the same \c io_service with the authoritative server.
+    /// It will eventually be removed once the wrapper interface is
+    /// generalized.
+    asio::io_service& get_io_service() { return io_service_.get_io_service(); }
+private:
+    DNSServiceImpl* impl_;
+    IOService& io_service_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_DNS_SERVICE_H

+ 0 - 1
src/lib/asiolink/internal/Makefile.am

@@ -1 +0,0 @@
-SUBDIRS = tests

+ 0 - 37
src/lib/asiolink/internal/tests/Makefile.am

@@ -1,37 +0,0 @@
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-
-AM_CXXFLAGS = $(B10_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-CLEANFILES = *.gcno *.gcda
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-run_unittests_SOURCES = udpdns_unittest.cc
-run_unittests_SOURCES += run_unittests.cc
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
-# B10_CXXFLAGS)
-run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_GXX
-run_unittests_CXXFLAGS += -Wno-unused-parameter
-endif
-if USE_CLANGPP
-# We need to disable -Werror for any test that uses internal definitions of
-# ASIO when using clang++
-run_unittests_CXXFLAGS += -Wno-error
-endif
-endif
-
-noinst_PROGRAMS = $(TESTS)

+ 0 - 253
src/lib/asiolink/internal/udpdns.h

@@ -1,253 +0,0 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __UDPDNS_H
-#define __UDPDNS_H 1
-
-#include <config.h>
-
-#include <asio.hpp>
-#include <boost/shared_array.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <dns/buffer.h>
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
-
-#include <asiolink/asiolink.h>
-#include <asiolink/internal/coroutine.h>
-
-// This file contains UDP-specific implementations of generic classes 
-// defined in asiolink.h.  It is *not* intended to be part of the public
-// API.
-
-namespace asiolink {
-/// \brief The \c UDPEndpoint class is a concrete derived class of
-/// \c IOEndpoint that represents an endpoint of a UDP packet.
-///
-/// Other notes about \c TCPEndpoint applies to this class, too.
-class UDPEndpoint : public IOEndpoint {
-public:
-    ///
-    /// \name Constructors and Destructor.
-    ///
-    //@{
-    /// \brief Constructor from a pair of address and port.
-    ///
-    /// \param address The IP address of the endpoint.
-    /// \param port The UDP port number of the endpoint.
-    UDPEndpoint(const IOAddress& address, const unsigned short port) :
-        asio_endpoint_placeholder_(
-            new asio::ip::udp::endpoint(asio::ip::address::from_string(address.toText()),
-                              port)),
-        asio_endpoint_(*asio_endpoint_placeholder_)
-    {}
-
-    /// \brief Constructor from an ASIO UDP endpoint.
-    ///
-    /// This constructor is designed to be an efficient wrapper for the
-    /// corresponding ASIO class, \c udp::endpoint.
-    ///
-    /// \param asio_endpoint The ASIO representation of the UDP endpoint.
-    UDPEndpoint(const asio::ip::udp::endpoint& asio_endpoint) :
-        asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
-    {}
-
-    /// \brief The destructor.
-    ~UDPEndpoint() { delete asio_endpoint_placeholder_; }
-    //@}
-
-    inline IOAddress getAddress() const {
-        return (asio_endpoint_.address());
-    }
-
-    inline uint16_t getPort() const {
-        return (asio_endpoint_.port());
-    }
-
-    inline short getProtocol() const {
-        return (asio_endpoint_.protocol().protocol());
-    }
-
-    inline short getFamily() const {
-        return (asio_endpoint_.protocol().family());
-    }
-
-    // This is not part of the exosed IOEndpoint API but allows
-    // direct access to the ASIO implementation of the endpoint
-    inline const asio::ip::udp::endpoint& getASIOEndpoint() const {
-        return (asio_endpoint_);
-    }
-
-private:
-    const asio::ip::udp::endpoint* asio_endpoint_placeholder_;
-    const asio::ip::udp::endpoint& asio_endpoint_;
-};
-
-/// \brief The \c UDPSocket class is a concrete derived class of
-/// \c IOSocket that represents a UDP socket.
-///
-/// Other notes about \c TCPSocket applies to this class, too.
-class UDPSocket : public IOSocket {
-private:
-    UDPSocket(const UDPSocket& source);
-    UDPSocket& operator=(const UDPSocket& source);
-public:
-    /// \brief Constructor from an ASIO UDP socket.
-    ///
-    /// \param socket The ASIO representation of the UDP socket.
-    UDPSocket(asio::ip::udp::socket& socket) : socket_(socket) {}
-
-    virtual int getNative() const { return (socket_.native()); }
-    virtual int getProtocol() const { return (IPPROTO_UDP); }
-
-private:
-    asio::ip::udp::socket& socket_;
-};
-
-//
-// Asynchronous UDP server coroutine
-//
-///
-/// \brief This class implements the coroutine to handle UDP
-///        DNS query event. As such, it is both a \c DNSServer and
-///        a \c coroutine
-///
-class UDPServer : public virtual DNSServer, public virtual coroutine {
-public:
-    /// \brief Constructor
-    /// \param io_service the asio::io_service to work with
-    /// \param addr the IP address to listen for queries on
-    /// \param port the port to listen for queries on
-    /// \param checkin the callbackprovider for non-DNS events
-    /// \param lookup the callbackprovider for DNS lookup events
-    /// \param answer the callbackprovider for DNS answer events
-    explicit UDPServer(asio::io_service& io_service,
-                       const asio::ip::address& addr, const uint16_t port,
-                       SimpleCallback* checkin = NULL,
-                       DNSLookup* lookup = NULL,
-                       DNSAnswer* answer = NULL);
-
-    /// \brief The function operator
-    void operator()(asio::error_code ec = asio::error_code(),
-                    size_t length = 0);
-
-    /// \brief Calls the lookup callback
-    void asyncLookup();
-
-    /// \brief Resume operation
-    ///
-    /// \param done Set this to true if the lookup action is done and
-    ///        we have an answer
-    void resume(const bool done);
-
-    /// \brief Check if we have an answer
-    ///
-    /// \return true if we have an answer
-    bool hasAnswer();
-
-    /// \brief Returns the coroutine state value
-    ///
-    /// \return the coroutine state value
-    int value() { return (get_value()); }
-
-    /// \brief Clones the object
-    ///
-    /// \return a newly allocated copy of this object
-    DNSServer* clone() {
-        UDPServer* s = new UDPServer(*this);
-        return (s);
-    }
-
-private:
-    enum { MAX_LENGTH = 4096 };
-
-    /**
-     * \brief Internal state and data.
-     *
-     * We use the pimple design pattern, but not because we need to hide
-     * internal data. This class and whole header is for private use anyway.
-     * It turned out that UDPServer is copied a lot, because it is a coroutine.
-     * This way the overhead of copying is lower, we copy only one shared
-     * pointer instead of about 10 of them.
-     */
-    class Data;
-    boost::shared_ptr<Data> data_;
-};
-
-//
-// Asynchronous UDP coroutine for upstream queries
-//
-class UDPQuery : public coroutine {
-public:
-    // TODO Maybe this should be more generic than just for UDPQuery?
-    ///
-    /// \brief Result of the query
-    ///
-    /// This is related only to contacting the remote server. If the answer
-    ///indicates error, it is still counted as SUCCESS here, if it comes back.
-    ///
-    enum Result {
-        SUCCESS,
-        TIME_OUT,
-        STOPPED
-    };
-    /// Abstract callback for the UDPQuery.
-    class Callback {
-    public:
-        virtual ~Callback() {}
-
-        /// This will be called when the UDPQuery is completed
-        virtual void operator()(Result result) = 0;
-    };
-    ///
-    /// \brief Constructor.
-    ///
-    /// It creates the query.
-    /// @param callback will be called when we terminate. It is your task to
-    ///        delete it if allocated on heap.
-    ///@param timeout in ms.
-    ///
-    explicit UDPQuery(asio::io_service& io_service,
-                      const isc::dns::Question& q,
-                      const IOAddress& addr, uint16_t port,
-                      isc::dns::OutputBufferPtr buffer,
-                      Callback* callback, int timeout = -1);
-    void operator()(asio::error_code ec = asio::error_code(),
-                    size_t length = 0);
-    /// Terminate the query.
-    void stop(Result reason = STOPPED);
-private:
-    enum { MAX_LENGTH = 4096 };
-
-    ///
-    /// \short Private data
-    ///
-    /// They are not private because of stability of the
-    /// interface (this is private class anyway), but because this class
-    /// will be copyed often (it is used as a coroutine and passed as callback
-    /// to many async_*() functions) and we want keep the same data. Some of
-    /// the data is not copyable too.
-    ///
-    struct PrivateData;
-    boost::shared_ptr<PrivateData> data_;
-};
-}
-
-
-#endif // __UDPDNS_H
-
-// Local Variables: 
-// mode: c++
-// End: 

+ 136 - 0
src/lib/asiolink/interval_timer.cc

@@ -0,0 +1,136 @@
+// 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>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_service.h>
+
+#include <boost/bind.hpp>
+
+namespace asiolink {
+
+class IntervalTimerImpl {
+private:
+    // prohibit copy
+    IntervalTimerImpl(const IntervalTimerImpl& source);
+    IntervalTimerImpl& operator=(const IntervalTimerImpl& source);
+public:
+    IntervalTimerImpl(IOService& io_service);
+    ~IntervalTimerImpl();
+    void setup(const IntervalTimer::Callback& cbfunc, const long interval);
+    void callback(const asio::error_code& error);
+    void cancel() {
+        timer_.cancel();
+        interval_ = 0;
+    }
+    long getInterval() const { return (interval_); }
+private:
+    // a function to update timer_ when it expires
+    void update();
+    // a function to call back when timer_ expires
+    IntervalTimer::Callback cbfunc_;
+    // interval in milliseconds
+    long interval_;
+    // asio timer
+    asio::deadline_timer timer_;
+};
+
+IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
+    interval_(0), timer_(io_service.get_io_service())
+{}
+
+IntervalTimerImpl::~IntervalTimerImpl()
+{}
+
+void
+IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
+                         const long interval)
+{
+    // Interval should not be less than or equal to 0.
+    if (interval <= 0) {
+        isc_throw(isc::BadValue, "Interval should not be less than or "
+                                 "equal to 0");
+    }
+    // Call back function should not be empty.
+    if (cbfunc.empty()) {
+        isc_throw(isc::InvalidParameter, "Callback function is empty");
+    }
+    cbfunc_ = cbfunc;
+    interval_ = interval;
+    // Set initial expire time.
+    // At this point the timer is not running yet and will not expire.
+    // After calling IOService::run(), the timer will expire.
+    update();
+    return;
+}
+
+void
+IntervalTimerImpl::update() {
+    if (interval_ == 0) {
+        // timer has been canceled.  Do nothing.
+        return;
+    }
+    try {
+        // Update expire time to (current time + interval_).
+        timer_.expires_from_now(boost::posix_time::millisec(interval_));
+    } catch (const asio::system_error& e) {
+        isc_throw(isc::Unexpected, "Failed to update timer");
+    }
+    // Reset timer.
+    timer_.async_wait(boost::bind(&IntervalTimerImpl::callback, this, _1));
+}
+
+void
+IntervalTimerImpl::callback(const asio::error_code& cancelled) {
+    // Do not call cbfunc_ in case the timer was cancelled.
+    // The timer will be canelled in the destructor of asio::deadline_timer.
+    if (!cancelled) {
+        cbfunc_();
+        // Set next expire time.
+        update();
+    }
+}
+
+IntervalTimer::IntervalTimer(IOService& io_service) {
+    impl_ = new IntervalTimerImpl(io_service);
+}
+
+IntervalTimer::~IntervalTimer() {
+    delete impl_;
+}
+
+void
+IntervalTimer::setup(const Callback& cbfunc, const long interval) {
+    return (impl_->setup(cbfunc, interval));
+}
+
+void
+IntervalTimer::cancel() {
+    impl_->cancel();
+}
+
+long
+IntervalTimer::getInterval() const {
+    return (impl_->getInterval());
+}
+
+}

+ 133 - 0
src/lib/asiolink/interval_timer.h

@@ -0,0 +1,133 @@
+// 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.
+
+#ifndef __ASIOLINK_INTERVAL_TIMER_H
+#define __ASIOLINK_INTERVAL_TIMER_H 1
+
+#include <boost/function.hpp>
+
+#include <asiolink/io_service.h>
+
+namespace asiolink {
+
+struct IntervalTimerImpl;
+
+/// \brief The \c IntervalTimer class is a wrapper for the ASIO
+/// \c asio::deadline_timer class.
+///
+/// This class is implemented to use \c asio::deadline_timer as interval
+/// timer.
+///
+/// \c setup() sets a timer to expire on (now + interval) and a call back
+/// function.
+///
+/// \c IntervalTimerImpl::callback() is called by the timer when it expires.
+///
+/// The function calls the call back function set by \c setup() and updates
+/// the timer to expire in (now + interval) milliseconds.
+/// The type of call back function is \c void(void).
+/// 
+/// The call back function will not be called if the instance of this class is
+/// destroyed before the timer is expired.
+///
+/// Note: Destruction of an instance of this class while call back is pending
+/// causes throwing an exception from \c IOService.
+///
+/// Sample code:
+/// \code
+///  void function_to_call_back() {
+///      // this function will be called periodically
+///  }
+///  int interval_in_milliseconds = 1000;
+///  IOService io_service;
+///
+///  IntervalTimer intervalTimer(io_service);
+///  intervalTimer.setup(function_to_call_back, interval_in_milliseconds);
+///  io_service.run();
+/// \endcode
+class IntervalTimer {
+public:
+    /// \name The type of timer callback function
+    typedef boost::function<void()> Callback;
+
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IntervalTimer(const IntervalTimer& source);
+    IntervalTimer& operator=(const IntervalTimer& source);
+public:
+    /// \brief The constructor with \c IOService.
+    ///
+    /// This constructor may throw a standard exception if
+    /// memory allocation fails inside the method.
+    /// This constructor may also throw \c asio::system_error.
+    ///
+    /// \param io_service A reference to an instance of IOService
+    IntervalTimer(IOService& io_service);
+
+    /// \brief The destructor.
+    ///
+    /// This destructor never throws an exception.
+    ///
+    /// On the destruction of this class the timer will be canceled
+    /// inside \c asio::deadline_timer.
+    ~IntervalTimer();
+    //@}
+
+    /// \brief Register timer callback function and interval.
+    ///
+    /// This function sets callback function and interval in milliseconds.
+    /// Timer will actually start after calling \c IOService::run().
+    ///
+    /// \param cbfunc A reference to a function \c void(void) to call back
+    /// when the timer is expired (should not be an empty functor)
+    /// \param interval Interval in milliseconds (greater than 0)
+    ///
+    /// Note: IntervalTimer will not pass \c asio::error_code to
+    /// call back function. In case the timer is cancelled, the function
+    /// will not be called.
+    ///
+    /// \throw isc::InvalidParameter cbfunc is empty
+    /// \throw isc::BadValue interval is less than or equal to 0
+    /// \throw isc::Unexpected ASIO library error
+    void setup(const Callback& cbfunc, const long interval);
+
+    /// Cancel the timer.
+    ///
+    /// If the timer has been set up, this method cancels any asynchronous
+    /// events waiting on the timer and stops the timer itself.
+    /// If the timer has already been canceled, this method effectively does
+    /// nothing.
+    ///
+    /// This method never throws an exception.
+    void cancel();
+
+    /// Return the timer interval.
+    ///
+    /// This method returns the timer interval in milliseconds if it's running;
+    /// if the timer has been canceled it returns 0.
+    ///
+    /// This method never throws an exception.
+    long getInterval() const;
+
+private:
+    IntervalTimerImpl* impl_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_INTERVAL_TIMER_H

src/lib/asiolink/ioaddress.cc → src/lib/asiolink/io_address.cc


src/lib/asiolink/ioaddress.h → src/lib/asiolink/io_address.h


+ 5 - 3
src/lib/asiolink/ioendpoint.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  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
@@ -18,9 +18,11 @@
 #include <sys/socket.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
 
 
+#include <asio.hpp>
+
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
-#include <internal/tcpdns.h>
-#include <internal/udpdns.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/udp_endpoint.h>
 
 
 using namespace std;
 using namespace std;
 
 

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

@@ -24,7 +24,7 @@
 #include <string>
 #include <string>
 
 
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
-#include <asiolink/ioaddress.h>
+#include <asiolink/io_address.h>
 
 
 namespace asiolink {
 namespace asiolink {
 
 

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

@@ -25,8 +25,8 @@
 
 
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
-#include <asiolink/ioendpoint.h>
-#include <asiolink/iosocket.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_socket.h>
 
 
 namespace asiolink {
 namespace asiolink {
 
 

+ 97 - 0
src/lib/asiolink/io_service.cc

@@ -0,0 +1,97 @@
+// 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>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <asiolink/io_service.h>
+
+namespace asiolink {
+
+class IOServiceImpl {
+private:
+    IOServiceImpl(const IOService& source);
+    IOServiceImpl& operator=(const IOService& source);
+public:
+    /// \brief The constructor
+    IOServiceImpl() :
+        io_service_(),
+        work_(io_service_)
+    {};
+    /// \brief The destructor.
+    ~IOServiceImpl() {};
+    //@}
+
+    /// \brief Start the underlying event loop.
+    ///
+    /// This method does not return control to the caller until
+    /// the \c stop() method is called via some handler.
+    void run() { io_service_.run(); };
+
+    /// \brief Run the underlying event loop for a single event.
+    ///
+    /// This method return control to the caller as soon as the
+    /// first handler has completed.  (If no handlers are ready when
+    /// it is run, it will block until one is.)
+    void run_one() { io_service_.run_one();} ;
+
+    /// \brief Stop the underlying event loop.
+    ///
+    /// This will return the control to the caller of the \c run() method.
+    void stop() { io_service_.stop();} ;
+
+    /// \brief Return the native \c io_service object used in this wrapper.
+    ///
+    /// This is a short term work around to support other BIND 10 modules
+    /// that share the same \c io_service with the authoritative server.
+    /// It will eventually be removed once the wrapper interface is
+    /// generalized.
+    asio::io_service& get_io_service() { return io_service_; };
+private:
+    asio::io_service io_service_;
+    asio::io_service::work work_;
+};
+
+IOService::IOService() {
+    io_impl_ = new IOServiceImpl();
+}
+
+IOService::~IOService() {
+    delete io_impl_;
+}
+
+void
+IOService::run() {
+    io_impl_->run();
+}
+
+void
+IOService::run_one() {
+    io_impl_->run_one();
+}
+
+void
+IOService::stop() {
+    io_impl_->stop();
+}
+
+asio::io_service&
+IOService::get_io_service() {
+    return (io_impl_->get_io_service());
+}
+
+} // namepsace asiolink

+ 77 - 0
src/lib/asiolink/io_service.h

@@ -0,0 +1,77 @@
+// 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.
+
+#ifndef __ASIOLINK_IO_SERVICE_H
+#define __ASIOLINK_IO_SERVICE_H 1
+
+namespace asio {
+    class io_service;
+}
+
+namespace asiolink {
+
+struct IOServiceImpl;
+
+/// \brief The \c IOService class is a wrapper for the ASIO \c io_service
+/// class.
+///
+class IOService {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IOService(const IOService& source);
+    IOService& operator=(const IOService& source);
+public:
+    /// \brief The constructor
+    IOService();
+    /// \brief The destructor.
+    ~IOService();
+    //@}
+
+    /// \brief Start the underlying event loop.
+    ///
+    /// This method does not return control to the caller until
+    /// the \c stop() method is called via some handler.
+    void run();
+
+    /// \brief Run the underlying event loop for a single event.
+    ///
+    /// This method return control to the caller as soon as the
+    /// first handler has completed.  (If no handlers are ready when
+    /// it is run, it will block until one is.)
+    void run_one();
+
+    /// \brief Stop the underlying event loop.
+    ///
+    /// This will return the control to the caller of the \c run() method.
+    void stop();
+
+    /// \brief Return the native \c io_service object used in this wrapper.
+    ///
+    /// This is a short term work around to support other BIND 10 modules
+    /// that share the same \c io_service with the authoritative server.
+    /// It will eventually be removed once the wrapper interface is
+    /// generalized.
+    asio::io_service& get_io_service();
+
+private:
+    IOServiceImpl* io_impl_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_IO_SERVICE_H

+ 1 - 1
src/lib/asiolink/iosocket.cc

@@ -14,7 +14,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-#include "iosocket.h"
+#include "io_socket.h"
 
 
 #include <asio.hpp>
 #include <asio.hpp>
 
 

src/lib/asiolink/iosocket.h → src/lib/asiolink/io_socket.h


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

@@ -0,0 +1,457 @@
+// 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 <stdlib.h>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <asiolink/recursive_query.h>
+#include <asiolink/dns_service.h>
+#include <asiolink/udp_query.h>
+
+#include <log/dummylog.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+
+#include <dns/question.h>
+#include <dns/message.h>
+
+#include <resolve/resolve.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)),
+    query_timeout_(query_timeout), client_timeout_(client_timeout),
+    lookup_timeout_(lookup_timeout), retries_(retries)
+{}
+
+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 UDPQuery::Callback {
+private:
+    // The io service to handle async calls
+    asio::io_service& 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_;
+
+    // Buffer to store the result.
+    OutputBufferPtr buffer_;
+
+    // Server to notify when we succeed or fail
+    //shared_ptr<DNSServer> server_;
+    isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
+
+    // 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_;
+
+    // (re)send the query to the server.
+    void send() {
+        const int uc = upstream_->size();
+        const int zs = zone_servers_.size();
+        buffer_->clear();
+        if (uc > 0) {
+            int serverIndex = rand() % uc;
+            dlog("Sending upstream query (" + question_.toText() +
+                ") to " + upstream_->at(serverIndex).first);
+            UDPQuery query(io_, question_,
+                upstream_->at(serverIndex).first,
+                upstream_->at(serverIndex).second, buffer_, this,
+                query_timeout_);
+            ++queries_out_;
+            io_.post(query);
+        } else if (zs > 0) {
+            int serverIndex = rand() % zs;
+            dlog("Sending query to zone server (" + question_.toText() +
+                ") to " + zone_servers_.at(serverIndex).first);
+            UDPQuery query(io_, question_,
+                zone_servers_.at(serverIndex).first,
+                zone_servers_.at(serverIndex).second, buffer_, this,
+                query_timeout_);
+            ++queries_out_;
+            io_.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_, true);
+
+        bool found_ns_address = false;
+
+        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());
+            send();
+            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;
+                    }
+                }
+            }
+            if (found_ns_address) {
+                // next resolver round
+                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::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:
+        case isc::resolve::ResponseClassifier::TRUNCATED:
+            // 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(asio::io_service& io,
+        const Question &question,
+        MessagePtr answer_message,
+        boost::shared_ptr<AddressVector> upstream,
+        boost::shared_ptr<AddressVector> upstream_root,
+        OutputBufferPtr buffer,
+        isc::resolve::ResolverInterface::CallbackPtr cb,
+        int query_timeout, int client_timeout, int lookup_timeout,
+        unsigned retries) :
+        io_(io),
+        question_(question),
+        answer_message_(answer_message),
+        upstream_(upstream),
+        upstream_root_(upstream_root),
+        buffer_(buffer),
+        resolvercallback_(cb),
+        cname_count_(0),
+        query_timeout_(query_timeout),
+        retries_(retries),
+        client_timer(io),
+        lookup_timer(io),
+        queries_out_(0),
+        done_(false),
+        answer_sent_(false)
+    {
+        // 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();
+        }
+
+        send();
+    }
+
+    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());
+        resolvercallback_->success(answer_message_);
+        answer_sent_ = true;
+    }
+
+    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_) {
+            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()(UDPQuery::Result result) {
+        // XXX is this the place for TCP retry?
+        --queries_out_;
+        if (!done_ && result != UDPQuery::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)
+{
+    asio::io_service& io = dns_service_.get_io_service();
+
+    MessagePtr answer_message(new Message(Message::RENDER));
+    OutputBufferPtr buffer(new OutputBuffer(0));
+    
+    // It will delete itself when it is done
+    new RunningQuery(io, *question, answer_message, upstream_,
+                     upstream_root_, buffer, callback, query_timeout_,
+                     client_timeout_, lookup_timeout_, retries_);
+}
+
+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.
+    asio::io_service& io = dns_service_.get_io_service();
+
+    isc::resolve::ResolverInterface::CallbackPtr crs(
+        new isc::resolve::ResolverCallbackServer(server));
+    
+    // It will delete itself when it is done
+    new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
+                         buffer, crs, query_timeout_, client_timeout_,
+                         lookup_timeout_, retries_);
+}
+
+
+
+} // namespace asiolink

+ 113 - 0
src/lib/asiolink/recursive_query.h

@@ -0,0 +1,113 @@
+// 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.
+
+#ifndef __ASIOLINK_RECURSIVE_QUERY_H
+#define __ASIOLINK_RECURSIVE_QUERY_H 1
+
+#include <asiolink/dns_service.h>
+#include <asiolink/dns_server.h>
+#include <dns/buffer.h>
+
+namespace asiolink {
+/// \brief The \c RecursiveQuery class provides a layer of abstraction around
+/// the ASIO code that carries out an upstream query.
+///
+/// This design is very preliminary; currently it is only capable of
+/// handling simple forward requests to a single resolver.
+class RecursiveQuery {
+    ///
+    /// \name Constructors
+    ///
+    //@{
+public:
+    /// \brief Constructor
+    ///
+    /// This is currently the only way to construct \c RecursiveQuery
+    /// object. If the addresses of the forward nameservers is specified,
+    /// and every upstream query will be sent to one random address, and
+    /// the result sent back directly. If not, it will do full resolving.
+    ///
+    /// \param dns_service The DNS Service to perform the recursive
+    ///        query on.
+    /// \param upstream Addresses and ports of the upstream servers
+    ///        to forward queries to.
+    /// \param upstream_root Addresses and ports of the root servers
+    ///        to use when resolving.
+    /// \param query_timeout Timeout value for queries we sent, in ms
+    /// \param client_timeout Timeout value for when we send back an
+    ///        error, in ms
+    /// \param lookup_timeout Timeout value for when we give up, in ms
+    /// \param retries how many times we try again (0 means just send and
+    ///     and return if it returs).
+    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 = 2000,
+                   int client_timeout = 4000,
+                   int lookup_timeout = 30000,
+                   unsigned retries = 3);
+    //@}
+
+    /// \brief Initiate resolving
+    /// 
+    /// When sendQuery() is called, a (set of) message(s) is sent
+    /// asynchronously. If upstream servers are set, one is chosen
+    /// and the response (if any) from that server will be returned.
+    ///
+    /// If not upstream is set, a root server is chosen from the
+    /// root_servers, and the RunningQuery shall do a full resolve
+    /// (i.e. if the answer is a delegation, it will be followed, etc.)
+    /// until there is an answer or an error.
+    ///
+    /// When there is a response or an error and we give up, the given
+    /// CallbackPtr object shall be called (with either success() or
+    /// failure(). See ResolverInterface::Callback for more information.
+    ///
+    /// \param question The question being answered <qname/qclass/qtype>
+    /// \param callback Callback object. See
+    ///        \c ResolverInterface::Callback for more information
+    void resolve(const isc::dns::QuestionPtr& question,
+                 const isc::resolve::ResolverInterface::CallbackPtr callback);
+
+
+    /// \brief Initiates resolving for the given question.
+    ///
+    /// This actually calls the previous sendQuery() with a default
+    /// callback object, which calls resume() on the given DNSServer
+    /// object.
+    ///
+    /// \param question The question being answered <qname/qclass/qtype>
+    /// \param answer_message An output Message into which the final response will be copied
+    /// \param buffer An output buffer into which the intermediate responses will be copied
+    /// \param server A pointer to the \c DNSServer object handling the client
+    void resolve(const isc::dns::Question& question,
+                 isc::dns::MessagePtr answer_message,
+                 isc::dns::OutputBufferPtr buffer,
+                 DNSServer* server);
+private:
+    DNSService& dns_service_;
+    boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
+        upstream_;
+    boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
+        upstream_root_;
+    int query_timeout_;
+    int client_timeout_;
+    int lookup_timeout_;
+    unsigned retries_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_RECURSIVE_QUERY_H

+ 71 - 0
src/lib/asiolink/simple_callback.h

@@ -0,0 +1,71 @@
+// 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.
+
+#ifndef __ASIOLINK_SIMPLE_CALLBACK_H
+#define __ASIOLINK_SIMPLE_CALLBACK_H 1
+
+#include <asiolink/io_message.h>
+
+namespace asiolink {
+
+/// \brief The \c SimpleCallback class is an abstract base class for a
+/// simple callback function with the signature:
+///
+/// void simpleCallback(const IOMessage& io_message) const;
+///
+/// Specific derived class implementations are hidden within the
+/// implementation.  Instances of the derived classes can be called
+/// as functions via the operator() interface.  Pointers to these
+/// instances can then be provided to the \c IOService class
+/// via its constructor.
+///
+/// The \c SimpleCallback is expected to be used for basic, generic
+/// tasks such as checking for configuration changes.  It may also be
+/// used for testing purposes.
+class SimpleCallback {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    SimpleCallback(const SimpleCallback& source);
+    SimpleCallback& operator=(const SimpleCallback& source);
+protected:
+    /// \brief The default constructor.
+    ///
+    /// This is intentionally defined as \c protected as this base class
+    /// should never be instantiated (except as part of a derived class).
+    SimpleCallback() : self_(this) {}
+public:
+    /// \brief The destructor
+    virtual ~SimpleCallback() {}
+    /// \brief The function operator
+    //@}
+    ///
+    /// This makes its call indirectly via the "self" pointer, ensuring
+    /// that the function ultimately invoked will be the one in the derived
+    /// class.
+    ///
+    /// \param io_message The event message to handle
+    virtual void operator()(const IOMessage& io_message) const {
+        (*self_)(io_message);
+    }
+private:
+    SimpleCallback* self_;
+};
+
+}      // namespace asiolink
+#endif // __ASIOLINK_SIMPLE_CALLBACK_H

+ 98 - 0
src/lib/asiolink/tcp_endpoint.h

@@ -0,0 +1,98 @@
+// 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.
+
+#ifndef __TCP_ENDPOINT_H
+#define __TCP_ENDPOINT_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/io_endpoint.h>
+
+namespace asiolink {
+
+/// \brief The \c TCPEndpoint class is a concrete derived class of
+/// \c IOEndpoint that represents an endpoint of a TCP connection.
+///
+/// In the current implementation, an object of this class is always
+/// instantiated within the wrapper routines.  Applications are expected to
+/// get access to the object via the abstract base class, \c IOEndpoint.
+/// This design may be changed when we generalize the wrapper interface.
+///
+/// Note: this implementation is optimized for the case where this object
+/// is created from an ASIO endpoint object in a receiving code path
+/// by avoiding to make a copy of the base endpoint.  For TCP it may not be
+/// a big deal, but when we receive UDP packets at a high rate, the copy
+/// overhead might be significant.
+class TCPEndpoint : public IOEndpoint {
+public:
+    ///
+    /// \name Constructors and Destructor
+    ///
+    //@{
+    /// \brief Constructor from a pair of address and port.
+    ///
+    /// \param address The IP address of the endpoint.
+    /// \param port The TCP port number of the endpoint.
+    TCPEndpoint(const IOAddress& address, const unsigned short port) :
+        asio_endpoint_placeholder_(
+            new asio::ip::tcp::endpoint(
+                asio::ip::address::from_string(address.toText()), port)),
+        asio_endpoint_(*asio_endpoint_placeholder_)
+    {}
+
+    /// \brief Constructor from an ASIO TCP endpoint.
+    ///
+    /// This constructor is designed to be an efficient wrapper for the
+    /// corresponding ASIO class, \c tcp::endpoint.
+    ///
+    /// \param asio_endpoint The ASIO representation of the TCP endpoint.
+    TCPEndpoint(const asio::ip::tcp::endpoint& asio_endpoint) :
+        asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
+    {}
+
+    /// \brief The destructor.
+    ~TCPEndpoint() { delete asio_endpoint_placeholder_; }
+    //@}
+
+    IOAddress getAddress() const {
+        return (asio_endpoint_.address());
+    }
+
+    uint16_t getPort() const {
+        return (asio_endpoint_.port());
+    }
+
+    short getProtocol() const {
+        return (asio_endpoint_.protocol().protocol());
+    }
+
+    short getFamily() const {
+        return (asio_endpoint_.protocol().family());
+    }
+
+    // This is not part of the exosed IOEndpoint API but allows
+    // direct access to the ASIO implementation of the endpoint
+    const asio::ip::tcp::endpoint& getASIOEndpoint() const {
+        return (asio_endpoint_);
+    }
+
+private:
+    const asio::ip::tcp::endpoint* asio_endpoint_placeholder_;
+    const asio::ip::tcp::endpoint& asio_endpoint_;
+};
+
+}      // namespace asiolink
+#endif // __TCP_ENDPOINT_H

+ 14 - 14
src/lib/asiolink/tcpdns.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  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
@@ -14,22 +14,20 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
-#include <unistd.h>             // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
+#include <boost/shared_array.hpp>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
 
 
 #include <asio.hpp>
 #include <asio.hpp>
-#include <asio/ip/address.hpp>
 
 
-#include <boost/array.hpp>
-#include <boost/shared_ptr.hpp>
+#include <log/dummylog.h>
+
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tcp_socket.h>
 
 
-#include <dns/buffer.h>
-#include <dns/message.h>
+#include <asiolink/tcp_server.h>
 
 
-#include <asiolink.h>
-#include <internal/coroutine.h>
-#include <internal/tcpdns.h>
 
 
 using namespace asio;
 using namespace asio;
 using asio::ip::udp;
 using asio::ip::udp;
@@ -39,7 +37,8 @@ using namespace std;
 using namespace isc::dns;
 using namespace isc::dns;
 
 
 namespace asiolink {
 namespace asiolink {
-/// The following functions implement the \c UDPServer class.
+
+/// The following functions implement the \c TCPServer class.
 ///
 ///
 /// The constructor
 /// The constructor
 TCPServer::TCPServer(io_service& io_service,
 TCPServer::TCPServer(io_service& io_service,
@@ -191,4 +190,5 @@ TCPServer::resume(const bool done) {
     io_.post(*this);
     io_.post(*this);
 }
 }
 
 
-}
+} // namespace asiolink
+

+ 9 - 114
src/lib/asiolink/internal/tcpdns.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  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
@@ -12,121 +12,21 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-#ifndef __TCPDNS_H
-#define __TCPDNS_H 1
+#ifndef __TCP_SERVER_H
+#define __TCP_SERVER_H 1
 
 
-#include <config.h>
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
 
 
-
-#include <asio.hpp>
 #include <boost/shared_array.hpp>
 #include <boost/shared_array.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 
 
-#include <dns/buffer.h>
-#include <dns/message.h>
-
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
-#include <asiolink/internal/coroutine.h>
+#include <coroutine.h>
 
 
-// This file contains TCP-specific implementations of generic classes 
-// defined in asiolink.h.  It is *not* intended to be part of the public
-// API.
 
 
 namespace asiolink {
 namespace asiolink {
-/// \brief The \c TCPEndpoint class is a concrete derived class of
-/// \c IOEndpoint that represents an endpoint of a TCP connection.
-///
-/// In the current implementation, an object of this class is always
-/// instantiated within the wrapper routines.  Applications are expected to
-/// get access to the object via the abstract base class, \c IOEndpoint.
-/// This design may be changed when we generalize the wrapper interface.
-///
-/// Note: this implementation is optimized for the case where this object
-/// is created from an ASIO endpoint object in a receiving code path
-/// by avoiding to make a copy of the base endpoint.  For TCP it may not be
-/// a big deal, but when we receive UDP packets at a high rate, the copy
-/// overhead might be significant.
-class TCPEndpoint : public IOEndpoint {
-public:
-    ///
-    /// \name Constructors and Destructor
-    ///
-    //@{
-    /// \brief Constructor from a pair of address and port.
-    ///
-    /// \param address The IP address of the endpoint.
-    /// \param port The TCP port number of the endpoint.
-    TCPEndpoint(const IOAddress& address, const unsigned short port) :
-        asio_endpoint_placeholder_(
-            new asio::ip::tcp::endpoint(
-                asio::ip::address::from_string(address.toText()), port)),
-        asio_endpoint_(*asio_endpoint_placeholder_)
-    {}
-
-    /// \brief Constructor from an ASIO TCP endpoint.
-    ///
-    /// This constructor is designed to be an efficient wrapper for the
-    /// corresponding ASIO class, \c tcp::endpoint.
-    ///
-    /// \param asio_endpoint The ASIO representation of the TCP endpoint.
-    TCPEndpoint(const asio::ip::tcp::endpoint& asio_endpoint) :
-        asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
-    {}
-
-    /// \brief The destructor.
-    ~TCPEndpoint() { delete asio_endpoint_placeholder_; }
-    //@}
-
-    IOAddress getAddress() const {
-        return (asio_endpoint_.address());
-    }
-
-    uint16_t getPort() const {
-        return (asio_endpoint_.port());
-    }
-
-    short getProtocol() const {
-        return (asio_endpoint_.protocol().protocol());
-    }
-
-    short getFamily() const {
-        return (asio_endpoint_.protocol().family());
-    }
-
-    // This is not part of the exosed IOEndpoint API but allows
-    // direct access to the ASIO implementation of the endpoint
-    const asio::ip::tcp::endpoint& getASIOEndpoint() const {
-        return (asio_endpoint_);
-    }
-
-private:
-    const asio::ip::tcp::endpoint* asio_endpoint_placeholder_;
-    const asio::ip::tcp::endpoint& asio_endpoint_;
-};
-
-/// \brief The \c TCPSocket class is a concrete derived class of
-/// \c IOSocket that represents a TCP socket.
-///
-/// In the current implementation, an object of this class is always
-/// instantiated within the wrapper routines.  Applications are expected to
-/// get access to the object via the abstract base class, \c IOSocket.
-/// This design may be changed when we generalize the wrapper interface.
-class TCPSocket : public IOSocket {
-private:
-    TCPSocket(const TCPSocket& source);
-    TCPSocket& operator=(const TCPSocket& source);
-public:
-    /// \brief Constructor from an ASIO TCP socket.
-    ///
-    /// \param socket The ASIO representation of the TCP socket.
-    TCPSocket(asio::ip::tcp::socket& socket) : socket_(socket) {}
-
-    int getNative() const { return (socket_.native()); }
-    int getProtocol() const { return (IPPROTO_TCP); }
-
-private:
-    asio::ip::tcp::socket& socket_;
-};
 
 
 /// \brief A TCP-specific \c DNSServer object.
 /// \brief A TCP-specific \c DNSServer object.
 ///
 ///
@@ -215,10 +115,5 @@ private:
     boost::shared_ptr<IOSocket> iosock_;
     boost::shared_ptr<IOSocket> iosock_;
 };
 };
 
 
-}
-
-#endif // __TCPDNS_H
-
-// Local Variables: 
-// mode: c++
-// End: 
+}      // namespace asiolink
+#endif // __TCP_SERVER_H

+ 52 - 0
src/lib/asiolink/tcp_socket.h

@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef __TCP_SOCKET_H
+#define __TCP_SOCKET_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/io_socket.h>
+
+namespace asiolink {
+
+/// \brief The \c TCPSocket class is a concrete derived class of
+/// \c IOSocket that represents a TCP socket.
+///
+/// In the current implementation, an object of this class is always
+/// instantiated within the wrapper routines.  Applications are expected to
+/// get access to the object via the abstract base class, \c IOSocket.
+/// This design may be changed when we generalize the wrapper interface.
+class TCPSocket : public IOSocket {
+private:
+    TCPSocket(const TCPSocket& source);
+    TCPSocket& operator=(const TCPSocket& source);
+public:
+    /// \brief Constructor from an ASIO TCP socket.
+    ///
+    /// \param socket The ASIO representation of the TCP socket.
+    TCPSocket(asio::ip::tcp::socket& socket) : socket_(socket) {}
+
+    int getNative() const { return (socket_.native()); }
+    int getProtocol() const { return (IPPROTO_TCP); }
+
+private:
+    asio::ip::tcp::socket& socket_;
+};
+
+
+}      // namespace asiolink
+#endif // __TCP_SOCKET_H

+ 11 - 1
src/lib/asiolink/tests/Makefile.am

@@ -17,7 +17,13 @@ if HAVE_GTEST
 TESTS += run_unittests
 TESTS += run_unittests
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
-run_unittests_SOURCES += asiolink_unittest.cc
+run_unittests_SOURCES += udp_query_unittest.cc
+run_unittests_SOURCES += ioaddress_unittest.cc
+run_unittests_SOURCES += ioendpoint_unittest.cc
+run_unittests_SOURCES += iosocket_unittest.cc
+run_unittests_SOURCES += io_service_unittest.cc
+run_unittests_SOURCES += interval_timer_unittest.cc
+run_unittests_SOURCES += recursive_query_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(LOG4CXX_LDFLAGS)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(LOG4CXX_LDFLAGS)
@@ -33,6 +39,10 @@ run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_GXX
 if USE_GXX
 run_unittests_CXXFLAGS += -Wno-unused-parameter
 run_unittests_CXXFLAGS += -Wno-unused-parameter
 endif
 endif
+if USE_CLANGPP
+# Same for clang++, but we need to turn off -Werror completely.
+run_unittests_CXXFLAGS += -Wno-error
+endif
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 noinst_PROGRAMS = $(TESTS)

+ 292 - 0
src/lib/asiolink/tests/interval_timer_unittest.cc

@@ -0,0 +1,292 @@
+// 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 <asiolink/asiolink.h>
+
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+namespace {
+// TODO: Consider this margin
+const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
+    boost::posix_time::milliseconds(50);
+}
+
+using namespace asiolink;
+
+// This fixture is for testing IntervalTimer. Some callback functors are 
+// registered as callback function of the timer to test if they are called
+// or not.
+class IntervalTimerTest : public ::testing::Test {
+protected:
+    IntervalTimerTest() : io_service_() {}
+    ~IntervalTimerTest() {}
+    class TimerCallBack : public std::unary_function<void, void> {
+    public:
+        TimerCallBack(IntervalTimerTest* test_obj) : test_obj_(test_obj) {}
+        void operator()() const {
+            test_obj_->timer_called_ = true;
+            test_obj_->io_service_.stop();
+            return;
+        }
+    private:
+        IntervalTimerTest* test_obj_;
+    };
+    class TimerCallBackCounter : public std::unary_function<void, void> {
+    public:
+        TimerCallBackCounter(IntervalTimerTest* test_obj) : test_obj_(test_obj) {
+            counter_ = 0;
+        }
+        void operator()() {
+            ++counter_;
+            return;
+        }
+        int counter_;
+    private:
+        IntervalTimerTest* test_obj_;
+    };
+    class TimerCallBackCancelDeleter : public std::unary_function<void, void> {
+    public:
+        TimerCallBackCancelDeleter(IntervalTimerTest* test_obj,
+                                   IntervalTimer* timer,
+                                   TimerCallBackCounter& counter)
+            : test_obj_(test_obj), timer_(timer), counter_(counter), count_(0)
+        {}
+        void operator()() {
+            ++count_;
+            if (count_ == 1) {
+                // First time of call back.
+                // Store the value of counter_.counter_.
+                prev_counter_ = counter_.counter_;
+                delete timer_;
+            } else if (count_ == 2) {
+                // Second time of call back.
+                // Stop io_service to stop all timers.
+                test_obj_->io_service_.stop();
+                // Compare the value of counter_.counter_ with stored one.
+                // If TimerCallBackCounter was not called (expected behavior),
+                // they are same.
+                if (counter_.counter_ == prev_counter_) {
+                    test_obj_->timer_cancel_success_ = true;
+                }
+            }
+            return;
+        }
+    private:
+        IntervalTimerTest* test_obj_;
+        IntervalTimer* timer_;
+        TimerCallBackCounter& counter_;
+        int count_;
+        int prev_counter_;
+    };
+    class TimerCallBackCanceller {
+    public:
+        TimerCallBackCanceller(unsigned int& counter, IntervalTimer& itimer) :
+            counter_(counter), itimer_(itimer)
+        {}
+        void operator()() {
+            ++counter_;
+            itimer_.cancel();
+        }
+    private:
+        unsigned int& counter_;
+        IntervalTimer& itimer_;
+    };
+    class TimerCallBackOverwriter : public std::unary_function<void, void> {
+    public:
+        TimerCallBackOverwriter(IntervalTimerTest* test_obj,
+                                IntervalTimer& timer)
+            : test_obj_(test_obj), timer_(timer), count_(0)
+        {}
+        void operator()() {
+            ++count_;
+            if (count_ == 1) {
+                // First time of call back.
+                // Call setup() to update callback function to TimerCallBack.
+                test_obj_->timer_called_ = false;
+                timer_.setup(TimerCallBack(test_obj_), 100);
+            } else if (count_ == 2) {
+                // Second time of call back.
+                // If it reaches here, re-setup() is failed (unexpected).
+                // We should stop here.
+                test_obj_->io_service_.stop();
+            }
+            return;
+        }
+    private:
+        IntervalTimerTest* test_obj_;
+        IntervalTimer& timer_;
+        int count_;
+    };
+protected:
+    IOService io_service_;
+    bool timer_called_;
+    bool timer_cancel_success_;
+};
+
+TEST_F(IntervalTimerTest, invalidArgumentToIntervalTimer) {
+    // Create asio_link::IntervalTimer and setup.
+    IntervalTimer itimer(io_service_);
+    // expect throw if call back function is empty
+    EXPECT_THROW(itimer.setup(IntervalTimer::Callback(), 1),
+                 isc::InvalidParameter);
+    // expect throw if interval is not greater than 0
+    EXPECT_THROW(itimer.setup(TimerCallBack(this), 0), isc::BadValue);
+    EXPECT_THROW(itimer.setup(TimerCallBack(this), -1), isc::BadValue);
+}
+
+TEST_F(IntervalTimerTest, startIntervalTimer) {
+    // Create asio_link::IntervalTimer and setup.
+    // Then run IOService and test if the callback function is called.
+    IntervalTimer itimer(io_service_);
+    timer_called_ = false;
+    // store start time
+    boost::posix_time::ptime start;
+    start = boost::posix_time::microsec_clock::universal_time();
+    // setup timer
+    itimer.setup(TimerCallBack(this), 100);
+    EXPECT_EQ(100, itimer.getInterval());
+    io_service_.run();
+    // reaches here after timer expired
+    // delta: difference between elapsed time and 100 milliseconds.
+    boost::posix_time::time_duration delta =
+        (boost::posix_time::microsec_clock::universal_time() - start)
+         - boost::posix_time::millisec(100);
+    if (delta.is_negative()) {
+        delta.invert_sign();
+    }
+    // expect TimerCallBack is called; timer_called_ is true
+    EXPECT_TRUE(timer_called_);
+    // expect interval is 100 milliseconds +/- TIMER_MARGIN_MSEC.
+    EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
+}
+
+TEST_F(IntervalTimerTest, destructIntervalTimer) {
+    // This code isn't exception safe, but we'd rather keep the code
+    // simpler and more readable as this is only for tests and if it throws
+    // the program would immediately terminate anyway.
+
+    // The call back function will not be called after the timer is
+    // destroyed.
+    //
+    // There are two timers:
+    //  itimer_counter (A)
+    //   (Calls TimerCallBackCounter)
+    //     - increments internal counter in callback function
+    //  itimer_canceller (B)
+    //   (Calls TimerCallBackCancelDeleter)
+    //     - first time of callback, it stores the counter value of
+    //       callback_canceller and destroys itimer_counter
+    //     - second time of callback, it compares the counter value of
+    //       callback_canceller with stored value
+    //       if they are same the timer was not called; expected result
+    //       if they are different the timer was called after destroyed
+    //
+    //     0  100  200  300  400  500  600 (ms)
+    // (A) i--------+----x
+    //                   ^
+    //                   |destroy itimer_counter
+    // (B) i-------------+--------------s
+    //                                  ^stop io_service
+    //                                   and check if itimer_counter have been
+    //                                   stopped
+
+    // itimer_counter will be deleted in TimerCallBackCancelDeleter
+    IntervalTimer* itimer_counter = new IntervalTimer(io_service_);
+    IntervalTimer itimer_canceller(io_service_);
+    timer_cancel_success_ = false;
+    TimerCallBackCounter callback_canceller(this);
+    itimer_counter->setup(callback_canceller, 200);
+    itimer_canceller.setup(
+        TimerCallBackCancelDeleter(this, itimer_counter, callback_canceller),
+        300);
+    io_service_.run();
+    EXPECT_TRUE(timer_cancel_success_);
+}
+
+TEST_F(IntervalTimerTest, cancel) {
+    // Similar to destructIntervalTimer test, but the first timer explicitly
+    // cancels itself on first callback.
+    IntervalTimer itimer_counter(io_service_);
+    IntervalTimer itimer_watcher(io_service_);
+    unsigned int counter = 0;
+    itimer_counter.setup(TimerCallBackCanceller(counter, itimer_counter), 100);
+    itimer_watcher.setup(TimerCallBack(this), 200);
+    io_service_.run();
+    EXPECT_EQ(1, counter);
+    EXPECT_EQ(0, itimer_counter.getInterval());
+
+    // canceling an already canceled timer shouldn't cause any surprise.
+    EXPECT_NO_THROW(itimer_counter.cancel());
+}
+
+TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
+    // Calling setup() multiple times updates call back function and interval.
+    //
+    // There are two timers:
+    //  itimer (A)
+    //   (Calls TimerCallBackCounter / TimerCallBack)
+    //     - increments internal counter in callback function
+    //       (TimerCallBackCounter)
+    //       interval: 300 milliseconds
+    //     - io_service_.stop() (TimerCallBack)
+    //       interval: 100 milliseconds
+    //  itimer_overwriter (B)
+    //   (Calls TimerCallBackOverwriter)
+    //     - first time of callback, it calls setup() to change call back
+    //       function to TimerCallBack and interval of itimer to 100
+    //       milliseconds
+    //       after 300 + 100 milliseconds from the beginning of this test,
+    //       TimerCallBack() will be called and io_service_ stops.
+    //     - second time of callback, it means the test fails.
+    //
+    //     0  100  200  300  400  500  600  700  800 (ms)
+    // (A) i-------------+----C----s
+    //                        ^    ^stop io_service
+    //                        |change call back function
+    // (B) i------------------+-------------------S
+    //                                            ^(stop io_service on fail)
+    //
+
+    IntervalTimer itimer(io_service_);
+    IntervalTimer itimer_overwriter(io_service_);
+    // store start time
+    boost::posix_time::ptime start;
+    start = boost::posix_time::microsec_clock::universal_time();
+    itimer.setup(TimerCallBackCounter(this), 300);
+    itimer_overwriter.setup(TimerCallBackOverwriter(this, itimer), 400);
+    io_service_.run();
+    // reaches here after timer expired
+    // if interval is updated, it takes
+    //   400 milliseconds for TimerCallBackOverwriter
+    //   + 100 milliseconds for TimerCallBack (stop)
+    //   = 500 milliseconds.
+    // otherwise (test fails), it takes
+    //   400 milliseconds for TimerCallBackOverwriter
+    //   + 400 milliseconds for TimerCallBackOverwriter (stop)
+    //   = 800 milliseconds.
+    // delta: difference between elapsed time and 400 + 100 milliseconds
+    boost::posix_time::time_duration delta =
+        (boost::posix_time::microsec_clock::universal_time() - start)
+         - boost::posix_time::millisec(400 + 100);
+    if (delta.is_negative()) {
+        delta.invert_sign();
+    }
+    // expect callback function is updated: TimerCallBack is called
+    EXPECT_TRUE(timer_called_);
+    // expect interval is updated
+    EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
+}

+ 115 - 0
src/lib/asiolink/tests/io_service_unittest.cc

@@ -0,0 +1,115 @@
+// 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 <asiolink/asiolink.h>
+
+using namespace asiolink;
+
+const char* const TEST_SERVER_PORT = "53535";
+const char* const TEST_CLIENT_PORT = "53536";
+const char* const TEST_IPV6_ADDR = "::1";
+const char* const TEST_IPV4_ADDR = "127.0.0.1";
+
+TEST(IOServiceTest, badPort) {
+    IOService io_service;
+    EXPECT_THROW(DNSService(io_service, *"65536", true, false, NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *"5300.0", true, false, NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *"-1", true, false, NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *"domain", true, false, NULL, NULL, NULL), IOError);
+}
+
+TEST(IOServiceTest, badAddress) {
+    IOService io_service;
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.1.1", NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"2001:db8:::1", NULL, NULL, NULL), IOError);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"localhost", NULL, NULL, NULL), IOError);
+}
+
+TEST(IOServiceTest, unavailableAddress) {
+    IOService io_service;
+    // These addresses should generally be unavailable as a valid local
+    // address, although there's no guarantee in theory.
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.0", NULL, NULL, NULL), IOError);
+
+    // Some OSes would simply reject binding attempt for an AF_INET6 socket
+    // to an IPv4-mapped IPv6 address.  Even if those that allow it, since
+    // the corresponding IPv4 address is the same as the one used in the
+    // AF_INET socket case above, it should at least show the same result
+    // as the previous one.
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:192.0.2.0", NULL, NULL, NULL), IOError);
+}
+
+TEST(IOServiceTest, duplicateBind_v6) {
+    // In each sub test case, second attempt should fail due to duplicate bind
+    IOService io_service;
+
+    // IPv6, "any" address
+    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL), IOError);
+    delete dns_service;
+
+}
+
+TEST(IOServiceTest, duplicateBind_v6_address) {
+    // In each sub test case, second attempt should fail due to duplicate bind
+    IOService io_service;
+
+    // IPv6, specific address
+    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL), IOError);
+    delete dns_service;
+
+}
+
+TEST(IOServiceTest, duplicateBind_v4) {
+    // In each sub test case, second attempt should fail due to duplicate bind
+    IOService io_service;
+
+    // IPv4, "any" address
+    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL), IOError);
+    delete dns_service;
+
+}
+
+TEST(IOServiceTest, duplicateBind_v4_address) {
+    // In each sub test case, second attempt should fail due to duplicate bind
+    IOService io_service;
+
+    // IPv4, specific address
+    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL), IOError);
+    delete dns_service;
+}
+
+// Disabled because IPv4-mapped addresses don't seem to be working with
+// the IOService constructor
+TEST(IOServiceTest, DISABLED_IPv4MappedDuplicateBind) {
+    IOService io_service;
+    // Duplicate bind on IPv4-mapped IPv6 address
+    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL), IOError);
+    delete dns_service;
+
+    // XXX:
+    // Currently, this throws an "invalid argument" exception.  I have
+    // not been able to get IPv4-mapped addresses to work.
+    dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL);
+    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL), IOError);
+    delete dns_service;
+}
+

+ 57 - 0
src/lib/asiolink/tests/ioaddress_unittest.cc

@@ -0,0 +1,57 @@
+// 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 <asiolink/asiolink.h>
+
+using namespace asiolink;
+
+TEST(IOAddressTest, fromText) {
+    IOAddress io_address_v4("192.0.2.1");
+    EXPECT_EQ("192.0.2.1", io_address_v4.toText());
+
+    IOAddress io_address_v6("2001:db8::1234");
+    EXPECT_EQ("2001:db8::1234", io_address_v6.toText());
+
+    // bogus IPv4 address-like input
+    EXPECT_THROW(IOAddress("192.0.2.2.1"), IOError);
+
+    // bogus IPv4 address-like input: out-of-range octet
+    EXPECT_THROW(IOAddress("192.0.2.300"), IOError);
+
+    // bogus IPv6 address-like input
+    EXPECT_THROW(IOAddress("2001:db8:::1234"), IOError);
+
+    // bogus IPv6 address-like input
+    EXPECT_THROW(IOAddress("2001:db8::efgh"), IOError);
+}
+
+TEST(IOAddressTest, Equality) {
+    EXPECT_TRUE(IOAddress("192.0.2.1") == IOAddress("192.0.2.1"));
+    EXPECT_FALSE(IOAddress("192.0.2.1") != IOAddress("192.0.2.1"));
+
+    EXPECT_TRUE(IOAddress("192.0.2.1") != IOAddress("192.0.2.2"));
+    EXPECT_FALSE(IOAddress("192.0.2.1") == IOAddress("192.0.2.2"));
+
+    EXPECT_TRUE(IOAddress("2001:db8::12") == IOAddress("2001:0DB8:0:0::0012"));
+    EXPECT_FALSE(IOAddress("2001:db8::12") != IOAddress("2001:0DB8:0:0::0012"));
+
+    EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("2001:db8::1235"));
+    EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("2001:db8::1235"));
+
+    EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("192.0.2.3"));
+    EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("192.0.2.3"));
+}

+ 67 - 0
src/lib/asiolink/tests/ioendpoint_unittest.cc

@@ -0,0 +1,67 @@
+// 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 <asiolink/asiolink.h>
+
+using namespace asiolink;
+
+TEST(IOEndpointTest, createUDPv4) {
+    const IOEndpoint* ep;
+    ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"), 5300);
+    EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
+    EXPECT_EQ(5300, ep->getPort());
+    EXPECT_EQ(AF_INET, ep->getFamily());
+    EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
+    EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
+}
+
+TEST(IOEndpointTest, createTCPv4) {
+    const IOEndpoint* ep;
+    ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"), 5301);
+    EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
+    EXPECT_EQ(5301, ep->getPort());
+    EXPECT_EQ(AF_INET, ep->getFamily());
+    EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
+    EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
+}
+
+TEST(IOEndpointTest, createUDPv6) {
+    const IOEndpoint* ep;
+    ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1234"), 5302);
+    EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
+    EXPECT_EQ(5302, ep->getPort());
+    EXPECT_EQ(AF_INET6, ep->getFamily());
+    EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
+    EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
+}
+
+TEST(IOEndpointTest, createTCPv6) {
+    const IOEndpoint* ep;
+    ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1234"), 5303);
+    EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
+    EXPECT_EQ(5303, ep->getPort());
+    EXPECT_EQ(AF_INET6, ep->getFamily());
+    EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
+    EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
+}
+
+TEST(IOEndpointTest, createIPProto) {
+    EXPECT_THROW(IOEndpoint::create(IPPROTO_IP, IOAddress("192.0.2.1"),
+                                    5300)->getAddress().toText(),
+                 IOError);
+}
+

+ 29 - 0
src/lib/asiolink/tests/iosocket_unittest.cc

@@ -0,0 +1,29 @@
+// 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 <asiolink/asiolink.h>
+
+using namespace asiolink;
+
+TEST(IOSocketTest, dummySockets) {
+    EXPECT_EQ(IPPROTO_UDP, IOSocket::getDummyUDPSocket().getProtocol());
+    EXPECT_EQ(IPPROTO_TCP, IOSocket::getDummyTCPSocket().getProtocol());
+    EXPECT_EQ(-1, IOSocket::getDummyUDPSocket().getNative());
+    EXPECT_EQ(-1, IOSocket::getDummyTCPSocket().getNative());
+}
+
+

+ 75 - 501
src/lib/asiolink/tests/asiolink_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  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
@@ -42,7 +42,7 @@
 // If we need to test something at the level of underlying ASIO and need
 // If we need to test something at the level of underlying ASIO and need
 // their definition, that test should go to asiolink/internal/tests.
 // their definition, that test should go to asiolink/internal/tests.
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
-#include <asiolink/iosocket.h>
+#include <asiolink/io_socket.h>
 
 
 using isc::UnitTestUtil;
 using isc::UnitTestUtil;
 using namespace std;
 using namespace std;
@@ -58,188 +58,6 @@ const char* const TEST_IPV4_ADDR = "127.0.0.1";
 // two octets encode the length of the rest of the data.  This is crucial
 // two octets encode the length of the rest of the data.  This is crucial
 // for the tests below.
 // for the tests below.
 const uint8_t test_data[] = {0, 4, 1, 2, 3, 4};
 const uint8_t test_data[] = {0, 4, 1, 2, 3, 4};
-// TODO: Consider this margin
-const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
-    boost::posix_time::milliseconds(50);
-
-TEST(IOAddressTest, fromText) {
-    IOAddress io_address_v4("192.0.2.1");
-    EXPECT_EQ("192.0.2.1", io_address_v4.toText());
-
-    IOAddress io_address_v6("2001:db8::1234");
-    EXPECT_EQ("2001:db8::1234", io_address_v6.toText());
-
-    // bogus IPv4 address-like input
-    EXPECT_THROW(IOAddress("192.0.2.2.1"), IOError);
-
-    // bogus IPv4 address-like input: out-of-range octet
-    EXPECT_THROW(IOAddress("192.0.2.300"), IOError);
-
-    // bogus IPv6 address-like input
-    EXPECT_THROW(IOAddress("2001:db8:::1234"), IOError);
-
-    // bogus IPv6 address-like input
-    EXPECT_THROW(IOAddress("2001:db8::efgh"), IOError);
-}
-
-TEST(IOAddressTest, Equality) {
-    EXPECT_TRUE(IOAddress("192.0.2.1") == IOAddress("192.0.2.1"));
-    EXPECT_FALSE(IOAddress("192.0.2.1") != IOAddress("192.0.2.1"));
-
-    EXPECT_TRUE(IOAddress("192.0.2.1") != IOAddress("192.0.2.2"));
-    EXPECT_FALSE(IOAddress("192.0.2.1") == IOAddress("192.0.2.2"));
-
-    EXPECT_TRUE(IOAddress("2001:db8::12") == IOAddress("2001:0DB8:0:0::0012"));
-    EXPECT_FALSE(IOAddress("2001:db8::12") != IOAddress("2001:0DB8:0:0::0012"));
-
-    EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("2001:db8::1235"));
-    EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("2001:db8::1235"));
-
-    EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("192.0.2.3"));
-    EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("192.0.2.3"));
-}
-
-TEST(IOEndpointTest, createUDPv4) {
-    const IOEndpoint* ep;
-    ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("192.0.2.1"), 5300);
-    EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
-    EXPECT_EQ(5300, ep->getPort());
-    EXPECT_EQ(AF_INET, ep->getFamily());
-    EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
-}
-
-TEST(IOEndpointTest, createTCPv4) {
-    const IOEndpoint* ep;
-    ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("192.0.2.1"), 5301);
-    EXPECT_EQ("192.0.2.1", ep->getAddress().toText());
-    EXPECT_EQ(5301, ep->getPort());
-    EXPECT_EQ(AF_INET, ep->getFamily());
-    EXPECT_EQ(AF_INET, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
-}
-
-TEST(IOEndpointTest, createUDPv6) {
-    const IOEndpoint* ep;
-    ep = IOEndpoint::create(IPPROTO_UDP, IOAddress("2001:db8::1234"), 5302);
-    EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
-    EXPECT_EQ(5302, ep->getPort());
-    EXPECT_EQ(AF_INET6, ep->getFamily());
-    EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_UDP, ep->getProtocol());
-}
-
-TEST(IOEndpointTest, createTCPv6) {
-    const IOEndpoint* ep;
-    ep = IOEndpoint::create(IPPROTO_TCP, IOAddress("2001:db8::1234"), 5303);
-    EXPECT_EQ("2001:db8::1234", ep->getAddress().toText());
-    EXPECT_EQ(5303, ep->getPort());
-    EXPECT_EQ(AF_INET6, ep->getFamily());
-    EXPECT_EQ(AF_INET6, ep->getAddress().getFamily());
-    EXPECT_EQ(IPPROTO_TCP, ep->getProtocol());
-}
-
-TEST(IOEndpointTest, createIPProto) {
-    EXPECT_THROW(IOEndpoint::create(IPPROTO_IP, IOAddress("192.0.2.1"),
-                                    5300)->getAddress().toText(),
-                 IOError);
-}
-
-TEST(IOSocketTest, dummySockets) {
-    EXPECT_EQ(IPPROTO_UDP, IOSocket::getDummyUDPSocket().getProtocol());
-    EXPECT_EQ(IPPROTO_TCP, IOSocket::getDummyTCPSocket().getProtocol());
-    EXPECT_EQ(-1, IOSocket::getDummyUDPSocket().getNative());
-    EXPECT_EQ(-1, IOSocket::getDummyTCPSocket().getNative());
-}
-
-TEST(IOServiceTest, badPort) {
-    IOService io_service;
-    EXPECT_THROW(DNSService(io_service, *"65536", true, false, NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *"5300.0", true, false, NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *"-1", true, false, NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *"domain", true, false, NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, badAddress) {
-    IOService io_service;
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.1.1", NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"2001:db8:::1", NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"localhost", NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, unavailableAddress) {
-    IOService io_service;
-    // These addresses should generally be unavailable as a valid local
-    // address, although there's no guarantee in theory.
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.0", NULL, NULL, NULL), IOError);
-
-    // Some OSes would simply reject binding attempt for an AF_INET6 socket
-    // to an IPv4-mapped IPv6 address.  Even if those that allow it, since
-    // the corresponding IPv4 address is the same as the one used in the
-    // AF_INET socket case above, it should at least show the same result
-    // as the previous one.
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:192.0.2.0", NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, duplicateBind_v6) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv6, "any" address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v6_address) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv6, specific address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v4) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv4, "any" address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v4_address) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv4, specific address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL), IOError);
-    delete dns_service;
-}
-
-// Disabled because IPv4-mapped addresses don't seem to be working with
-// the IOService constructor
-TEST(IOServiceTest, DISABLED_IPv4MappedDuplicateBind) {
-    IOService io_service;
-    // Duplicate bind on IPv4-mapped IPv6 address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-    // XXX:
-    // Currently, this throws an "invalid argument" exception.  I have
-    // not been able to get IPv4-mapped addresses to work.
-    dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL), IOError);
-    delete dns_service;
-}
 
 
 // This function returns an addrinfo structure for use by tests, using
 // This function returns an addrinfo structure for use by tests, using
 // different addresses and ports depending on whether we're testing
 // different addresses and ports depending on whether we're testing
@@ -276,12 +94,12 @@ resolveAddress(const int family, const int protocol, const bool client) {
 // expected parameters.
 // expected parameters.
 // If initialization parameters of the IOService should be modified, the test
 // If initialization parameters of the IOService should be modified, the test
 // case can do it using the setDNSService() method.
 // case can do it using the setDNSService() method.
-// Note: the set of tests in ASIOLinkTest use actual network services and may
+// Note: the set of tests in RecursiveQueryTest use actual network services and may
 // involve undesirable side effects such as blocking.
 // involve undesirable side effects such as blocking.
-class ASIOLinkTest : public ::testing::Test {
+class RecursiveQueryTest : public ::testing::Test {
 protected:
 protected:
-    ASIOLinkTest();
-    ~ASIOLinkTest() {
+    RecursiveQueryTest();
+    ~RecursiveQueryTest() {
         if (res_ != NULL) {
         if (res_ != NULL) {
             freeaddrinfo(res_);
             freeaddrinfo(res_);
         }
         }
@@ -520,15 +338,48 @@ protected:
             bool* done_;
             bool* done_;
     };
     };
 
 
+    // This version of mock server just stops the io_service when it is resumed
+    // the second time. (Used in the clientTimeout test, where resume
+    // is called initially with the error answer, and later when the
+    // lookup times out, it is called without an answer to send back)
+    class MockServerStop2 : public MockServer {
+        public:
+            explicit MockServerStop2(IOService& io_service,
+                                     bool* done1, bool* done2) :
+                MockServer(io_service),
+                done1_(done1),
+                done2_(done2),
+                stopped_once_(false)
+            {}
+
+            void resume(const bool done) {
+                if (stopped_once_) {
+                    *done2_ = done;
+                    io_.stop();
+                } else {
+                    *done1_ = done;
+                    stopped_once_ = true;
+                }
+            }
+
+            DNSServer* clone() {
+                return (new MockServerStop2(*this));
+            }
+        private:
+            bool* done1_;
+            bool* done2_;
+            bool stopped_once_;
+    };
+
 private:
 private:
     class ASIOCallBack : public SimpleCallback {
     class ASIOCallBack : public SimpleCallback {
     public:
     public:
-        ASIOCallBack(ASIOLinkTest* test_obj) : test_obj_(test_obj) {}
+        ASIOCallBack(RecursiveQueryTest* test_obj) : test_obj_(test_obj) {}
         void operator()(const IOMessage& io_message) const {
         void operator()(const IOMessage& io_message) const {
             test_obj_->callBack(io_message);
             test_obj_->callBack(io_message);
         }
         }
     private:
     private:
-        ASIOLinkTest* test_obj_;
+        RecursiveQueryTest* test_obj_;
     };
     };
     void callBack(const IOMessage& io_message) {
     void callBack(const IOMessage& io_message) {
         callback_protocol_ = io_message.getSocket().getProtocol();
         callback_protocol_ = io_message.getSocket().getProtocol();
@@ -555,30 +406,30 @@ protected:
     struct addrinfo* res_;
     struct addrinfo* res_;
 };
 };
 
 
-ASIOLinkTest::ASIOLinkTest() :
+RecursiveQueryTest::RecursiveQueryTest() :
     dns_service_(NULL), callback_(NULL), sock_(-1), res_(NULL)
     dns_service_(NULL), callback_(NULL), sock_(-1), res_(NULL)
 {
 {
     io_service_ = new IOService();
     io_service_ = new IOService();
     setDNSService(true, true);
     setDNSService(true, true);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v6UDPSend) {
+TEST_F(RecursiveQueryTest, v6UDPSend) {
     doTest(AF_INET6, IPPROTO_UDP);
     doTest(AF_INET6, IPPROTO_UDP);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v6TCPSend) {
+TEST_F(RecursiveQueryTest, v6TCPSend) {
     doTest(AF_INET6, IPPROTO_TCP);
     doTest(AF_INET6, IPPROTO_TCP);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v4UDPSend) {
+TEST_F(RecursiveQueryTest, v4UDPSend) {
     doTest(AF_INET, IPPROTO_UDP);
     doTest(AF_INET, IPPROTO_UDP);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v4TCPSend) {
+TEST_F(RecursiveQueryTest, v4TCPSend) {
     doTest(AF_INET, IPPROTO_TCP);
     doTest(AF_INET, IPPROTO_TCP);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v6UDPSendSpecific) {
+TEST_F(RecursiveQueryTest, v6UDPSendSpecific) {
     // Explicitly set a specific address to be bound to the socket.
     // Explicitly set a specific address to be bound to the socket.
     // The subsequent test does not directly ensures the underlying socket
     // The subsequent test does not directly ensures the underlying socket
     // is bound to the expected address, but the success of the tests should
     // is bound to the expected address, but the success of the tests should
@@ -594,26 +445,26 @@ TEST_F(ASIOLinkTest, v6UDPSendSpecific) {
     doTest(AF_INET6, IPPROTO_UDP);
     doTest(AF_INET6, IPPROTO_UDP);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v6TCPSendSpecific) {
+TEST_F(RecursiveQueryTest, v6TCPSendSpecific) {
     setDNSService(*TEST_IPV6_ADDR);
     setDNSService(*TEST_IPV6_ADDR);
     doTest(AF_INET6, IPPROTO_TCP);
     doTest(AF_INET6, IPPROTO_TCP);
 
 
     EXPECT_THROW(sendTCP(AF_INET), IOError);
     EXPECT_THROW(sendTCP(AF_INET), IOError);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v4UDPSendSpecific) {
+TEST_F(RecursiveQueryTest, v4UDPSendSpecific) {
     setDNSService(*TEST_IPV4_ADDR);
     setDNSService(*TEST_IPV4_ADDR);
     doTest(AF_INET, IPPROTO_UDP);
     doTest(AF_INET, IPPROTO_UDP);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v4TCPSendSpecific) {
+TEST_F(RecursiveQueryTest, v4TCPSendSpecific) {
     setDNSService(*TEST_IPV4_ADDR);
     setDNSService(*TEST_IPV4_ADDR);
     doTest(AF_INET, IPPROTO_TCP);
     doTest(AF_INET, IPPROTO_TCP);
 
 
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v6AddServer) {
+TEST_F(RecursiveQueryTest, v6AddServer) {
     setDNSService();
     setDNSService();
     dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV6_ADDR);
     dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV6_ADDR);
     doTest(AF_INET6, IPPROTO_TCP);
     doTest(AF_INET6, IPPROTO_TCP);
@@ -621,7 +472,7 @@ TEST_F(ASIOLinkTest, v6AddServer) {
     EXPECT_THROW(sendTCP(AF_INET), IOError);
     EXPECT_THROW(sendTCP(AF_INET), IOError);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v4AddServer) {
+TEST_F(RecursiveQueryTest, v4AddServer) {
     setDNSService();
     setDNSService();
     dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV4_ADDR);
     dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV4_ADDR);
     doTest(AF_INET, IPPROTO_TCP);
     doTest(AF_INET, IPPROTO_TCP);
@@ -629,7 +480,7 @@ TEST_F(ASIOLinkTest, v4AddServer) {
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
 }
 }
 
 
-TEST_F(ASIOLinkTest, DISABLED_clearServers) {
+TEST_F(RecursiveQueryTest, DISABLED_clearServers) {
     // FIXME: Enable when clearServers actually close the sockets
     // FIXME: Enable when clearServers actually close the sockets
     //    See #388
     //    See #388
     setDNSService();
     setDNSService();
@@ -639,7 +490,7 @@ TEST_F(ASIOLinkTest, DISABLED_clearServers) {
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v6TCPOnly) {
+TEST_F(RecursiveQueryTest, v6TCPOnly) {
     // Open only IPv6 TCP socket.  A subsequent attempt of establishing an
     // Open only IPv6 TCP socket.  A subsequent attempt of establishing an
     // IPv4/TCP connection should fail.  See above for why we only test this
     // IPv4/TCP connection should fail.  See above for why we only test this
     // for TCP.
     // for TCP.
@@ -647,7 +498,7 @@ TEST_F(ASIOLinkTest, v6TCPOnly) {
     EXPECT_THROW(sendTCP(AF_INET), IOError);
     EXPECT_THROW(sendTCP(AF_INET), IOError);
 }
 }
 
 
-TEST_F(ASIOLinkTest, v4TCPOnly) {
+TEST_F(RecursiveQueryTest, v4TCPOnly) {
     setDNSService(true, false);
     setDNSService(true, false);
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
 }
 }
@@ -659,7 +510,7 @@ singleAddress(const string &address, uint16_t port) {
     return (result);
     return (result);
 }
 }
 
 
-TEST_F(ASIOLinkTest, recursiveSetupV4) {
+TEST_F(RecursiveQueryTest, recursiveSetupV4) {
     setDNSService(true, false);
     setDNSService(true, false);
     uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
     uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
     EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
     EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
@@ -667,7 +518,7 @@ TEST_F(ASIOLinkTest, recursiveSetupV4) {
                                    singleAddress(TEST_IPV4_ADDR, port)));
                                    singleAddress(TEST_IPV4_ADDR, port)));
 }
 }
 
 
-TEST_F(ASIOLinkTest, recursiveSetupV6) {
+TEST_F(RecursiveQueryTest, recursiveSetupV6) {
     setDNSService(false, true);
     setDNSService(false, true);
     uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
     uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
     EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
     EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
@@ -680,7 +531,7 @@ TEST_F(ASIOLinkTest, recursiveSetupV6) {
 // a routine that can do this with variable address family, address, and
 // a routine that can do this with variable address family, address, and
 // port, and with the various callbacks defined in such a way as to ensure
 // port, and with the various callbacks defined in such a way as to ensure
 // full code coverage including error cases.
 // full code coverage including error cases.
-TEST_F(ASIOLinkTest, forwarderSend) {
+TEST_F(RecursiveQueryTest, forwarderSend) {
     setDNSService(true, false);
     setDNSService(true, false);
 
 
     // Note: We use the test prot plus one to ensure we aren't binding
     // Note: We use the test prot plus one to ensure we aren't binding
@@ -734,7 +585,7 @@ setSocketTimeout(int sock_, size_t tv_sec, size_t tv_usec) {
     const struct timeval timeo = { tv_sec, tv_usec };
     const struct timeval timeo = { tv_sec, tv_usec };
     int recv_options = 0;
     int recv_options = 0;
     if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo))) {
     if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo))) {
-        if (errno == ENOPROTOOPT) { // see ASIOLinkTest::recvUDP()
+        if (errno == ENOPROTOOPT) { // see RecursiveQueryTest::recvUDP()
             recv_options = MSG_DONTWAIT;
             recv_options = MSG_DONTWAIT;
         } else {
         } else {
             isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
             isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
@@ -762,7 +613,7 @@ bool tryRead(int sock_, int recv_options, size_t max, int* num) {
 
 
 
 
 // Test it tries the correct amount of times before giving up
 // Test it tries the correct amount of times before giving up
-TEST_F(ASIOLinkTest, forwardQueryTimeout) {
+TEST_F(RecursiveQueryTest, forwardQueryTimeout) {
     // Prepare the service (we do not use the common setup, we do not answer
     // Prepare the service (we do not use the common setup, we do not answer
     setDNSService();
     setDNSService();
 
 
@@ -802,15 +653,16 @@ TEST_F(ASIOLinkTest, forwardQueryTimeout) {
 // If we set client timeout to lower than querytimeout, we should
 // If we set client timeout to lower than querytimeout, we should
 // get a failure answer, but still see retries
 // get a failure answer, but still see retries
 // (no actual answer is given here yet)
 // (no actual answer is given here yet)
-TEST_F(ASIOLinkTest, forwardClientTimeout) {
+TEST_F(RecursiveQueryTest, forwardClientTimeout) {
     // Prepare the service (we do not use the common setup, we do not answer
     // Prepare the service (we do not use the common setup, we do not answer
     setDNSService();
     setDNSService();
 
 
     sock_ = createTestSocket();
     sock_ = createTestSocket();
 
 
     // Prepare the server
     // Prepare the server
-    bool done(true);
-    MockServerStop server(*io_service_, &done);
+    bool done1(true);
+    bool done2(true);
+    MockServerStop2 server(*io_service_, &done1, &done2);
 
 
     MessagePtr answer(new Message(Message::RENDER));
     MessagePtr answer(new Message(Message::RENDER));
 
 
@@ -818,11 +670,11 @@ TEST_F(ASIOLinkTest, forwardClientTimeout) {
     const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
     const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
     // Set it up to retry twice before client timeout fires
     // Set it up to retry twice before client timeout fires
     // Since the lookup timer has not fired, it should retry
     // Since the lookup timer has not fired, it should retry
-    // a third time
+    // four times
     RecursiveQuery query(*dns_service_,
     RecursiveQuery query(*dns_service_,
                          singleAddress(TEST_IPV4_ADDR, port),
                          singleAddress(TEST_IPV4_ADDR, port),
                          singleAddress(TEST_IPV4_ADDR, port),
                          singleAddress(TEST_IPV4_ADDR, port),
-                         50, 120, 1000, 3);
+                         50, 120, 1000, 4);
     Question question(Name("example.net"), RRClass::IN(), RRType::A());
     Question question(Name("example.net"), RRClass::IN(), RRType::A());
     OutputBufferPtr buffer(new OutputBuffer(0));
     OutputBufferPtr buffer(new OutputBuffer(0));
     query.resolve(question, answer, buffer, &server);
     query.resolve(question, answer, buffer, &server);
@@ -833,22 +685,20 @@ TEST_F(ASIOLinkTest, forwardClientTimeout) {
     // we know it'll fail, so make it a shorter timeout
     // we know it'll fail, so make it a shorter timeout
     int recv_options = setSocketTimeout(sock_, 1, 0);
     int recv_options = setSocketTimeout(sock_, 1, 0);
 
 
-    // Try to read 5 times, should stop after 3 reads
+    // Try to read 5 times
     int num = 0;
     int num = 0;
     bool read_success = tryRead(sock_, recv_options, 5, &num);
     bool read_success = tryRead(sock_, recv_options, 5, &num);
 
 
-    // The query should fail (for resolver it should send back servfail,
-    // but currently, and perhaps for forwarder in general, the effect
-    // will be the same as on a lookup timeout, i.e. no answer is sent
-    // back)
-    EXPECT_FALSE(done);
-    EXPECT_EQ(3, num);
-    EXPECT_FALSE(read_success);
+    // The query should fail, but we should have kept on trying
+    EXPECT_TRUE(done1);
+    EXPECT_FALSE(done2);
+    EXPECT_EQ(5, num);
+    EXPECT_TRUE(read_success);
 }
 }
 
 
 // If we set lookup timeout to lower than querytimeout*retries, we should
 // If we set lookup timeout to lower than querytimeout*retries, we should
 // fail before the full amount of retries
 // fail before the full amount of retries
-TEST_F(ASIOLinkTest, forwardLookupTimeout) {
+TEST_F(RecursiveQueryTest, forwardLookupTimeout) {
     // Prepare the service (we do not use the common setup, we do not answer
     // Prepare the service (we do not use the common setup, we do not answer
     setDNSService();
     setDNSService();
 
 
@@ -894,7 +744,7 @@ TEST_F(ASIOLinkTest, forwardLookupTimeout) {
 // for the skeleton code, it shouldn't be too much of a problem
 // for the skeleton code, it shouldn't be too much of a problem
 // Ok so even we don't all have access to the DNS world right now,
 // Ok so even we don't all have access to the DNS world right now,
 // so disabling these tests too.
 // so disabling these tests too.
-TEST_F(ASIOLinkTest, DISABLED_recursiveSendOk) {
+TEST_F(RecursiveQueryTest, DISABLED_recursiveSendOk) {
     setDNSService(true, false);
     setDNSService(true, false);
     bool done;
     bool done;
     
     
@@ -919,7 +769,7 @@ TEST_F(ASIOLinkTest, DISABLED_recursiveSendOk) {
 }
 }
 
 
 // see comments at previous test
 // see comments at previous test
-TEST_F(ASIOLinkTest, DISABLED_recursiveSendNXDOMAIN) {
+TEST_F(RecursiveQueryTest, DISABLED_recursiveSendNXDOMAIN) {
     setDNSService(true, false);
     setDNSService(true, false);
     bool done;
     bool done;
     
     
@@ -938,280 +788,4 @@ TEST_F(ASIOLinkTest, DISABLED_recursiveSendNXDOMAIN) {
     EXPECT_EQ(0, answer->getRRCount(Message::SECTION_ANSWER));
     EXPECT_EQ(0, answer->getRRCount(Message::SECTION_ANSWER));
 }
 }
 
 
-
-
-// This fixture is for testing IntervalTimer. Some callback functors are 
-// registered as callback function of the timer to test if they are called
-// or not.
-class IntervalTimerTest : public ::testing::Test {
-protected:
-    IntervalTimerTest() : io_service_() {}
-    ~IntervalTimerTest() {}
-    class TimerCallBack : public std::unary_function<void, void> {
-    public:
-        TimerCallBack(IntervalTimerTest* test_obj) : test_obj_(test_obj) {}
-        void operator()() const {
-            test_obj_->timer_called_ = true;
-            test_obj_->io_service_.stop();
-            return;
-        }
-    private:
-        IntervalTimerTest* test_obj_;
-    };
-    class TimerCallBackCounter : public std::unary_function<void, void> {
-    public:
-        TimerCallBackCounter(IntervalTimerTest* test_obj) : test_obj_(test_obj) {
-            counter_ = 0;
-        }
-        void operator()() {
-            ++counter_;
-            return;
-        }
-        int counter_;
-    private:
-        IntervalTimerTest* test_obj_;
-    };
-    class TimerCallBackCancelDeleter : public std::unary_function<void, void> {
-    public:
-        TimerCallBackCancelDeleter(IntervalTimerTest* test_obj,
-                                   IntervalTimer* timer,
-                                   TimerCallBackCounter& counter)
-            : test_obj_(test_obj), timer_(timer), counter_(counter), count_(0)
-        {}
-        void operator()() {
-            ++count_;
-            if (count_ == 1) {
-                // First time of call back.
-                // Store the value of counter_.counter_.
-                prev_counter_ = counter_.counter_;
-                delete timer_;
-            } else if (count_ == 2) {
-                // Second time of call back.
-                // Stop io_service to stop all timers.
-                test_obj_->io_service_.stop();
-                // Compare the value of counter_.counter_ with stored one.
-                // If TimerCallBackCounter was not called (expected behavior),
-                // they are same.
-                if (counter_.counter_ == prev_counter_) {
-                    test_obj_->timer_cancel_success_ = true;
-                }
-            }
-            return;
-        }
-    private:
-        IntervalTimerTest* test_obj_;
-        IntervalTimer* timer_;
-        TimerCallBackCounter& counter_;
-        int count_;
-        int prev_counter_;
-    };
-    class TimerCallBackCanceller {
-    public:
-        TimerCallBackCanceller(unsigned int& counter, IntervalTimer& itimer) :
-            counter_(counter), itimer_(itimer)
-        {}
-        void operator()() {
-            ++counter_;
-            itimer_.cancel();
-        }
-    private:
-        unsigned int& counter_;
-        IntervalTimer& itimer_;
-    };
-    class TimerCallBackOverwriter : public std::unary_function<void, void> {
-    public:
-        TimerCallBackOverwriter(IntervalTimerTest* test_obj,
-                                IntervalTimer& timer)
-            : test_obj_(test_obj), timer_(timer), count_(0)
-        {}
-        void operator()() {
-            ++count_;
-            if (count_ == 1) {
-                // First time of call back.
-                // Call setupTimer() to update callback function
-                // to TimerCallBack.
-                test_obj_->timer_called_ = false;
-                timer_.setupTimer(TimerCallBack(test_obj_), 1);
-            } else if (count_ == 2) {
-                // Second time of call back.
-                // If it reaches here, re-setupTimer() is failed (unexpected).
-                // We should stop here.
-                test_obj_->io_service_.stop();
-            }
-            return;
-        }
-    private:
-        IntervalTimerTest* test_obj_;
-        IntervalTimer& timer_;
-        int count_;
-    };
-protected:
-    IOService io_service_;
-    bool timer_called_;
-    bool timer_cancel_success_;
-};
-
-TEST_F(IntervalTimerTest, invalidArgumentToIntervalTimer) {
-    // Create asio_link::IntervalTimer and setup.
-    IntervalTimer itimer(io_service_);
-    // expect throw if call back function is empty
-    EXPECT_THROW(itimer.setupTimer(IntervalTimer::Callback(), 1),
-                     isc::InvalidParameter);
-    // expect throw if interval is 0
-    EXPECT_THROW(itimer.setupTimer(TimerCallBack(this), 0), isc::BadValue);
-}
-
-TEST_F(IntervalTimerTest, startIntervalTimer) {
-    // Create asio_link::IntervalTimer and setup.
-    // Then run IOService and test if the callback function is called.
-    IntervalTimer itimer(io_service_);
-    timer_called_ = false;
-    // store start time
-    boost::posix_time::ptime start;
-    start = boost::posix_time::microsec_clock::universal_time();
-    // setup timer
-    itimer.setupTimer(TimerCallBack(this), 1);
-    EXPECT_EQ(1, itimer.getInterval());
-    io_service_.run();
-    // reaches here after timer expired
-    // delta: difference between elapsed time and 1 second
-    boost::posix_time::time_duration delta =
-        (boost::posix_time::microsec_clock::universal_time() - start)
-         - boost::posix_time::seconds(1);
-    if (delta.is_negative()) {
-        delta.invert_sign();
-    }
-    // expect TimerCallBack is called; timer_called_ is true
-    EXPECT_TRUE(timer_called_);
-    // expect interval is 1 second +/- TIMER_MARGIN_MSEC.
-    EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
-}
-
-TEST_F(IntervalTimerTest, destructIntervalTimer) {
-    // Note: This test currently takes 6 seconds. The timer should have
-    // finer granularity and timer periods in this test should be shorter
-    // in the future.
-    // This code isn't exception safe, but we'd rather keep the code
-    // simpler and more readable as this is only for tests and if it throws
-    // the program would immediately terminate anyway.
-
-    // The call back function will not be called after the timer is
-    // destructed.
-    //
-    // There are two timers:
-    //  itimer_counter (A)
-    //   (Calls TimerCallBackCounter)
-    //     - increments internal counter in callback function
-    //  itimer_canceller (B)
-    //   (Calls TimerCallBackCancelDeleter)
-    //     - first time of callback, it stores the counter value of
-    //       callback_canceller and destructs itimer_counter
-    //     - second time of callback, it compares the counter value of
-    //       callback_canceller with stored value
-    //       if they are same the timer was not called; expected result
-    //       if they are different the timer was called after destructed
-    //
-    //     0  1  2  3  4  5  6 (s)
-    // (A) i-----+--x
-    //              ^
-    //              |destruct itimer_counter
-    // (B) i--------+--------s
-    //                       ^stop io_service
-    //                        and test itimer_counter have been stopped
-    //
-
-    // itimer_counter will be deleted in TimerCallBackCancelDeleter
-    IntervalTimer* itimer_counter = new IntervalTimer(io_service_);
-    IntervalTimer itimer_canceller(io_service_);
-    timer_cancel_success_ = false;
-    TimerCallBackCounter callback_canceller(this);
-    itimer_counter->setupTimer(callback_canceller, 2);
-    itimer_canceller.setupTimer(
-        TimerCallBackCancelDeleter(this, itimer_counter,
-                                   callback_canceller),
-        3);
-    io_service_.run();
-    EXPECT_TRUE(timer_cancel_success_);
-}
-
-TEST_F(IntervalTimerTest, cancel) {
-    // Similar to destructIntervalTimer test, but the first timer explicitly
-    // cancels itself on first callback.
-    IntervalTimer itimer_counter(io_service_);
-    IntervalTimer itimer_watcher(io_service_);
-    unsigned int counter = 0;
-    itimer_counter.setupTimer(TimerCallBackCanceller(counter, itimer_counter),
-                              1);
-    itimer_watcher.setupTimer(TimerCallBack(this), 3);
-    io_service_.run();
-    EXPECT_EQ(1, counter);
-    EXPECT_EQ(0, itimer_counter.getInterval());
-
-    // canceling an already canceled timer shouldn't cause any surprise.
-    EXPECT_NO_THROW(itimer_counter.cancel());
-}
-
-TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
-    // Note: This test currently takes 4 seconds. The timer should have
-    // finer granularity and timer periods in this test should be shorter
-    // in the future.
-
-    // Calling setupTimer() multiple times updates call back function
-    // and interval.
-    //
-    // There are two timers:
-    //  itimer (A)
-    //   (Calls TimerCallBackCounter / TimerCallBack)
-    //     - increments internal counter in callback function
-    //       (TimerCallBackCounter)
-    //       interval: 2 seconds
-    //     - io_service_.stop() (TimerCallBack)
-    //       interval: 1 second
-    //  itimer_overwriter (B)
-    //   (Calls TimerCallBackOverwriter)
-    //     - first time of callback, it calls setupTimer() to change
-    //       call back function and interval of itimer to
-    //       TimerCallBack / 1 second
-    //       after 3 + 1 seconds from the beginning of this test,
-    //       TimerCallBack() will be called and io_service_ stops.
-    //     - second time of callback, it means the test fails.
-    //
-    //     0  1  2  3  4  5  6 (s)
-    // (A) i-----+--C--s
-    //              ^  ^stop io_service
-    //              |change call back function
-    // (B) i--------+--------S
-    //                       ^(stop io_service on fail)
-    //
-
-    IntervalTimer itimer(io_service_);
-    IntervalTimer itimer_overwriter(io_service_);
-    // store start time
-    boost::posix_time::ptime start;
-    start = boost::posix_time::microsec_clock::universal_time();
-    itimer.setupTimer(TimerCallBackCounter(this), 2);
-    itimer_overwriter.setupTimer(TimerCallBackOverwriter(this, itimer), 3);
-    io_service_.run();
-    // reaches here after timer expired
-    // if interval is updated, it takes
-    //   3 seconds for TimerCallBackOverwriter
-    //   + 1 second for TimerCallBack (stop)
-    //   = 4 seconds.
-    // otherwise (test fails), it takes
-    //   3 seconds for TimerCallBackOverwriter
-    //   + 3 seconds for TimerCallBackOverwriter (stop)
-    //   = 6 seconds.
-    // delta: difference between elapsed time and 3 + 1 seconds
-    boost::posix_time::time_duration delta =
-        (boost::posix_time::microsec_clock::universal_time() - start)
-         - boost::posix_time::seconds(3 + 1);
-    if (delta.is_negative()) {
-        delta.invert_sign();
-    }
-    // expect callback function is updated: TimerCallBack is called
-    EXPECT_TRUE(timer_called_);
-    // expect interval is updated
-    EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
-}
-
 }
 }

+ 2 - 2
src/lib/asiolink/internal/tests/udpdns_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  CZ.NIC
+// Copyright (C) 2011  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
@@ -19,7 +19,7 @@
 
 
 #include <dns/question.h>
 #include <dns/question.h>
 
 
-#include <asiolink/internal/udpdns.h>
+#include <asiolink/udp_query.h>
 
 
 using namespace asio;
 using namespace asio;
 using namespace isc::dns;
 using namespace isc::dns;

+ 89 - 0
src/lib/asiolink/udp_endpoint.h

@@ -0,0 +1,89 @@
+// 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.
+
+#ifndef __UDP_ENDPOINT_H
+#define __UDP_ENDPOINT_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/io_endpoint.h>
+
+namespace asiolink {
+
+/// \brief The \c UDPEndpoint class is a concrete derived class of
+/// \c IOEndpoint that represents an endpoint of a UDP packet.
+///
+/// Other notes about \c TCPEndpoint applies to this class, too.
+class UDPEndpoint : public IOEndpoint {
+public:
+    ///
+    /// \name Constructors and Destructor.
+    ///
+    //@{
+    /// \brief Constructor from a pair of address and port.
+    ///
+    /// \param address The IP address of the endpoint.
+    /// \param port The UDP port number of the endpoint.
+    UDPEndpoint(const IOAddress& address, const unsigned short port) :
+        asio_endpoint_placeholder_(
+            new asio::ip::udp::endpoint(asio::ip::address::from_string(address.toText()),
+                              port)),
+        asio_endpoint_(*asio_endpoint_placeholder_)
+    {}
+
+    /// \brief Constructor from an ASIO UDP endpoint.
+    ///
+    /// This constructor is designed to be an efficient wrapper for the
+    /// corresponding ASIO class, \c udp::endpoint.
+    ///
+    /// \param asio_endpoint The ASIO representation of the UDP endpoint.
+    UDPEndpoint(const asio::ip::udp::endpoint& asio_endpoint) :
+        asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
+    {}
+
+    /// \brief The destructor.
+    ~UDPEndpoint() { delete asio_endpoint_placeholder_; }
+    //@}
+
+    inline IOAddress getAddress() const {
+        return (asio_endpoint_.address());
+    }
+
+    inline uint16_t getPort() const {
+        return (asio_endpoint_.port());
+    }
+
+    inline short getProtocol() const {
+        return (asio_endpoint_.protocol().protocol());
+    }
+
+    inline short getFamily() const {
+        return (asio_endpoint_.protocol().family());
+    }
+
+    // This is not part of the exosed IOEndpoint API but allows
+    // direct access to the ASIO implementation of the endpoint
+    inline const asio::ip::udp::endpoint& getASIOEndpoint() const {
+        return (asio_endpoint_);
+    }
+
+private:
+    const asio::ip::udp::endpoint* asio_endpoint_placeholder_;
+    const asio::ip::udp::endpoint& asio_endpoint_;
+};
+
+}      // namespace asiolink
+#endif // __UDP_ENDPOINT_H

+ 189 - 0
src/lib/asiolink/udp_query.cc

@@ -0,0 +1,189 @@
+// 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>
+
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
+
+#include <asio.hpp>
+
+#include <boost/bind.hpp>
+#include <boost/shared_array.hpp>
+
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+
+#include <log/dummylog.h>
+
+#include <asio.hpp>
+
+#include <asiolink.h>
+
+#include <coroutine.h>
+#include <asiolink/udp_endpoint.h>
+
+#include <asiolink/udp_query.h>
+
+using namespace asio;
+using asio::ip::udp;
+using asio::ip::tcp;
+using isc::log::dlog;
+
+using namespace std;
+using namespace isc::dns;
+
+namespace asiolink {
+
+
+// Private UDPQuery data (see internal/udpdns.h for reasons)
+struct UDPQuery::PrivateData {
+    // Socket we send query to and expect reply from there
+    udp::socket socket;
+    // Where was the query sent
+    udp::endpoint remote;
+    // What we ask the server
+    Question question;
+    // We will store the answer here
+    OutputBufferPtr buffer;
+    OutputBufferPtr msgbuf;
+    // Temporary buffer for answer
+    boost::shared_array<char> data;
+    // This will be called when the data arrive or timeouts
+    Callback* callback;
+    // Did we already stop operating (data arrived, we timed out, someone
+    // called stop). This can be so when we are cleaning up/there are
+    // still pointers to us.
+    bool stopped;
+    // Timer to measure timeouts.
+    deadline_timer timer;
+    // How many milliseconds are we willing to wait for answer?
+    int timeout;
+
+    PrivateData(io_service& service,
+        const udp::socket::protocol_type& protocol, const Question &q,
+        OutputBufferPtr b, Callback *c) :
+        socket(service, protocol),
+        question(q),
+        buffer(b),
+        msgbuf(new OutputBuffer(512)),
+        callback(c),
+        stopped(false),
+        timer(service)
+    { }
+};
+
+/// The following functions implement the \c UDPQuery class.
+///
+/// The constructor
+UDPQuery::UDPQuery(io_service& io_service,
+                   const Question& q, const IOAddress& addr, uint16_t port,
+                   OutputBufferPtr buffer, Callback *callback, int timeout) :
+    data_(new PrivateData(io_service,
+        addr.getFamily() == AF_INET ? udp::v4() : udp::v6(), q, buffer,
+        callback))
+{
+    data_->remote = UDPEndpoint(addr, port).getASIOEndpoint();
+    data_->timeout = timeout;
+}
+
+/// The function operator is implemented with the "stackless coroutine"
+/// pattern; see internal/coroutine.h for details.
+void
+UDPQuery::operator()(error_code ec, size_t length) {
+    if (ec || data_->stopped) {
+        return;
+    }
+
+    CORO_REENTER (this) {
+        /// Generate the upstream query and render it to wire format
+        /// This is done in a different scope to allow inline variable
+        /// declarations.
+        {
+            Message msg(Message::RENDER);
+            
+            // XXX: replace with boost::random or some other suitable PRNG
+            msg.setQid(0);
+            msg.setOpcode(Opcode::QUERY());
+            msg.setRcode(Rcode::NOERROR());
+            msg.setHeaderFlag(Message::HEADERFLAG_RD);
+            msg.addQuestion(data_->question);
+            MessageRenderer renderer(*data_->msgbuf);
+            msg.toWire(renderer);
+            dlog("Sending " + msg.toText() + " to " +
+                data_->remote.address().to_string());
+        }
+
+
+        // If we timeout, we stop, which will shutdown everything and
+        // cancel all other attempts to run inside the coroutine
+        if (data_->timeout != -1) {
+            data_->timer.expires_from_now(boost::posix_time::milliseconds(
+                data_->timeout));
+            data_->timer.async_wait(boost::bind(&UDPQuery::stop, *this,
+                TIME_OUT));
+        }
+
+        // Begin an asynchronous send, and then yield.  When the
+        // send completes, we will resume immediately after this point.
+        CORO_YIELD data_->socket.async_send_to(buffer(data_->msgbuf->getData(),
+            data_->msgbuf->getLength()), data_->remote, *this);
+
+        /// Allocate space for the response.  (XXX: This should be
+        /// optimized by maintaining a free list of pre-allocated blocks)
+        data_->data.reset(new char[MAX_LENGTH]);
+
+        /// Begin an asynchronous receive, and yield.  When the receive
+        /// completes, we will resume immediately after this point.
+        CORO_YIELD data_->socket.async_receive_from(buffer(data_->data.get(),
+            MAX_LENGTH), data_->remote, *this);
+        // The message is not rendered yet, so we can't print it easilly
+        dlog("Received response from " + data_->remote.address().to_string());
+
+        /// Copy the answer into the response buffer.  (XXX: If the
+        /// OutputBuffer object were made to meet the requirements of
+        /// a MutableBufferSequence, then it could be written to directly
+        /// by async_recieve_from() and this additional copy step would
+        /// be unnecessary.)
+        data_->buffer->writeData(data_->data.get(), length);
+
+        /// We are done
+        stop(SUCCESS);
+    }
+}
+
+void
+UDPQuery::stop(Result result) {
+    if (!data_->stopped) {
+        switch (result) {
+            case TIME_OUT:
+                dlog("Query timed out");
+                break;
+            case STOPPED:
+                dlog("Query stopped");
+                break;
+            default:;
+        }
+        data_->stopped = true;
+        data_->socket.cancel();
+        data_->socket.close();
+        data_->timer.cancel();
+        if (data_->callback) {
+            (*data_->callback)(result);
+        }
+    }
+}
+
+} // namespace asiolink

+ 88 - 0
src/lib/asiolink/udp_query.h

@@ -0,0 +1,88 @@
+// 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.
+
+#ifndef __UDP_QUERY_H
+#define __UDP_QUERY_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <dns/buffer.h>
+
+#include <asiolink/io_address.h>
+#include <coroutine.h>
+
+namespace asiolink {
+
+//
+// Asynchronous UDP coroutine for upstream queries
+//
+class UDPQuery : public coroutine {
+public:
+    // TODO Maybe this should be more generic than just for UDPQuery?
+    ///
+    /// \brief Result of the query
+    ///
+    /// This is related only to contacting the remote server. If the answer
+    ///indicates error, it is still counted as SUCCESS here, if it comes back.
+    ///
+    enum Result {
+        SUCCESS,
+        TIME_OUT,
+        STOPPED
+    };
+    /// Abstract callback for the UDPQuery.
+    class Callback {
+    public:
+        virtual ~Callback() {}
+
+        /// This will be called when the UDPQuery is completed
+        virtual void operator()(Result result) = 0;
+    };
+    ///
+    /// \brief Constructor.
+    ///
+    /// It creates the query.
+    /// @param callback will be called when we terminate. It is your task to
+    ///        delete it if allocated on heap.
+    ///@param timeout in ms.
+    ///
+    explicit UDPQuery(asio::io_service& io_service,
+                      const isc::dns::Question& q,
+                      const IOAddress& addr, uint16_t port,
+                      isc::dns::OutputBufferPtr buffer,
+                      Callback* callback, int timeout = -1);
+    void operator()(asio::error_code ec = asio::error_code(),
+                    size_t length = 0);
+    /// Terminate the query.
+    void stop(Result reason = STOPPED);
+private:
+    enum { MAX_LENGTH = 4096 };
+
+    ///
+    /// \short Private data
+    ///
+    /// They are not private because of stability of the
+    /// interface (this is private class anyway), but because this class
+    /// will be copyed often (it is used as a coroutine and passed as callback
+    /// to many async_*() functions) and we want keep the same data. Some of
+    /// the data is not copyable too.
+    ///
+    struct PrivateData;
+    boost::shared_ptr<PrivateData> data_;
+};
+
+}      // namespace asiolink
+#endif // __UDP_QUERY_H

+ 9 - 157
src/lib/asiolink/udpdns.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  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
@@ -14,29 +14,19 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
-#include <unistd.h>             // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
+#include <boost/shared_array.hpp>
 
 
-#include <boost/bind.hpp>
+// unistd is needed for asio.hpp with SunStudio
+#include <unistd.h>
 
 
 #include <asio.hpp>
 #include <asio.hpp>
-#include <asio/deadline_timer.hpp>
 
 
-#include <memory>
-#include <boost/shared_ptr.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-
-#include <dns/buffer.h>
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
 #include <log/dummylog.h>
 #include <log/dummylog.h>
-#include <dns/opcode.h>
-#include <dns/rcode.h>
 
 
-#include <asiolink.h>
-#include <internal/coroutine.h>
-#include <internal/udpdns.h>
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_socket.h>
+
+#include <asiolink/udp_server.h>
 
 
 using namespace asio;
 using namespace asio;
 using asio::ip::udp;
 using asio::ip::udp;
@@ -291,142 +281,4 @@ UDPServer::hasAnswer() {
     return (data_->done_);
     return (data_->done_);
 }
 }
 
 
-// Private UDPQuery data (see internal/udpdns.h for reasons)
-struct UDPQuery::PrivateData {
-    // Socket we send query to and expect reply from there
-    udp::socket socket;
-    // Where was the query sent
-    udp::endpoint remote;
-    // What we ask the server
-    Question question;
-    // We will store the answer here
-    OutputBufferPtr buffer;
-    OutputBufferPtr msgbuf;
-    // Temporary buffer for answer
-    boost::shared_array<char> data;
-    // This will be called when the data arrive or timeouts
-    Callback* callback;
-    // Did we already stop operating (data arrived, we timed out, someone
-    // called stop). This can be so when we are cleaning up/there are
-    // still pointers to us.
-    bool stopped;
-    // Timer to measure timeouts.
-    deadline_timer timer;
-    // How many milliseconds are we willing to wait for answer?
-    int timeout;
-
-    PrivateData(io_service& service,
-        const udp::socket::protocol_type& protocol, const Question &q,
-        OutputBufferPtr b, Callback *c) :
-        socket(service, protocol),
-        question(q),
-        buffer(b),
-        msgbuf(new OutputBuffer(512)),
-        callback(c),
-        stopped(false),
-        timer(service)
-    { }
-};
-
-/// The following functions implement the \c UDPQuery class.
-///
-/// The constructor
-UDPQuery::UDPQuery(io_service& io_service,
-                   const Question& q, const IOAddress& addr, uint16_t port,
-                   OutputBufferPtr buffer, Callback *callback, int timeout) :
-    data_(new PrivateData(io_service,
-        addr.getFamily() == AF_INET ? udp::v4() : udp::v6(), q, buffer,
-        callback))
-{
-    data_->remote = UDPEndpoint(addr, port).getASIOEndpoint();
-    data_->timeout = timeout;
-}
-
-/// The function operator is implemented with the "stackless coroutine"
-/// pattern; see internal/coroutine.h for details.
-void
-UDPQuery::operator()(error_code ec, size_t length) {
-    if (ec || data_->stopped) {
-        return;
-    }
-
-    CORO_REENTER (this) {
-        /// Generate the upstream query and render it to wire format
-        /// This is done in a different scope to allow inline variable
-        /// declarations.
-        {
-            Message msg(Message::RENDER);
-            
-            // XXX: replace with boost::random or some other suitable PRNG
-            msg.setQid(0);
-            msg.setOpcode(Opcode::QUERY());
-            msg.setRcode(Rcode::NOERROR());
-            msg.setHeaderFlag(Message::HEADERFLAG_RD);
-            msg.addQuestion(data_->question);
-            MessageRenderer renderer(*data_->msgbuf);
-            msg.toWire(renderer);
-            dlog("Sending " + msg.toText() + " to " +
-                data_->remote.address().to_string());
-        }
-
-
-        // If we timeout, we stop, which will shutdown everything and
-        // cancel all other attempts to run inside the coroutine
-        if (data_->timeout != -1) {
-            data_->timer.expires_from_now(boost::posix_time::milliseconds(
-                data_->timeout));
-            data_->timer.async_wait(boost::bind(&UDPQuery::stop, *this,
-                TIME_OUT));
-        }
-
-        // Begin an asynchronous send, and then yield.  When the
-        // send completes, we will resume immediately after this point.
-        CORO_YIELD data_->socket.async_send_to(buffer(data_->msgbuf->getData(),
-            data_->msgbuf->getLength()), data_->remote, *this);
-
-        /// Allocate space for the response.  (XXX: This should be
-        /// optimized by maintaining a free list of pre-allocated blocks)
-        data_->data.reset(new char[MAX_LENGTH]);
-
-        /// Begin an asynchronous receive, and yield.  When the receive
-        /// completes, we will resume immediately after this point.
-        CORO_YIELD data_->socket.async_receive_from(buffer(data_->data.get(),
-            MAX_LENGTH), data_->remote, *this);
-        // The message is not rendered yet, so we can't print it easilly
-        dlog("Received response from " + data_->remote.address().to_string());
-
-        /// Copy the answer into the response buffer.  (XXX: If the
-        /// OutputBuffer object were made to meet the requirements of
-        /// a MutableBufferSequence, then it could be written to directly
-        /// by async_recieve_from() and this additional copy step would
-        /// be unnecessary.)
-        data_->buffer->writeData(data_->data.get(), length);
-
-        /// We are done
-        stop(SUCCESS);
-    }
-}
-
-void
-UDPQuery::stop(Result result) {
-    if (!data_->stopped) {
-        switch (result) {
-            case TIME_OUT:
-                dlog("Query timed out");
-                break;
-            case STOPPED:
-                dlog("Query stopped");
-                break;
-            default:;
-        }
-        data_->stopped = true;
-        data_->socket.cancel();
-        data_->socket.close();
-        data_->timer.cancel();
-        if (data_->callback) {
-            (*data_->callback)(result);
-        }
-    }
-}
-
-}
+} // namespace asiolink

+ 102 - 0
src/lib/asiolink/udp_server.h

@@ -0,0 +1,102 @@
+// 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.
+
+#ifndef __UDP_SERVER_H
+#define __UDP_SERVER_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/dns_server.h>
+#include <asiolink/simple_callback.h>
+#include <asiolink/dns_lookup.h>
+#include <asiolink/dns_answer.h>
+
+#include <coroutine.h>
+
+namespace asiolink {
+
+//
+// Asynchronous UDP server coroutine
+//
+///
+/// \brief This class implements the coroutine to handle UDP
+///        DNS query event. As such, it is both a \c DNSServer and
+///        a \c coroutine
+///
+class UDPServer : public virtual DNSServer, public virtual coroutine {
+public:
+    /// \brief Constructor
+    /// \param io_service the asio::io_service to work with
+    /// \param addr the IP address to listen for queries on
+    /// \param port the port to listen for queries on
+    /// \param checkin the callbackprovider for non-DNS events
+    /// \param lookup the callbackprovider for DNS lookup events
+    /// \param answer the callbackprovider for DNS answer events
+    explicit UDPServer(asio::io_service& io_service,
+                       const asio::ip::address& addr, const uint16_t port,
+                       SimpleCallback* checkin = NULL,
+                       DNSLookup* lookup = NULL,
+                       DNSAnswer* answer = NULL);
+
+    /// \brief The function operator
+    void operator()(asio::error_code ec = asio::error_code(),
+                    size_t length = 0);
+
+    /// \brief Calls the lookup callback
+    void asyncLookup();
+
+    /// \brief Resume operation
+    ///
+    /// \param done Set this to true if the lookup action is done and
+    ///        we have an answer
+    void resume(const bool done);
+
+    /// \brief Check if we have an answer
+    ///
+    /// \return true if we have an answer
+    bool hasAnswer();
+
+    /// \brief Returns the coroutine state value
+    ///
+    /// \return the coroutine state value
+    int value() { return (get_value()); }
+
+    /// \brief Clones the object
+    ///
+    /// \return a newly allocated copy of this object
+    DNSServer* clone() {
+        UDPServer* s = new UDPServer(*this);
+        return (s);
+    }
+
+private:
+    enum { MAX_LENGTH = 4096 };
+
+    /**
+     * \brief Internal state and data.
+     *
+     * We use the pimple design pattern, but not because we need to hide
+     * internal data. This class and whole header is for private use anyway.
+     * It turned out that UDPServer is copied a lot, because it is a coroutine.
+     * This way the overhead of copying is lower, we copy only one shared
+     * pointer instead of about 10 of them.
+     */
+    class Data;
+    boost::shared_ptr<Data> data_;
+};
+
+}      // namespace asiolink
+#endif // __UDP_SERVER_H

+ 48 - 0
src/lib/asiolink/udp_socket.h

@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef __UDP_SOCKET_H
+#define __UDP_SOCKET_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/io_socket.h>
+
+namespace asiolink {
+
+/// \brief The \c UDPSocket class is a concrete derived class of
+/// \c IOSocket that represents a UDP socket.
+///
+/// Other notes about \c TCPSocket applies to this class, too.
+class UDPSocket : public IOSocket {
+private:
+    UDPSocket(const UDPSocket& source);
+    UDPSocket& operator=(const UDPSocket& source);
+public:
+    /// \brief Constructor from an ASIO UDP socket.
+    ///
+    /// \param socket The ASIO representation of the UDP socket.
+    UDPSocket(asio::ip::udp::socket& socket) : socket_(socket) {}
+
+    virtual int getNative() const { return (socket_.native()); }
+    virtual int getProtocol() const { return (IPPROTO_UDP); }
+
+private:
+    asio::ip::udp::socket& socket_;
+};
+
+}      // namespace asiolink
+#endif // __UDP_SOCKET_H

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

@@ -0,0 +1,33 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/nsas -I$(top_builddir)/src/lib/nsas
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cache -I$(top_builddir)/src/lib/cache
+AM_CPPFLAGS += $(SQLITE_CFLAGS)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+if USE_CLANGPP
+# clang++ complains about unused function parameters in some boost header
+# files.
+AM_CXXFLAGS += -Wno-unused-parameter
+endif
+
+lib_LTLIBRARIES = libcache.la
+libcache_la_SOURCES  = resolver_cache.h resolver_cache.cc
+libcache_la_SOURCES  += message_cache.h message_cache.cc
+libcache_la_SOURCES  += message_entry.h message_entry.cc
+libcache_la_SOURCES  += rrset_cache.h rrset_cache.cc
+libcache_la_SOURCES  += rrset_entry.h rrset_entry.cc
+libcache_la_SOURCES  += cache_entry_key.h cache_entry_key.cc
+libcache_la_SOURCES  += rrset_copy.h rrset_copy.cc
+libcache_la_SOURCES  += local_zone_data.h local_zone_data.cc
+
+CLEANFILES = *.gcno *.gcda

+ 14 - 0
src/lib/cache/TODO

@@ -0,0 +1,14 @@
+* Revisit the algorithm used by getRRsetTrustLevel() in message_entry.cc.
+* Implement dump/load/resize interfaces of rrset/message/recursor cache.
+* Once LRU hash table is implemented, it should be used by message/rrset cache.
+* Once the hash/lrulist related files in /lib/nsas is moved to seperated
+  folder, the code of recursor cache has to be updated.
+* Set proper AD flags once DNSSEC is supported by the cache.
+* When the message or rrset entry has expired, it should be removed
+  from the cache, or just moved to the head of LRU list, so that it
+  can removed first.
+* Make resolver cache be smart to refetch the messages that are about
+  to expire.
+* When the rrset beging updated is an NS rrset, NSAS should be updated
+  together.
+

+ 44 - 0
src/lib/cache/cache_entry_key.cc

@@ -0,0 +1,44 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 <sstream>
+#include "cache_entry_key.h"
+
+using namespace std;
+
+namespace isc {
+namespace cache {
+const std::string
+genCacheEntryName(const isc::dns::Name& name, const isc::dns::RRType& type) {
+    std::string keystr = name.toText();
+    ostringstream stream;
+    stream << type.getCode();
+    keystr += stream.str();
+    return (keystr);
+}
+
+const std::string
+genCacheEntryName(const std::string& namestr, const uint16_t type) {
+    std::string keystr = namestr;
+    ostringstream stream;
+    stream << type;
+    keystr += stream.str();
+    return (keystr);
+}
+
+} // namespace cache
+} // namespace isc
+

+ 56 - 0
src/lib/cache/cache_entry_key.h

@@ -0,0 +1,56 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 __CACHE_ENTRY_KEY_H
+#define __CACHE_ENTRY_KEY_H
+
+#include <string>
+#include <dns/name.h>
+#include <dns/rrtype.h>
+
+namespace isc {
+namespace cache {
+
+/// \brief Entry Name Generation Functions
+///
+/// Generate the name for message/rrset entries.
+///
+/// Concatenates the string representation of the Name and the
+/// string representation of the type number.
+///
+/// Note: the returned name is a text string, not wire format.
+/// eg. if name is 'example.com.', type is 'A', the return
+/// value is 'example.com.1'
+///
+/// \param name The Name to create a text entry for
+/// \param type The RRType to create a text entry for
+/// \return return the entry name.
+const std::string
+genCacheEntryName(const isc::dns::Name& name, const isc::dns::RRType& type);
+
+///
+/// \overload
+///
+/// \param namestr A string representation of a DNS Name
+/// \param type The value of a DNS RRType
+const std::string
+genCacheEntryName(const std::string& namestr, const uint16_t type);
+
+} // namespace cache
+} // namespace isc
+
+#endif // __CACHE_ENTRY_KEY_H
+

+ 58 - 0
src/lib/cache/local_zone_data.cc

@@ -0,0 +1,58 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 <dns/rrset.h>
+#include "local_zone_data.h"
+#include "cache_entry_key.h"
+#include "rrset_copy.h"
+
+using namespace std;
+using namespace isc::dns;
+
+namespace isc {
+namespace cache {
+
+typedef pair<std::string, RRsetPtr> RRsetMapPair;
+typedef map<std::string, RRsetPtr>::iterator RRsetMapIterator;
+
+isc::dns::RRsetPtr
+LocalZoneData::lookup(const isc::dns::Name& name,
+                      const isc::dns::RRType& type)
+{
+    string key = genCacheEntryName(name, type);
+    RRsetMapIterator iter = rrsets_map_.find(key);
+    if (iter == rrsets_map_.end()) {
+        return (RRsetPtr());
+    } else {
+        return (iter->second);
+    }
+}
+
+void
+LocalZoneData::update(const isc::dns::RRset& rrset) {
+    //TODO Do we really need to recreate the rrset again?
+    string key = genCacheEntryName(rrset.getName(), rrset.getType());
+    RRset* rrset_copy = new RRset(rrset.getName(), rrset.getClass(),
+                                  rrset.getType(), rrset.getTTL());
+
+    rrsetCopy(rrset, *rrset_copy);
+    RRsetPtr rrset_ptr(rrset_copy);
+    rrsets_map_[key] = rrset_ptr;
+}
+
+} // namespace cache
+} // namespace isc
+

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

@@ -0,0 +1,66 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 _LOCAL_ZONE_DATA
+#define _LOCAL_ZONE_DATA
+
+#include <map>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <dns/rrset.h>
+
+namespace isc {
+namespace cache {
+
+/// \brief Local Zone Data
+/// The object of LocalZoneData represents the data of one
+/// local zone. It provides the interface for lookup the rrsets
+/// in the zone.
+class LocalZoneData {
+public:
+    LocalZoneData(uint16_t rrset_class) : class_(rrset_class)
+    {}
+
+    /// \brief Look up one rrset.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    /// \return return the shared_ptr of rrset if it is
+    /// found in the local zone, or else, return NULL.
+    isc::dns::RRsetPtr lookup(const isc::dns::Name& qname,
+                              const isc::dns::RRType& qtype);
+
+    /// \brief Update the rrset in the local zone.
+    ///
+    /// If the rrset doesn't exist, it will be added.
+    /// Otherwise, the existed one will be overwritten.
+    ///
+    /// \param rrset The rrset to update
+    void update(const isc::dns::RRset& rrset);
+
+private:
+    std::map<std::string, isc::dns::RRsetPtr> rrsets_map_; // RRsets of the zone
+    uint16_t class_; // The class of the zone
+};
+
+typedef boost::shared_ptr<LocalZoneData> LocalZoneDataPtr;
+typedef boost::shared_ptr<const LocalZoneData> ConstLocalZoneDataPtr;
+
+} // namespace cache
+} // namespace isc
+
+#endif // _LOCAL_ZONE_DATA
+

+ 97 - 0
src/lib/cache/message_cache.cc

@@ -0,0 +1,97 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 <nsas/nsas_entry_compare.h>
+#include <nsas/hash_table.h>
+#include <nsas/hash_deleter.h>
+#include "message_cache.h"
+#include "cache_entry_key.h"
+
+using namespace isc::nsas;
+using namespace isc::dns;
+using namespace std;
+
+namespace isc {
+namespace cache {
+
+MessageCache::MessageCache(boost::shared_ptr<RRsetCache> rrset_cache,
+    uint32_t cache_size, uint16_t message_class):
+    message_class_(message_class),
+    rrset_cache_(rrset_cache),
+    message_table_(new NsasEntryCompare<MessageEntry>, cache_size),
+    message_lru_((3 * cache_size),
+                  new HashDeleter<MessageEntry>(message_table_))
+{
+}
+
+bool
+MessageCache::lookup(const isc::dns::Name& qname,
+                     const isc::dns::RRType& qtype,
+                     isc::dns::Message& response)
+{
+    std::string entry_name = genCacheEntryName(qname, qtype);
+    HashKey entry_key = HashKey(entry_name, RRClass(message_class_));
+    MessageEntryPtr msg_entry = message_table_.get(entry_key);
+    if(msg_entry) {
+        message_lru_.touch(msg_entry);
+        return (msg_entry->genMessage(time(NULL), response));
+    }
+
+    return (false);
+}
+
+bool
+MessageCache::update(const Message& msg) {
+    QuestionIterator iter = msg.beginQuestion();
+    std::string entry_name = genCacheEntryName((*iter)->getName(), (*iter)->getType());
+    HashKey entry_key = HashKey(entry_name, RRClass(message_class_));
+
+    // The simplest way to update is removing the old message entry directly.
+    // We have find the existed message entry, since we need to delete it
+    // from lru list too.
+    // TODO, but there should be a better way, since we here have to remove and
+    // add the message entry, maybe there is one way to touch it once.
+    MessageEntryPtr old_msg_entry = message_table_.get(entry_key);
+    if (old_msg_entry) {
+        message_lru_.remove(old_msg_entry);
+    }
+
+    MessageEntryPtr msg_entry(new MessageEntry(msg, rrset_cache_));
+    message_lru_.add(msg_entry);
+    return (message_table_.add(msg_entry, entry_key, true));
+}
+
+void
+MessageCache::dump(const std::string&) {
+    //TODO
+}
+
+void
+MessageCache::load(const std::string&) {
+    //TODO
+}
+
+bool
+MessageCache::resize(uint32_t) {
+    //TODO
+    return (true);
+}
+
+} // namespace cache
+} // namespace isc
+

+ 95 - 0
src/lib/cache/message_cache.h

@@ -0,0 +1,95 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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_CACHE_H
+#define __MESSAGE_CACHE_H
+
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <dns/message.h>
+#include "message_entry.h"
+#include <nsas/hash_table.h>
+#include <nsas/lru_list.h>
+
+namespace isc {
+namespace cache {
+
+class RRsetCache;
+
+/// \brief Message Cache
+/// The object of MessageCache represents the cache for class-specific
+/// messages.
+///
+class MessageCache {
+// Noncopyable
+private:
+    MessageCache(const MessageCache& source);
+    MessageCache& operator=(const MessageCache& source);
+public:
+    /// \param cache_size The size of message cache.
+    MessageCache(boost::shared_ptr<RRsetCache> rrset_cache_,
+                 uint32_t cache_size, uint16_t message_class);
+
+    /// \brief Look up message in cache.
+    /// \param message generated response message if the message entry
+    ///        can be found.
+    ///
+    /// \return return true if the message can be found in cache, or else,
+    /// return false.
+    //TODO Maybe some user just want to get the message_entry.
+    bool lookup(const isc::dns::Name& qname,
+                const isc::dns::RRType& qtype,
+                isc::dns::Message& message);
+
+    /// \brief Update the message in the cache with the new one.
+    /// If the message doesn't exist in the cache, it will be added
+    /// directly.
+    bool update(const isc::dns::Message& msg);
+
+    /// \brief Dump the message cache to specified file.
+    /// \todo It should can be dumped to one configured database.
+    void dump(const std::string& file_name);
+
+    /// \brief Load the cache from one file.
+    /// \todo It should can be loaded from one configured database.
+    void load(const std::string& file_name);
+
+    /// \brief Resize the size of message cache in runtime.
+    bool resize(uint32_t size);
+
+protected:
+    /// \brief Get the hash key for the message entry in the cache.
+    /// \param name query name of the message.
+    /// \param type query type of the message.
+    /// \return return the hash key.
+    HashKey getEntryHashKey(const isc::dns::Name& name,
+                            const isc::dns::RRType& type) const;
+
+    // Make these variants be protected for easy unittest.
+protected:
+    uint16_t message_class_; // The class of the message cache.
+    boost::shared_ptr<RRsetCache> rrset_cache_;
+    isc::nsas::HashTable<MessageEntry> message_table_;
+    isc::nsas::LruList<MessageEntry> message_lru_;
+};
+
+typedef boost::shared_ptr<MessageCache> MessageCachePtr;
+
+} // namespace cache
+} // namespace isc
+
+#endif // __MESSAGE_CACHE_H
+

+ 251 - 0
src/lib/cache/message_entry.cc

@@ -0,0 +1,251 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 <limits>
+#include <dns/message.h>
+#include <nsas/nsas_entry.h>
+#include "message_entry.h"
+#include "rrset_cache.h"
+
+using namespace isc::dns;
+using namespace std;
+
+namespace isc {
+namespace cache {
+
+static uint32_t MAX_UINT32 = numeric_limits<uint32_t>::max();
+
+MessageEntry::MessageEntry(const isc::dns::Message& msg,
+                           boost::shared_ptr<RRsetCache> rrset_cache):
+    rrset_cache_(rrset_cache),
+    headerflag_aa_(false),
+    headerflag_tc_(false)
+{
+    initMessageEntry(msg);
+    entry_name_ = genCacheEntryName(query_name_, query_type_);
+    hash_key_ptr_ = new HashKey(entry_name_, RRClass(query_class_));
+}
+
+bool
+MessageEntry::getRRsetEntries(vector<RRsetEntryPtr>& rrset_entry_vec,
+                              const time_t time_now)
+{
+    uint16_t entry_count = answer_count_ + authority_count_ + additional_count_;
+    rrset_entry_vec.reserve(rrset_entry_vec.size() + entry_count);
+    for (int index = 0; index < entry_count; ++index) {
+        RRsetEntryPtr rrset_entry = rrset_cache_->lookup(rrsets_[index].name_,
+                                                        rrsets_[index].type_);
+        if (time_now < rrset_entry->getExpireTime()) {
+            rrset_entry_vec.push_back(rrset_entry);
+        } else {
+            return (false);
+        }
+    }
+
+    return (true);
+}
+
+void
+MessageEntry::addRRset(isc::dns::Message& message,
+                       const vector<RRsetEntryPtr>& rrset_entry_vec,
+                       const isc::dns::Message::Section& section,
+                       bool dnssec_need)
+{
+    uint16_t start_index = 0;
+    uint16_t end_index = answer_count_;
+    assert(section != Message::SECTION_QUESTION);
+
+    if (section == Message::SECTION_AUTHORITY) {
+        start_index = answer_count_;
+        end_index = answer_count_ + authority_count_;
+    } else if (section == Message::SECTION_ADDITIONAL) {
+        start_index = answer_count_ + authority_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);
+    }
+}
+
+bool
+MessageEntry::genMessage(const time_t& time_now,
+                         isc::dns::Message& msg)
+{
+    if (time_now >= expire_time_) {
+        // The message entry has expired.
+        return (false);
+    } else {
+        // Before do any generation, we should check if some rrset
+        // has expired, if it is, return false.
+        vector<RRsetEntryPtr> rrset_entry_vec;
+        if (false == getRRsetEntries(rrset_entry_vec, time_now)) {
+            return (false);
+        }
+
+        // Begin message generation. We don't need to add question
+        // section, since it has been included in the message.
+        // Set cached header flags.
+        msg.setHeaderFlag(Message::HEADERFLAG_AA, headerflag_aa_);
+        msg.setHeaderFlag(Message::HEADERFLAG_TC, headerflag_tc_);
+
+        bool dnssec_need = msg.getEDNS().get();
+        addRRset(msg, rrset_entry_vec, Message::SECTION_ANSWER, dnssec_need);
+        addRRset(msg, rrset_entry_vec, Message::SECTION_AUTHORITY, dnssec_need);
+        addRRset(msg, rrset_entry_vec, Message::SECTION_ADDITIONAL, dnssec_need);
+
+        return (true);
+    }
+}
+
+RRsetTrustLevel
+MessageEntry::getRRsetTrustLevel(const Message& message,
+    const isc::dns::RRsetPtr& rrset,
+    const isc::dns::Message::Section& section)
+{
+    bool aa = message.getHeaderFlag(Message::HEADERFLAG_AA);
+    switch(section) {
+        case Message::SECTION_ANSWER: {
+            if (aa) {
+                RRsetIterator rrset_iter = message.beginSection(section);
+
+                // Make sure we are inspecting the right RRset
+                while((*rrset_iter)->getName() != rrset->getName() &&
+                      (*rrset_iter)->getType() != rrset->getType() &&
+                      rrset_iter != message.endSection(section)) {
+                    ++rrset_iter;
+                }
+                assert(rrset_iter != message.endSection(section));
+
+                // According RFC2181 section 5.4.1, only the record
+                // describing that ailas is necessarily authoritative.
+                // If there is one or more CNAME records in answer section.
+                // CNAME records is assumed as the first rrset.
+                if ((*rrset_iter)->getType() == RRType::CNAME()) {
+                    // TODO: real equals for RRsets?
+                    if ((*rrset_iter).get() == rrset.get()) {
+                        return (RRSET_TRUST_ANSWER_AA);
+                    } else {
+                        return (RRSET_TRUST_ANSWER_NONAA);
+                    }
+                }
+
+                // Here, if the first rrset is DNAME, then assume the
+                // second rrset is synchronized CNAME record, except
+                // these two records, any other records in answer section
+                // should be treated as non-authoritative.
+                // TODO, this part logic should be revisited later,
+                // since it's not mentioned by RFC2181.
+                if ((*rrset_iter)->getType() == RRType::DNAME()) {
+                    // TODO: real equals for RRsets?
+                    if ((*rrset_iter).get() == rrset.get() ||
+                        ((++rrset_iter) != message.endSection(section) &&
+                                     (*rrset_iter).get() == rrset.get())) {
+                        return (RRSET_TRUST_ANSWER_AA);
+                    } else {
+                        return (RRSET_TRUST_ANSWER_NONAA);
+                    }
+                }
+
+                return (RRSET_TRUST_ANSWER_AA);
+
+            } else {
+                return (RRSET_TRUST_ANSWER_NONAA);
+            }
+            break;
+        }
+
+        case Message::SECTION_AUTHORITY: {
+            if (aa) {
+                return (RRSET_TRUST_AUTHORITY_AA);
+            } else {
+                return (RRSET_TRUST_AUTHORITY_NONAA);
+            }
+            break;
+        }
+
+        case Message::SECTION_ADDITIONAL: {
+            if (aa) {
+                return (RRSET_TRUST_ADDITIONAL_AA);
+            } else {
+                return (RRSET_TRUST_ADDITIONAL_NONAA);
+            }
+            break;
+        }
+
+        default:
+            return (RRSET_TRUST_DEFAULT);
+    }
+}
+
+void
+MessageEntry::parseSection(const isc::dns::Message& msg,
+                         const Message::Section& section,
+                         uint32_t& smaller_ttl,
+                         uint16_t& rrset_count)
+{
+    RRsetIterator iter;
+    int count = 0;
+    for (iter = msg.beginSection(section);
+         iter != msg.endSection(section);
+         ++iter) {
+        // Add the rrset entry to rrset_cache or update the existed
+        // rrset entry if the new one is more authoritative.
+        //TODO set proper rrset trust level.
+        RRsetPtr rrset_ptr = *iter;
+        RRsetTrustLevel level = getRRsetTrustLevel(msg, rrset_ptr, section);
+        RRsetEntryPtr rrset_entry = rrset_cache_->update(*rrset_ptr, level);
+        rrsets_.push_back(RRsetRef(rrset_ptr->getName(), rrset_ptr->getType()));
+
+        uint32_t rrset_ttl = rrset_entry->getTTL();
+        if (smaller_ttl > rrset_ttl) {
+            smaller_ttl = rrset_ttl;
+        }
+
+        count++;
+    }
+
+    rrset_count = count;
+}
+
+void
+MessageEntry::initMessageEntry(const isc::dns::Message& msg) {
+    //TODO better way to cache the header flags?
+    headerflag_aa_ = msg.getHeaderFlag(Message::HEADERFLAG_AA);
+    headerflag_tc_ = msg.getHeaderFlag(Message::HEADERFLAG_TC);
+
+    // We only cache the first question in question section.
+    // TODO, do we need to support muptiple questions?
+    query_count_ = 1;
+    QuestionIterator iter = msg.beginQuestion();
+    query_name_ = (*iter)->getName().toText();
+    query_type_ = (*iter)->getType().getCode();
+    query_class_ = (*iter)->getClass().getCode();
+
+    uint32_t min_ttl = MAX_UINT32;
+    parseSection(msg, Message::SECTION_ANSWER, min_ttl, answer_count_);
+    parseSection(msg, Message::SECTION_AUTHORITY, min_ttl, authority_count_);
+    parseSection(msg, Message::SECTION_ADDITIONAL, min_ttl, additional_count_);
+
+    expire_time_ = time(NULL) + min_ttl;
+}
+
+} // namespace cache
+} // namespace isc
+
+

+ 180 - 0
src/lib/cache/message_entry.h

@@ -0,0 +1,180 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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_ENTRY_H
+#define __MESSAGE_ENTRY_H
+
+#include <vector>
+#include <dns/message.h>
+#include <dns/rrset.h>
+#include <nsas/nsas_entry.h>
+#include "rrset_entry.h"
+
+
+using namespace isc::nsas;
+
+namespace isc {
+namespace cache {
+
+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
+///
+/// The object of MessageEntry represents one response message
+/// answered to the resolver client.
+class MessageEntry : public NsasEntry<MessageEntry> {
+// Noncopyable
+private:
+    MessageEntry(const MessageEntry& source);
+    MessageEntry& operator=(const MessageEntry& source);
+public:
+
+    /// \brief Initialize the message entry object with one dns
+    ///        message.
+    /// \param message The message used to initialize MessageEntry.
+    /// \param rrset_cache the pointer of RRsetCache. When one message
+    ///        entry is created, rrset cache needs to be updated,
+    ///        since some new rrset entries may be inserted into
+    ///        rrset cache, or the existed rrset entries need
+    ///        to be updated.
+    MessageEntry(const isc::dns::Message& message,
+                 boost::shared_ptr<RRsetCache> rrset_cache);
+
+    /// \brief generate one dns message according
+    ///        the rrsets information of the message.
+    ///
+    /// \param time_now set the ttl of each rrset in the message
+    ///        as "expire_time - time_now" (expire_time is the
+    ///        expiration time of the rrset).
+    /// \param response generated dns message.
+    /// \return return true if the response message can be generated
+    ///         from the cached information, or else, return false.
+    bool genMessage(const time_t& time_now, isc::dns::Message& response);
+
+    /// \brief Get the hash key of the message entry.
+    ///
+    /// \return return hash key
+    virtual HashKey hashKey() const {
+        return (*hash_key_ptr_);
+    }
+
+    /// \short Protected memebers, so they can be accessed by tests.
+    //@{
+protected:
+    /// \brief Initialize the message entry with dns message.
+    ///
+    /// \param message The Message to initialize the entry with
+    void initMessageEntry(const isc::dns::Message& message);
+
+    /// \brief Parse the rrsets in specified section.
+    ///
+    /// \param msg The message to parse the RRsets from
+    /// \param section The Section to parse the RRsets from
+    /// \param smaller_ttl Get the smallest ttl of rrsets in
+    ///        specified section, if it's smaller than the given value.
+    /// \param rrset_count the rrset count of the section.
+    ///        (TODO for Message, getRRsetCount() should be one
+    ///        interface provided by Message.)
+    void parseSection(const isc::dns::Message& msg,
+                      const isc::dns::Message::Section& section,
+                      uint32_t& smaller_ttl,
+                      uint16_t& rrset_count);
+
+    /// \brief Get RRset Trustworthiness
+    ///        The algorithm refers to RFC2181 section 5.4.1
+    ///        Only the rrset can be updated by the rrsets
+    ///        with higher trust level.
+    ///
+    /// \param message Message that the rrset belongs to
+    /// \param rrset specified rrset which needs to get its
+    ///        trust worthiness
+    /// \param section Section of the rrset
+    /// \return return rrset trust level.
+    RRsetTrustLevel getRRsetTrustLevel(const isc::dns::Message& message,
+        const isc::dns::RRsetPtr& rrset,
+        const isc::dns::Message::Section& section);
+
+    /// \brief Add rrset to one section of message.
+    ///
+    /// \param message The message to add rrsets to.
+    /// \param rrset_entry_vec vector for rrset entries in
+    ///        different sections.
+    /// \param section The section to add to
+    /// \param dnssec_need need dnssec records or not.
+    void addRRset(isc::dns::Message& message,
+                  const std::vector<RRsetEntryPtr>& rrset_entry_vec,
+                  const isc::dns::Message::Section& section,
+                  bool dnssec_need);
+
+    /// \brief Get the all the rrset entries for the message entry.
+    ///
+    /// \param rrset_entry_vec vector to add unexpired rrset entries to
+    /// \param time_now the time of now. Used to compare with rrset
+    ///        entry's expire time.
+    /// \return return false if any rrset entry has expired, true
+    ///         otherwise.
+    bool getRRsetEntries(std::vector<RRsetEntryPtr>& rrset_entry_vec,
+                         const time_t time_now);
+
+    time_t expire_time_;  // Expiration time of the message.
+    //@}
+
+private:
+    std::string entry_name_; // The name for this entry(name + type)
+    HashKey* hash_key_ptr_;  // the key for messag entry in hash table.
+
+    std::vector<RRsetRef> rrsets_;
+    boost::shared_ptr<RRsetCache> rrset_cache_;
+
+    std::string query_name_; // query name of the message.
+    uint16_t query_class_; // query class of the message.
+    uint16_t query_type_; // query type of message.
+
+    uint16_t query_count_; // query count in query section.
+    uint16_t answer_count_; // rrset count in answer section.
+    uint16_t authority_count_; // rrset count in authority section.
+    uint16_t additional_count_; // rrset count in addition section.
+
+    //TODO, there should be a better way to cache these header flags
+    bool headerflag_aa_; // Whether AA bit is set.
+    bool headerflag_tc_; // Whether TC bit is set.
+};
+
+typedef boost::shared_ptr<MessageEntry> MessageEntryPtr;
+
+} // namespace cache
+} // namespace isc
+
+#endif // __MESSAGE_ENTRY_H
+

+ 246 - 0
src/lib/cache/resolver_cache.cc

@@ -0,0 +1,246 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 "resolver_cache.h"
+#include "dns/message.h"
+#include "rrset_cache.h"
+#include <string>
+#include <algorithm>
+
+using namespace isc::dns;
+using namespace std;
+
+namespace isc {
+namespace cache {
+
+ResolverClassCache::ResolverClassCache(const RRClass& cache_class) :
+    cache_class_(cache_class)
+{
+    local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(cache_class_.getCode()));
+    rrsets_cache_ = RRsetCachePtr(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE,
+                                                 cache_class_.getCode()));
+    messages_cache_ = MessageCachePtr(new MessageCache(rrsets_cache_,
+                                      MESSAGE_CACHE_DEFAULT_SIZE,
+                                      cache_class_.getCode()));
+}
+
+ResolverClassCache::ResolverClassCache(CacheSizeInfo cache_info) :
+    cache_class_(cache_info.cclass)
+{
+    uint16_t klass = cache_class_.getCode();
+    // TODO We should find one way to load local zone data.
+    local_zone_data_ = LocalZoneDataPtr(new LocalZoneData(klass));
+    rrsets_cache_ = RRsetCachePtr(new
+                        RRsetCache(cache_info.rrset_cache_size, klass));
+    messages_cache_ = MessageCachePtr(new MessageCache(rrsets_cache_,
+                                      cache_info.message_cache_size,
+                                      klass));
+}
+
+const RRClass&
+ResolverClassCache::getClass() const {
+    return cache_class_;
+}
+
+bool
+ResolverClassCache::lookup(const isc::dns::Name& qname,
+                      const isc::dns::RRType& qtype,
+                      isc::dns::Message& response) const
+{
+    // message response should has question section already.
+    if (response.beginQuestion() == response.endQuestion()) {
+        isc_throw(MessageNoQuestionSection, "Message has no question section");
+    }
+
+    // First, query in local zone, if the rrset(qname, qtype, qclass) can be
+    // found in local zone, generated reply message with only the rrset in
+    // answer section.
+    RRsetPtr rrset_ptr = local_zone_data_->lookup(qname, qtype);
+    if (rrset_ptr) {
+        response.addRRset(Message::SECTION_ANSWER, rrset_ptr);
+        return (true);
+    }
+
+    // Search in class-specific message cache.
+    return (messages_cache_->lookup(qname, qtype, response));
+}
+
+isc::dns::RRsetPtr
+ResolverClassCache::lookup(const isc::dns::Name& qname,
+               const isc::dns::RRType& qtype) const
+{
+    // Algorithm:
+    // 1. Search in local zone data first,
+    // 2. Then do search in rrsets_cache_.
+    RRsetPtr rrset_ptr = local_zone_data_->lookup(qname, qtype);
+    if (rrset_ptr) {
+        return (rrset_ptr);
+    } else {
+        RRsetEntryPtr rrset_entry = rrsets_cache_->lookup(qname, qtype);
+        if (rrset_entry) {
+            return (rrset_entry->getRRset());
+        } else {
+            return (RRsetPtr());
+        }
+    }
+}
+
+bool
+ResolverClassCache::update(const isc::dns::Message& msg) {
+    return (messages_cache_->update(msg));
+}
+
+bool
+ResolverClassCache::updateRRsetCache(const isc::dns::ConstRRsetPtr rrset_ptr,
+                                RRsetCachePtr rrset_cache_ptr)
+{
+    RRsetTrustLevel level;
+    if (rrset_ptr->getType() == RRType::A() || 
+        rrset_ptr->getType() == RRType::AAAA()) {
+        level = RRSET_TRUST_PRIM_GLUE;
+    } else {
+        level = RRSET_TRUST_PRIM_ZONE_NONGLUE;
+    }
+
+    rrset_cache_ptr->update((*rrset_ptr.get()), level);
+    return (true);
+}
+
+bool
+ResolverClassCache::update(const isc::dns::ConstRRsetPtr rrset_ptr) {
+    // First update local zone, then update rrset cache.
+    local_zone_data_->update((*rrset_ptr.get()));
+    updateRRsetCache(rrset_ptr, rrsets_cache_);
+    return (true);
+}
+
+
+ResolverCache::ResolverCache()
+{
+    class_caches_.push_back(new ResolverClassCache(RRClass::IN()));
+}
+
+ResolverCache::ResolverCache(std::vector<CacheSizeInfo> caches_info)
+{
+    for (int i = 0; i < caches_info.size(); ++i) {
+        class_caches_.push_back(new ResolverClassCache(caches_info[i]));
+    }
+}
+
+ResolverCache::~ResolverCache()
+{
+    for (int i = 0; i < class_caches_.size(); ++i) {
+        delete class_caches_[i];
+    }
+}
+
+bool
+ResolverCache::lookup(const isc::dns::Name& qname,
+                      const isc::dns::RRType& qtype,
+                      const isc::dns::RRClass& qclass,
+                      isc::dns::Message& response) const
+{
+    ResolverClassCache* cc = getClassCache(qclass);
+    if (cc) {
+        return (cc->lookup(qname, qtype, response));
+    } else {
+        return (false);
+    }
+}
+
+isc::dns::RRsetPtr
+ResolverCache::lookup(const isc::dns::Name& qname,
+               const isc::dns::RRType& qtype,
+               const isc::dns::RRClass& qclass) const
+{
+    ResolverClassCache* cc = getClassCache(qclass);
+    if (cc) {
+        return (cc->lookup(qname, qtype));
+    } else {
+        return (RRsetPtr());
+    }
+}
+
+isc::dns::RRsetPtr
+ResolverCache::lookupClosestRRset(const isc::dns::Name& qname,
+                                  const isc::dns::RRType& qtype,
+                                  const isc::dns::RRClass& qclass) const
+{
+    ResolverClassCache* cc = getClassCache(qclass);
+    if (cc) {
+        unsigned int count = qname.getLabelCount();
+        unsigned int level = 0;
+        while(level < count) {
+            Name close_name = qname.split(level);
+            RRsetPtr rrset_ptr = cc->lookup(close_name, qtype);
+            if (rrset_ptr) {
+                return (rrset_ptr);
+            } else {
+                ++level;
+            }
+        }
+    }
+
+    return (RRsetPtr());
+}
+
+bool
+ResolverCache::update(const isc::dns::Message& msg) {
+    
+    QuestionIterator iter = msg.beginQuestion();
+    ResolverClassCache* cc = getClassCache((*iter)->getClass());
+    if (cc) {
+        return (cc->update(msg));
+    } else {
+        return (false);
+    }
+}
+
+bool
+ResolverCache::update(const isc::dns::ConstRRsetPtr rrset_ptr) {
+    ResolverClassCache* cc = getClassCache(rrset_ptr->getClass());
+    if (cc) {
+        return (cc->update(rrset_ptr));
+    } else {
+        return (false);
+    }
+}
+
+void
+ResolverCache::dump(const std::string&) {
+    //TODO
+}
+
+void
+ResolverCache::load(const std::string&) {
+    //TODO
+}
+
+ResolverClassCache*
+ResolverCache::getClassCache(const isc::dns::RRClass& cache_class) const {
+    for (int i = 0; i < class_caches_.size(); ++i) {
+        if (class_caches_[i]->getClass() == cache_class) {
+            return class_caches_[i];
+        }
+    }
+    return NULL;
+}
+
+} // namespace cache
+} // namespace isc
+

+ 331 - 0
src/lib/cache/resolver_cache.h

@@ -0,0 +1,331 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 __RESOLVER_CACHE_H
+#define __RESOLVER_CACHE_H
+
+#include <map>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <dns/rrclass.h>
+#include <dns/message.h>
+#include <exceptions/exceptions.h>
+#include "message_cache.h"
+#include "rrset_cache.h"
+#include "local_zone_data.h"
+
+namespace isc {
+namespace cache {
+class RRsetCache;
+
+//TODO a better proper default cache size
+#define MESSAGE_CACHE_DEFAULT_SIZE 10000
+#define RRSET_CACHE_DEFAULT_SIZE   20000
+
+/// \brief Cache Size Information.
+///
+/// Used to initialize the size of class-specific rrset/message cache.
+struct CacheSizeInfo
+{
+public:
+    /// \brief Constructor
+    ///
+    /// \param cls The RRClass code
+    /// \param msg_cache_size The size for the message cache
+    /// \param rst_cache_size The size for the RRset cache
+    CacheSizeInfo(const isc::dns::RRClass& cls, 
+                  uint32_t msg_cache_size,
+                  uint32_t rst_cache_size):
+                    cclass(cls),
+                    message_cache_size(msg_cache_size),
+                    rrset_cache_size(rst_cache_size)
+    {}
+
+    isc::dns::RRClass cclass; // class of the cache.
+    uint32_t message_cache_size; // the size for message cache.
+    uint32_t rrset_cache_size; // The size for rrset cache.
+};
+
+/// \brief  Message has no question section.
+///
+/// Thrown if the given message has no question section when looking up
+/// the message in cache.
+class MessageNoQuestionSection : public isc::Exception {
+public:
+    MessageNoQuestionSection(const char*file, size_t line, const char*what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+/// \brief Class-specific Resolver Cache.
+///
+/// The object of ResolverCache represents the cache of the resolver. It may hold
+/// a list of message/rrset cache which are in different class.
+///
+/// \note Public interaction with the cache should be through ResolverCache,
+/// not directly with this one. (TODO: make this private/hidden/local to the .cc?)
+class ResolverClassCache {
+public:
+    /// \brief Default Constructor.
+    ///
+    /// Only support for class "IN", and message cache size is
+    /// MESSAGE_CACHE_DEFAULT_SIZE, rrset cache size is
+    /// RRSET_CACHE_DEFAULT_SIZE
+    ResolverClassCache(const isc::dns::RRClass& cache_class);
+
+    /// \brief Construct Function.
+    /// \param caches_size cache size information for each
+    ///        messages/rrsets of different classes.
+    ResolverClassCache(CacheSizeInfo cache_info);
+
+    /// \name Lookup Interfaces
+    //@{
+    /// \brief Look up message in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    /// \param response the query message (must be in RENDER mode)
+    ///        which has question section already (exception
+    ///        MessageNoQeustionSection will be thrown if it has
+    ///        no question section). If the message can be found
+    ///        in cache, rrsets for the message will be added to
+    ///        different sections(answer, authority, additional).
+    /// \return return true if the message can be found, or else,
+    ///         return false.
+    bool lookup(const isc::dns::Name& qname,
+                const isc::dns::RRType& qtype,
+                isc::dns::Message& response) const;
+
+    /// \brief Look up rrset in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    ///
+    /// \return return the shared_ptr of rrset if it can be found,
+    ///         or else, return NULL. When looking up, local zone
+    ///         data will be searched first, if not found, then
+    ///         search in rrset cache.
+    ///
+    /// \overload
+    ///
+    isc::dns::RRsetPtr lookup(const isc::dns::Name& qname,
+                              const isc::dns::RRType& qtype) const;
+
+    /// \brief Update the message in the cache with the new one.
+    ///
+    /// \param msg The message to update
+    ///
+    /// \return return true if the message is updated successfully,
+    ///         or else, return false.
+    ///
+    /// \note the function doesn't do any message validation check,
+    ///       the user should make sure the message is valid, and of
+    ///       the right class
+    bool update(const isc::dns::Message& msg);
+
+    /// \brief Update the rrset in the cache with the new one.
+    ///
+    /// local zone data and rrset cache will be updated together.
+    /// If the rrset doesn't exist in both of them, then the rrset
+    /// will be added into both of them.
+    ///
+    /// \param rrset_ptr The RRset to update
+    ///
+    /// \return return false, if the class of the parameter rrset is
+    ///        allowed to be cached.
+    ///
+    /// \overload
+    ///
+    /// \note The class of the RRset must have been checked. It is not
+    /// here.
+    bool update(const isc::dns::ConstRRsetPtr rrset_ptr);
+
+    /// \brief Get the RRClass this cache is for
+    ///
+    /// \return The RRClass of this cache
+    const isc::dns::RRClass& getClass() const;
+    
+private:
+    /// \brief Update rrset cache.
+    ///
+    /// \param rrset_ptr The rrset to update with
+    /// \param rrset_cache_ptr the rrset cache to update
+    ///
+    /// \return return true if the rrset is updated in the rrset cache,
+    ///         or else return false if failed.
+    /// \param rrset_cache_ptr The rrset cache need to be updated.
+    bool updateRRsetCache(const isc::dns::ConstRRsetPtr rrset_ptr,
+                          RRsetCachePtr rrset_cache_ptr);
+
+    /// \brief Class this cache is for.
+    const isc::dns::RRClass cache_class_;
+
+    /// \brief map of message caches for configured classes(each message
+    /// cache is class-specific)
+    MessageCachePtr messages_cache_;
+
+    /// \name rrset caches
+    //@{
+    /// \brief Local Zone data cache
+    /// Cache for rrsets in local zones, rrsets
+    /// in it never expire.
+    LocalZoneDataPtr local_zone_data_;
+
+    /// \brief cache the rrsets parsed from the received message.
+    RRsetCachePtr rrsets_cache_;
+    //@}
+};
+
+class ResolverCache {
+public:
+    /// \brief Default Constructor.
+    ///
+    /// Right now, only support for class "IN", and message cache size is
+    /// MESSAGE_CACHE_DEFAULT_SIZE, rrset cache size is
+    /// RRSET_CACHE_DEFAULT_SIZE
+    ResolverCache();
+
+    /// \brief Construct Function.
+    /// \param caches_size cache size information for each
+    ///        messages/rrsets of different classes.
+    ResolverCache(std::vector<CacheSizeInfo> caches_size);
+
+    /// \brief Destructor
+    ~ResolverCache();
+
+    /// \name Lookup Interfaces
+    //@{
+    /// \brief Look up message in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    /// \param qclass The query class to look up
+    /// \param response the query message (must be in RENDER mode)
+    ///        which has question section already (exception
+    ///        MessageNoQeustionSection will be thrown if it has
+    ///        no question section). If the message can be found
+    ///        in cache, rrsets for the message will be added to
+    ///        different sections(answer, authority, additional).
+    /// \return return true if the message can be found, or else,
+    ///         return false.
+    bool lookup(const isc::dns::Name& qname,
+                const isc::dns::RRType& qtype,
+                const isc::dns::RRClass& qclass,
+                isc::dns::Message& response) const;
+
+    /// \brief Look up rrset in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    /// \param qclass The query class to look up
+    ///
+    /// \return return the shared_ptr of rrset if it can be found,
+    ///         or else, return NULL. When looking up, local zone
+    ///         data will be searched first, if not found, then
+    ///         search in rrset cache.
+    ///
+    /// \overload
+    ///
+    isc::dns::RRsetPtr lookup(const isc::dns::Name& qname,
+                              const isc::dns::RRType& qtype,
+                              const isc::dns::RRClass& qclass) const;
+
+    /// \brief Look up closest rrset in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type to look up
+    /// \param qclass The query class to look up
+    ///
+    /// \return return the shared_ptr of rrset if it can be found in
+    ///         cache, or else return NULL.
+    ///
+    /// Currently the implementation is: search exact rrset
+    /// label by lable, If the rrset can't be found, remove the last
+    /// label, then search again. The efficiency may be very low when
+    /// the name of rrset is very long but it's closest rrset's name
+    /// is very short.
+    /// If a good perfermance is needed when looking up the closest rrset,
+    /// rrset cache structure(HashTable) should be redesigned. By using
+    /// HashTable, it can only garantee the performance for looking
+    /// up exact rrset.
+    /// So here there is another question, which rrset looking up interface
+    /// is used frequently? Exact or closest looking up.
+    isc::dns::RRsetPtr lookupClosestRRset(const isc::dns::Name& qname,
+                              const isc::dns::RRType& qtype,
+                              const isc::dns::RRClass& qclass) const;
+    //@}
+
+    /// \brief Update the message in the cache with the new one.
+    ///
+    /// \param msg The message to update
+    ///
+    /// \return return true if the message is updated successfully,
+    ///         or else, return false.
+    ///
+    /// \note the function doesn't do any message validation check,
+    ///       the user should make sure the message is valid.
+    bool update(const isc::dns::Message& msg);
+
+    /// \brief Update the rrset in the cache with the new one.
+    ///
+    /// local zone data and rrset cache will be updated together.
+    /// If the rrset doesn't exist in both of them, then the rrset
+    /// will be added into both of them.
+    ///
+    /// \param rrset_ptr The RRset to update
+    ///
+    /// \return return false, if the class of the parameter rrset is
+    ///        allowed to be cached.
+    ///
+    /// \overload
+    ///
+    bool update(const isc::dns::ConstRRsetPtr rrset_ptr);
+
+    /// \name Cache Serialization
+    //@{
+    /// \brief Dump the cache content to one file.
+    ///
+    /// \param file_name file to write to
+    ///
+    /// \todo It should can be dumped to one configured database.
+    void dump(const std::string& file_name);
+
+    /// \brief Load the cache from one file.
+    ///
+    /// \param file to load from
+    ///
+    /// \todo It should can be loaded from one configured database.
+    void load(const std::string& file_name);
+    //@}
+
+private:
+    /// \brief Returns the class-specific subcache
+    ///
+    /// \param cache_class the class to get the subcache for
+    /// \return The subcache, or NULL if there is no cache for this class
+    ResolverClassCache* getClassCache(const isc::dns::RRClass& cache_class) const;
+
+    /// The class-specific caches.
+    /// TODO: I think we can optimize for IN, and always have that
+    /// one directly available, use the vector for the rest?
+    std::vector<ResolverClassCache*> class_caches_;
+};
+
+} // namespace cache
+} // namespace isc
+
+#endif // __RESOLVER_CACHE_H
+

+ 106 - 0
src/lib/cache/rrset_cache.cc

@@ -0,0 +1,106 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 "rrset_cache.h"
+#include <nsas/nsas_entry_compare.h>
+#include <nsas/hash_table.h>
+#include <nsas/hash_deleter.h>
+
+using namespace isc::nsas;
+using namespace isc::dns;
+using namespace std;
+
+namespace isc {
+namespace cache {
+
+RRsetCache::RRsetCache(uint32_t cache_size,
+                       uint16_t rrset_class):
+    class_(rrset_class),
+    rrset_table_(new NsasEntryCompare<RRsetEntry>, cache_size),
+    rrset_lru_((3 * cache_size),
+                  new HashDeleter<RRsetEntry>(rrset_table_))
+{
+}
+
+RRsetEntryPtr
+RRsetCache::lookup(const isc::dns::Name& qname,
+                   const isc::dns::RRType& qtype)
+{
+    const string entry_name = genCacheEntryName(qname, qtype);
+    RRsetEntryPtr entry_ptr = rrset_table_.get(HashKey(entry_name, RRClass(class_)));
+
+    //If the rrset entry has expired, return NULL.
+    if(entry_ptr && (time(NULL) > entry_ptr->getExpireTime())) {
+        return (RRsetEntryPtr());
+    }
+    return (entry_ptr);
+}
+
+RRsetEntryPtr
+RRsetCache::update(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) {
+    // TODO: If the RRset is an NS, we should update the NSAS as well
+    
+    // lookup first
+    RRsetEntryPtr entry_ptr = lookup(rrset.getName(), rrset.getType());
+    if(!entry_ptr) {
+        // rrset entry doesn't exist, create one rrset entry for the rrset
+        // and add it directly.
+        entry_ptr.reset(new RRsetEntry(rrset, level));
+        // Replace the expired rrset entry if it exists.
+        rrset_table_.add(entry_ptr, entry_ptr->hashKey(), true);
+        //TODO , lru list touch.
+        return (entry_ptr);
+    } else {
+        // there is one rrset entry in the cache, need to check whether
+        // the new rrset is more authoritative.
+        if (entry_ptr->getTrustLevel() > level) {
+            // existed rrset entry is more authoritative, do nothing,
+            // just return it.
+            //TODO, lru list touch
+            return (entry_ptr);
+        } else {
+            HashKey key = entry_ptr->hashKey();
+            entry_ptr.reset(new RRsetEntry(rrset, level));
+            //TODO, lru list touch.
+            // Replace the expired rrset entry if it exists.
+            rrset_table_.add(entry_ptr, entry_ptr->hashKey(), true);
+            return (entry_ptr);
+        }
+    }
+}
+
+void
+RRsetCache::dump(const std::string&) {
+    //TODO
+}
+
+void
+RRsetCache::load(const std::string&) {
+    //TODO
+}
+
+bool
+RRsetCache::resize(uint32_t) {
+    //TODO
+    return (true);
+}
+
+} // namespace cache
+} // namespace isc
+

+ 108 - 0
src/lib/cache/rrset_cache.h

@@ -0,0 +1,108 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 __RRSET_CACHE_H
+#define __RRSET_CACHE_H
+
+#include <rrset_entry.h>
+#include <nsas/hash_table.h>
+#include <nsas/lru_list.h>
+
+using namespace isc::nsas;
+
+namespace isc {
+namespace cache {
+
+class RRsetEntry;
+
+/// \brief RRset Cache
+/// The object of RRsetCache represented the cache for class-specific
+/// RRsets.
+class RRsetCache{
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are intentionally
+    /// defined as private to make it uncopyable
+    //@{
+private:
+    RRsetCache(const RRsetCache&);
+    RRsetCache& operator=(const RRsetCache&);
+public:
+    /// \brief Constructor
+    ///
+    /// \param cache_size the size of rrset cache.
+    /// \param rrset_class the class of rrset cache.
+    RRsetCache(uint32_t cache_size, uint16_t rrset_class);
+    ~RRsetCache() {}
+    //@}
+
+    /// \brief Look up rrset in cache.
+    ///
+    /// \param qname The query name to look up
+    /// \param qtype The query type 
+    /// \return return the shared_ptr of rrset entry if it can be
+    /// found in the cache, or else, return NULL.
+    RRsetEntryPtr lookup(const isc::dns::Name& qname,
+                         const isc::dns::RRType& qtype);
+
+    /// \brief Update RRset Cache
+    /// Update the rrset entry in the cache with the new one.
+    /// If the rrset has expired or doesn't exist in the cache,
+    /// it will be added directly. It may be ingored if the new
+    /// rrset is not more authoritative than the old rrset in cache.
+    ///
+    /// \param rrset The new rrset used to update cache.
+    /// \param level trustworthiness of the rrset.
+    /// \return return the rrset entry in the cache, it may be the
+    /// new added rrset entry or existed one if it is not replaced.
+    RRsetEntryPtr update(const isc::dns::RRset& rrset,
+                         const RRsetTrustLevel& level);
+
+    /// \brief Dump the rrset cache to specified file.
+    ///
+    /// \param file_name The file to write to
+    ///
+    /// \todo It should can be dumped to one configured database.
+    void dump(const std::string& file_name);
+
+    /// \brief Load the cache from one file.
+    ///
+    /// \param file_name The file to read from
+    ///
+    /// \todo It should can be loaded from one configured database.
+    void load(const std::string& file_name);
+
+    /// \brief Resize the size of rrset cache in runtime.
+    ///
+    /// \param The size to resize to
+    /// \return true
+    bool resize(uint32_t size);
+
+private:
+    uint16_t class_; // The class of the rrset cache.
+    isc::nsas::HashTable<RRsetEntry> rrset_table_;
+    isc::nsas::LruList<RRsetEntry> rrset_lru_;
+};
+
+typedef boost::shared_ptr<RRsetCache> RRsetCachePtr;
+typedef boost::shared_ptr<const RRsetCache> ConstRRsetCachePtr;
+
+} // namespace cache
+} // namespace isc
+
+#endif // __RRSET_CACHE_H
+

+ 40 - 0
src/lib/cache/rrset_copy.cc

@@ -0,0 +1,40 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 "rrset_copy.h"
+
+using namespace isc::dns;
+
+namespace isc {
+namespace cache {
+
+void
+rrsetCopy(const isc::dns::RRset& src, isc::dns::RRset& dst) {
+    RdataIteratorPtr rdata_itor = src.getRdataIterator();
+    rdata_itor->first();
+    while(!rdata_itor->isLast()){
+        dst.addRdata(rdata_itor->getCurrent());
+        rdata_itor->next();
+    }
+
+    RRsetPtr rrsig = src.getRRsig();
+    if (rrsig != NULL){
+        dst.addRRsig(rrsig);
+    }
+}
+
+} // namespace cache
+} // namespace isc

+ 44 - 0
src/lib/cache/rrset_copy.h

@@ -0,0 +1,44 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 __RRSET_COPY_
+#define __RRSET_COPY_
+
+#include <dns/rrset.h>
+
+namespace isc {
+namespace cache {
+
+/// \brief RRset Copy Function
+///
+/// Adds all Rdatas and the RRsig in the source RRset to the target
+/// RRset
+///
+/// \param src RRset to copy from
+/// \param dst RRset to copy to
+///
+/// \note RRset class doesn't provide the interface for
+///       doing RRset copy. But in cache's code, sometime
+///       we have to do the copy.
+
+void
+rrsetCopy(const isc::dns::RRset& src, isc::dns::RRset& dst);
+
+} // namespace cache
+} // namespace isc
+
+#endif // __RRSET_COPY_
+

+ 68 - 0
src/lib/cache/rrset_entry.cc

@@ -0,0 +1,68 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 <dns/message.h>
+#include <nsas/nsas_entry.h>
+#include <nsas/fetchable.h>
+#include "rrset_entry.h"
+#include "rrset_copy.h"
+
+using namespace isc::dns;
+
+namespace isc {
+namespace cache {
+
+RRsetEntry::RRsetEntry(const isc::dns::RRset& rrset, const RRsetTrustLevel& level):
+    entry_name_(genCacheEntryName(rrset.getName(), rrset.getType())),
+    expire_time_(time(NULL) + rrset.getTTL().getValue()),
+    trust_level_(level),
+    rrset_(new RRset(rrset.getName(), rrset.getClass(), rrset.getType(), rrset.getTTL())),
+    hash_key_(HashKey(entry_name_, rrset_->getClass()))
+{
+    rrsetCopy(rrset, *(rrset_.get()));
+}
+
+isc::dns::RRsetPtr
+RRsetEntry::getRRset() {
+    updateTTL();
+    return (rrset_);
+}
+
+time_t
+RRsetEntry::getExpireTime() const {
+    return (expire_time_);
+}
+
+void
+RRsetEntry::updateTTL(){
+    uint32_t oldTTL = rrset_->getTTL().getValue();
+    if(oldTTL == 0) {
+        return;
+    }
+
+    uint32_t now = time(NULL);
+    uint32_t newTTL = now < expire_time_ ? (expire_time_ - now) : 0;
+
+    RRTTL ttl(newTTL);
+    rrset_->setTTL(ttl);
+}
+
+} // namespace cache
+} // namespace isc
+
+

+ 137 - 0
src/lib/cache/rrset_entry.h

@@ -0,0 +1,137 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 __RRSET_ENTRY_H
+#define __RRSET_ENTRY_H
+
+#include <dns/rrset.h>
+#include <dns/message.h>
+#include <dns/rrttl.h>
+#include <nsas/nsas_entry.h>
+#include <nsas/fetchable.h>
+#include "cache_entry_key.h"
+
+using namespace isc::nsas;
+
+namespace isc {
+namespace cache {
+
+/// \enum RRset Trustworthiness
+/// For detail of RRset trustworthiness, please refer to
+/// RFC2181 section5.4.1.
+/// Bigger value is more trustworthy.
+enum RRsetTrustLevel {
+    /// Default trust for RRset.
+    RRSET_TRUST_DEFAULT = 0,
+    /// Additional information from non-authoritative answer.
+    RRSET_TRUST_ADDITIONAL_NONAA,
+    /// Data from the authority section of a non-authoritative answer
+    RRSET_TRUST_AUTHORITY_NONAA,
+    /// Additional information from an authoritative answer.
+    RRSET_TRUST_ADDITIONAL_AA,
+    /// Non-authoritative data from the answer section of authoritative
+    /// answers
+    RRSET_TRUST_NONAUTH_ANSWER_AA,
+    /// Data from the answer section of a non-authoritative answer.
+    RRSET_TRUST_ANSWER_NONAA,
+    /// Glue from a primary zone, or glue from a zone transfer.
+    RRSET_TRUST_PRIM_GLUE,
+    /// Data from the authority section of an authoritative answer.
+    RRSET_TRUST_AUTHORITY_AA,
+    /// Authoritative data included in the answer section of
+    /// an authoritative reply.
+    RRSET_TRUST_ANSWER_AA,
+    /// Data from a primary zone file, other than glue data.
+    RRSET_TRUST_PRIM_ZONE_NONGLUE
+};
+
+/// \brief RRset Entry
+/// The object of RRsetEntry represents one cached RRset.
+/// Each RRset entry may be refered using shared_ptr by several message
+/// entries.
+class RRsetEntry : public NsasEntry<RRsetEntry>
+{
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are intentionally
+    /// defined as private to make it uncopyable
+    //@{
+private:
+    RRsetEntry(const RRsetEntry&);
+    RRsetEntry& operator=(const RRsetEntry&);
+public:
+    /// \brief Constructor
+    /// \param rrset The RRset used to initialize the RRset entry.
+    /// \param level trustworthiness of the RRset.
+    RRsetEntry(const isc::dns::RRset& rrset, const RRsetTrustLevel& level);
+
+    /// The destructor.
+    ~RRsetEntry() {}
+    //@}
+
+    /// \brief Return a pointer to a generated RRset
+    ///
+    /// \return Pointer to the generated RRset
+    isc::dns::RRsetPtr getRRset();
+
+    /// \brief Get the expiration time of the RRset.
+    ///
+    /// \return The expiration time of the RRset
+    ///
+    /// \todo RRsig expiration processing
+    time_t getExpireTime() const;
+
+    /// \brief Get the ttl of the RRset.
+    ///
+    /// \return The TTL of the RRset
+    uint32_t getTTL() {
+        updateTTL();
+        return (rrset_->getTTL().getValue());
+    }
+
+    /// \brief Get the hash key
+    ///
+    /// \return return hash key
+    HashKey hashKey() const {
+        return (hash_key_);
+    }
+
+    /// \brief get RRset trustworthiness
+    ///
+    /// \return return the trust level
+    RRsetTrustLevel getTrustLevel() const {
+        return (trust_level_);
+    }
+private:
+    /// \brief Update TTL according to expiration time
+    void updateTTL();
+
+private:
+    std::string entry_name_; // The entry name for this rrset entry.
+    time_t expire_time_;     // Expiration time of rrset.
+    RRsetTrustLevel trust_level_; // RRset trustworthiness.
+    boost::shared_ptr<isc::dns::RRset> rrset_;
+    HashKey hash_key_;       // RRsetEntry hash key
+};
+
+typedef boost::shared_ptr<RRsetEntry> RRsetEntryPtr;
+
+} // namespace cache
+} // namespace isc
+
+#endif // __RRSET_ENTRY_H
+

+ 66 - 0
src/lib/cache/tests/Makefile.am

@@ -0,0 +1,66 @@
+SUBDIRS = .
+
+AM_CPPFLAGS  = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cache -I$(top_builddir)/src/lib/cache
+AM_CPPFLAGS += -DTEST_DATA_SRCDIR=\"$(srcdir)/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/cache/tests/testdata\"
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+
+AM_LDFLAGS = $(PTHREAD_LDFLAGS)
+if USE_STATIC_LINK
+AM_LDFLAGS += -static
+endif
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+if USE_CLANGPP
+# see ../Makefile.am
+AM_CXXFLAGS += -Wno-unused-parameter
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES  = run_unittests.cc
+run_unittests_SOURCES  += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
+run_unittests_SOURCES  += rrset_entry_unittest.cc
+run_unittests_SOURCES  += rrset_cache_unittest.cc
+run_unittests_SOURCES  += message_cache_unittest.cc
+run_unittests_SOURCES  += message_entry_unittest.cc
+run_unittests_SOURCES  += local_zone_data_unittest.cc
+run_unittests_SOURCES  += resolver_cache_unittest.cc
+run_unittests_SOURCES  += cache_test_messagefromfile.h
+run_unittests_SOURCES  += cache_test_sectioncount.h
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+
+# NOTE: we may have to clean up this hack later (see the note in configure.ac)
+if NEED_LIBBOOST_THREAD
+run_unittests_LDADD += -lboost_thread
+endif
+
+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/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
+
+EXTRA_DIST = testdata/message_fromWire1
+EXTRA_DIST += testdata/message_fromWire2
+EXTRA_DIST += testdata/message_fromWire3
+EXTRA_DIST += testdata/message_fromWire4
+EXTRA_DIST += testdata/message_fromWire5
+EXTRA_DIST += testdata/message_fromWire6

+ 40 - 0
src/lib/cache/tests/cache_test_messagefromfile.h

@@ -0,0 +1,40 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 <vector>
+#include <dns/tests/unittest_util.h>
+#include <dns/buffer.h>
+#include <dns/message.h>
+
+using namespace isc;
+using namespace isc::dns;
+
+namespace {
+
+/// \brief Reads a Message from a data file
+///
+/// \param message Message to put the read data in
+/// \param datafile The file to read from
+void
+messageFromFile(Message& message, const char* datafile) {
+    std::vector<unsigned char> data;
+    UnitTestUtil::readWireData(datafile, data);
+
+    InputBuffer buffer(&data[0], data.size());
+    message.fromWire(buffer);
+}
+
+}   // namespace
+

+ 45 - 0
src/lib/cache/tests/cache_test_sectioncount.h

@@ -0,0 +1,45 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 <vector>
+#include <dns/tests/unittest_util.h>
+#include <dns/buffer.h>
+#include <dns/message.h>
+
+using namespace isc;
+using namespace isc::dns;
+
+namespace {
+
+/// \brief Counts the number of rrsets in the given section
+///
+/// \param msg The message to count in
+/// \param section The section to count
+///
+/// \return The number of RRsets in the given section
+int
+sectionRRsetCount(Message& msg, Message::Section section) {
+    int count = 0;
+    for (RRsetIterator rrset_iter = msg.beginSection(section);
+         rrset_iter != msg.endSection(section);
+         ++rrset_iter) {
+        ++count;
+    }
+
+    return count;
+}
+
+}   // namespace
+

+ 65 - 0
src/lib/cache/tests/local_zone_data_unittest.cc

@@ -0,0 +1,65 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 <cache/local_zone_data.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include "cache_test_messagefromfile.h"
+
+using namespace isc::cache;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+class LocalZoneDataTest: public testing::Test {
+protected:
+    LocalZoneDataTest(): local_zone_data(1) 
+    {
+    }
+
+    LocalZoneData local_zone_data;
+};
+
+TEST_F(LocalZoneDataTest, updateAndLookup) {
+    Message msg(Message::PARSE);
+    messageFromFile(msg, "message_fromWire3");
+    RRsetIterator rrset_iter = msg.beginSection(Message::SECTION_AUTHORITY);
+    Name name = (*rrset_iter)->getName();
+    RRType type = (*rrset_iter)->getType();
+
+    EXPECT_FALSE(local_zone_data.lookup(name, type));
+    local_zone_data.update((*(*rrset_iter).get()));
+    EXPECT_TRUE(local_zone_data.lookup(name, type));
+
+    // Test whether the old one is replaced
+    uint32_t ttl = (*rrset_iter)->getTTL().getValue();
+    // Make sure it is not zero
+    ASSERT_NE(ttl / 2, ttl);
+    
+    RRsetPtr rrset_ptr = local_zone_data.lookup(name, type);
+    EXPECT_EQ(ttl, rrset_ptr->getTTL().getValue());
+
+    (*rrset_iter)->setTTL(RRTTL(ttl/2));
+
+    local_zone_data.update((*(*rrset_iter).get()));
+    rrset_ptr = local_zone_data.lookup(name, type);
+    EXPECT_EQ(ttl/2, rrset_ptr->getTTL().getValue());
+}
+
+}

+ 98 - 0
src/lib/cache/tests/message_cache_unittest.cc

@@ -0,0 +1,98 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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/tests/unittest_util.h>
+#include <dns/buffer.h>
+#include "../message_cache.h"
+#include "../rrset_cache.h"
+#include "../resolver_cache.h"
+#include "cache_test_messagefromfile.h"
+
+using namespace isc::cache;
+using namespace isc;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+/// \brief Derived from base class to make it easy to test
+/// its internals.
+class DerivedMessageCache: public MessageCache {
+public:
+    DerivedMessageCache(boost::shared_ptr<RRsetCache> rrset_cache_,
+                        uint32_t cache_size, uint16_t message_class):
+        MessageCache(rrset_cache_, cache_size, message_class)
+    {}
+
+    uint16_t messages_count() {
+        return message_lru_.size();
+    }
+};
+
+class MessageCacheTest: public testing::Test {
+public:
+    MessageCacheTest(): message_parse(Message::PARSE),
+                        message_render(Message::RENDER)
+    {
+        uint16_t class_ = RRClass::IN().getCode();
+        rrset_cache_.reset(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE, class_));
+        message_cache_.reset(new DerivedMessageCache(rrset_cache_, 
+                                          MESSAGE_CACHE_DEFAULT_SIZE, class_ ));
+    }
+
+protected:
+    boost::shared_ptr<DerivedMessageCache> message_cache_;
+    RRsetCachePtr rrset_cache_;
+    Message message_parse;
+    Message message_render;
+};
+
+TEST_F(MessageCacheTest, testLookup) {
+    messageFromFile(message_parse, "message_fromWire1");
+    EXPECT_TRUE(message_cache_->update(message_parse));
+    Name qname("test.example.com.");
+    EXPECT_TRUE(message_cache_->lookup(qname, RRType::A(), message_render));
+    EXPECT_EQ(message_cache_->messages_count(), 1);
+
+    Message message_net(Message::PARSE);
+    messageFromFile(message_net, "message_fromWire2");
+    EXPECT_TRUE(message_cache_->update(message_net));
+    EXPECT_EQ(message_cache_->messages_count(), 2);
+
+    Name qname1("test.example.net.");
+    EXPECT_TRUE(message_cache_->lookup(qname1, RRType::A(), message_render));
+}
+
+TEST_F(MessageCacheTest, testUpdate) {
+    messageFromFile(message_parse, "message_fromWire4");
+    EXPECT_TRUE(message_cache_->update(message_parse));
+
+    Name qname("example.com.");
+    EXPECT_TRUE(message_cache_->lookup(qname, RRType::SOA(), message_render));
+    EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_AA));
+
+    Message new_msg(Message::PARSE);
+    messageFromFile(new_msg, "message_fromWire3");
+    EXPECT_TRUE(message_cache_->update(new_msg));
+    Message new_msg_render(Message::RENDER);
+    EXPECT_TRUE(message_cache_->lookup(qname, RRType::SOA(), new_msg_render));
+    EXPECT_TRUE(new_msg_render.getHeaderFlag(Message::HEADERFLAG_AA));
+}
+
+}   // namespace
+

+ 237 - 0
src/lib/cache/tests/message_entry_unittest.cc

@@ -0,0 +1,237 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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/tests/unittest_util.h>
+#include <dns/message.h>
+#include <dns/buffer.h>
+#include "../message_entry.h"
+#include "../rrset_cache.h"
+#include "../resolver_cache.h"
+#include "cache_test_messagefromfile.h"
+#include "cache_test_sectioncount.h"
+
+using namespace isc::cache;
+using namespace isc;
+using namespace isc::dns;
+using namespace std;
+
+static uint32_t MAX_UINT32 = numeric_limits<uint32_t>::max();    
+
+namespace {
+
+/// \brief Derived from base class to make it easy to test
+/// its internals.
+class DerivedMessageEntry: public MessageEntry {
+public:
+    DerivedMessageEntry(const isc::dns::Message& message,
+                        boost::shared_ptr<RRsetCache> rrset_cache_):
+             MessageEntry(message, rrset_cache_)
+    {}
+
+    /// \brief Wrap the protected function so that it can be tested.   
+    void parseSectionForTest(const Message& msg,
+                           const Message::Section& section,
+                           uint32_t& smaller_ttl, 
+                           uint16_t& rrset_count)
+    {
+        parseSection(msg, section, smaller_ttl, rrset_count);
+    }
+
+    RRsetTrustLevel getRRsetTrustLevelForTest(const Message& message,
+                                              const RRsetPtr rrset,
+                                              const Message::Section& section) 
+    {
+        return getRRsetTrustLevel(message, rrset, section);
+    }
+
+    bool getRRsetEntriesForTest(vector<RRsetEntryPtr> vec, time_t now) {
+        return getRRsetEntries(vec, now);
+    }
+
+    time_t getExpireTime() {
+        return expire_time_;
+    }
+
+};
+
+class MessageEntryTest: public testing::Test {
+public:
+    MessageEntryTest(): class_(1),
+                        message_parse(Message::PARSE),
+                        message_render(Message::RENDER)
+    {
+        
+        rrset_cache_.reset(new RRsetCache(RRSET_CACHE_DEFAULT_SIZE, class_));
+    }
+
+protected:
+    uint16_t class_;
+    RRsetCachePtr rrset_cache_;
+    Message message_parse;
+    Message message_render;
+};
+
+TEST_F(MessageEntryTest, testParseRRset) {
+    messageFromFile(message_parse, "message_fromWire3");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    uint32_t ttl = MAX_UINT32;
+    uint16_t rrset_count = 0;
+    message_entry.parseSectionForTest(message_parse, Message::SECTION_ANSWER, ttl, rrset_count);
+    EXPECT_EQ(ttl, 21600);
+    EXPECT_EQ(rrset_count, 1);
+
+    ttl = MAX_UINT32;
+    message_entry.parseSectionForTest(message_parse, Message::SECTION_AUTHORITY, ttl, rrset_count);
+    EXPECT_EQ(ttl, 21600);
+    EXPECT_EQ(rrset_count, 1);
+
+    ttl = MAX_UINT32;
+    message_entry.parseSectionForTest(message_parse, Message::SECTION_ADDITIONAL, ttl, rrset_count);
+    EXPECT_EQ(ttl, 10800);
+    EXPECT_EQ(rrset_count, 5);
+}
+
+TEST_F(MessageEntryTest, testGetRRsetTrustLevel_AA) {
+    messageFromFile(message_parse, "message_fromWire3");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    
+
+    RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
+    RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                                    *rrset_iter,
+                                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+
+    rrset_iter = message_parse.beginSection(Message::SECTION_AUTHORITY);
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_AUTHORITY);
+    EXPECT_EQ(level, RRSET_TRUST_AUTHORITY_AA);
+
+    rrset_iter = message_parse.beginSection(Message::SECTION_ADDITIONAL);
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_ADDITIONAL);
+    EXPECT_EQ(level, RRSET_TRUST_ADDITIONAL_AA);
+}
+
+TEST_F(MessageEntryTest, testGetRRsetTrustLevel_NONAA) {
+    messageFromFile(message_parse, "message_fromWire4");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
+    RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                                    *rrset_iter,
+                                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_NONAA);
+
+    rrset_iter = message_parse.beginSection(Message::SECTION_AUTHORITY);
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_AUTHORITY);
+    EXPECT_EQ(level, RRSET_TRUST_AUTHORITY_NONAA);
+
+    rrset_iter = message_parse.beginSection(Message::SECTION_ADDITIONAL);
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_ADDITIONAL);
+    EXPECT_EQ(level, RRSET_TRUST_ADDITIONAL_NONAA);
+}
+
+TEST_F(MessageEntryTest, testGetRRsetTrustLevel_CNAME) {
+    messageFromFile(message_parse, "message_fromWire5");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
+    RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                                    *rrset_iter,
+                                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+
+    ++rrset_iter; // Get the rrset after the first cname rrset.
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+}
+
+TEST_F(MessageEntryTest, testGetRRsetTrustLevel_DNAME) {
+    messageFromFile(message_parse, "message_fromWire6");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    RRsetIterator rrset_iter = message_parse.beginSection(Message::SECTION_ANSWER);
+    RRsetTrustLevel level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                                    *rrset_iter,
+                                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+
+    ++rrset_iter; // Get the rrset after the first dname rrset.
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+
+    ++rrset_iter; // Get the second cname rrset
+    level = message_entry.getRRsetTrustLevelForTest(message_parse,
+                                                    *rrset_iter,
+                                                    Message::SECTION_ANSWER);
+    EXPECT_EQ(level, RRSET_TRUST_ANSWER_AA);
+}
+
+// We only test the expire_time of the message entry.
+// The test for genMessage() will make sure whether InitMessageEntry()
+// is right
+TEST_F(MessageEntryTest, testInitMessageEntry) {
+    messageFromFile(message_parse, "message_fromWire3");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    time_t expire_time = message_entry.getExpireTime();
+    // 1 second should be enough to do the compare
+    EXPECT_TRUE((time(NULL) + 10801) > expire_time);
+}
+
+TEST_F(MessageEntryTest, testGetRRsetEntries) {
+    messageFromFile(message_parse, "message_fromWire3");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    vector<RRsetEntryPtr> vec;
+    
+    // the time is bigger than the smallest expire time of 
+    // the rrset in message.
+    time_t expire_time = time(NULL) + 10802;
+    EXPECT_FALSE(message_entry.getRRsetEntriesForTest(vec, expire_time));
+}
+
+TEST_F(MessageEntryTest, testGenMessage) {
+    messageFromFile(message_parse, "message_fromWire3");
+    DerivedMessageEntry message_entry(message_parse, rrset_cache_);
+    time_t expire_time = message_entry.getExpireTime();
+    
+    Message msg(Message::RENDER);
+    EXPECT_FALSE(message_entry.genMessage(expire_time + 2, msg));
+    message_entry.genMessage(time(NULL), msg);
+    // Check whether the generated message is same with cached one.
+    
+    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
+    EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_TC));
+    EXPECT_EQ(1, sectionRRsetCount(msg, Message::SECTION_ANSWER)); 
+    EXPECT_EQ(1, sectionRRsetCount(msg, Message::SECTION_AUTHORITY)); 
+    EXPECT_EQ(5, sectionRRsetCount(msg, Message::SECTION_ADDITIONAL)); 
+
+    // Check the rrset in answer section.
+    EXPECT_EQ(1, msg.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(5, msg.getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_EQ(7, msg.getRRCount(Message::SECTION_ADDITIONAL));
+}
+
+}   // namespace

+ 138 - 0
src/lib/cache/tests/resolver_cache_unittest.cc

@@ -0,0 +1,138 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 "resolver_cache.h"
+#include "cache_test_messagefromfile.h"
+
+using namespace isc::cache;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+class ResolverCacheTest: public testing::Test {
+public:
+    ResolverCacheTest() {
+        vector<CacheSizeInfo> vec;
+        CacheSizeInfo class_in(RRClass::IN(), 100, 200);
+        CacheSizeInfo class_ch(RRClass::CH(), 100, 200);
+        vec.push_back(class_in);
+        vec.push_back(class_ch);
+        cache = new ResolverCache(vec);
+    }
+
+    ~ResolverCacheTest() {
+        delete cache;
+    }
+
+    ResolverCache* cache;
+};
+
+TEST_F(ResolverCacheTest, testUpdateMessage) {
+    Message msg(Message::PARSE);
+    messageFromFile(msg, "message_fromWire3");
+    cache->update(msg);
+
+    Name qname("example.com.");
+
+    msg.makeResponse();
+    EXPECT_TRUE(cache->lookup(qname, RRType::SOA(), RRClass::IN(), msg));
+    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
+
+    // Test whether the old message can be updated
+    Message new_msg(Message::PARSE);
+    messageFromFile(new_msg, "message_fromWire4");
+    cache->update(new_msg);
+
+    new_msg.makeResponse();
+    EXPECT_TRUE(cache->lookup(qname, RRType::SOA(), RRClass::IN(), new_msg));
+    EXPECT_FALSE(new_msg.getHeaderFlag(Message::HEADERFLAG_AA));
+}
+#if 0
+TEST_F(ResolverCacheTest, testUpdateRRset) {
+    Message msg(Message::PARSE);
+    messageFromFile(msg, "message_fromWire3");
+    cache->update(msg);
+
+    Name qname("example.com.");
+
+    msg.makeResponse();
+    EXPECT_TRUE(cache->lookup(qname, RRType::SOA(), RRClass::IN(), msg));
+
+    Message except_msg(Message::RENDER);
+    EXPECT_THROW(cache->lookup(qname, RRType::SOA(), RRClass::IN(), except_msg), 
+                 MessageNoQuestionSection);
+
+    // Get one rrset in the message, then use it to 
+    // update rrset cache-> Test whether the local zone
+    // data is updated.
+    RRsetIterator iter = msg.beginSection(Message::SECTION_AUTHORITY);
+    RRsetPtr rrset_ptr = *iter;
+    cache->update(rrset_ptr);
+
+    Message new_msg(Message::RENDER);
+    Question question(qname, klass, RRType::NS());
+    new_msg.addQuestion(question);
+    EXPECT_TRUE(cache->lookup(qname, RRType::NS(), RRClass::IN(), new_msg));
+    EXPECT_EQ(0, sectionRRsetCount(new_msg, Message::SECTION_AUTHORITY));
+    EXPECT_EQ(0, sectionRRsetCount(new_msg, Message::SECTION_ADDITIONAL));
+}
+
+TEST_F(ResolverCacheTest, testLookupUnsupportedClass) {
+    Message msg(Message::PARSE);
+    messageFromFile(msg, "message_fromWire3");
+    cache->update(msg);
+
+    Name qname("example.com.");
+
+    msg.makeResponse();
+    EXPECT_FALSE(cache->lookup(qname, RRType::SOA(), RRClass::CH(), msg));
+    EXPECT_FALSE(cache->lookup(qname, RRType::SOA(), RRClass::CH()));
+}
+
+TEST_F(ResolverCacheTest, testLookupClosestRRset) {
+    Message msg(Message::PARSE);
+    messageFromFile(msg, "message_fromWire3");
+    cache->update(msg);
+
+    Name qname("www.test.example.com.");
+
+    RRsetPtr rrset_ptr = cache->lookupClosestRRset(qname, RRType::NS(),
+                                                  RRClass::IN());
+    EXPECT_TRUE(rrset_ptr);
+    EXPECT_EQ(rrset_ptr->getName(), Name("example.com."));
+
+    rrset_ptr = cache->lookupClosestRRset(Name("example.com."),
+                                         RRType::NS(), RRClass::IN());
+    EXPECT_TRUE(rrset_ptr);
+    EXPECT_EQ(rrset_ptr->getName(), Name("example.com."));
+
+    rrset_ptr = cache->lookupClosestRRset(Name("com."),
+                                         RRType::NS(), RRClass::IN());
+    EXPECT_FALSE(rrset_ptr);
+}
+
+TEST_F(ResolverCacheTest, testHasClass) {
+    EXPECT_TRUE(cache->getClassCache(RRClass::IN()));
+    EXPECT_TRUE(cache->getClassCache(RRClass::CH()));
+    EXPECT_FALSE(cache->getClassCache(RRClass::ANY()));
+}
+#endif
+
+}

+ 84 - 0
src/lib/cache/tests/rrset_cache_unittest.cc

@@ -0,0 +1,84 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 <cache/resolver_cache.h>
+#include <cache/cache_entry_key.h>
+#include <cache/rrset_entry.h>
+#include <cache/rrset_cache.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rrset.h>
+
+using namespace isc::cache;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+class RRsetCacheTest : public testing::Test {
+protected:
+    RRsetCacheTest():
+        cache(RRSET_CACHE_DEFAULT_SIZE, RRClass::IN().getCode()),
+        name("example.com"),
+        rrset1(name, RRClass::IN(), RRType::A(), RRTTL(20)),
+        rrset2(name, RRClass::IN(), RRType::A(), RRTTL(10)),
+        rrset_entry1(rrset1, RRSET_TRUST_ADDITIONAL_AA),
+        rrset_entry2(rrset2, RRSET_TRUST_PRIM_ZONE_NONGLUE)
+    {
+    }
+
+    RRsetCache cache;
+    Name name;
+    RRset rrset1;
+    RRset rrset2;
+    RRsetEntry rrset_entry1;
+    RRsetEntry rrset_entry2;
+};
+
+TEST_F(RRsetCacheTest, lookup) {
+    const RRType& type = RRType::A();
+    EXPECT_TRUE(cache.lookup(name, type) == NULL);
+
+    cache.update(rrset1, rrset_entry1.getTrustLevel());
+    RRsetEntryPtr rrset_entry_ptr = cache.lookup(name, type);
+    EXPECT_EQ(rrset_entry_ptr->getTrustLevel(), rrset_entry1.getTrustLevel());
+    EXPECT_EQ(rrset_entry_ptr->getRRset()->getName(), rrset_entry1.getRRset()->getName());
+    EXPECT_EQ(rrset_entry_ptr->getRRset()->getType(), rrset_entry1.getRRset()->getType());
+    EXPECT_EQ(rrset_entry_ptr->getRRset()->getClass(), rrset_entry1.getRRset()->getClass());
+}
+
+TEST_F(RRsetCacheTest, update) {
+    const RRType& type = RRType::A();
+
+    cache.update(rrset1, rrset_entry1.getTrustLevel());
+    RRsetEntryPtr rrset_entry_ptr = cache.lookup(name, type);
+    EXPECT_EQ(rrset_entry_ptr->getTrustLevel(), rrset_entry1.getTrustLevel());
+
+    cache.update(rrset2, rrset_entry2.getTrustLevel());
+    rrset_entry_ptr = cache.lookup(name, type);
+    // The trust level should be updated
+    EXPECT_EQ(rrset_entry_ptr->getTrustLevel(), rrset_entry2.getTrustLevel());
+
+    cache.update(rrset1, rrset_entry1.getTrustLevel());
+    // The trust level should not be updated
+    EXPECT_EQ(rrset_entry_ptr->getTrustLevel(), rrset_entry2.getTrustLevel());
+}
+
+}

+ 107 - 0
src/lib/cache/tests/rrset_entry_unittest.cc

@@ -0,0 +1,107 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// 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 <cache/cache_entry_key.h>
+#include <cache/rrset_entry.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rrset.h>
+
+using namespace isc::cache;
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+class GenCacheKeyTest: public testing::Test {
+};
+
+TEST_F(GenCacheKeyTest, genCacheEntryKey1) {
+    string name = "example.com.";
+    uint16_t type = 12;
+    string name_type = "example.com.12";
+
+    EXPECT_EQ(name_type, genCacheEntryName(name, type));
+}
+
+TEST_F(GenCacheKeyTest, genCacheEntryKey2) {
+    Name name("example.com");
+    RRType type(1234);
+    string keystr = "example.com.1234";
+    EXPECT_EQ(keystr, genCacheEntryName(name, type));
+}
+
+class DerivedRRsetEntry: public RRsetEntry {
+public:
+    DerivedRRsetEntry(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) : RRsetEntry(rrset, level) {};
+
+    void updateTTLForTest() {
+
+    }
+};
+
+#define TEST_TTL 100
+class RRsetEntryTest : public ::testing::Test {
+protected:
+    RRsetEntryTest():
+        name("test.example.com"),
+        rrset(name, RRClass::IN(), RRType::A(), RRTTL(TEST_TTL)),
+        trust_level(RRSET_TRUST_ADDITIONAL_AA),
+        rrset_entry(rrset, trust_level)
+    {
+    }
+    Name name;
+    RRset rrset;
+    RRsetTrustLevel trust_level;
+    RRsetEntry rrset_entry;
+};
+
+TEST_F(RRsetEntryTest, constructor) {
+    EXPECT_EQ(trust_level, rrset_entry.getTrustLevel());
+    EXPECT_EQ(rrset.getName(), rrset_entry.getRRset()->getName());
+    EXPECT_EQ(rrset.getClass(), rrset_entry.getRRset()->getClass());
+    EXPECT_EQ(rrset.getType(), rrset_entry.getRRset()->getType());
+    EXPECT_EQ(rrset.getRdataCount(), rrset_entry.getRRset()->getRdataCount());
+}
+
+TEST_F(RRsetEntryTest, updateTTL) {
+    uint32_t ttl = rrset_entry.getTTL();
+    sleep(1);
+    // The TTL should be decreased
+    EXPECT_TRUE(rrset_entry.getTTL() < ttl);
+}
+
+TEST_F(RRsetEntryTest, TTLExpire) {
+    RRset exp_rrset(name, RRClass::IN(), RRType::A(), RRTTL(1));
+    RRsetEntry rrset_entry(exp_rrset, RRSET_TRUST_ANSWER_AA);
+    sleep(1);
+    uint32_t ttl = rrset_entry.getTTL();
+    EXPECT_LT(ttl, 1);
+    sleep(1);
+    ttl = rrset_entry.getTTL();
+    EXPECT_LT(ttl, 1);
+}
+
+TEST_F(RRsetEntryTest, getExpireTime){
+    uint32_t exp_time = time(NULL) + TEST_TTL;
+    EXPECT_EQ(exp_time, rrset_entry.getExpireTime());
+}
+
+}   // namespace
+

+ 9 - 1
src/lib/asiolink/internal/tests/run_unittests.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2009  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
@@ -12,10 +12,18 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
+// $Id: run_unittests.cc 3020 2010-09-26 03:47:26Z jinmei $
+#include <config.h>
+
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
+#include <dns/tests/unittest_util.h>
+
 int
 int
 main(int argc, char* argv[]) {
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::InitGoogleTest(&argc, argv);
+    isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR);
+    isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
+
     return (RUN_ALL_TESTS());
     return (RUN_ALL_TESTS());
 }
 }

+ 22 - 0
src/lib/cache/tests/testdata/message_fromWire1

@@ -0,0 +1,22 @@
+#
+# A simple DNS response message
+# ID = 0x1035
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=2, other COUNTS=0
+# Question: test.example.com. IN A
+# Answer:
+#  test.example.com. 3600 IN A 192.0.2.1
+#  test.example.com. 7200 IN A 192.0.2.2
+#
+1035 8500
+0001 0002 0000 0000
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0001 0001
+# same name, fully compressed
+c0 0c
+# TTL=3600, A, IN, RDLENGTH=4, RDATA
+0001 0001 00000e10 0004 c0 00 02 01
+# mostly same, with the slight difference in RDATA and TTL
+c0 0c
+0001 0001 00001c20 0004 c0 00 02 02

+ 22 - 0
src/lib/cache/tests/testdata/message_fromWire2

@@ -0,0 +1,22 @@
+#
+# A simple DNS response message
+# ID = 0x1035
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=2, other COUNTS=0
+# Question: test.example.net. IN A
+# Answer:
+#  test.example.net. 3600 IN A 192.0.2.1
+#  test.example.net. 7200 IN A 192.0.2.2
+#
+1035 8500
+0001 0002 0000 0000
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) n  e  t  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 6e 65 74 00
+0001 0001
+# same name, fully compressed
+c0 0c
+# TTL=3600, A, IN, RDLENGTH=4, RDATA
+0001 0001 00000e10 0004 c0 00 02 01
+# mostly same, with the slight difference in RDATA and TTL
+c0 0c
+0001 0001 00001c20 0004 c0 00 02 02

+ 76 - 0
src/lib/cache/tests/testdata/message_fromWire3

@@ -0,0 +1,76 @@
+#
+# A simple DNS response message
+# ID = 0x0513
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=1, AUTHORITY COUNT=5, ADDITIONAL COUNT=7
+# Question: example.com. IN SOA
+# ANSWER:
+#   ;; QUESTION SECTION:
+#   ;example.com.                   IN      SOA
+
+#   ;; ANSWER SECTION:
+#   example.com.            21600   IN      SOA     a.dns.example.com. root.example.com. 2009070811 7200 3600 2419200 21600
+
+#   ;; AUTHORITY SECTION:
+#   example.com.            21600   IN      NS      b.dns.example.com.
+#   example.com.            21600   IN      NS      c.dns.example.com.
+#   example.com.            21600   IN      NS      a.dns.example.com.
+#   example.com.            21600   IN      NS      e.dns.example.com.
+#   example.com.            21600   IN      NS      d.dns.example.com.
+
+#    ;; ADDITIONAL SECTION:
+#    a.dns.example.com.      21600   IN      A       1.1.1.1
+#    a.dns.example.com.      21600   IN      A       2.2.2.2
+#    b.dns.example.com.      21600   IN      A       3.3.3.3
+#    c.dns.example.com.      10800   IN      A       4.4.4.4
+#    d.dns.example.com.      43200   IN      A       5.5.5.5
+#    e.dns.example.com.      21600   IN      A       7.7.7.7
+#    e.dns.example.com.      21600   IN      A       6.6.6.6
+
+0513 8500
+0001 0001 0005 0007
+#(7) e x  a  m  p  l  e (3) c  o  m  .
+ 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0006 0001
+# same name, fully compressed
+c0 0c
+# SOA IN  TTL=6h   RDLENGTH=35 rdata
+0006 0001 00005460 0023 01 61 03 64 6e 73 c0 0c 04 72 6f 6f 74 c0 0c 77 bf fc db 00 00 1c 20 00 00 0e 10 00 24 ea 00 00 00 54 60
+#Authority section begin
+c0 0c
+# NS IN   TTL=6h   RDLENGTH=4  b.dns.example.com.
+0002 0001 00005460 0004 01 62 c0 2b
+# NS IN   TTL=6h  c.dns.example.com.
+c0 0c
+0002 0001 00005460 00 04 01 63 c0 2b
+# NS IN a.dns.example.com.
+c0 0c
+0002 0001 00005460 00 02 c0 29
+# NS IN e.dns.example.com.
+c0 0c
+0002 0001 00005460 0004 01 65 c0 2b
+# NS IN d.dns.example.com.
+c0 0c
+0002 0001 00005460 0004 01 64 c0 2b
+# additional section begin
+# a.dns.example.com. A
+c0 29
+0001 0001 00005460 0004 01 01 01 01
+# a.dns.example.com. A
+c0 29
+0001 0001 00005460 0004 02 02 02 02
+#b.dns.example.com.  A
+c0 58
+0001 0001 00002A30 0004 03 03 03 03
+#c.dns.example.com.  A
+c0 68
+0001 0001 00005460 0004 04 04 04 04
+# d.dns.example.com. A
+c0 96
+0001 0001 0000A8C0 0004 05 05 05 05
+# e.dns.example.com. A
+c0 86
+0001 0001 00005460 0004 07 07 07 07
+# e.dns.example.com. A
+c0 86
+0001 0001 00005460 0004 06 06 06 06

+ 80 - 0
src/lib/cache/tests/testdata/message_fromWire4

@@ -0,0 +1,80 @@
+# Note: This message is same with message_fromWire3, except
+#       AA bit is not set. There should be a better way to
+#       avoid the duplicated file by clear the AA bit flags
+#       after reading the message from message_fromWire4.
+# 
+# A simple DNS response message
+# ID = 0x0513
+# QR=1 (response), Opcode=0, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=1, AUTHORITY COUNT=5, ADDITIONAL COUNT=7
+# Question: example.com. IN SOA
+# ANSWER:
+#   ;; QUESTION SECTION:
+#   ;example.com.                   IN      SOA
+
+#   ;; ANSWER SECTION:
+#   example.com.            21600   IN      SOA     a.dns.example.com. root.example.com. 2009070811 7200 3600 2419200 21600
+
+#   ;; AUTHORITY SECTION:
+#   example.com.            21600   IN      NS      b.dns.example.com.
+#   example.com.            21600   IN      NS      c.dns.example.com.
+#   example.com.            21600   IN      NS      a.dns.example.com.
+#   example.com.            21600   IN      NS      e.dns.example.com.
+#   example.com.            21600   IN      NS      d.dns.example.com.
+
+#    ;; ADDITIONAL SECTION:
+#    a.dns.example.com.      21600   IN      A       1.1.1.1
+#    a.dns.example.com.      21600   IN      A       2.2.2.2
+#    b.dns.example.com.      21600   IN      A       3.3.3.3
+#    c.dns.example.com.      10800   IN      A       4.4.4.4
+#    d.dns.example.com.      43200   IN      A       5.5.5.5
+#    e.dns.example.com.      21600   IN      A       7.7.7.7
+#    e.dns.example.com.      21600   IN      A       6.6.6.6
+
+0513 8100
+0001 0001 0005 0007
+#(7) e x  a  m  p  l  e (3) c  o  m  .
+ 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0006 0001
+# same name, fully compressed
+c0 0c
+# SOA IN  TTL=6h   RDLENGTH=35 rdata
+0006 0001 00005460 0023 01 61 03 64 6e 73 c0 0c 04 72 6f 6f 74 c0 0c 77 bf fc db 00 00 1c 20 00 00 0e 10 00 24 ea 00 00 00 54 60
+#Authority section begin
+c0 0c
+# NS IN   TTL=6h   RDLENGTH=4  b.dns.example.com.
+0002 0001 00005460 0004 01 62 c0 2b
+# NS IN   TTL=6h  c.dns.example.com.
+c0 0c
+0002 0001 00005460 00 04 01 63 c0 2b
+# NS IN a.dns.example.com.
+c0 0c
+0002 0001 00005460 00 02 c0 29
+# NS IN e.dns.example.com.
+c0 0c
+0002 0001 00005460 0004 01 65 c0 2b
+# NS IN d.dns.example.com.
+c0 0c
+0002 0001 00005460 0004 01 64 c0 2b
+# additional section begin
+# a.dns.example.com. A
+c0 29
+0001 0001 00005460 0004 01 01 01 01
+# a.dns.example.com. A
+c0 29
+0001 0001 00005460 0004 02 02 02 02
+#b.dns.example.com.  A
+c0 58
+0001 0001 00002A30 0004 03 03 03 03
+#c.dns.example.com.  A
+c0 68
+0001 0001 00005460 0004 04 04 04 04
+# d.dns.example.com. A
+c0 96
+0001 0001 0000A8C0 0004 05 05 05 05
+# e.dns.example.com. A
+c0 86
+0001 0001 00005460 0004 07 07 07 07
+# e.dns.example.com. A
+c0 86
+0001 0001 00005460 0004 06 06 06 06

+ 36 - 0
src/lib/cache/tests/testdata/message_fromWire5

@@ -0,0 +1,36 @@
+#
+# A simple DNS response message
+# ID = 0x07b2
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=2, other COUNTS=0
+# Question: a.example.net. IN A
+# Answer:
+#    ANSWER SECTION:
+#    a.example.com.          21600   IN      CNAME   cname.example.com.
+#    cname.example.com.      21600   IN      A       1.1.1.1
+#
+#    AUTHORITY SECTION:
+#    example.com.            21600   IN      NS      a.dns.example.com.
+#
+#    ADDITIONAL SECTION:
+#    a.dns.example.com.      21600   IN      A       1.1.1.1
+#
+07b2 8500
+0001 0002 0001 0001
+#(1) a (7)  e  x  a  m  p  l  e  (3) c o  m  .
+ 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# A  IN
+0001 0001
+#
+c0 0c
+#CNAME IN  TTL     RDATA_LEN
+0005 0001 00005460 0008 05 63 6e 61 6d 65 c0 0e
+#
+c0 2b
+0001 0001 00005460 0004 01 01 01 01
+#
+c0 0e
+0002 0001 00005460 0008 01 61 03 64 6e 73 c0 0e
+#
+c0 4f
+0001 0001 00005460 0004 01 01 01 01

+ 40 - 0
src/lib/cache/tests/testdata/message_fromWire6

@@ -0,0 +1,40 @@
+#
+# A simple DNS response message
+# ID = 0x005e
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=2, other COUNTS=0
+# Question: a.d.example.net. IN A
+# Answer:
+#    ;; ANSWER SECTION:
+#    d.example.com.          21600   IN      DNAME   dname.example.com.
+#    a.d.example.com.        21600   IN      CNAME   a.dname.example.com.
+#    a.dname.example.com.    21600   IN      A       1.1.1.1
+#
+#    ;; AUTHORITY SECTION:
+#    example.com.            21600   IN      NS      a.dns.example.com.
+#
+#    ;; ADDITIONAL SECTION:
+#    a.dns.example.com.      21600   IN      A       1.1.1.1
+#
+#
+005e 8500
+0001 0003 0001 0001
+#(1)a (1) b (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 01 61 01 64 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# A  IN
+0001 0001
+#
+c0 0e
+0027 0001 00005460 0013 05 64 6e 61 6d 65 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# 
+c0 0c
+0005 0001 00005460 0004 01 61 c0 2d
+#
+c0 4c
+0001 0001 00005460 0004 01 01 01 01
+#
+c0 33
+0002 0001 00005460 0008 01 61 03 64 6e 73 c0 33
+#
+c0 6c
+0001 0001 00005460 0004 01 01 01 01

+ 0 - 0
src/lib/datasrc/memory_datasrc.cc


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