Browse Source

[trac575] Merge branch 'master' into work/ports/auth

Master contains fix of #338, which influences this code quite a lot.
The merge includes not only the code-level conflict resolutions, but
also logic-level conflicts and enabling of the tests that failed because
of #338.

Conflicts:
	src/bin/auth/tests/Makefile.am
	src/bin/resolver/Makefile.am
	src/bin/resolver/resolver.cc
	src/bin/resolver/tests/Makefile.am
	src/bin/resolver/tests/resolver_config_unittest.cc
	src/lib/Makefile.am
Michal 'vorner' Vaner 14 years ago
parent
commit
fa05a53484
100 changed files with 2973 additions and 899 deletions
  1. 39 0
      ChangeLog
  2. 7 0
      Makefile.am
  3. 6 6
      README
  4. 79 114
      configure.ac
  5. 44 38
      doc/guide/bind10-guide.html
  6. 27 24
      doc/guide/bind10-guide.xml
  7. 2 1
      src/bin/auth/Makefile.am
  8. 1 0
      src/bin/auth/benchmarks/Makefile.am
  9. 1 1
      src/bin/auth/config.cc
  10. 1 0
      src/bin/auth/tests/Makefile.am
  11. 1 2
      src/bin/auth/tests/config_unittest.cc
  12. 1 1
      src/bin/auth/tests/query_unittest.cc
  13. 1 2
      src/bin/bind10/Makefile.am
  14. 1 1
      src/bin/bind10/bind10.8
  15. 86 8
      src/bin/bind10/bind10.py.in
  16. 5 1
      src/bin/bind10/bind10.xml
  17. 211 72
      src/bin/bind10/tests/bind10_test.py
  18. 1 1
      src/bin/bindctl/Makefile.am
  19. 1 1
      src/bin/bindctl/bindctl-source.py.in
  20. 0 1
      src/bin/cfgmgr/Makefile.am
  21. 1 2
      src/bin/cmdctl/Makefile.am
  22. 5 2
      src/bin/host/host.cc
  23. 0 1
      src/bin/msgq/Makefile.am
  24. 1 1
      src/bin/msgq/msgq.py.in
  25. 3 1
      src/bin/resolver/Makefile.am
  26. 25 24
      src/bin/resolver/b10-resolver.8
  27. 42 11
      src/bin/resolver/b10-resolver.xml
  28. 7 14
      src/bin/resolver/resolver.cc
  29. 2 0
      src/bin/resolver/tests/Makefile.am
  30. 2 2
      src/bin/resolver/tests/resolver_config_unittest.cc
  31. 1 2
      src/bin/stats/Makefile.am
  32. 1 2
      src/bin/usermgr/Makefile.am
  33. 1 2
      src/bin/xfrin/Makefile.am
  34. 1 2
      src/bin/xfrout/Makefile.am
  35. 1 1
      src/bin/zonemgr/Makefile.am
  36. 15 0
      src/cppcheck-suppress.lst
  37. 1 1
      src/lib/Makefile.am
  38. 21 14
      src/lib/asiolink/Makefile.am
  39. 80 1
      src/lib/asiolink/README
  40. 1 14
      src/lib/asiolink/asiolink.h
  41. 3 0
      src/lib/asiolink/dns_server.h
  42. 13 5
      src/lib/asiolink/dns_service.cc
  43. 6 0
      src/lib/asiolink/dns_service.h
  44. 51 0
      src/lib/asiolink/dummy_io_cb.h
  45. 57 20
      src/lib/asiolink/udp_query.h
  46. 5 5
      src/lib/asiolink/interval_timer.cc
  47. 4 1
      src/lib/asiolink/io_address.cc
  48. 3 3
      src/lib/asiolink/io_address.h
  49. 309 0
      src/lib/asiolink/io_asio_socket.h
  50. 2 1
      src/lib/asiolink/io_endpoint.cc
  51. 3 3
      src/lib/asiolink/io_endpoint.h
  52. 35 0
      src/lib/asiolink/io_error.h
  53. 193 0
      src/lib/asiolink/io_fetch.cc
  54. 226 0
      src/lib/asiolink/io_fetch.h
  55. 4 3
      src/lib/asiolink/io_message.h
  56. 5 4
      src/lib/asiolink/io_service.cc
  57. 4 7
      src/lib/asiolink/io_socket.h
  58. 122 39
      src/lib/asiolink/recursive_query.cc
  59. 4 0
      src/lib/asiolink/recursive_query.h
  60. 31 8
      src/lib/asiolink/tcp_server.cc
  61. 4 0
      src/lib/asiolink/tcp_server.h
  62. 241 16
      src/lib/asiolink/tcp_socket.h
  63. 16 8
      src/lib/asiolink/tests/Makefile.am
  64. 6 2
      src/lib/asiolink/tests/interval_timer_unittest.cc
  65. 7 1
      src/lib/asiolink/tests/ioaddress_unittest.cc
  66. 2 1
      src/lib/asiolink/tests/ioendpoint_unittest.cc
  67. 191 0
      src/lib/asiolink/tests/io_fetch_unittest.cc
  68. 1 0
      src/lib/asiolink/tests/io_service_unittest.cc
  69. 4 1
      src/lib/asiolink/tests/iosocket_unittest.cc
  70. 10 5
      src/lib/asiolink/tests/recursive_query_unittest.cc
  71. 55 0
      src/lib/asiolink/tests/udp_endpoint_unittest.cc
  72. 0 145
      src/lib/asiolink/tests/udp_query_unittest.cc
  73. 287 0
      src/lib/asiolink/tests/udp_socket_unittest.cc
  74. 21 8
      src/lib/asiolink/udp_endpoint.h
  75. 0 189
      src/lib/asiolink/udp_query.cc
  76. 41 10
      src/lib/asiolink/udp_server.cc
  77. 4 0
      src/lib/asiolink/udp_server.h
  78. 239 11
      src/lib/asiolink/udp_socket.h
  79. 11 8
      src/lib/bench/tests/benchmark_unittest.cc
  80. 1 1
      src/lib/bench/tests/loadquery_unittest.cc
  81. 2 0
      src/lib/cache/message_cache.cc
  82. 2 0
      src/lib/cache/message_cache.h
  83. 2 0
      src/lib/cache/rrset_cache.cc
  84. 3 1
      src/lib/cache/rrset_cache.h
  85. 1 1
      src/lib/cc/tests/session_unittests.cc
  86. 2 2
      src/lib/config/ccsession.cc
  87. 1 1
      src/lib/config/module_spec.cc
  88. 1 1
      src/lib/config/tests/ccsession_unittests.cc
  89. 2 2
      src/lib/config/tests/module_spec_unittests.cc
  90. 1 1
      src/lib/datasrc/data_source.cc
  91. 2 2
      src/lib/datasrc/memory_datasrc.cc
  92. 1 1
      src/lib/datasrc/tests/rbtree_unittest.cc
  93. 1 3
      src/lib/dns/message.cc
  94. 1 2
      src/lib/dns/rdata/generic/nsec3_50.cc
  95. 1 2
      src/lib/dns/rdata/generic/nsec_47.cc
  96. 1 1
      src/lib/dns/tests/name_unittest.cc
  97. 0 1
      src/lib/log/Makefile.am
  98. 1 1
      src/lib/log/strutil.cc
  99. 1 1
      src/lib/log/strutil.h
  100. 0 0
      src/lib/nsas/Makefile.am

+ 39 - 0
ChangeLog

@@ -1,3 +1,42 @@
+  182.	[func]		jinmei
+	Support cppcheck for static code check on C++ code.  If cppcheck
+	is available, 'make cppcheck' on the top source directory will run
+	the checker and should cleanly complete with an exit code of 0
+	(at least with cppcheck 1.47).
+	Note: the suppression list isn't included in the final
+	distributions.  It should be created by hand or retrieved from
+	the git repository.
+	(Trac #613, git b973f67520682b63ef38b1451d309be9f4f4b218)
+
+  181.  [func]      feng
+	Add stop interface into dns server, so we can stop each running
+	server individually. With it, user can reconfigure her running server
+	with different ip address or port.
+	(Trac #388, git 6df94e2db856c1adc020f658cc77da5edc967555)
+
+  180.  [build]     jreed
+	Fix custom DESTDIR for make install. Patch from Jan Engelhardt.
+	(Trac #629, git 5ac67ede03892a5eacf42ce3ace1e4e376164c9f)
+
+bind10-devel-20110224 released on February 24, 2011
+
+  179.  [func]      vorner
+	It is possible to start and stop resolver and authoritative
+	server without restart of the whole system. Change of the
+	configuration (Boss/start_auth and Boss/start_resolver) is
+	enough.
+	(Trac #565, git 0ac0b4602fa30852b0d86cc3c0b4730deb1a58fe)
+
+  178.  [func]      jelte
+	Resolver now makes (limited) use of the cache
+	(Trac #491, git 8b41f77f0099ddc7ca7d34d39ad8c39bb1a8363c)
+
+  177.  [func]      stephen
+	The upstream fetch code in asiolink is now protocol agnostic to
+	allow for the addition of fallback to TCP if a fetch response
+	indicates truncation.
+	(Trac #554, git 9739cbce2eaffc7e80640db58a8513295cf684de)
+
   176.  [func]      zhang likun
   176.  [func]      zhang likun
 	src/lib/cache: Rename one interface: from lookupClosestRRset()
 	src/lib/cache: Rename one interface: from lookupClosestRRset()
 	to lookupDeepestNS(), and remove one parameter of it.
 	to lookupDeepestNS(), and remove one parameter of it.

+ 7 - 0
Makefile.am

@@ -70,6 +70,13 @@ clean-coverage: clean-cpp-coverage clean-python-coverage
 
 
 report-coverage: report-cpp-coverage report-python-coverage
 report-coverage: report-cpp-coverage report-python-coverage
 
 
+# for static C++ check using cppcheck (when available)
+cppcheck:
+	cppcheck --enable=all --suppressions src/cppcheck-suppress.lst \
+		--quiet --error-exitcode=1 \
+		--template '{file}:{line}: check_fail: {message} ({severity},{id})' \
+		src
+
 #### include external sources in the distributed tarball:
 #### include external sources in the distributed tarball:
 EXTRA_DIST = ext/asio/README
 EXTRA_DIST = ext/asio/README
 EXTRA_DIST += ext/asio/asio/local/stream_protocol.hpp
 EXTRA_DIST += ext/asio/asio/local/stream_protocol.hpp

+ 6 - 6
README

@@ -14,12 +14,12 @@ five year plan are described here:
 	https://bind10.isc.org/wiki/Year2Milestones
 	https://bind10.isc.org/wiki/Year2Milestones
 
 
 This release includes the bind10 master process, b10-msgq message
 This release includes the bind10 master process, b10-msgq message
-bus, b10-auth authoritative DNS server (with SQLite3 backend),
-b10-resolver forwarding DNS server, b10-cmdctl remote control daemon,
-b10-cfgmgr configuration manager, b10-xfrin AXFR inbound service,
-b10-xfrout outgoing AXFR service, b10-zonemgr secondary manager,
-b10-stats statistics collection and reporting daemon, and a new
-libdns++ library for C++ with a python wrapper.
+bus, b10-auth authoritative DNS server (with SQLite3 and in-memory
+backends), b10-resolver forwarding DNS server, b10-cmdctl remote
+control daemon, b10-cfgmgr configuration manager, b10-xfrin AXFR
+inbound service, b10-xfrout outgoing AXFR service, b10-zonemgr
+secondary manager, b10-stats statistics collection and reporting
+daemon, and a new libdns++ library for C++ with a python wrapper.
 
 
 Documentation is included and also available via the BIND 10
 Documentation is included and also available via the BIND 10
 website at http://bind10.isc.org/
 website at http://bind10.isc.org/

+ 79 - 114
configure.ac

@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 # Process this file with autoconf to produce a configure script.
 
 
 AC_PREREQ([2.59])
 AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20110120, bind10-dev@isc.org)
+AC_INIT(bind10-devel, 20110224, bind10-dev@isc.org)
 AC_CONFIG_SRCDIR(README)
 AC_CONFIG_SRCDIR(README)
 AM_INIT_AUTOMAKE
 AM_INIT_AUTOMAKE
 AC_CONFIG_HEADERS([config.h])
 AC_CONFIG_HEADERS([config.h])
@@ -14,18 +14,22 @@ AC_PROG_CXX
 #
 #
 # On FreeBSD (and probably some others), clang++ does not meet an autoconf
 # On FreeBSD (and probably some others), clang++ does not meet an autoconf
 # assumption in identifying libtool configuration regarding shared library:
 # assumption in identifying libtool configuration regarding shared library:
-# the configure script will execute "$CC -shared $CFLAGS -v -o" and expect
-# the output contains -Lxxx or -Ryyy.  This is the case for g++, but not for
-# clang++, and, as a result, it will cause various errors in linking programs
-# or running them with a shared object (such as some of our python scripts).
+# the configure script will execute "$CC -shared $CFLAGS/$CXXFLAGS -v" and
+# expect the output contains -Lxxx or -Ryyy.  This is the case for g++, but
+# not for clang++, and, as a result, it will cause various errors in linking
+# programs or running them with a shared object (such as some of our python
+# scripts).
 # To work around this problem we define a temporary variable
 # To work around this problem we define a temporary variable
 # "CXX_LIBTOOL_LDFLAGS".  It's expected to be defined as, e.g, "-L/usr/lib"
 # "CXX_LIBTOOL_LDFLAGS".  It's expected to be defined as, e.g, "-L/usr/lib"
 # to temporarily fake the output so that it will be compatible with that of
 # to temporarily fake the output so that it will be compatible with that of
 # g++.
 # g++.
 CFLAGS_SAVED=$CFLAGS
 CFLAGS_SAVED=$CFLAGS
+CXXFLAGS_SAVED=$CXXFLAGS
 CFLAGS="$CFLAGS $CXX_LIBTOOL_LDFLAGS"
 CFLAGS="$CFLAGS $CXX_LIBTOOL_LDFLAGS"
+CXXFLAGS="$CXXFLAGS $CXX_LIBTOOL_LDFLAGS"
 AC_PROG_LIBTOOL
 AC_PROG_LIBTOOL
 CFLAGS=$CFLAGS_SAVED
 CFLAGS=$CFLAGS_SAVED
+CXXFLAGS=$CXXFLAGS_SAVED
 
 
 # Use C++ language
 # Use C++ language
 AC_LANG([C++])
 AC_LANG([C++])
@@ -66,6 +70,11 @@ if test $enable_shared = no; then
 	AC_MSG_ERROR([BIND 10 requires shared libraries to be built])
 	AC_MSG_ERROR([BIND 10 requires shared libraries to be built])
 fi
 fi
 
 
+AC_ARG_ENABLE(boost-threads,
+AC_HELP_STRING([--enable-boost-threads],
+  [use boost threads. Currently this only means using its locks instead of dummy locks, in the cache and NSAS]),
+  use_boost_threads=$enableval, use_boost_threads=no)
+
 # allow configuring without setproctitle.
 # allow configuring without setproctitle.
 AC_ARG_ENABLE(setproctitle-check,
 AC_ARG_ENABLE(setproctitle-check,
 AC_HELP_STRING([--disable-setproctitle-check],
 AC_HELP_STRING([--disable-setproctitle-check],
@@ -193,8 +202,9 @@ if test "$setproctitle_check" = "yes" ; then
         AC_MSG_RESULT(ok)
         AC_MSG_RESULT(ok)
     else
     else
         AC_MSG_RESULT(missing)
         AC_MSG_RESULT(missing)
-        AC_MSG_ERROR([Missing setproctitle module. Either install it or provide --disable-setproctitle-check.
-In that case we will continue, but naming of python processes will not work.])
+        AC_MSG_WARN([Missing setproctitle python module.
+Use --disable-setproctitle-check to skip this check.
+In this case we will continue, but naming of python processes will not work.])
     fi
     fi
 fi
 fi
 
 
@@ -285,6 +295,7 @@ AC_SUBST(B10_CXXFLAGS)
 
 
 AC_SEARCH_LIBS(inet_pton, [nsl])
 AC_SEARCH_LIBS(inet_pton, [nsl])
 AC_SEARCH_LIBS(recvfrom, [socket])
 AC_SEARCH_LIBS(recvfrom, [socket])
+AC_SEARCH_LIBS(nanosleep, [rt])
 
 
 # Checks for header files.
 # Checks for header files.
 
 
@@ -363,57 +374,6 @@ if test "$lcov" != "no"; then
 fi
 fi
 AC_SUBST(USE_LCOV)
 AC_SUBST(USE_LCOV)
 
 
-# Configure log4cxx header and library path
-#
-# 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"
-  ])
-
-# 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
 #
 #
@@ -443,62 +403,69 @@ AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync
 CPPFLAGS="$CPPFLAGS_SAVES"
 CPPFLAGS="$CPPFLAGS_SAVES"
 AC_SUBST(BOOST_INCLUDES)
 AC_SUBST(BOOST_INCLUDES)
 
 
-# Using boost::mutex can result in requiring libboost_thread with older
-# versions of Boost.  We'd like to avoid relying on a compiled Boost library
-# whenever possible, so we need to check for it step by step.
-#
-# NOTE: another fix of this problem is to simply require newer versions of
-# boost.  If we choose that solution we should simplify the following tricky
-# checks accordingly and all Makefile.am's that refer to NEED_LIBBOOST_THREAD.
-AC_MSG_CHECKING(for boost::mutex)
-CPPFLAGS_SAVES="$CPPFLAGS"
-LIBS_SAVES="$LIBS"
-CPPFLAGS="$BOOST_INCLUDES $CPPFLAGS $MULTITHREADING_FLAG"
-need_libboost_thread=0
-need_sunpro_workaround=0
-AC_TRY_LINK([
-#include <boost/thread.hpp>
-],[
-boost::mutex m;
-],
-	[ AC_MSG_RESULT(yes (without libboost_thread)) ],
-
-    # there is one specific problem with SunStudio 5.10
-    # where including boost/thread causes a compilation failure
-    # There is a workaround in boost but it checks the version not being 5.10
-    # This will probably be fixed in the future, in which case this
-    # is only a temporary workaround
-    [ AC_TRY_LINK([
-#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-#undef __SUNPRO_CC
-#define __SUNPRO_CC 0x5090
-#endif
-#include <boost/thread.hpp>
-],[
-boost::mutex m;
-],
-    [ AC_MSG_RESULT(yes (with SUNOS workaround))
-      need_sunpro_workaround=1 ],
-    	[ LIBS=" $LIBS -lboost_thread"
-	  AC_TRY_LINK([
-#include <boost/thread.hpp>
-],[
-boost::mutex m;
-],
-		  [ AC_MSG_RESULT(yes (with libboost_thread))
-		    need_libboost_thread=1 ],
-		  [ AC_MSG_RESULT(no)
-		    AC_MSG_ERROR([boost::mutex cannot be linked in this build environment.
-Perhaps you are using an older version of Boost that requires libboost_thread for the mutex support, which does not appear to be available.
-You may want to check the availability of the library or to upgrade Boost.])
-   		  ])])])
-CPPFLAGS="$CPPFLAGS_SAVES"
-LIBS="$LIBS_SAVES"
-AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test $need_libboost_thread = 1)
-if test $need_sunpro_workaround = 1; then
-    AC_DEFINE([NEED_SUNPRO_WORKAROUND], [], [Need boost sunstudio workaround])
+
+if test "${use_boost_threads}" = "yes" ; then
+    AC_DEFINE([USE_BOOST_THREADS], [], [Use boost threads])
+
+    # Using boost::mutex can result in requiring libboost_thread with older
+    # versions of Boost.  We'd like to avoid relying on a compiled Boost library
+    # whenever possible, so we need to check for it step by step.
+    #
+    # NOTE: another fix of this problem is to simply require newer versions of
+    # boost.  If we choose that solution we should simplify the following tricky
+    # checks accordingly and all Makefile.am's that refer to NEED_LIBBOOST_THREAD.
+    AC_MSG_CHECKING(for boost::mutex)
+    CPPFLAGS_SAVES="$CPPFLAGS"
+    LIBS_SAVES="$LIBS"
+    CPPFLAGS="$BOOST_INCLUDES $CPPFLAGS $MULTITHREADING_FLAG"
+    need_libboost_thread=0
+    need_sunpro_workaround=0
+    AC_TRY_LINK([
+    #include <boost/thread.hpp>
+    ],[
+    boost::mutex m;
+    ],
+        [ AC_MSG_RESULT(yes (without libboost_thread)) ],
+        # there is one specific problem with SunStudio 5.10
+        # where including boost/thread causes a compilation failure
+        # There is a workaround in boost but it checks the version not being 5.10
+        # This will probably be fixed in the future, in which case this
+        # is only a temporary workaround
+        [ AC_TRY_LINK([
+    #if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
+    #undef __SUNPRO_CC
+    #define __SUNPRO_CC 0x5090
+    #endif
+    #include <boost/thread.hpp>
+    ],[
+    boost::mutex m;
+    ],
+        [ AC_MSG_RESULT(yes (with SUNOS workaround))
+          need_sunpro_workaround=1 ],
+            [ LIBS=" $LIBS -lboost_thread"
+          AC_TRY_LINK([
+    #include <boost/thread.hpp>
+    ],[
+    boost::mutex m;
+    ],
+              [ AC_MSG_RESULT(yes (with libboost_thread))
+                need_libboost_thread=1 ],
+              [ AC_MSG_RESULT(no)
+                AC_MSG_ERROR([boost::mutex cannot be linked in this build environment.
+    Perhaps you are using an older version of Boost that requires libboost_thread for the mutex support, which does not appear to be available.
+    You may want to check the availability of the library or to upgrade Boost.])
+              ])])])
+    CPPFLAGS="$CPPFLAGS_SAVES"
+    LIBS="$LIBS_SAVES"
+    AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test $need_libboost_thread = 1)
+    if test $need_sunpro_workaround = 1; then
+        AC_DEFINE([NEED_SUNPRO_WORKAROUND], [], [Need boost sunstudio workaround])
+    fi
+else
+    AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test "${use_boost_threads}" = "yes")
 fi
 fi
 
 
+
 #
 #
 # Check availability of gtest, which will be used for unit tests.
 # Check availability of gtest, which will be used for unit tests.
 #
 #
@@ -823,8 +790,6 @@ dnl includes too
                  ${PYTHON_LDFLAGS}
                  ${PYTHON_LDFLAGS}
                  ${PYTHON_LIB}
                  ${PYTHON_LIB}
   Boost:         ${BOOST_INCLUDES}
   Boost:         ${BOOST_INCLUDES}
-  log4cxx:       ${LOG4CXX_INCLUDES}
-                 ${LOG4CXX_LDFLAGS}
   SQLite:        $SQLITE_CFLAGS
   SQLite:        $SQLITE_CFLAGS
                  $SQLITE_LIBS
                  $SQLITE_LIBS
 
 

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


+ 27 - 24
doc/guide/bind10-guide.xml

@@ -52,7 +52,8 @@
     <note>
     <note>
       <para>
       <para>
         BIND 10 provides a EDNS0- and DNSSEC-capable
         BIND 10 provides a EDNS0- and DNSSEC-capable
-        authoritative DNS server and a forwarding DNS server.
+        authoritative DNS server and a caching recursive name server
+        which also provides forwarding.
       </para>
       </para>
     </note>
     </note>
 
 
@@ -79,9 +80,7 @@
       </para>
       </para>
 
 
       <note><para>
       <note><para>
-	For this development prototype release, the only supported
-	data source backend is SQLite3. The authoritative server
-	requires SQLite 3.3.9 or newer.
+	The authoritative server requires SQLite 3.3.9 or newer.
 	The <command>b10-xfrin</command>, <command>b10-xfrout</command>,
 	The <command>b10-xfrin</command>, <command>b10-xfrout</command>,
 	and <command>b10-zonemgr</command> modules require the
 	and <command>b10-zonemgr</command> modules require the
 	libpython3 library and the Python _sqlite3.so module.
 	libpython3 library and the Python _sqlite3.so module.
@@ -1108,9 +1107,10 @@ This may be a temporary setting until then.
 
 
       <note><para>
       <note><para>
         For the development prototype release, <command>b10-auth</command>
         For the development prototype release, <command>b10-auth</command>
-        only supports the SQLite3 data source backend.
+        supports a SQLite3 data source backend and in-memory data source
+        backend.
         Upcoming versions will be able to use multiple different
         Upcoming versions will be able to use multiple different
-        data sources, such as MySQL, Berkeley DB, or in-memory DB.
+        data sources, such as MySQL and Berkeley DB.
       </para></note>
       </para></note>
 
 
 
 
@@ -1309,12 +1309,6 @@ what is XfroutClient xfr_client??
 -->
 -->
     </para>
     </para>
 
 
-    <note><simpara>
-      The current version only provides a forwarding DNS server.
-      It does not cache and does not iterate to find answers.
-      It simply forwards the query on to another full resolver.
-    </simpara></note>
-
     <para>
     <para>
       The main <command>bind10</command> process can be configured
       The main <command>bind10</command> process can be configured
       to select to run either the authoritative or resolver.
       to select to run either the authoritative or resolver.
@@ -1331,15 +1325,26 @@ what is XfroutClient xfr_client??
 
 
     </para>
     </para>
 
 
-<!-- TODO: -->
-    <note><simpara>
-       In the current version, the master <command>bind10</command>
-       process must be stopped and restarted to start up the resolver.
-    </simpara></note>
+    <para>
+       The master <command>bind10</command> will stop and start
+       the desired services.
+    </para>
 
 
     <para>
     <para>
-      Then the upstream address and port must be configured to
-      forward queries to, such as:
+      The resolver also needs to be configured to listen on an address
+      and port:
+
+      <screen>
+&gt; <userinput>config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }]</userinput>
+&gt; <userinput>config commit</userinput>
+</screen>
+    </para>
+
+<!-- TODO: later the above will have some defaults -->
+
+    <para>
+      To enable forwarding, the upstream address and port must be
+      configured to forward queries to, such as:
 
 
       <screen>
       <screen>
 &gt; <userinput>config set Resolver/forward_addresses [{ "address": "<replaceable>192.168.1.1</replaceable>", "port": 53 }]</userinput>
 &gt; <userinput>config set Resolver/forward_addresses [{ "address": "<replaceable>192.168.1.1</replaceable>", "port": 53 }]</userinput>
@@ -1351,17 +1356,15 @@ what is XfroutClient xfr_client??
     </para>
     </para>
 
 
     <para>
     <para>
-      The resolver also needs to be configured to listen on an address
-      and port:
+      Normal iterative name service can be re-enabled by clearing the
+      forwarding address(es); for example:
 
 
       <screen>
       <screen>
-&gt; <userinput>config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }]</userinput>
+&gt; <userinput>config set Resolver/forward_addresses []</userinput>
 &gt; <userinput>config commit</userinput>
 &gt; <userinput>config commit</userinput>
 </screen>
 </screen>
     </para>
     </para>
 
 
-<!-- TODO: later the above will have some defaults -->
-
 <!-- TODO: later try this
 <!-- TODO: later try this
 
 
 > config set Resolver/forward_addresses[0]/address "192.168.8.8"
 > config set Resolver/forward_addresses[0]/address "192.168.8.8"

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

@@ -50,12 +50,13 @@ b10_auth_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
 b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 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/nsas/libnsas.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/server_common/libserver_common.la
 b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.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
 # and can't use @datadir@ because doesn't expand default ${prefix}
 # and can't use @datadir@ because doesn't expand default ${prefix}
-b10_authdir = $(DESTDIR)$(pkgdatadir)
+b10_authdir = $(pkgdatadir)
 b10_auth_DATA = auth.spec
 b10_auth_DATA = auth.spec
 
 

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

@@ -21,6 +21,7 @@ query_bench_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
 query_bench_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 query_bench_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 query_bench_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 query_bench_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 query_bench_LDADD += $(top_builddir)/src/lib/log/liblog.la
 query_bench_LDADD += $(top_builddir)/src/lib/log/liblog.la
+query_bench_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 query_bench_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 query_bench_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 query_bench_LDADD += $(SQLITE_LIBS)
 query_bench_LDADD += $(SQLITE_LIBS)

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

@@ -331,7 +331,7 @@ configureAuthServer(AuthSrv& server, ConstElementPtr config_set) {
             parsers.push_back(parser);
             parsers.push_back(parser);
         }
         }
     } catch (const AuthConfigError& ex) {
     } catch (const AuthConfigError& ex) {
-        throw ex;                  // simply rethrowing it
+        throw;                  // simply rethrowing it
     } catch (const isc::Exception& ex) {
     } catch (const isc::Exception& ex) {
         isc_throw(AuthConfigError, "Server configuration failed: " <<
         isc_throw(AuthConfigError, "Server configuration failed: " <<
                   ex.what());
                   ex.what());

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

@@ -46,6 +46,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
+run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 noinst_PROGRAMS = $(TESTS)

+ 1 - 2
src/bin/auth/tests/config_unittest.cc

@@ -128,8 +128,7 @@ TEST_F(AuthConfigTest, invalidListenAddressConfig) {
 }
 }
 
 
 // Try setting addresses trough config
 // Try setting addresses trough config
-// TODO Enable after #388 is solved
-TEST_F(AuthConfigTest, DISABLED_listenAddressConfig) {
+TEST_F(AuthConfigTest, listenAddressConfig) {
     isc::testutils::portconfig::listenAddressConfig(server);
     isc::testutils::portconfig::listenAddressConfig(server);
 }
 }
 
 

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

@@ -201,7 +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));

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

@@ -5,7 +5,7 @@ CLEANFILES = bind10 bind10.pyc
 
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 
-bind10dir = $(DESTDIR)$(pkgdatadir)
+bind10dir = $(pkgdatadir)
 bind10_DATA = bob.spec
 bind10_DATA = bob.spec
 EXTRA_DIST = bob.spec
 EXTRA_DIST = bob.spec
 
 
@@ -19,7 +19,6 @@ bind10.8: bind10.xml
 
 
 endif
 endif
 
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 bind10: bind10.py
 bind10: bind10.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \

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

@@ -71,7 +71,7 @@ daemon to listen on\&. The default is 5300\&.
 \fBNote\fR
 \fBNote\fR
 .ps -1
 .ps -1
 .br
 .br
-The Y1 prototype release uses a non\-default port for domain service\&.
+This prototype release uses a non\-default port for domain service\&.
 .sp .5v
 .sp .5v
 .RE
 .RE
 .RE
 .RE

+ 86 - 8
src/bin/bind10/bind10.py.in

@@ -72,7 +72,7 @@ isc.util.process.rename(sys.argv[0])
 # This is the version that gets displayed to the user.
 # This is the version that gets displayed to the user.
 # The VERSION string consists of the module name, the module version
 # The VERSION string consists of the module name, the module version
 # number, and the overall BIND 10 version number (set in configure.ac).
 # number, and the overall BIND 10 version number (set in configure.ac).
-VERSION = "bind10 20101129 (BIND 10 @PACKAGE_VERSION@)"
+VERSION = "bind10 20110223 (BIND 10 @PACKAGE_VERSION@)"
 
 
 # This is for bind10.boottime of stats module
 # This is for bind10.boottime of stats module
 _BASETIME = time.gmtime()
 _BASETIME = time.gmtime()
@@ -209,23 +209,68 @@ class BoB:
         self.ccs = None
         self.ccs = None
         self.cfg_start_auth = True
         self.cfg_start_auth = True
         self.cfg_start_resolver = False
         self.cfg_start_resolver = False
+        self.started_auth_family = False
+        self.started_resolver_family = False
         self.curproc = None
         self.curproc = None
         self.dead_processes = {}
         self.dead_processes = {}
         self.msgq_socket_file = msgq_socket_file
         self.msgq_socket_file = msgq_socket_file
         self.nocache = nocache
         self.nocache = nocache
         self.processes = {}
         self.processes = {}
+        self.expected_shutdowns = {}
         self.runnable = False
         self.runnable = False
         self.uid = setuid
         self.uid = setuid
         self.username = username
         self.username = username
         self.verbose = verbose
         self.verbose = verbose
 
 
     def config_handler(self, new_config):
     def config_handler(self, new_config):
+        # If this is initial update, don't do anything now, leave it to startup
+        if not self.runnable:
+            return
+        # Now we declare few functions used only internally here. Besides the
+        # benefit of not polluting the name space, they are closures, so we
+        # don't need to pass some variables
+        def start_stop(name, started, start, stop):
+            if not'start_' + name in new_config:
+                return
+            if new_config['start_' + name]:
+                if not started:
+                    if self.uid is not None:
+                        sys.stderr.write("[bind10] Starting " + name + " as " +
+                            "a user, not root. This might fail.\n")
+                    start()
+            else:
+                stop()
+        # These four functions are passed to start_stop (smells like functional
+        # programming little bit)
+        def resolver_on():
+            self.start_resolver(self.c_channel_env)
+            self.started_resolver_family = True
+        def resolver_off():
+            self.stop_resolver()
+            self.started_resolver_family = False
+        def auth_on():
+            self.start_auth(self.c_channel_env)
+            self.start_xfrout(self.c_channel_env)
+            self.start_xfrin(self.c_channel_env)
+            self.start_zonemgr(self.c_channel_env)
+            self.started_auth_family = True
+        def auth_off():
+            self.stop_zonemgr()
+            self.stop_xfrin()
+            self.stop_xfrout()
+            self.stop_auth()
+            self.started_auth_family = False
+
+        # The real code of the config handler function follows here
         if self.verbose:
         if self.verbose:
             sys.stdout.write("[bind10] Handling new configuration: " +
             sys.stdout.write("[bind10] Handling new configuration: " +
                 str(new_config) + "\n")
                 str(new_config) + "\n")
+        start_stop('resolver', self.started_resolver_family, resolver_on,
+            resolver_off)
+        start_stop('auth', self.started_auth_family, auth_on, auth_off)
+
         answer = isc.config.ccsession.create_answer(0)
         answer = isc.config.ccsession.create_answer(0)
         return answer
         return answer
-        # TODO
 
 
     def command_handler(self, command, args):
     def command_handler(self, command, args):
         if self.verbose:
         if self.verbose:
@@ -465,11 +510,12 @@ class BoB:
         # XXX: we hardcode port 8080
         # XXX: we hardcode port 8080
         self.start_simple("b10-cmdctl", c_channel_env, 8080)
         self.start_simple("b10-cmdctl", c_channel_env, 8080)
 
 
-    def start_all_processes(self, c_channel_env):
+    def start_all_processes(self):
         """
         """
             Starts up all the processes.  Any exception generated during the
             Starts up all the processes.  Any exception generated during the
             starting of the processes is handled by the caller.
             starting of the processes is handled by the caller.
         """
         """
+        c_channel_env = self.c_channel_env
         self.start_msgq(c_channel_env)
         self.start_msgq(c_channel_env)
         self.start_cfgmgr(c_channel_env)
         self.start_cfgmgr(c_channel_env)
         self.start_ccsession(c_channel_env)
         self.start_ccsession(c_channel_env)
@@ -486,6 +532,7 @@ class BoB:
         # ... and resolver (if selected):
         # ... and resolver (if selected):
         if self.cfg_start_resolver:
         if self.cfg_start_resolver:
             self.start_resolver(c_channel_env)
             self.start_resolver(c_channel_env)
+            self.started_resolver_family = True
 
 
         # Everything after the main components can run as non-root.
         # Everything after the main components can run as non-root.
         # TODO: this is only temporary - once the privileged socket creator is
         # TODO: this is only temporary - once the privileged socket creator is
@@ -499,6 +546,7 @@ class BoB:
             self.start_xfrout(c_channel_env)
             self.start_xfrout(c_channel_env)
             self.start_xfrin(c_channel_env)
             self.start_xfrin(c_channel_env)
             self.start_zonemgr(c_channel_env)
             self.start_zonemgr(c_channel_env)
+            self.started_auth_family = True
 
 
         # ... and finally start the remaining processes
         # ... and finally start the remaining processes
         self.start_stats(c_channel_env)
         self.start_stats(c_channel_env)
@@ -529,7 +577,8 @@ class BoB:
         # Start all processes.  If any one fails to start, kill all started
         # Start all processes.  If any one fails to start, kill all started
         # processes and exit with an error indication.
         # processes and exit with an error indication.
         try:
         try:
-            self.start_all_processes(c_channel_env)
+            self.c_channel_env = c_channel_env
+            self.start_all_processes()
         except Exception as e:
         except Exception as e:
             self.kill_started_processes()
             self.kill_started_processes()
             return "Unable to start " + self.curproc + ": " + str(e)
             return "Unable to start " + self.curproc + ": " + str(e)
@@ -551,10 +600,35 @@ class BoB:
         self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr")
         self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr")
         self.cc_session.group_sendmsg(cmd, "Stats", "Stats")
         self.cc_session.group_sendmsg(cmd, "Stats", "Stats")
 
 
-    def stop_process(self, process):
-        """Stop the given process, friendly-like."""
-        # XXX nothing yet
-        pass
+    def stop_process(self, process, recipient):
+        """
+        Stop the given process, friendly-like. The process is the name it has
+        (in logs, etc), the recipient is the address on msgq.
+        """
+        if self.verbose:
+            sys.stdout.write("[bind10] Asking %s to terminate\n" % process)
+        # TODO: Some timeout to solve processes that don't want to die would
+        # help. We can even store it in the dict, it is used only as a set
+        self.expected_shutdowns[process] = 1
+        # Ask the process to die willingly
+        self.cc_session.group_sendmsg({'command': ['shutdown']}, recipient,
+            recipient)
+
+    # Series of stop_process wrappers
+    def stop_resolver(self):
+        self.stop_process('b10-resolver', 'Resolver')
+
+    def stop_auth(self):
+        self.stop_process('b10-auth', 'Auth')
+
+    def stop_xfrout(self):
+        self.stop_process('b10-xfrout', 'Xfrout')
+
+    def stop_xfrin(self):
+        self.stop_process('b10-xfrin', 'Xfrin')
+
+    def stop_zonemgr(self):
+        self.stop_process('b10-zonemgr', 'Zonemgr')
 
 
     def shutdown(self):
     def shutdown(self):
         """Stop the BoB instance."""
         """Stop the BoB instance."""
@@ -660,6 +734,10 @@ class BoB:
         still_dead = {}
         still_dead = {}
         now = time.time()
         now = time.time()
         for proc_info in self.dead_processes.values():
         for proc_info in self.dead_processes.values():
+            if proc_info.name in self.expected_shutdowns:
+                # We don't restart, we wanted it to die
+                del self.expected_shutdowns[proc_info.name]
+                continue
             restart_time = proc_info.restart_schedule.get_restart_time(now)
             restart_time = proc_info.restart_schedule.get_restart_time(now)
             if restart_time > now:
             if restart_time > now:
                 if (next_restart is None) or (next_restart > restart_time):
                 if (next_restart is None) or (next_restart > restart_time):

+ 5 - 1
src/bin/bind10/bind10.xml

@@ -155,7 +155,11 @@
           <para>The name this process should have in tools like
           <para>The name this process should have in tools like
           <command>ps</command> or <command>top</command>. This
           <command>ps</command> or <command>top</command>. This
           is handy if you have multiple versions/installations
           is handy if you have multiple versions/installations
-          of <command>bind10</command>.</para>
+          of <command>bind10</command>.
+<!-- TODO: only supported with setproctitle feature
+The default is the basename of ARG 0.
+-->
+</para>
         </listitem>
         </listitem>
       </varlistentry>
       </varlistentry>
 
 

+ 211 - 72
src/bin/bind10/tests/bind10_test.py

@@ -142,13 +142,13 @@ class TestBoB(unittest.TestCase):
         self.assertEqual(bob.cfg_start_auth, True)
         self.assertEqual(bob.cfg_start_auth, True)
         self.assertEqual(bob.cfg_start_resolver, False)
         self.assertEqual(bob.cfg_start_resolver, False)
 
 
-# Class for testing the Bob.start_all_processes() method call.
+# Class for testing the BoB start/stop components routines.
 #
 #
 # Although testing that external processes start is outside the scope
 # Although testing that external processes start is outside the scope
 # of the unit test, by overriding the process start methods we can check
 # of the unit test, by overriding the process start methods we can check
 # that the right processes are started depending on the configuration
 # that the right processes are started depending on the configuration
 # options.
 # options.
-class StartAllProcessesBob(BoB):
+class StartStopCheckBob(BoB):
     def __init__(self):
     def __init__(self):
         BoB.__init__(self)
         BoB.__init__(self)
 
 
@@ -163,6 +163,7 @@ class StartAllProcessesBob(BoB):
         self.zonemgr = False
         self.zonemgr = False
         self.stats = False
         self.stats = False
         self.cmdctl = False
         self.cmdctl = False
+        self.c_channel_env = {}
 
 
     def read_bind10_config(self):
     def read_bind10_config(self):
         # Configuration options are set directly
         # Configuration options are set directly
@@ -198,118 +199,256 @@ class StartAllProcessesBob(BoB):
     def start_cmdctl(self, c_channel_env):
     def start_cmdctl(self, c_channel_env):
         self.cmdctl = True
         self.cmdctl = True
 
 
-# Check that the start_all_processes method starts the right combination
-# of processes.
-class TestStartAllProcessesBob(unittest.TestCase):
+    # We don't really use all of these stop_ methods. But it might turn out
+    # someone would add some stop_ method to BoB and we want that one overriden
+    # in case he forgets to update the tests.
+    def stop_msgq(self):
+        self.msgq = False
+
+    def stop_cfgmgr(self):
+        self.cfgmgr = False
+
+    def stop_ccsession(self):
+        self.ccsession = False
+
+    def stop_auth(self):
+        self.auth = False
+
+    def stop_resolver(self):
+        self.resolver = False
+
+    def stop_xfrout(self):
+        self.xfrout = False
+
+    def stop_xfrin(self):
+        self.xfrin = False
+
+    def stop_zonemgr(self):
+        self.zonemgr = False
+
+    def stop_stats(self):
+        self.stats = False
+
+    def stop_cmdctl(self):
+        self.cmdctl = False
+
+class TestStartStopProcessesBob(unittest.TestCase):
+    """
+    Check that the start_all_processes method starts the right combination
+    of processes and that the right processes are started and stopped
+    according to changes in configuration.
+    """
+    def check_started(self, bob, core, auth, resolver):
+        """
+        Check that the right sets of services are started. The ones that
+        should be running are specified by the core, auth and resolver parameters
+        (they are groups of processes, eg. auth means b10-auth, -xfrout, -xfrin
+        and -zonemgr).
+        """
+        self.assertEqual(bob.msgq, core)
+        self.assertEqual(bob.cfgmgr, core)
+        self.assertEqual(bob.ccsession, core)
+        self.assertEqual(bob.auth, auth)
+        self.assertEqual(bob.resolver, resolver)
+        self.assertEqual(bob.xfrout, auth)
+        self.assertEqual(bob.xfrin, auth)
+        self.assertEqual(bob.zonemgr, auth)
+        self.assertEqual(bob.stats, core)
+        self.assertEqual(bob.cmdctl, core)
+
     def check_preconditions(self, bob):
     def check_preconditions(self, bob):
-        self.assertEqual(bob.msgq, False)
-        self.assertEqual(bob.cfgmgr, False)
-        self.assertEqual(bob.ccsession, False)
-        self.assertEqual(bob.auth, False)
-        self.assertEqual(bob.resolver, False)
-        self.assertEqual(bob.xfrout, False)
-        self.assertEqual(bob.xfrin, False)
-        self.assertEqual(bob.zonemgr, False)
-        self.assertEqual(bob.stats, False)
-        self.assertEqual(bob.cmdctl, False)
+        self.check_started(bob, False, False, False)
+
+    def check_started_none(self, bob):
+        """
+        Check that the situation is according to configuration where no servers
+        should be started. Some processes still need to be running.
+        """
+        self.check_started(bob, True, False, False)
+
+    def check_started_both(self, bob):
+        """
+        Check the situation is according to configuration where both servers
+        (auth and resolver) are enabled.
+        """
+        self.check_started(bob, True, True, True)
+
+    def check_started_auth(self, bob):
+        """
+        Check the set of processes needed to run auth only is started.
+        """
+        self.check_started(bob, True, True, False)
+
+    def check_started_resolver(self, bob):
+        """
+        Check the set of processes needed to run resolver only is started.
+        """
+        self.check_started(bob, True, False, True)
 
 
     # Checks the processes started when starting neither auth nor resolver
     # Checks the processes started when starting neither auth nor resolver
     # is specified.
     # is specified.
     def test_start_none(self):
     def test_start_none(self):
-        # Created Bob and ensure initialization correct
-        bob = StartAllProcessesBob()
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
         self.check_preconditions(bob)
         self.check_preconditions(bob)
 
 
         # Start processes and check what was started
         # Start processes and check what was started
-        c_channel_env = {}
         bob.cfg_start_auth = False
         bob.cfg_start_auth = False
         bob.cfg_start_resolver = False
         bob.cfg_start_resolver = False
 
 
-        bob.start_all_processes(c_channel_env)
-
-        self.assertEqual(bob.msgq, True)
-        self.assertEqual(bob.cfgmgr, True)
-        self.assertEqual(bob.ccsession, True)
-        self.assertEqual(bob.auth, False)
-        self.assertEqual(bob.resolver, False)
-        self.assertEqual(bob.xfrout, False)
-        self.assertEqual(bob.xfrin, False)
-        self.assertEqual(bob.zonemgr, False)
-        self.assertEqual(bob.stats, True)
-        self.assertEqual(bob.cmdctl, True)
+        bob.start_all_processes()
+        self.check_started_none(bob)
 
 
     # Checks the processes started when starting only the auth process
     # Checks the processes started when starting only the auth process
     def test_start_auth(self):
     def test_start_auth(self):
-        # Created Bob and ensure initialization correct
-        bob = StartAllProcessesBob()
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
         self.check_preconditions(bob)
         self.check_preconditions(bob)
 
 
         # Start processes and check what was started
         # Start processes and check what was started
-        c_channel_env = {}
         bob.cfg_start_auth = True
         bob.cfg_start_auth = True
         bob.cfg_start_resolver = False
         bob.cfg_start_resolver = False
 
 
-        bob.start_all_processes(c_channel_env)
+        bob.start_all_processes()
 
 
-        self.assertEqual(bob.msgq, True)
-        self.assertEqual(bob.cfgmgr, True)
-        self.assertEqual(bob.ccsession, True)
-        self.assertEqual(bob.auth, True)
-        self.assertEqual(bob.resolver, False)
-        self.assertEqual(bob.xfrout, True)
-        self.assertEqual(bob.xfrin, True)
-        self.assertEqual(bob.zonemgr, True)
-        self.assertEqual(bob.stats, True)
-        self.assertEqual(bob.cmdctl, True)
+        self.check_started_auth(bob)
 
 
     # Checks the processes started when starting only the resolver process
     # Checks the processes started when starting only the resolver process
     def test_start_resolver(self):
     def test_start_resolver(self):
-        # Created Bob and ensure initialization correct
-        bob = StartAllProcessesBob()
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
         self.check_preconditions(bob)
         self.check_preconditions(bob)
 
 
         # Start processes and check what was started
         # Start processes and check what was started
-        c_channel_env = {}
         bob.cfg_start_auth = False
         bob.cfg_start_auth = False
         bob.cfg_start_resolver = True
         bob.cfg_start_resolver = True
 
 
-        bob.start_all_processes(c_channel_env)
+        bob.start_all_processes()
 
 
-        self.assertEqual(bob.msgq, True)
-        self.assertEqual(bob.cfgmgr, True)
-        self.assertEqual(bob.ccsession, True)
-        self.assertEqual(bob.auth, False)
-        self.assertEqual(bob.resolver, True)
-        self.assertEqual(bob.xfrout, False)
-        self.assertEqual(bob.xfrin, False)
-        self.assertEqual(bob.zonemgr, False)
-        self.assertEqual(bob.stats, True)
-        self.assertEqual(bob.cmdctl, True)
+        self.check_started_resolver(bob)
 
 
     # Checks the processes started when starting both auth and resolver process
     # Checks the processes started when starting both auth and resolver process
     def test_start_both(self):
     def test_start_both(self):
-        # Created Bob and ensure initialization correct
-        bob = StartAllProcessesBob()
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
         self.check_preconditions(bob)
         self.check_preconditions(bob)
 
 
         # Start processes and check what was started
         # Start processes and check what was started
-        c_channel_env = {}
         bob.cfg_start_auth = True
         bob.cfg_start_auth = True
         bob.cfg_start_resolver = True
         bob.cfg_start_resolver = True
 
 
-        bob.start_all_processes(c_channel_env)
+        bob.start_all_processes()
+
+        self.check_started_both(bob)
+
+    def test_config_start(self):
+        """
+        Test that the configuration starts and stops processes according
+        to configuration changes.
+        """
+
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
+
+        # Start processes (nothing much should be started, as in
+        # test_start_none)
+        bob.cfg_start_auth = False
+        bob.cfg_start_resolver = False
+
+        bob.start_all_processes()
+        bob.runnable = True
+        self.check_started_none(bob)
+
+        # Enable both at once
+        bob.config_handler({'start_auth': True, 'start_resolver': True})
+        self.check_started_both(bob)
+
+        # Not touched by empty change
+        bob.config_handler({})
+        self.check_started_both(bob)
+
+        # Not touched by change to the same configuration
+        bob.config_handler({'start_auth': True, 'start_resolver': True})
+        self.check_started_both(bob)
+
+        # Turn them both off again
+        bob.config_handler({'start_auth': False, 'start_resolver': False})
+        self.check_started_none(bob)
+
+        # Not touched by empty change
+        bob.config_handler({})
+        self.check_started_none(bob)
+
+        # Not touched by change to the same configuration
+        bob.config_handler({'start_auth': False, 'start_resolver': False})
+        self.check_started_none(bob)
+
+        # Start and stop auth separately
+        bob.config_handler({'start_auth': True})
+        self.check_started_auth(bob)
+
+        bob.config_handler({'start_auth': False})
+        self.check_started_none(bob)
+
+        # Start and stop resolver separately
+        bob.config_handler({'start_resolver': True})
+        self.check_started_resolver(bob)
+
+        bob.config_handler({'start_resolver': False})
+        self.check_started_none(bob)
+
+        # Alternate
+        bob.config_handler({'start_auth': True})
+        self.check_started_auth(bob)
+
+        bob.config_handler({'start_auth': False, 'start_resolver': True})
+        self.check_started_resolver(bob)
+
+        bob.config_handler({'start_auth': True, 'start_resolver': False})
+        self.check_started_auth(bob)
+
+    def test_config_start_once(self):
+        """
+        Tests that a process is started only once.
+        """
+        # Create BoB and ensure correct initialization
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
+
+        # Start processes (both)
+        bob.cfg_start_auth = True
+        bob.cfg_start_resolver = True
+
+        bob.start_all_processes()
+        bob.runnable = True
+        self.check_started_both(bob)
+
+        bob.start_auth = lambda: self.fail("Started auth again")
+        bob.start_xfrout = lambda: self.fail("Started xfrout again")
+        bob.start_xfrin = lambda: self.fail("Started xfrin again")
+        bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
+        bob.start_resolver = lambda: self.fail("Started resolver again")
+
+        # Send again we want to start them. Should not do it, as they are.
+        bob.config_handler({'start_auth': True})
+        bob.config_handler({'start_resolver': True})
+
+    def test_config_not_started_early(self):
+        """
+        Test that processes are not started by the config handler before
+        startup.
+        """
+        bob = StartStopCheckBob()
+        self.check_preconditions(bob)
 
 
-        self.assertEqual(bob.msgq, True)
-        self.assertEqual(bob.cfgmgr, True)
-        self.assertEqual(bob.ccsession, True)
-        self.assertEqual(bob.auth, True)
-        self.assertEqual(bob.resolver, True)
-        self.assertEqual(bob.xfrout, True)
-        self.assertEqual(bob.xfrin, True)
-        self.assertEqual(bob.zonemgr, True)
-        self.assertEqual(bob.stats, True)
-        self.assertEqual(bob.cmdctl, True)
+        bob.start_auth = lambda: self.fail("Started auth again")
+        bob.start_xfrout = lambda: self.fail("Started xfrout again")
+        bob.start_xfrin = lambda: self.fail("Started xfrin again")
+        bob.start_zonemgr = lambda: self.fail("Started zonemgr again")
+        bob.start_resolver = lambda: self.fail("Started resolver again")
 
 
+        bob.config_handler({'start_auth': True, 'start_resolver': True})
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':
     unittest.main()
     unittest.main()

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

@@ -8,7 +8,7 @@ EXTRA_DIST = $(man_MANS) bindctl.xml
 python_PYTHON = __init__.py bindcmd.py cmdparse.py exception.py moduleinfo.py mycollections.py
 python_PYTHON = __init__.py bindcmd.py cmdparse.py exception.py moduleinfo.py mycollections.py
 pythondir = $(pyexecdir)/bindctl
 pythondir = $(pyexecdir)/bindctl
 
 
-bindctldir = $(DESTDIR)$(pkgdatadir)
+bindctldir = $(pkgdatadir)
 
 
 CLEANFILES = bindctl
 CLEANFILES = bindctl
 
 

+ 1 - 1
src/bin/bindctl/bindctl-source.py.in

@@ -31,7 +31,7 @@ isc.util.process.rename()
 # This is the version that gets displayed to the user.
 # This is the version that gets displayed to the user.
 # The VERSION string consists of the module name, the module version
 # The VERSION string consists of the module name, the module version
 # number, and the overall BIND 10 version number (set in configure.ac).
 # number, and the overall BIND 10 version number (set in configure.ac).
-VERSION = "bindctl 20101201 (BIND 10 @PACKAGE_VERSION@)"
+VERSION = "bindctl 20110217 (BIND 10 @PACKAGE_VERSION@)"
 
 
 DEFAULT_IDENTIFIER_DESC = "The identifier specifies the config item. Child elements are separated with the '/' character. List indices can be specified with '[i]', where i is an integer specifying the index, starting with 0. Examples: 'Boss/start_auth', 'Recurse/listen_on[0]/address'. If no identifier is given, shows the item at the current location."
 DEFAULT_IDENTIFIER_DESC = "The identifier specifies the config item. Child elements are separated with the '/' character. List indices can be specified with '[i]', where i is an integer specifying the index, starting with 0. Examples: 'Boss/start_auth', 'Recurse/listen_on[0]/address'. If no identifier is given, shows the item at the current location."
 
 

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

@@ -19,7 +19,6 @@ b10-cfgmgr.8: b10-cfgmgr.xml
 
 
 endif
 endif
 
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-cfgmgr: b10-cfgmgr.py
 b10-cfgmgr: b10-cfgmgr.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" b10-cfgmgr.py >$@
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" b10-cfgmgr.py >$@

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

@@ -4,7 +4,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 
 pkglibexec_SCRIPTS = b10-cmdctl
 pkglibexec_SCRIPTS = b10-cmdctl
 
 
-b10_cmdctldir = $(DESTDIR)$(pkgdatadir)
+b10_cmdctldir = $(pkgdatadir)
 
 
 # NOTE: this will overwrite on install
 # NOTE: this will overwrite on install
 # So these generic copies are placed in share/bind10 instead of to etc
 # So these generic copies are placed in share/bind10 instead of to etc
@@ -33,7 +33,6 @@ endif
 cmdctl.spec: cmdctl.spec.pre
 cmdctl.spec: cmdctl.spec.pre
 	$(SED) -e "s|@@SYSCONFDIR@@|$(sysconfdir)|" cmdctl.spec.pre >$@
 	$(SED) -e "s|@@SYSCONFDIR@@|$(sysconfdir)|" cmdctl.spec.pre >$@
 
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-cmdctl: cmdctl.py
 b10-cmdctl: cmdctl.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@

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

@@ -70,12 +70,15 @@ host_lookup(const char* const name, const char* const type) {
     msg.toWire(renderer);
     msg.toWire(renderer);
 
 
     struct addrinfo hints, *res;
     struct addrinfo hints, *res;
-    int e;
     memset(&hints, 0, sizeof(hints));
     memset(&hints, 0, sizeof(hints));
     hints.ai_family = AF_UNSPEC;
     hints.ai_family = AF_UNSPEC;
     hints.ai_socktype = SOCK_DGRAM;
     hints.ai_socktype = SOCK_DGRAM;
     hints.ai_flags = 0; // not using AI_NUMERICHOST in case to bootstrap
     hints.ai_flags = 0; // not using AI_NUMERICHOST in case to bootstrap
-    e = getaddrinfo(server, server_port, &hints, &res);
+    if (getaddrinfo(server, server_port, &hints, &res) != 0) {
+        cerr << "address/port conversion for " << server << ":"
+             << server_port << " failed" << endl;
+        return (1);
+    }
 
 
     if (verbose) {
     if (verbose) {
         cout << "Trying \"" << name << "\"\n";
         cout << "Trying \"" << name << "\"\n";

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

@@ -16,7 +16,6 @@ b10-msgq.8: msgq.xml
 
 
 endif
 endif
 
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-msgq: msgq.py
 b10-msgq: msgq.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" msgq.py >$@
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" msgq.py >$@

+ 1 - 1
src/bin/msgq/msgq.py.in

@@ -40,7 +40,7 @@ isc.util.process.rename()
 # This is the version that gets displayed to the user.
 # This is the version that gets displayed to the user.
 # The VERSION string consists of the module name, the module version
 # The VERSION string consists of the module name, the module version
 # number, and the overall BIND 10 version number (set in configure.ac).
 # number, and the overall BIND 10 version number (set in configure.ac).
-VERSION = "b10-msgq 20100818 (BIND 10 @PACKAGE_VERSION@)"
+VERSION = "b10-msgq 20110127 (BIND 10 @PACKAGE_VERSION@)"
 
 
 class MsgQReceiveError(Exception): pass
 class MsgQReceiveError(Exception): pass
 
 

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

@@ -49,11 +49,13 @@ b10_resolver_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/log/liblog.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/log/liblog.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libcache.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 b10_resolver_LDADD += $(top_builddir)/src/bin/auth/change_user.o
 b10_resolver_LDADD += $(top_builddir)/src/bin/auth/change_user.o
 b10_resolver_LDFLAGS = -pthread
 b10_resolver_LDFLAGS = -pthread
 
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
 # and can't use @datadir@ because doesn't expand default ${prefix}
 # and can't use @datadir@ because doesn't expand default ${prefix}
-b10_resolverdir = $(DESTDIR)$(pkgdatadir)
+b10_resolverdir = $(pkgdatadir)
 b10_resolver_DATA = resolver.spec
 b10_resolver_DATA = resolver.spec
 
 

+ 25 - 24
src/bin/resolver/b10-resolver.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-resolver
 .\"     Title: b10-resolver
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: January 19, 2011
+.\"      Date: February 17, 2011
 .\"    Manual: BIND10
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"  Language: English
 .\"
 .\"
-.TH "B10\-RESOLVER" "8" "January 19, 2011" "BIND10" "BIND10"
+.TH "B10\-RESOLVER" "8" "February 17, 2011" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" -----------------------------------------------------------------
@@ -39,23 +39,6 @@ will exit\&.
 .PP
 .PP
 It also receives its configurations from
 It also receives its configurations from
 \fBb10-cfgmgr\fR(8)\&.
 \fBb10-cfgmgr\fR(8)\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBNote\fR
-.ps -1
-.br
-.PP
-This prototype version only supports forwarding\&. Future versions will introduce full recursion, cache, lookup of local authoritative data (as in
-\fBb10\-auth\fR), and DNSSEC validation\&.
-.sp .5v
-.RE
 .SH "OPTIONS"
 .SH "OPTIONS"
 .PP
 .PP
 The arguments are as follows:
 The arguments are as follows:
@@ -95,11 +78,30 @@ number\&. The defaults are address ::1 port 5300 and address 127\&.0\&.0\&.1 por
 .PP
 .PP
 
 
 \fIretries\fR
 \fIretries\fR
-is the number of times to retry (resend query) after a timeout\&. The default is 0 (do not retry)\&.
+is the number of times to retry (resend query) after a query timeout (\fItimeout_query\fR)\&. The default is 3\&.
+.PP
+
+\fIroot_addresses\fR
+is a list of addresses and ports for
+\fBb10\-resolver\fR
+to use directly as root servers to start resolving\&. The list items are the
+\fIaddress\fR
+string and
+\fIport\fR
+number\&. If empty, a hardcoded address for F\-root (192\&.5\&.5\&.241) is used\&.
+.PP
+
+\fItimeout_client\fR
+is the number of milliseconds to wait before timing out the incoming client query\&. If set to \-1, this timeout is disabled\&. The default is 4000\&. After this timeout, a SERVFAIL is sent back to the client asking the question\&. (The lookup may continue after the timeout, but a later answer is not returned for the now\-past query\&.)
 .PP
 .PP
 
 
-\fItimeout\fR
-is the number of milliseconds to wait for answer\&. If set to \-1, the timeout is disabled\&. The default is 2000\&.
+\fItimeout_lookup\fR
+is the number of milliseconds before it stops trying the query\&. If set to \-1, this timeout is disabled\&. The default is 30000\&.
+.PP
+
+
+\fItimeout_query\fR
+is the number of milliseconds to wait before it retries a query\&. If set to \-1, this timeout is disabled\&. The default is 2000\&.
 .PP
 .PP
 The configuration command is:
 The configuration command is:
 .PP
 .PP
@@ -119,8 +121,7 @@ BIND 10 Guide\&.
 .PP
 .PP
 The
 The
 \fBb10\-resolver\fR
 \fBb10\-resolver\fR
-daemon was first coded in September 2010\&. The initial implementation only provided forwarding\&.
-
+daemon was first coded in September 2010\&. The initial implementation only provided forwarding\&. Iteration was introduced in January 2011\&.
 
 
 .SH "COPYRIGHT"
 .SH "COPYRIGHT"
 .br
 .br

+ 42 - 11
src/bin/resolver/b10-resolver.xml

@@ -20,7 +20,7 @@
 <refentry>
 <refentry>
 
 
   <refentryinfo>
   <refentryinfo>
-    <date>January 19, 2011</date>
+    <date>February 17, 2011</date>
   </refentryinfo>
   </refentryinfo>
 
 
   <refmeta>
   <refmeta>
@@ -69,11 +69,13 @@
 <citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
 <citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
     </para>
     </para>
 
 
+<!--
     <note><para>
     <note><para>
-      This prototype version only supports forwarding.  Future versions
-      will introduce full recursion, cache, lookup of local authoritative
-      data (as in <command>b10-auth</command>), and DNSSEC validation.
+      Future versions will introduce lookup of local authoritative
+      data (as in <command>b10-auth</command>) and DNSSEC validation.
     </para></note>
     </para></note>
+-->
+
   </refsect1>
   </refsect1>
 
 
   <refsect1>
   <refsect1>
@@ -128,7 +130,7 @@ port
 -->
 -->
     </para>
     </para>
 
 
-<!-- trac386:
+<!-- trac384:
 
 
 once that is merged you can for instance do 'config add Resolver/forward_addresses { "port": 123 } and it will fill in the rest (in this case ::1 for the address)
 once that is merged you can for instance do 'config add Resolver/forward_addresses { "port": 123 } and it will fill in the rest (in this case ::1 for the address)
 
 
@@ -145,13 +147,43 @@ once that is merged you can for instance do 'config add Resolver/forward_address
 
 
     <para>
     <para>
       <varname>retries</varname> is the number of times to retry
       <varname>retries</varname> is the number of times to retry
-      (resend query) after a timeout.
-      The default is 0 (do not retry).
+      (resend query) after a query timeout
+      (<varname>timeout_query</varname>).
+      The default is 3.
+    </para>
+
+    <para>
+      <varname>root_addresses</varname> is a list of addresses and ports
+      for <command>b10-resolver</command> to use directly as
+      root servers to start resolving.
+      The list items are the <varname>address</varname> string
+      and <varname>port</varname> number.
+      If empty, a hardcoded address for F-root (192.5.5.241) is used.
+    </para>
+
+    <para>
+      <varname>timeout_client</varname> is the number of milliseconds
+      to wait before timing out the incoming client query.
+      If set to -1, this timeout is disabled.
+      The default is 4000.
+      After this timeout, a SERVFAIL is sent back to the client asking
+      the question.
+      (The lookup may continue after the timeout, but a later answer
+      is not returned for the now-past query.)
+    </para>
+
+    <para>
+      <varname>timeout_lookup</varname> is the number of milliseconds
+      before it stops trying the query.
+      If set to -1, this timeout is disabled.
+      The default is 30000.
     </para>
     </para>
 
 
     <para>
     <para>
-      <varname>timeout</varname> is the number of milliseconds to
-      wait for answer. If set to -1, the timeout is disabled.
+<!-- previous timeout was renamed to timeout_query -->
+      <varname>timeout_query</varname> is the number of milliseconds to
+      wait before it retries a query.
+      If set to -1, this timeout is disabled.
       The default is 2000.
       The default is 2000.
     </para>
     </para>
 
 
@@ -200,9 +232,8 @@ once that is merged you can for instance do 'config add Resolver/forward_address
     <para>
     <para>
       The <command>b10-resolver</command> daemon was first coded in
       The <command>b10-resolver</command> daemon was first coded in
       September 2010. The initial implementation only provided
       September 2010. The initial implementation only provided
-      forwarding.
+      forwarding. Iteration was introduced in January 2011.
 <!-- TODO: document when caching was added -->
 <!-- TODO: document when caching was added -->
-<!-- TODO: document when iteration was added -->
 <!-- TODO: document when validation was added -->
 <!-- TODO: document when validation was added -->
     </para>
     </para>
   </refsect1>
   </refsect1>

+ 7 - 14
src/bin/resolver/resolver.cc

@@ -182,6 +182,8 @@ public:
     MessagePtr message_;
     MessagePtr message_;
 };
 };
 
 
+
+// TODO: REMOVE, USE isc::resolve::MakeErrorMessage?
 void
 void
 makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
 makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
                  const Rcode& rcode)
                  const Rcode& rcode)
@@ -256,25 +258,16 @@ public:
         const qid_t qid = query_message->getQid();
         const qid_t qid = query_message->getQid();
         const bool rd = query_message->getHeaderFlag(Message::HEADERFLAG_RD);
         const bool rd = query_message->getHeaderFlag(Message::HEADERFLAG_RD);
         const bool cd = query_message->getHeaderFlag(Message::HEADERFLAG_CD);
         const bool cd = query_message->getHeaderFlag(Message::HEADERFLAG_CD);
-        const Opcode& opcode = query_message->getOpcode();
-
-        // Fill in the final details of the answer message
+        
+        // The opcode and question section should have already been set,
+        // fill in the final details of the answer message
         answer_message->setQid(qid);
         answer_message->setQid(qid);
-        answer_message->setOpcode(opcode);
 
 
         answer_message->setHeaderFlag(Message::HEADERFLAG_QR);
         answer_message->setHeaderFlag(Message::HEADERFLAG_QR);
         answer_message->setHeaderFlag(Message::HEADERFLAG_RA);
         answer_message->setHeaderFlag(Message::HEADERFLAG_RA);
-        if (rd) {
-            answer_message->setHeaderFlag(Message::HEADERFLAG_RD);
-        }
-        if (cd) {
-            answer_message->setHeaderFlag(Message::HEADERFLAG_CD);
-        }
+        answer_message->setHeaderFlag(Message::HEADERFLAG_RD, rd);
+        answer_message->setHeaderFlag(Message::HEADERFLAG_CD, cd);
 
 
-        vector<QuestionPtr> questions;
-        questions.assign(query_message->beginQuestion(), query_message->endQuestion());
-        for_each(questions.begin(), questions.end(), QuestionInserter(answer_message));
-        
         // Now we can clear the buffer and render the new message into it
         // Now we can clear the buffer and render the new message into it
         buffer->clear();
         buffer->clear();
         MessageRenderer renderer(*buffer);
         MessageRenderer renderer(*buffer);

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

@@ -38,6 +38,8 @@ run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
+run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 
 
 # Note the ordering matters: -Wno-... must follow -Wextra (defined in
 # Note the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS
 # B10_CXXFLAGS

+ 2 - 2
src/bin/resolver/tests/resolver_config_unittest.cc

@@ -157,8 +157,8 @@ TEST_F(ResolverConfig, listenAddresses) {
     isc::testutils::portconfig::listenAddresses(server);
     isc::testutils::portconfig::listenAddresses(server);
 }
 }
 
 
-// Switch addresses
-TEST_F(ResolverConfig, DISABLED_listenAddressConfig) {
+// Try setting some addresses and a rollback
+TEST_F(ResolverConfig, listenAddressConfig) {
     isc::testutils::portconfig::listenAddressConfig(server);
     isc::testutils::portconfig::listenAddressConfig(server);
 }
 }
 
 

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

@@ -5,7 +5,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 pkglibexec_SCRIPTS = b10-stats
 pkglibexec_SCRIPTS = b10-stats
 noinst_SCRIPTS = b10-stats_stub
 noinst_SCRIPTS = b10-stats_stub
 
 
-b10_statsdir = $(DESTDIR)$(pkgdatadir)
+b10_statsdir = $(pkgdatadir)
 b10_stats_DATA = stats.spec
 b10_stats_DATA = stats.spec
 
 
 CLEANFILES = stats.spec b10-stats stats.pyc stats.pyo b10-stats_stub stats_stub.pyc stats_stub.pyo
 CLEANFILES = stats.spec b10-stats stats.pyc stats.pyo b10-stats_stub stats_stub.pyc stats_stub.pyo
@@ -23,7 +23,6 @@ endif
 stats.spec: stats.spec.pre
 stats.spec: stats.spec.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" stats.spec.pre >$@
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" stats.spec.pre >$@
 
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-stats: stats.py
 b10-stats: stats.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \

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

@@ -1,6 +1,6 @@
 sbin_SCRIPTS = b10-cmdctl-usermgr
 sbin_SCRIPTS = b10-cmdctl-usermgr
 
 
-b10_cmdctl_usermgrdir = $(DESTDIR)$(pkgdatadir)
+b10_cmdctl_usermgrdir = $(pkgdatadir)
 
 
 CLEANFILES=	b10-cmdctl-usermgr
 CLEANFILES=	b10-cmdctl-usermgr
 
 
@@ -14,7 +14,6 @@ b10-cmdctl-usermgr.8: b10-cmdctl-usermgr.xml
 
 
 endif
 endif
 
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-cmdctl-usermgr: b10-cmdctl-usermgr.py
 b10-cmdctl-usermgr: b10-cmdctl-usermgr.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" b10-cmdctl-usermgr.py >$@
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" b10-cmdctl-usermgr.py >$@

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

@@ -4,7 +4,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 
 pkglibexec_SCRIPTS = b10-xfrin
 pkglibexec_SCRIPTS = b10-xfrin
 
 
-b10_xfrindir = $(DESTDIR)$(pkgdatadir)
+b10_xfrindir = $(pkgdatadir)
 b10_xfrin_DATA = xfrin.spec
 b10_xfrin_DATA = xfrin.spec
 
 
 CLEANFILES = b10-xfrin xfrin.pyc 
 CLEANFILES = b10-xfrin xfrin.pyc 
@@ -20,7 +20,6 @@ b10-xfrin.8: b10-xfrin.xml
 
 
 endif
 endif
 
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-xfrin: xfrin.py
 b10-xfrin: xfrin.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \

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

@@ -4,7 +4,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 
 pkglibexec_SCRIPTS = b10-xfrout
 pkglibexec_SCRIPTS = b10-xfrout
 
 
-b10_xfroutdir = $(DESTDIR)$(pkgdatadir)
+b10_xfroutdir = $(pkgdatadir)
 b10_xfrout_DATA = xfrout.spec
 b10_xfrout_DATA = xfrout.spec
 
 
 CLEANFILES=	b10-xfrout xfrout.pyc xfrout.spec
 CLEANFILES=	b10-xfrout xfrout.pyc xfrout.spec
@@ -23,7 +23,6 @@ endif
 xfrout.spec: xfrout.spec.pre
 xfrout.spec: xfrout.spec.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.spec.pre >$@
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.spec.pre >$@
 
 
-# TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-xfrout: xfrout.py
 b10-xfrout: xfrout.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \

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

@@ -4,7 +4,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 
 pkglibexec_SCRIPTS = b10-zonemgr
 pkglibexec_SCRIPTS = b10-zonemgr
 
 
-b10_zonemgrdir = $(DESTDIR)$(pkgdatadir)
+b10_zonemgrdir = $(pkgdatadir)
 b10_zonemgr_DATA = zonemgr.spec
 b10_zonemgr_DATA = zonemgr.spec
 
 
 CLEANFILES = b10-zonemgr zonemgr.pyc zonemgr.spec
 CLEANFILES = b10-zonemgr zonemgr.pyc zonemgr.spec

+ 15 - 0
src/cppcheck-suppress.lst

@@ -0,0 +1,15 @@
+// On some systems cppcheck produces false alarms about 'missing includes'.
+// the following two will suppress, depending on the cppcheck version
+debug
+missingInclude
+// This is a template, and should be excluded from the check
+unreadVariable:src/lib/dns/rdata/template.cc:59
+// These two trigger warnings due to the incomplete implementation.  This is
+// our problem, but we need to suppress the warnings for now.
+functionConst:src/lib/cache/resolver_cache.h
+functionConst:src/lib/cache/message_cache.h
+functionConst:src/lib/cache/rrset_cache.h
+// Intentional self assignment tests.  Suppress warning about them.
+selfAssignment:src/lib/dns/tests/name_unittest.cc:292
+selfAssignment:src/lib/dns/tests/rdata_unittest.cc:227
+selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:104

+ 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 cache server_common
+          resolve nsas cache asiolink testutils server_common

+ 21 - 14
src/lib/asiolink/Makefile.am

@@ -12,24 +12,29 @@ 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.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  = asiolink.h
 libasiolink_la_SOURCES += dns_answer.h
 libasiolink_la_SOURCES += dns_answer.h
-libasiolink_la_SOURCES += simple_callback.h
+libasiolink_la_SOURCES += dns_lookup.h
+libasiolink_la_SOURCES += dns_server.h
+libasiolink_la_SOURCES += dns_service.h dns_service.cc
+libasiolink_la_SOURCES += dummy_io_cb.h
 libasiolink_la_SOURCES += interval_timer.h interval_timer.cc
 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_address.h io_address.cc
+libasiolink_la_SOURCES += io_asio_socket.h
+libasiolink_la_SOURCES += io_endpoint.h io_endpoint.cc
+libasiolink_la_SOURCES += io_error.h
+libasiolink_la_SOURCES += io_fetch.h io_fetch.cc
 libasiolink_la_SOURCES += io_message.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 += io_service.h io_service.cc
+libasiolink_la_SOURCES += io_socket.h io_socket.cc
+libasiolink_la_SOURCES += recursive_query.h recursive_query.cc
+libasiolink_la_SOURCES += simple_callback.h
+libasiolink_la_SOURCES += tcp_endpoint.h
 libasiolink_la_SOURCES += tcp_server.h tcp_server.cc
 libasiolink_la_SOURCES += tcp_server.h tcp_server.cc
+libasiolink_la_SOURCES += tcp_socket.h
+libasiolink_la_SOURCES += udp_endpoint.h
+libasiolink_la_SOURCES += udp_server.h udp_server.cc
+libasiolink_la_SOURCES += udp_socket.h
 # 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)
@@ -43,3 +48,5 @@ endif
 libasiolink_la_CPPFLAGS = $(AM_CPPFLAGS)
 libasiolink_la_CPPFLAGS = $(AM_CPPFLAGS)
 libasiolink_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
 libasiolink_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
 libasiolink_la_LIBADD += $(top_builddir)/src/lib/resolve/libresolve.la
 libasiolink_la_LIBADD += $(top_builddir)/src/lib/resolve/libresolve.la
+libasiolink_la_LIBADD += $(top_builddir)/src/lib/cache/libcache.la
+libasiolink_la_LIBADD += $(top_builddir)/src/lib/nsas/libnsas.la

+ 80 - 1
src/lib/asiolink/README

@@ -33,7 +33,7 @@ This is intended to simplify development a bit, since it allows the
 routines to be written in a straightfowrard step-step-step fashion rather
 routines to be written in a straightfowrard step-step-step fashion rather
 than as a complex chain of separate handler functions.
 than as a complex chain of separate handler functions.
 
 
-Coroutine objects (i.e., UDPServer, TCPServer and UDPQuery) are objects
+Coroutine objects (i.e., UDPServer, TCPServer and IOFetch) are objects
 with reenterable operator() members.  When an instance of one of these
 with reenterable operator() members.  When an instance of one of these
 classes is called as a function, it resumes at the position where it left
 classes is called as a function, it resumes at the position where it left
 off.  Thus, a UDPServer can issue an asynchronous I/O call and specify
 off.  Thus, a UDPServer can issue an asynchronous I/O call and specify
@@ -101,3 +101,82 @@ when the answer has arrived.  In simplified form, the DNSQuery routine is:
 Currently, DNSQuery is only implemented for UDP queries.  In future work
 Currently, DNSQuery is only implemented for UDP queries.  In future work
 it will be necessary to write code to fall back to TCP when circumstances
 it will be necessary to write code to fall back to TCP when circumstances
 require it.
 require it.
+
+
+Upstream Fetches
+================
+Upstream fetches (queries by the resolver on behalf of a client) are made
+using a slightly-modified version of the pattern described above.
+
+Sockets
+-------
+First, it will be useful to understand the class hierarchy used in the
+fetch logic:
+
+        IOSocket
+           |
+      IOAsioSocket
+           |
+     +-----+-----+                
+     |           |
+UDPSocket    TCPSocket
+
+IOSocket is a wrapper class for a socket and is used by the authoritative
+server code.  It is an abstract base class, providing little more that the ability to hold the socket and to return the protocol in use.
+
+Built on this is IOAsioSocket, which adds the open, close, asyncSend and
+asyncReceive methods.  This is a template class, which takes as template
+argument the class of the object that will be used as the callback when the
+asynchronous operation completes. This object can be of any type, but must
+include an operator() method with the signature:
+
+   operator()(asio::error_code ec, size_t length)
+
+... the two arguments being the status of the completed I/O operation and
+the number of bytes transferred. (In the case of the open method, the second
+argument will be zero.)
+
+Finally, the TCPSocket and UDPSocket classes provide the body of the
+asynchronous operations.
+
+Fetch Sequence
+--------------
+The fetch is implemented by the IOFetch class, which takes as argument the
+protocol to use.  The sequence is:
+
+  REENTER:
+    render the question into a wire-format query packet
+    open()                           // Open socket and optionally connect
+    if (! synchronous) {
+        YIELD;
+    }
+    YIELD asyncSend(query)           // Send query 
+    do {
+        YIELD asyncReceive(response) // Read response
+    } while (! complete(response))
+    close()                          // Drop connection and close socket
+    server->resume
+
+The open() method opens a socket for use.  On TCP, it also makes a
+connection to the remote end.  So under UDP the operation will complete
+immediately, but under TCP it could take a long time.  One solution would be
+for the open operation to post an event to the I/O queue; then both cases
+could be regarded as being equivalent, with the completion being signalled
+by the posting of the completion event.  However UDP is the most common case
+and that would involve extra overhead.  So the open() returns a status
+indicating whether the operation completed asynchronously.  If it did, the
+code yields back to the coroutine; if not the yield is bypassed.
+
+The asynchronous send is straightforward, invoking the underlying ASIO
+function.  (Note that the address/port is supplied to both the open() and
+asyncSend() methods - it is used by the TCPSocket in open() and by the
+UDPSocket in asyncSend().)
+
+The asyncReceive() method issues an asynchronous read and waits for completion.
+The fetch object keeps track of the amount of data received so far and when
+the receive completes it calls a method on the socket to determine if the
+entire message has been received.  (This will always be the case for UDP.  On
+TCP though, the message is preceded by a count field as several reads may be
+required to read all the data.)  The fetch loops until all the data is read.
+
+Finally, the socket is closed and the server called to resume operation.

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

@@ -32,6 +32,7 @@
 #include <asiolink/io_endpoint.h>
 #include <asiolink/io_endpoint.h>
 #include <asiolink/io_message.h>
 #include <asiolink/io_message.h>
 #include <asiolink/io_socket.h>
 #include <asiolink/io_socket.h>
+#include <asiolink/io_error.h>
 
 
 /// \namespace asiolink
 /// \namespace asiolink
 /// \brief A wrapper interface for the ASIO library.
 /// \brief A wrapper interface for the ASIO library.
@@ -83,20 +84,6 @@
 /// the placeholder of callback handlers:
 /// the placeholder of callback handlers:
 /// 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 {
-
-
-/// \brief An exception that is thrown if an error occurs within the IO
-/// module.  This is mainly intended to be a wrapper exception class for
-/// ASIO specific exceptions.
-class IOError : public isc::Exception {
-public:
-    IOError(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-
-}      // asiolink
 #endif // __ASIOLINK_H
 #endif // __ASIOLINK_H
 
 
 // Local Variables: 
 // Local Variables: 

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

@@ -75,6 +75,9 @@ public:
         (*self_)(ec, length);
         (*self_)(ec, length);
     }
     }
 
 
+    /// \brief Stop current running server
+    virtual void stop() { self_->stop();}
+
     /// \brief Resume processing of the server coroutine after an 
     /// \brief Resume processing of the server coroutine after an 
     /// asynchronous call (e.g., to the DNS Lookup provider) has completed.
     /// asynchronous call (e.g., to the DNS Lookup provider) has completed.
     ///
     ///

+ 13 - 5
src/lib/asiolink/dns_service.cc

@@ -12,13 +12,19 @@
 // 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 <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
+
+#include <boost/lexical_cast.hpp>
+
 #include <config.h>
 #include <config.h>
 
 
-// unistd is needed for asio.hpp with SunStudio
-#include <unistd.h>
+#include <log/dummylog.h>
 
 
 #include <asio.hpp>
 #include <asio.hpp>
-
+#include <asiolink/dns_service.h>
+#include <asiolink/io_service.h>
 #include <asiolink/io_service.h>
 #include <asiolink/io_service.h>
 #include <asiolink/tcp_server.h>
 #include <asiolink/tcp_server.h>
 #include <asiolink/udp_server.h>
 #include <asiolink/udp_server.h>
@@ -26,6 +32,7 @@
 #include <log/dummylog.h>
 #include <log/dummylog.h>
 
 
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
+#include <boost/foreach.hpp>
 
 
 using isc::log::dlog;
 using isc::log::dlog;
 
 
@@ -182,8 +189,9 @@ DNSService::addServer(uint16_t port, const std::string& address) {
 
 
 void
 void
 DNSService::clearServers() {
 DNSService::clearServers() {
-    // FIXME: This does not work, it does not close the socket.
-    // How is it done?
+    BOOST_FOREACH(const DNSServiceImpl::DNSServerPtr& s, impl_->servers_) {
+        s->stop();
+    }
     impl_->servers_.clear();
     impl_->servers_.clear();
 }
 }
 
 

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

@@ -97,6 +97,12 @@ public:
     /// It will eventually be removed once the wrapper interface is
     /// It will eventually be removed once the wrapper interface is
     /// generalized.
     /// generalized.
     asio::io_service& get_io_service() { return io_service_.get_io_service(); }
     asio::io_service& get_io_service() { return io_service_.get_io_service(); }
+
+    /// \brief Return the IO Service Object
+    ///
+    /// \return IOService object for this DNS service.
+    asiolink::IOService& getIOService() { return (io_service_);}
+
 private:
 private:
     DNSServiceImpl* impl_;
     DNSServiceImpl* impl_;
     IOService& io_service_;
     IOService& io_service_;

+ 51 - 0
src/lib/asiolink/dummy_io_cb.h

@@ -0,0 +1,51 @@
+// 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 __DUMMY_IO_CB_H
+#define __DUMMY_IO_CB_H
+
+#include <iostream>
+
+#include <asio/error.hpp>
+#include <asio/error_code.hpp>
+
+namespace asiolink {
+
+/// \brief Asynchronous I/O Completion Callback
+///
+/// The two socket classes (UDPSocket and TCPSocket) require that the I/O
+/// completion callback function have an operator() method with the appropriate
+/// signature.  The classes are templates, any class with that method and
+/// signature can be passed as the callback object - there is no need for a
+/// base class defining the interface.  However, some users of the socket
+/// classes do not use the asynchronous I/O operations, yet have to supply a
+/// template parameter.  This is the reason for this class - it is the dummy
+/// template parameter.
+
+class DummyIOCallback {
+public:
+
+    /// \brief Asynchronous I/O callback method
+    ///
+    /// \param error Unused
+    /// \param length Unused
+    void operator()(asio::error_code, size_t)
+    {
+        // TODO: log an error if this method ever gets called.
+    }
+};
+
+} // namespace asiolink
+
+#endif // __DUMMY_IO_CB_H

+ 57 - 20
src/lib/asiolink/udp_query.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // 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,26 +12,34 @@
 // 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 __UDP_QUERY_H
-#define __UDP_QUERY_H 1
+#ifndef __IOFETCH_H
+#define __IOFETCH_H 1
 
 
-#ifndef ASIO_HPP
-#error "asio.hpp must be included before including this, see asiolink.h as to why"
-#endif
+#include <config.h>
+
+#include <asio.hpp>
+#include <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
 
 
 #include <dns/buffer.h>
 #include <dns/buffer.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
 
 
-#include <asiolink/io_address.h>
-#include <coroutine.h>
+#include <asiolink/asiolink.h>
+#include <asiolink/internal/coroutine.h>
 
 
-namespace asiolink {
+// This file contains TCP/UDP-specific implementations of generic classes 
+// defined in asiolink.h.  It is *not* intended to be part of the public
+// API.
 
 
+namespace asiolink {
 //
 //
-// Asynchronous UDP coroutine for upstream queries
+// Asynchronous UDP/TCP coroutine for upstream fetches
 //
 //
-class UDPQuery : public coroutine {
+//class IOFetch : public coroutine, public UdpFetch, public TcpFetch {
+class IOFetch : public coroutine {
 public:
 public:
-    // TODO Maybe this should be more generic than just for UDPQuery?
+    // TODO Maybe this should be more generic than just for IOFetch?
     ///
     ///
     /// \brief Result of the query
     /// \brief Result of the query
     ///
     ///
@@ -43,12 +51,12 @@ public:
         TIME_OUT,
         TIME_OUT,
         STOPPED
         STOPPED
     };
     };
-    /// Abstract callback for the UDPQuery.
+    /// Abstract callback for the IOFetch.
     class Callback {
     class Callback {
     public:
     public:
         virtual ~Callback() {}
         virtual ~Callback() {}
 
 
-        /// This will be called when the UDPQuery is completed
+        /// This will be called when the IOFetch is completed
         virtual void operator()(Result result) = 0;
         virtual void operator()(Result result) = 0;
     };
     };
     ///
     ///
@@ -59,11 +67,12 @@ public:
     ///        delete it if allocated on heap.
     ///        delete it if allocated on heap.
     ///@param timeout in ms.
     ///@param timeout in ms.
     ///
     ///
-    explicit UDPQuery(asio::io_service& io_service,
+    IOFetch(asio::io_service& io_service,
                       const isc::dns::Question& q,
                       const isc::dns::Question& q,
                       const IOAddress& addr, uint16_t port,
                       const IOAddress& addr, uint16_t port,
                       isc::dns::OutputBufferPtr buffer,
                       isc::dns::OutputBufferPtr buffer,
-                      Callback* callback, int timeout = -1);
+                      Callback* callback, int timeout = -1, 
+                      int protocol = IPPROTO_UDP);
     void operator()(asio::error_code ec = asio::error_code(),
     void operator()(asio::error_code ec = asio::error_code(),
                     size_t length = 0);
                     size_t length = 0);
     /// Terminate the query.
     /// Terminate the query.
@@ -80,9 +89,37 @@ private:
     /// to many async_*() functions) and we want keep the same data. Some of
     /// to many async_*() functions) and we want keep the same data. Some of
     /// the data is not copyable too.
     /// the data is not copyable too.
     ///
     ///
-    struct PrivateData;
-    boost::shared_ptr<PrivateData> data_;
+    //struct IOFetchProtocol;
+    //boost::shared_ptr<IOFetchProtocol> data_;
+    //struct UdpData;
+    //struct TcpData;
+    boost::shared_ptr<UdpFetch> data_;
+    boost::shared_ptr<TcpFetch> tcp_data_;
+};
+class UdpFetch : public IOFetch {
+    public:
+        struct UdpData;
+        explicit UdpFetch(asio::io_service& io_service,
+                          const isc::dns::Question& q,
+                          const IOAddress& addr,
+                          uint16_t port,
+                          isc::dns::OutputBufferPtr buffer,
+                          IOFetch::Callback *callback,
+                          int timeout);
 };
 };
+class TcpFetch : public IOFetch {
+    public:
+        struct TcpData;
+        explicit TcpFetch(io_service& io_service, const Question& q,
+                 const IOAddress& addr, uint16_t port,
+                 OutputBufferPtr buffer, Callback *callback, int timeout);
+};
+
+}
+
+
+#endif // __IOFETCH_H
 
 
-}      // namespace asiolink
-#endif // __UDP_QUERY_H
+// Local Variables: 
+// mode: c++
+// End: 

+ 5 - 5
src/lib/asiolink/interval_timer.cc

@@ -14,18 +14,18 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
-// unistd is needed for asio.hpp with SunStudio
-#include <unistd.h>
+#include <unistd.h>             // for some IPC/network system calls
+#include <sys/socket.h>
+#include <netinet/in.h>
 
 
-#include <asio.hpp>
+#include <boost/bind.hpp>
 
 
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
+#include <asio.hpp>
 #include <asiolink/interval_timer.h>
 #include <asiolink/interval_timer.h>
 #include <asiolink/io_service.h>
 #include <asiolink/io_service.h>
 
 
-#include <boost/bind.hpp>
-
 namespace asiolink {
 namespace asiolink {
 
 
 class IntervalTimerImpl {
 class IntervalTimerImpl {

+ 4 - 1
src/lib/asiolink/io_address.cc

@@ -20,7 +20,10 @@
 
 
 #include <asio.hpp>
 #include <asio.hpp>
 
 
-#include <asiolink/asiolink.h>
+#include <exceptions/exceptions.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_error.h>
+
 
 
 using namespace asio;
 using namespace asio;
 using asio::ip::udp;
 using asio::ip::udp;

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

@@ -12,8 +12,8 @@
 // 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 __IOADDRESS_H
-#define __IOADDRESS_H 1
+#ifndef __IO_ADDRESS_H
+#define __IO_ADDRESS_H 1
 
 
 // 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.
@@ -120,7 +120,7 @@ private:
 };
 };
 
 
 }      // asiolink
 }      // asiolink
-#endif // __IOADDRESS_H
+#endif // __IO_ADDRESS_H
 
 
 // Local Variables: 
 // Local Variables: 
 // mode: c++
 // mode: c++

+ 309 - 0
src/lib/asiolink/io_asio_socket.h

@@ -0,0 +1,309 @@
+// 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 __IO_ASIO_SOCKET_H
+#define __IO_ASIO_SOCKET_H 1
+
+// IMPORTANT NOTE: only very few ASIO headers files can be included in
+// this file.  In particular, asio.hpp should never be included here.
+// See the description of the namespace below.
+#include <unistd.h>             // for some network system calls
+
+#include <functional>
+#include <string>
+
+#include <exceptions/exceptions.h>
+#include <coroutine.h>
+
+#include <asiolink/io_error.h>
+#include <asiolink/io_socket.h>
+
+
+namespace asiolink {
+
+/// \brief Socket not open
+///
+/// Thrown on an attempt to do read/write to a socket that is not open.
+class SocketNotOpen : public IOError {
+public:
+    SocketNotOpen(const char* file, size_t line, const char* what) :
+        IOError(file, line, what) {}
+};
+
+
+
+/// Forward declaration of an IOEndpoint
+class IOEndpoint;
+
+
+/// \brief I/O Socket with asynchronous operations
+///
+/// This class is a wrapper for the ASIO socket classes such as
+/// \c ip::tcp::socket and \c ip::udp::socket.
+///
+/// This is the basic IOSocket with additional operations - open, send, receive
+/// and close.  Depending on how the asiolink code develops, it may be a
+/// temporary class: its main use is to add the template parameter needed for
+/// the derived classes UDPSocket and TCPSocket but without changing the
+/// signature of the more basic IOSocket class.
+///
+/// We may revisit this decision when we generalize the wrapper and more
+/// modules use it.  Also, at that point we may define a separate (visible)
+/// derived class for testing purposes rather than providing factory methods
+/// (i.e., getDummy variants below).
+///
+/// TODO: Check if IOAsioSocket class is still needed
+///
+/// \param C Template parameter identifying type of the callback object.
+
+template <typename C>
+class IOAsioSocket : public IOSocket {
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private, making this class non-copyable.
+    //@{
+private:
+    IOAsioSocket(const IOAsioSocket<C>& source);
+    IOAsioSocket& operator=(const IOAsioSocket<C>& 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).
+    IOAsioSocket() {}
+public:
+    /// The destructor.
+    virtual ~IOAsioSocket() {}
+    //@}
+
+    /// \brief Return the "native" representation of the socket.
+    ///
+    /// In practice, this is the file descriptor of the socket for
+    /// UNIX-like systems so the current implementation simply uses
+    /// \c int as the type of the return value.
+    /// We may have to need revisit this decision later.
+    ///
+    /// In general, the application should avoid using this method;
+    /// it essentially discloses an implementation specific "handle" that
+    /// can change the internal state of the socket (consider the
+    /// application closes it, for example).
+    /// But we sometimes need to perform very low-level operations that
+    /// requires the native representation.  Passing the file descriptor
+    /// to a different process is one example.
+    /// This method is provided as a necessary evil for such limited purposes.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return The native representation of the socket.  This is the socket
+    /// file descriptor for UNIX-like systems.
+    virtual int getNative() const = 0;
+
+    /// \brief Return the transport protocol of the socket.
+    ///
+    /// Currently, it returns \c IPPROTO_UDP for UDP sockets, and
+    /// \c IPPROTO_TCP for TCP sockets.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return IPPROTO_UDP for UDP sockets
+    /// \return IPPROTO_TCP for TCP sockets
+    virtual int getProtocol() const = 0;
+
+    /// \brief Open AsioSocket
+    ///
+    /// Opens the socket for asynchronous I/O.  On a UDP socket, this is merely
+    /// an "open()" on the underlying socket (so completes immediately), but on
+    /// a TCP socket it also connects to the remote end (which is done as an
+    /// asynchronous operation).
+    ///
+    /// For TCP, signalling of the completion of the operation is done by
+    /// by calling the callback function in the normal way.  This could be done
+    /// for UDP (by posting en event on the event queue); however, that will
+    /// incur additional overhead in the most common case.  Instead, the return
+    /// value indicates whether the operation was asynchronous or not. If yes,
+    /// (i.e. TCP) the callback has been posted to the event queue: if no (UDP),
+    /// no callback has been posted (in which case it is up to the caller as to
+    /// whether they want to manually post the callback themself.)
+    ///
+    /// \param endpoint Pointer to the endpoint object.  This is ignored for
+    /// a UDP socket (the target is specified in the send call), but should
+    /// be of type TCPEndpoint for a TCP connection.
+    /// \param callback I/O Completion callback, called when the operation has
+    /// completed, but only if the operation was asynchronous.
+    ///
+    /// \return true if an asynchronous operation was started and the caller
+    /// should yield and wait for completion, false if the operation was
+    /// completed synchronously and no callback was queued.
+    virtual bool open(const IOEndpoint* endpoint, C& callback) = 0;
+
+    /// \brief Send Asynchronously
+    ///
+    /// This corresponds to async_send_to() for UDP sockets and async_send()
+    /// for TCP.  In both cases an endpoint argument is supplied indicating the
+    /// target of the send - this is ignored for TCP.
+    ///
+    /// \param data Data to send
+    /// \param length Length of data to send
+    /// \param endpoint Target of the send
+    /// \param callback Callback object.
+    virtual void asyncSend(const void* data, size_t length,
+        const IOEndpoint* endpoint, C& callback) = 0;
+
+    /// \brief Receive Asynchronously
+    ///
+    /// This correstponds to async_receive_from() for UDP sockets and
+    /// async_receive() for TCP.  In both cases, an endpoint argument is
+    /// supplied to receive the source of the communication.  For TCP it will
+    /// be filled in with details of the connection.
+    ///
+    /// \param data Buffer to receive incoming message
+    /// \param length Length of the data buffer
+    /// \param cumulative Amount of data that should already be in the buffer.
+    /// \param endpoint Source of the communication
+    /// \param callback Callback object
+    virtual void asyncReceive(void* data, size_t length, size_t cumulative,
+        IOEndpoint* endpoint, C& callback) = 0;
+
+    /// \brief Checks if the data received is complete.
+    ///
+    /// This applies to TCP receives, where the data is a byte stream and a
+    /// receive is not guaranteed to receive the entire message.  DNS messages
+    /// over TCP are prefixed by a two-byte count field.  This method takes the
+    /// amount received so far and the amount received in this I/O and checks
+    /// if the message is complete, returning the appropriate indication.  As
+    /// a side-effect, it also updates the amount received.
+    ///
+    /// For a UDP receive, all the data is received in one I/O, so this is
+    /// effectively a no-op (although it does update the amount received).
+    ///
+    /// \param data Data buffer containing data to date
+    /// \param length Amount of data received in last asynchronous I/O
+    /// \param cumulative On input, amount of data received before the last
+    /// I/O.  On output, the total amount of data received to date.
+    ///
+    /// \return true if the receive is complete, false if another receive is
+    /// needed.
+    virtual bool receiveComplete(void* data, size_t length,
+        size_t& cumulative) = 0;
+
+    /// \brief Cancel I/O On AsioSocket
+    virtual void cancel() = 0;
+
+    /// \brief Close socket
+    virtual void close() = 0;
+};
+
+
+#include "io_socket.h"
+
+/// \brief The \c DummyAsioSocket class is a concrete derived class of
+/// \c IOAsioSocket that is not associated with any real socket.
+///
+/// This main purpose of this class is tests, where it may be desirable to
+/// instantiate an \c IOAsioSocket object without involving system resource
+/// allocation such as real network sockets.
+///
+/// \param C Template parameter identifying type of the callback object.
+
+template <typename C>
+class DummyAsioSocket : public IOAsioSocket<C> {
+private:
+    DummyAsioSocket(const DummyAsioSocket<C>& source);
+    DummyAsioSocket& operator=(const DummyAsioSocket<C>& source);
+public:
+    /// \brief Constructor from the protocol number.
+    ///
+    /// The protocol must validly identify a standard network protocol.
+    /// For example, to specify TCP \c protocol must be \c IPPROTO_TCP.
+    ///
+    /// \param protocol The network protocol number for the socket.
+    DummyAsioSocket(const int protocol) : protocol_(protocol) {}
+
+    /// \brief A dummy derived method of \c IOAsioSocket::getNative().
+    ///
+    /// \return Always returns -1 as the object is not associated with a real
+    /// (native) socket.
+    virtual int getNative() const { return (-1); }
+
+    /// \brief A dummy derived method of \c IOAsioSocket::getProtocol().
+    ///
+    /// \return Protocol socket was created with
+    virtual int getProtocol() const { return (protocol_); }
+
+
+    /// \brief Open AsioSocket
+    ///
+    /// A call that is a no-op on UDP sockets, this opens a connection to the
+    /// system identified by the given endpoint.
+    ///
+    /// \param endpoint Unused
+    /// \param callback Unused.
+    ///false indicating that the operation completed synchronously.
+    virtual bool open(const IOEndpoint*, C&) {
+        return (false);
+    }
+
+    /// \brief Send Asynchronously
+    ///
+    /// Must be supplied as it is abstract in the base class.
+    ///
+    /// \param data Unused
+    /// \param length Unused
+    /// \param endpoint Unused
+    /// \param callback Unused
+    virtual void asyncSend(const void*, size_t, const IOEndpoint*, C&) {
+    }
+
+    /// \brief Receive Asynchronously
+    ///
+    /// Must be supplied as it is abstract in the base class.
+    ///
+    /// \param data Unused
+    /// \param length Unused
+    /// \param cumulative Unused
+    /// \param endpoint Unused
+    /// \param callback Unused
+    virtual void asyncReceive(void* data, size_t, size_t, IOEndpoint*, C&) { } 
+    /// \brief Checks if the data received is complete.
+    ///
+    /// \param data Unused
+    /// \param length Unused
+    /// \param cumulative Unused
+    ///
+    /// \return Always true
+    virtual bool receiveComplete(void*, size_t, size_t&) {
+        return (true);
+    }
+
+    /// \brief Cancel I/O On AsioSocket
+    ///
+    /// Must be supplied as it is abstract in the base class.
+    virtual void cancel() {
+    }
+
+    /// \brief Close socket
+    ///
+    /// Must be supplied as it is abstract in the base class.
+    virtual void close() {
+    }
+
+private:
+    const int protocol_;
+};
+
+} // namespace asiolink
+
+#endif // __IO_ASIO_SOCKET_H

+ 2 - 1
src/lib/asiolink/io_endpoint.cc

@@ -20,7 +20,8 @@
 
 
 #include <asio.hpp>
 #include <asio.hpp>
 
 
-#include <asiolink/asiolink.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_error.h>
 #include <asiolink/tcp_endpoint.h>
 #include <asiolink/tcp_endpoint.h>
 #include <asiolink/udp_endpoint.h>
 #include <asiolink/udp_endpoint.h>
 
 

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

@@ -12,8 +12,8 @@
 // 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 __IOENDPOINT_H
-#define __IOENDPOINT_H 1
+#ifndef __IO_ENDPOINT_H
+#define __IO_ENDPOINT_H 1
 
 
 // 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.
@@ -115,7 +115,7 @@ public:
 };
 };
 
 
 }      // asiolink
 }      // asiolink
-#endif // __IOENDPOINT_H
+#endif // __IO_ENDPOINT_H
 
 
 // Local Variables: 
 // Local Variables: 
 // mode: c++
 // mode: c++

+ 35 - 0
src/lib/asiolink/io_error.h

@@ -0,0 +1,35 @@
+// 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 __IO_ERROR_H
+#define __IO_ERROR_H
+
+#include <exceptions/exceptions.h>
+
+namespace asiolink {
+
+/// \brief An exception that is thrown if an error occurs within the IO
+/// module.  This is mainly intended to be a wrapper exception class for
+/// ASIO specific exceptions.
+class IOError : public isc::Exception {
+public:
+    IOError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+
+}      // asiolink
+
+#endif // __IO_ERROR_H

+ 193 - 0
src/lib/asiolink/io_fetch.cc

@@ -0,0 +1,193 @@
+// 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 <unistd.h>             // for some IPC/network system calls
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <boost/bind.hpp>
+
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <log/dummylog.h>
+
+#include <asio.hpp>
+#include <asiolink/io_fetch.h>
+
+using namespace asio;
+using namespace isc::dns;
+using namespace isc::log;
+using namespace std;
+
+namespace asiolink {
+
+/// IOFetch Constructor - just initialize the private data
+
+IOFetch::IOFetch(int protocol, IOService& service,
+    const isc::dns::Question& question, const IOAddress& address, uint16_t port,
+    isc::dns::OutputBufferPtr& buff, Callback* cb, int wait)
+    :
+    data_(new IOFetch::IOFetchData(protocol, service, question, address,
+        port, buff, cb, wait))
+{
+}
+
+/// The function operator is implemented with the "stackless coroutine"
+/// pattern; see internal/coroutine.h for details.
+
+void
+IOFetch::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);
+            
+            // TODO: 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);
+
+            // As this is a new fetch, clear the amount of data received
+            data_->cumulative = 0;
+
+            dlog("Sending " + msg.toText() + " to " +
+                data_->remote->getAddress().toText());
+        }
+
+
+        // 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(&IOFetch::stop, *this,
+                TIME_OUT));
+        }
+
+        // Open a connection to the target system.  For speed, if the operation
+        // was completed synchronously (i.e. UDP operation) we bypass the yield.
+        if (data_->socket->open(data_->remote.get(), *this)) {
+            CORO_YIELD;
+        }
+
+        // Begin an asynchronous send, and then yield.  When the send completes
+        // send completes, we will resume immediately after this point.
+        CORO_YIELD data_->socket->asyncSend(data_->msgbuf->getData(),
+            data_->msgbuf->getLength(), data_->remote.get(), *this);
+
+        // Now receive the response.  Since TCP may not receive the entire
+        // message in one operation, we need to loop until we have received
+        // it. (This can't be done within the asyncReceive() method because
+        // each I/O operation will be done asynchronously and between each one
+        // we need to yield ... and we *really* don't want to set up another
+        // coroutine within that method.)  So after each receive (and yield),
+        // we check if the operation is complete and if not, loop to read again.
+        do {
+            CORO_YIELD data_->socket->asyncReceive(data_->data.get(),
+                static_cast<size_t>(MAX_LENGTH), data_->cumulative,
+                data_->remote.get(), *this);
+        } while (!data_->socket->receiveComplete(data_->data.get(), length,
+            data_->cumulative));
+
+        // The message is not rendered yet, so we can't print it easily
+        dlog("Received response from " + data_->remote->getAddress().toText());
+
+        /// Copy the answer into the response buffer.  (TODO: If the
+        /// OutputBuffer object were made to meet the requirements of
+        /// a MutableBufferSequence, then it could be written to directly
+        /// by async_receive_from() and this additional copy step would
+        /// be unnecessary.)
+        data_->buffer->writeData(data_->data.get(), length);
+
+        // Finished with this socket, so close it.
+        data_->socket->close();
+
+        /// We are done
+        stop(SUCCESS);
+    }
+}
+
+// Function that stops the coroutine sequence.  It is called either when the
+// query finishes or when the timer times out.  Either way, it sets the
+// "stopped_" flag and cancels anything that is in progress.
+//
+// As the function may be entered multiple times as things wind down, the
+// stopped_ flag checks if stop() has already been called.  If it has,
+// subsequent calls are no-ops.
+
+void
+IOFetch::stop(Result result) {
+
+    if (!data_->stopped) {
+
+        // Mark the fetch as stopped to prevent other completion callbacks
+        // (invoked because of the calls to cancel()) from executing the
+        // cancel calls again.
+        //
+        // In a single threaded environment, the callbacks won't be invoked
+        // until this one completes. In a multi-threaded environment, they may
+        // well be, in which case the testing (and setting) of the stopped_
+        // variable should be done inside a mutex (and the stopped_ variable
+        // declared as "volatile").
+        //
+        // TODO: Update testing of stopped_ if threads are used.
+        data_->stopped = true;
+
+        switch (result) {
+            case TIME_OUT:
+                dlog("Query timed out");
+                break;
+
+            case STOPPED:
+                dlog("Query stopped");
+                break;
+
+            default:
+                ;
+        }
+
+        // Stop requested, cancel and I/O's on the socket and shut it down,
+        // and cancel the timer.
+        data_->socket->cancel();
+        data_->socket->close();
+
+        data_->timer.cancel();
+
+        // Execute the I/O completion callback (if present).
+        if (data_->callback) {
+            (*(data_->callback))(result);
+        }
+
+        // Mark that stop() has now been called.
+
+    }
+}
+
+} // namespace asiolink
+

+ 226 - 0
src/lib/asiolink/io_fetch.h

@@ -0,0 +1,226 @@
+// 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 __IO_FETCH_H
+#define __IO_FETCH_H 1
+
+#include <config.h>
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
+
+#include <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <asio/deadline_timer.hpp>
+
+#include <coroutine.h>
+
+#include <dns/buffer.h>
+#include <dns/question.h>
+
+#include <asiolink/io_asio_socket.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_socket.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/udp_socket.h>
+#include <asiolink/udp_endpoint.h>
+
+
+namespace asiolink {
+
+
+/// \brief Upstream Fetch Processing
+///
+/// IOFetch is the class used to send upstream fetches and to handle responses.
+///
+/// \param E Endpoint type to use.
+
+class IOFetch : public coroutine {
+public:
+
+    /// \brief Result of Upstream Fetch
+    ///
+    /// Note that this applies to the status of I/Os in the fetch - a fetch
+    /// that resulted in a packet being received from the server is a SUCCESS,
+    /// even if the contents of the packet indicate that some error occurred.
+    enum Result {
+        SUCCESS = 0,        ///< Success, fetch completed
+        TIME_OUT,           ///< Failure, fetch timed out
+        STOPPED,            ///< Control code, fetch has been stopped
+        NOTSET              ///< For testing, indicates value not set
+    };
+
+    // The next enum is a "trick" to allow constants to be defined in a class
+    // declaration.
+
+    /// \brief Integer Constants
+    enum {
+        MAX_LENGTH = 4096   ///< Maximum size of receive buffer
+    };
+
+    /// \brief I/O Fetch Callback
+    ///
+    /// Class of callback object for when the fetch itself has completed - an
+    /// object of this class is passed to the IOFetch constructor and its
+    /// operator() method called when the fetch completes.
+    ///
+    /// Note the difference between the two operator() methods:
+    /// - IOFetch::operator() callback is called when an asynchronous I/O has
+    ///   completed.
+    /// - IOFetch::Callback::operator() is called when an upstream fetch - which
+    ///   may have involved several asynchronous I/O operations - has completed.
+    ///
+    /// This is an abstract class.
+    class Callback {
+    public:
+        /// \brief Default Constructor
+        Callback()
+        {}
+
+        /// \brief Virtual Destructor
+        virtual ~Callback()
+        {}
+
+        /// \brief Callback method called when the fetch completes
+        ///
+        /// \brief result Result of the fetch
+        virtual void operator()(Result result) = 0;
+    };
+
+    /// \brief IOFetch Data
+    ///
+    /// The data for IOFetch is held in a separate struct pointed to by a
+    /// shared_ptr object.  This is because the IOFetch object will be copied
+    /// often (it is used as a coroutine and passed as callback to many
+    /// async_*() functions) and we want keep the same data).  Organising the
+    /// data in this way keeps copying to a minimum.
+    struct IOFetchData {
+
+        // The next two members are shared pointers to a base class because what
+        // is actually instantiated depends on whether the fetch is over UDP or
+        // TCP, which is not known until construction of the IOFetch.  Use of
+        // a shared pointer here is merely to ensure deletion when the data
+        // object is deleted.
+        boost::shared_ptr<IOAsioSocket<IOFetch> > socket;
+                                                ///< Socket to use for I/O
+        boost::shared_ptr<IOEndpoint> remote;   ///< Where the fetch was sent
+        isc::dns::Question          question;   ///< Question to be asked
+        isc::dns::OutputBufferPtr   msgbuf;     ///< Wire buffer for question
+        isc::dns::OutputBufferPtr   buffer;     ///< Received data held here
+        boost::shared_array<char>   data;       ///< Temporary array for data
+        IOFetch::Callback*          callback;   ///< Called on I/O Completion
+        size_t                      cumulative; ///< Cumulative received amount
+        bool                        stopped;    ///< Have we stopped running?
+        asio::deadline_timer        timer;      ///< Timer to measure timeouts
+        int                         timeout;    ///< Timeout in ms
+
+        /// \brief Constructor
+        ///
+        /// Just fills in the data members of the IOFetchData structure
+        ///
+        /// \param protocol either IPPROTO_UDP or IPPROTO_TCP
+        /// \param service I/O Service object to handle the asynchronous
+        ///     operations.
+        /// \param query DNS question to send to the upstream server.
+        /// \param address IP address of upstream server
+        /// \param port Port to use for the query
+        /// \param buff Output buffer into which the response (in wire format)
+        ///     is written (if a response is received).
+        /// \param cb Callback object containing the callback to be called
+        ///     when we terminate.  The caller is responsible for managing this
+        ///     object and deleting it if necessary.
+        /// \param wait Timeout for the fetch (in ms).
+        ///
+        /// TODO: May need to alter constructor (see comment 4 in Trac ticket #554)
+        IOFetchData(int protocol, IOService& service,
+            const isc::dns::Question& query, const IOAddress& address,
+            uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
+            int wait)
+            :
+            socket((protocol == IPPROTO_UDP) ?
+                static_cast<IOAsioSocket<IOFetch>*>(
+                    new UDPSocket<IOFetch>(service)) :
+                static_cast<IOAsioSocket<IOFetch>*>(
+                    new TCPSocket<IOFetch>(service))
+                ),
+            remote((protocol == IPPROTO_UDP) ?
+                static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
+                static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
+                ),
+            question(query),
+            msgbuf(new isc::dns::OutputBuffer(512)),
+            buffer(buff),
+            data(new char[IOFetch::MAX_LENGTH]),
+            callback(cb),
+            cumulative(0),
+            stopped(false),
+            timer(service.get_io_service()),
+            timeout(wait)
+        {}
+    };
+
+    /// \brief Constructor.
+    ///
+    /// Creates the object that will handle the upstream fetch.
+    ///
+    /// TODO: Need to randomise the source port
+    ///
+    /// \param protocol Fetch protocol, either IPPROTO_UDP or IPPROTO_TCP
+    /// \param service I/O Service object to handle the asynchronous
+    ///     operations.
+    /// \param question DNS question to send to the upstream server.
+    /// \param buff Output buffer into which the response (in wire format)
+    ///     is written (if a response is received).
+    /// \param cb Callback object containing the callback to be called
+    ///     when we terminate.  The caller is responsible for managing this
+    ///     object and deleting it if necessary.
+    /// \param address IP address of upstream server
+    /// \param port Port to which to connect on the upstream server
+    /// (default = 53)
+    /// \param wait Timeout for the fetch (in ms).  The default value of
+    ///     -1 indicates no timeout.
+    IOFetch(int protocol, IOService& service,
+        const isc::dns::Question& question, const IOAddress& address,
+        uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
+        int wait = -1);
+    
+    /// \brief Coroutine entry point
+    ///
+    /// The operator() method is the method in which the coroutine code enters
+    /// this object when an operation has been completed.
+    ///
+    /// \param ec Error code, the result of the last asynchronous I/O operation.
+    /// \param length Amount of data received on the last asynchronous read
+    void operator()(asio::error_code ec = asio::error_code(),
+        size_t length = 0);
+
+    /// \brief Terminate query
+    ///
+    /// This method can be called at any point.  It terminates the current
+    /// query with the specified reason.
+    ///
+    /// \param reason Reason for terminating the query
+    void stop(Result reason = STOPPED);
+
+private:
+    boost::shared_ptr<IOFetchData>  data_;   ///< Private data
+
+};
+
+} // namespace asiolink
+
+#endif // __IO_FETCH_H

+ 4 - 3
src/lib/asiolink/io_message.h

@@ -12,8 +12,8 @@
 // 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 __IOMESSAGE_H
-#define __IOMESSAGE_H 1
+#ifndef __IO_MESSAGE_H
+#define __IO_MESSAGE_H 1
 
 
 // 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.
@@ -46,6 +46,7 @@ class IOMessage {
     ///
     ///
     /// \name Constructors and Destructor
     /// \name Constructors and Destructor
     ///
     ///
+
     /// Note: The copy constructor and the assignment operator are
     /// Note: The copy constructor and the assignment operator are
     /// intentionally defined as private, making this class non-copyable.
     /// intentionally defined as private, making this class non-copyable.
     //@{
     //@{
@@ -96,7 +97,7 @@ private:
 
 
 
 
 }      // asiolink
 }      // asiolink
-#endif // __IOMESSAGE_H
+#endif // __IO_MESSAGE_H
 
 
 // Local Variables: 
 // Local Variables: 
 // mode: c++
 // mode: c++

+ 5 - 4
src/lib/asiolink/io_service.cc

@@ -11,13 +11,14 @@
 // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 // 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
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
-#include <config.h>
 
 
-// unistd is needed for asio.hpp with SunStudio
-#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
 
 
-#include <asio.hpp>
+#include <config.h>
 
 
+#include <asio.hpp>
 #include <asiolink/io_service.h>
 #include <asiolink/io_service.h>
 
 
 namespace asiolink {
 namespace asiolink {

+ 4 - 7
src/lib/asiolink/io_socket.h

@@ -12,8 +12,8 @@
 // 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 __IOSOCKET_H
-#define __IOSOCKET_H 1
+#ifndef __IO_SOCKET_H
+#define __IO_SOCKET_H 1
 
 
 // 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.
@@ -119,9 +119,6 @@ public:
     static IOSocket& getDummyTCPSocket();
     static IOSocket& getDummyTCPSocket();
 };
 };
 
 
-}      // asiolink
-#endif // __IOSOCKET_H
+} // namespace asiolink
 
 
-// Local Variables: 
-// mode: c++
-// End: 
+#endif // __IO_SOCKET_H

+ 122 - 39
src/lib/asiolink/recursive_query.cc

@@ -12,28 +12,30 @@
 // 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 <config.h>
-
+#include <netinet/in.h>
 #include <stdlib.h>
 #include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
 
 
-// unistd is needed for asio.hpp with SunStudio
-#include <unistd.h>
-
-#include <asio.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
 
 
-#include <asiolink/recursive_query.h>
-#include <asiolink/dns_service.h>
-#include <asiolink/udp_query.h>
+#include <config.h>
 
 
 #include <log/dummylog.h>
 #include <log/dummylog.h>
 
 
-#include <boost/lexical_cast.hpp>
-#include <boost/bind.hpp>
-
 #include <dns/question.h>
 #include <dns/question.h>
 #include <dns/message.h>
 #include <dns/message.h>
+#include <dns/opcode.h>
 
 
 #include <resolve/resolve.h>
 #include <resolve/resolve.h>
+#include <cache/resolver_cache.h>
+
+#include <asio.hpp>
+#include <asiolink/dns_service.h>
+#include <asiolink/io_fetch.h>
+#include <asiolink/io_service.h>
+#include <asiolink/recursive_query.h>
 
 
 using isc::log::dlog;
 using isc::log::dlog;
 using namespace isc::dns;
 using namespace isc::dns;
@@ -68,10 +70,10 @@ typedef std::pair<std::string, uint16_t> addr_t;
  *
  *
  * Used by RecursiveQuery::sendQuery.
  * Used by RecursiveQuery::sendQuery.
  */
  */
-class RunningQuery : public UDPQuery::Callback {
+class RunningQuery : public IOFetch::Callback {
 private:
 private:
     // The io service to handle async calls
     // The io service to handle async calls
-    asio::io_service& io_;
+    IOService& io_;
 
 
     // Info for (re)sending the query (the question and destination)
     // Info for (re)sending the query (the question and destination)
     Question question_;
     Question question_;
@@ -123,7 +125,7 @@ private:
     asio::deadline_timer lookup_timer;
     asio::deadline_timer lookup_timer;
 
 
     size_t queries_out_;
     size_t queries_out_;
-
+    
     // If we timed out ourselves (lookup timeout), stop issuing queries
     // If we timed out ourselves (lookup timeout), stop issuing queries
     bool done_;
     bool done_;
 
 
@@ -132,6 +134,26 @@ private:
     // answer if we do find one later (or if we have a lookup_timeout)
     // answer if we do find one later (or if we have a lookup_timeout)
     bool answer_sent_;
     bool answer_sent_;
 
 
+    // Reference to our cache
+    isc::cache::ResolverCache& cache_;
+
+    // perform a single lookup; first we check the cache to see
+    // if we have a response for our query stored already. if
+    // so, call handlerecursiveresponse(), if not, we call send()
+    void doLookup() {
+        dlog("doLookup: try cache");
+        Message cached_message(Message::RENDER);
+        isc::resolve::initResponseMessage(question_, cached_message);
+        if (cache_.lookup(question_.getName(), question_.getType(),
+                          question_.getClass(), cached_message)) {
+            dlog("Message found in cache, returning that");
+            handleRecursiveAnswer(cached_message);
+        } else {
+            send();
+        }
+        
+    }
+
     // (re)send the query to the server.
     // (re)send the query to the server.
     void send() {
     void send() {
         const int uc = upstream_->size();
         const int uc = upstream_->size();
@@ -141,22 +163,22 @@ private:
             int serverIndex = rand() % uc;
             int serverIndex = rand() % uc;
             dlog("Sending upstream query (" + question_.toText() +
             dlog("Sending upstream query (" + question_.toText() +
                 ") to " + upstream_->at(serverIndex).first);
                 ") to " + upstream_->at(serverIndex).first);
-            UDPQuery query(io_, question_,
+            IOFetch query(IPPROTO_UDP, io_, question_,
                 upstream_->at(serverIndex).first,
                 upstream_->at(serverIndex).first,
                 upstream_->at(serverIndex).second, buffer_, this,
                 upstream_->at(serverIndex).second, buffer_, this,
                 query_timeout_);
                 query_timeout_);
             ++queries_out_;
             ++queries_out_;
-            io_.post(query);
+            io_.get_io_service().post(query);
         } else if (zs > 0) {
         } else if (zs > 0) {
             int serverIndex = rand() % zs;
             int serverIndex = rand() % zs;
             dlog("Sending query to zone server (" + question_.toText() +
             dlog("Sending query to zone server (" + question_.toText() +
                 ") to " + zone_servers_.at(serverIndex).first);
                 ") to " + zone_servers_.at(serverIndex).first);
-            UDPQuery query(io_, question_,
+            IOFetch query(IPPROTO_UDP, io_, question_,
                 zone_servers_.at(serverIndex).first,
                 zone_servers_.at(serverIndex).first,
                 zone_servers_.at(serverIndex).second, buffer_, this,
                 zone_servers_.at(serverIndex).second, buffer_, this,
                 query_timeout_);
                 query_timeout_);
             ++queries_out_;
             ++queries_out_;
-            io_.post(query);
+            io_.get_io_service().post(query);
         } else {
         } else {
             dlog("Error, no upstream servers to send to.");
             dlog("Error, no upstream servers to send to.");
         }
         }
@@ -184,6 +206,11 @@ private:
                 question_, incoming, cname_target, cname_count_, true);
                 question_, incoming, cname_target, cname_count_, true);
 
 
         bool found_ns_address = false;
         bool found_ns_address = false;
+            
+        // If the packet is OK, store it in the cache
+        if (!isc::resolve::ResponseClassifier::error(category)) {
+            cache_.update(incoming);
+        }
 
 
         switch (category) {
         switch (category) {
         case isc::resolve::ResponseClassifier::ANSWER:
         case isc::resolve::ResponseClassifier::ANSWER:
@@ -213,7 +240,7 @@ private:
                                  question_.getType());
                                  question_.getType());
 
 
             dlog("Following CNAME chain to " + question_.toText());
             dlog("Following CNAME chain to " + question_.toText());
-            send();
+            doLookup();
             return false;
             return false;
             break;
             break;
         case isc::resolve::ResponseClassifier::NXDOMAIN:
         case isc::resolve::ResponseClassifier::NXDOMAIN:
@@ -245,11 +272,16 @@ private:
                         // TODO should use NSAS
                         // TODO should use NSAS
                         zone_servers_.push_back(addr_t(addr_str, 53));
                         zone_servers_.push_back(addr_t(addr_str, 53));
                         found_ns_address = true;
                         found_ns_address = true;
+                        break;
                     }
                     }
                 }
                 }
             }
             }
             if (found_ns_address) {
             if (found_ns_address) {
                 // next resolver round
                 // next resolver round
+                // we do NOT use doLookup() here, but send() (i.e. we
+                // skip the cache), since if we had the final answer
+                // instead of a delegation cached, we would have been
+                // there by now.
                 send();
                 send();
                 return false;
                 return false;
             } else {
             } else {
@@ -283,7 +315,7 @@ private:
     }
     }
     
     
 public:
 public:
-    RunningQuery(asio::io_service& io,
+    RunningQuery(IOService& io,
         const Question &question,
         const Question &question,
         MessagePtr answer_message,
         MessagePtr answer_message,
         boost::shared_ptr<AddressVector> upstream,
         boost::shared_ptr<AddressVector> upstream,
@@ -291,7 +323,8 @@ public:
         OutputBufferPtr buffer,
         OutputBufferPtr buffer,
         isc::resolve::ResolverInterface::CallbackPtr cb,
         isc::resolve::ResolverInterface::CallbackPtr cb,
         int query_timeout, int client_timeout, int lookup_timeout,
         int query_timeout, int client_timeout, int lookup_timeout,
-        unsigned retries) :
+        unsigned retries,
+        isc::cache::ResolverCache& cache) :
         io_(io),
         io_(io),
         question_(question),
         question_(question),
         answer_message_(answer_message),
         answer_message_(answer_message),
@@ -302,11 +335,12 @@ public:
         cname_count_(0),
         cname_count_(0),
         query_timeout_(query_timeout),
         query_timeout_(query_timeout),
         retries_(retries),
         retries_(retries),
-        client_timer(io),
-        lookup_timer(io),
+        client_timer(io.get_io_service()),
+        lookup_timer(io.get_io_service()),
         queries_out_(0),
         queries_out_(0),
         done_(false),
         done_(false),
-        answer_sent_(false)
+        answer_sent_(false),
+        cache_(cache)
     {
     {
         // Setup the timer to stop trying (lookup_timeout)
         // Setup the timer to stop trying (lookup_timeout)
         if (lookup_timeout >= 0) {
         if (lookup_timeout >= 0) {
@@ -328,7 +362,7 @@ public:
             setZoneServersToRoot();
             setZoneServersToRoot();
         }
         }
 
 
-        send();
+        doLookup();
     }
     }
 
 
     void setZoneServersToRoot() {
     void setZoneServersToRoot() {
@@ -369,6 +403,26 @@ public:
         done_ = true;
         done_ = true;
         if (resume && !answer_sent_) {
         if (resume && !answer_sent_) {
             answer_sent_ = true;
             answer_sent_ = true;
+
+            // There are two types of messages we could store in the
+            // cache;
+            // 1. answers to our fetches from authoritative servers,
+            //    exactly as we receive them, and
+            // 2. answers to queries we received from clients, which
+            //    have received additional processing (following CNAME
+            //    chains, for instance)
+            //
+            // Doing only the first would mean we would have to re-do
+            // processing when we get data from our cache, and doing
+            // only the second would miss out on the side-effect of
+            // having nameserver data in our cache.
+            //
+            // So right now we do both. Since the cache (currently)
+            // stores Messages on their question section only, this
+            // does mean that we overwrite the messages we stored in
+            // the previous iteration if we are following a delegation.
+            cache_.update(*answer_message_);
+
             resolvercallback_->success(answer_message_);
             resolvercallback_->success(answer_message_);
         } else {
         } else {
             resolvercallback_->failure();
             resolvercallback_->failure();
@@ -386,10 +440,10 @@ public:
     }
     }
 
 
     // This function is used as callback from DNSQuery.
     // This function is used as callback from DNSQuery.
-    virtual void operator()(UDPQuery::Result result) {
+    virtual void operator()(IOFetch::Result result) {
         // XXX is this the place for TCP retry?
         // XXX is this the place for TCP retry?
         --queries_out_;
         --queries_out_;
-        if (!done_ && result != UDPQuery::TIME_OUT) {
+        if (!done_ && result != IOFetch::TIME_OUT) {
             // we got an answer
             // we got an answer
             Message incoming(Message::PARSE);
             Message incoming(Message::PARSE);
             InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
             InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
@@ -423,15 +477,29 @@ void
 RecursiveQuery::resolve(const QuestionPtr& question,
 RecursiveQuery::resolve(const QuestionPtr& question,
     const isc::resolve::ResolverInterface::CallbackPtr callback)
     const isc::resolve::ResolverInterface::CallbackPtr callback)
 {
 {
-    asio::io_service& io = dns_service_.get_io_service();
+    IOService& io = dns_service_.getIOService();
 
 
     MessagePtr answer_message(new Message(Message::RENDER));
     MessagePtr answer_message(new Message(Message::RENDER));
+    isc::resolve::initResponseMessage(*question, *answer_message);
+
     OutputBufferPtr buffer(new OutputBuffer(0));
     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_);
+
+    dlog("Try out cache first (direct call to resolve)");
+    // First try to see if we have something cached in the messagecache
+    if (cache_.lookup(question->getName(), question->getType(),
+                      question->getClass(), *answer_message)) {
+        dlog("Message found in cache, returning that");
+        // TODO: err, should cache set rcode as well?
+        answer_message->setRcode(Rcode::NOERROR());
+        callback->success(answer_message);
+    } else {
+        dlog("Message not found in cache, starting recursive query");
+        // It will delete itself when it is done
+        new RunningQuery(io, *question, answer_message, upstream_,
+                         upstream_root_, buffer, callback, query_timeout_,
+                         client_timeout_, lookup_timeout_, retries_,
+                         cache_);
+    }
 }
 }
 
 
 void
 void
@@ -444,15 +512,30 @@ RecursiveQuery::resolve(const Question& question,
     // the message should be sent via TCP or UDP, or sent initially via
     // 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
     // UDP and then fall back to TCP on failure, but for the moment
     // we're only going to handle UDP.
     // we're only going to handle UDP.
-    asio::io_service& io = dns_service_.get_io_service();
+    IOService& io = dns_service_.getIOService();
 
 
     isc::resolve::ResolverInterface::CallbackPtr crs(
     isc::resolve::ResolverInterface::CallbackPtr crs(
         new isc::resolve::ResolverCallbackServer(server));
         new isc::resolve::ResolverCallbackServer(server));
+
+    // TODO: general 'prepareinitialanswer'
+    answer_message->setOpcode(isc::dns::Opcode::QUERY());
+    answer_message->addQuestion(question);
     
     
-    // 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_);
+    // First try to see if we have something cached in the messagecache
+    dlog("Try out cache first (started by incoming event)");
+    if (cache_.lookup(question.getName(), question.getType(),
+                      question.getClass(), *answer_message)) {
+        dlog("Message found in cache, returning that");
+        // TODO: err, should cache set rcode as well?
+        answer_message->setRcode(Rcode::NOERROR());
+        crs->success(answer_message);
+    } else {
+        dlog("Message not found in cache, starting recursive query");
+        // It will delete itself when it is done
+        new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
+                             buffer, crs, query_timeout_, client_timeout_,
+                             lookup_timeout_, retries_, cache_);
+    }
 }
 }
 
 
 
 

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

@@ -18,6 +18,7 @@
 #include <asiolink/dns_service.h>
 #include <asiolink/dns_service.h>
 #include <asiolink/dns_server.h>
 #include <asiolink/dns_server.h>
 #include <dns/buffer.h>
 #include <dns/buffer.h>
+#include <cache/resolver_cache.h>
 
 
 namespace asiolink {
 namespace asiolink {
 /// \brief The \c RecursiveQuery class provides a layer of abstraction around
 /// \brief The \c RecursiveQuery class provides a layer of abstraction around
@@ -107,6 +108,9 @@ private:
     int client_timeout_;
     int client_timeout_;
     int lookup_timeout_;
     int lookup_timeout_;
     unsigned retries_;
     unsigned retries_;
+    // Cache. TODO: I think we want this initialized in Resolver class,
+    // not here
+    isc::cache::ResolverCache cache_;
 };
 };
 
 
 }      // namespace asiolink
 }      // namespace asiolink

+ 31 - 8
src/lib/asiolink/tcp_server.cc

@@ -14,18 +14,18 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
-#include <boost/shared_array.hpp>
-
-// unistd is needed for asio.hpp with SunStudio
-#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
 
 
-#include <asio.hpp>
+#include <boost/shared_array.hpp>
 
 
 #include <log/dummylog.h>
 #include <log/dummylog.h>
 
 
+#include <asio.hpp>
+#include <asiolink/dummy_io_cb.h>
 #include <asiolink/tcp_endpoint.h>
 #include <asiolink/tcp_endpoint.h>
 #include <asiolink/tcp_socket.h>
 #include <asiolink/tcp_socket.h>
-
 #include <asiolink/tcp_server.h>
 #include <asiolink/tcp_server.h>
 
 
 
 
@@ -46,7 +46,7 @@ TCPServer::TCPServer(io_service& io_service,
                      const SimpleCallback* checkin,
                      const SimpleCallback* checkin,
                      const DNSLookup* lookup,
                      const DNSLookup* lookup,
                      const DNSAnswer* answer) :
                      const DNSAnswer* answer) :
-    io_(io_service), done_(false),
+    io_(io_service), done_(false), stopped_by_hand_(false),
     checkin_callback_(checkin), lookup_callback_(lookup),
     checkin_callback_(checkin), lookup_callback_(lookup),
     answer_callback_(answer)
     answer_callback_(answer)
 {
 {
@@ -68,6 +68,13 @@ TCPServer::operator()(error_code ec, size_t length) {
     /// Because the coroutine reeentry block is implemented as
     /// Because the coroutine reeentry block is implemented as
     /// a switch statement, inline variable declarations are not
     /// a switch statement, inline variable declarations are not
     /// permitted.  Certain variables used below can be declared here.
     /// permitted.  Certain variables used below can be declared here.
+
+    /// If user has stopped the server, we won't enter the
+    /// coroutine body, just return
+    if (stopped_by_hand_) {
+        return;
+    }
+
     boost::array<const_buffer,2> bufs;
     boost::array<const_buffer,2> bufs;
     OutputBuffer lenbuf(TCP_MESSAGE_LENGTHSIZE);
     OutputBuffer lenbuf(TCP_MESSAGE_LENGTHSIZE);
 
 
@@ -118,7 +125,14 @@ TCPServer::operator()(error_code ec, size_t length) {
         // that would quickly generate an IOMessage object without
         // that would quickly generate an IOMessage object without
         // all these calls to "new".)
         // all these calls to "new".)
         peer_.reset(new TCPEndpoint(socket_->remote_endpoint()));
         peer_.reset(new TCPEndpoint(socket_->remote_endpoint()));
-        iosock_.reset(new TCPSocket(*socket_));
+
+        // The TCP socket class has been extended with asynchronous functions
+        // and takes as a template parameter a completion callback class.  As
+        // TCPServer does not use these extended functions (only those defined
+        // in the IOSocket base class) - but needs a TCPSocket to get hold of
+        // the underlying Boost TCP socket - DummyIOCallback is used.  This
+        // provides the appropriate operator() but is otherwise functionless.
+        iosock_.reset(new TCPSocket<DummyIOCallback>(*socket_));
         io_message_.reset(new IOMessage(data_.get(), length, *iosock_, *peer_));
         io_message_.reset(new IOMessage(data_.get(), length, *iosock_, *peer_));
         bytes_ = length;
         bytes_ = length;
 
 
@@ -181,6 +195,15 @@ TCPServer::asyncLookup() {
                         answer_message_, respbuf_, this);
                         answer_message_, respbuf_, this);
 }
 }
 
 
+void TCPServer::stop() {
+    //server should not be stopped twice
+    if (stopped_by_hand_)
+        return;
+
+    stopped_by_hand_ = true;
+    acceptor_->close();
+    socket_->close();
+}
 /// Post this coroutine on the ASIO service queue so that it will
 /// Post this coroutine on the ASIO service queue so that it will
 /// resume processing where it left off.  The 'done' parameter indicates
 /// resume processing where it left off.  The 'done' parameter indicates
 /// whether there is an answer to return to the client.
 /// whether there is an answer to return to the client.

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

@@ -43,6 +43,7 @@ public:
     void operator()(asio::error_code ec = asio::error_code(),
     void operator()(asio::error_code ec = asio::error_code(),
                     size_t length = 0);
                     size_t length = 0);
     void asyncLookup();
     void asyncLookup();
+    void stop();
     void resume(const bool done);
     void resume(const bool done);
     bool hasAnswer() { return (done_); }
     bool hasAnswer() { return (done_); }
     int value() { return (get_value()); }
     int value() { return (get_value()); }
@@ -106,6 +107,9 @@ private:
     size_t bytes_;
     size_t bytes_;
     bool done_;
     bool done_;
 
 
+    // whether user has stopped the server
+    bool stopped_by_hand_;
+
     // Callback functions provided by the caller
     // Callback functions provided by the caller
     const SimpleCallback* checkin_callback_;
     const SimpleCallback* checkin_callback_;
     const DNSLookup* lookup_callback_;
     const DNSLookup* lookup_callback_;

+ 241 - 16
src/lib/asiolink/tcp_socket.h

@@ -19,34 +19,259 @@
 #error "asio.hpp must be included before including this, see asiolink.h as to why"
 #error "asio.hpp must be included before including this, see asiolink.h as to why"
 #endif
 #endif
 
 
-#include <asiolink/io_socket.h>
+#include <log/dummylog.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
+
+#include <iostream>
+#include <cstddef>
+
+#include <config.h>
+
+#include <asiolink/io_asio_socket.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_endpoint.h>
 
 
 namespace asiolink {
 namespace asiolink {
 
 
-/// \brief The \c TCPSocket class is a concrete derived class of
-/// \c IOSocket that represents a TCP socket.
+/// \brief The \c TCPSocket class is a concrete derived class of \c IOAsioSocket
+/// 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 {
+/// \param C Callback type
+template <typename C>
+class TCPSocket : public IOAsioSocket<C> {
 private:
 private:
-    TCPSocket(const TCPSocket& source);
-    TCPSocket& operator=(const TCPSocket& source);
+    /// \brief Class is non-copyable
+    TCPSocket(const TCPSocket&);
+    TCPSocket& operator=(const TCPSocket&);
+
 public:
 public:
+    
     /// \brief Constructor from an ASIO TCP socket.
     /// \brief Constructor from an ASIO TCP socket.
     ///
     ///
-    /// \param socket The ASIO representation of the TCP socket.
-    TCPSocket(asio::ip::tcp::socket& socket) : socket_(socket) {}
+    /// \param socket The ASIO representation of the TCP socket.  It
+    /// is assumed that the caller will open and close the socket, so
+    /// these operations are a no-op for that socket.
+    TCPSocket(asio::ip::tcp::socket& socket);
+
+    /// \brief Constructor
+    ///
+    /// Used when the TCPSocket is being asked to manage its own internal
+    /// socket.  It is assumed that open() and close() will not be used.
+    ///
+    /// \param service I/O Service object used to manage the socket.
+    TCPSocket(IOService& service);
+
+    /// \brief Destructor
+    virtual ~TCPSocket();
+
+    virtual int getNative() const { return (socket_.native()); }
+    virtual int getProtocol() const { return (IPPROTO_TCP); }
+
+    /// \brief Open Socket
+    ///
+    /// Opens the TCP socket.  In the model for transport-layer agnostic I/O,
+    /// an "open" operation includes a connection to the remote end (which
+    /// may take time).  This does not happen for TCP, so the method returns
+    /// "false" to indicate that the operation completed synchronously.
+    ///
+    /// \param endpoint Endpoint to which the socket will connect to.
+    /// \param callback Unused.
+    ///
+    /// \return false to indicate that the "operation" completed synchronously.
+    virtual bool open(const IOEndpoint* endpoint, C&);
+
+    /// \brief Send Asynchronously
+    ///
+    /// This corresponds to async_send_to() for TCP sockets and async_send()
+    /// for TCP.  In both cases an endpoint argument is supplied indicating the
+    /// target of the send - this is ignored for TCP.
+    ///
+    /// \param data Data to send
+    /// \param length Length of data to send
+    /// \param endpoint Target of the send
+    /// \param callback Callback object.
+    virtual void asyncSend(const void* data, size_t length,
+        const IOEndpoint* endpoint, C& callback);
+
+    /// \brief Receive Asynchronously
+    ///
+    /// This correstponds to async_receive_from() for TCP sockets and
+    /// async_receive() for TCP.  In both cases, an endpoint argument is
+    /// supplied to receive the source of the communication.  For TCP it will
+    /// be filled in with details of the connection.
+    ///
+    /// \param data Buffer to receive incoming message
+    /// \param length Length of the data buffer
+    /// \param cumulative Amount of data that should already be in the buffer.
+    /// (This is ignored - every UPD receive fills the buffer from the start.)
+    /// \param endpoint Source of the communication
+    /// \param callback Callback object
+    virtual void asyncReceive(void* data, size_t length, size_t cumulative,
+        IOEndpoint* endpoint, C& callback);
+
+    /// \brief Checks if the data received is complete.
+    ///
+    /// As all the data is received in one I/O, so this is, this is effectively
+    /// a no-op (although it does update the amount of data received).
+    ///
+    /// \param data Data buffer containing data to date.  (This is ignored
+    /// for TCP receives.)
+    /// \param length Amount of data received in last asynchronous I/O
+    /// \param cumulative On input, amount of data received before the last
+    /// I/O.  On output, the total amount of data received to date.
+    ///
+    /// \return true if the receive is complete, false if another receive is
+    /// needed.
+    virtual bool receiveComplete(void*, size_t length, size_t& cumulative) {
+        cumulative = length;
+        return (true);
+    }
+
+    /// \brief Cancel I/O On Socket
+    virtual void cancel();
+
+    /// \brief Close socket
+    virtual void close();
 
 
-    int getNative() const { return (socket_.native()); }
-    int getProtocol() const { return (IPPROTO_TCP); }
 
 
 private:
 private:
-    asio::ip::tcp::socket& socket_;
+    // Two variables to hold the socket - a socket and a pointer to it.  This
+    // handles the case where a socket is passed to the TCPSocket on
+    // construction, or where it is asked to manage its own socket.
+    asio::ip::tcp::socket*      socket_ptr_;    ///< Pointer to own socket
+    asio::ip::tcp::socket&      socket_;        ///< Socket
+    bool                        isopen_;        ///< true when socket is open
 };
 };
 
 
+// Constructor - caller manages socket
+
+template <typename C>
+TCPSocket<C>::TCPSocket(asio::ip::tcp::socket& socket) :
+    socket_ptr_(NULL), socket_(socket), isopen_(true)
+{
+}
+
+// Constructor - create socket on the fly
+
+template <typename C>
+TCPSocket<C>::TCPSocket(IOService& service) :
+    socket_ptr_(new asio::ip::tcp::socket(service.get_io_service())),
+    socket_(*socket_ptr_), isopen_(false)
+{
+}
+
+// Destructor.  Only delete the socket if we are managing it.
+
+template <typename C>
+TCPSocket<C>::~TCPSocket()
+{
+    delete socket_ptr_;
+}
+
+// Open the socket.  Throws an error on failure
+// TODO: Make the open more resilient
+
+template <typename C> bool
+TCPSocket<C>::open(const IOEndpoint* endpoint, C&) {
+
+    // Ignore opens on already-open socket.  Don't throw a failure because
+    // of uncertainties as to what precedes whan when using asynchronous I/O.
+    // At also allows us a treat a passed-in socket as a self-managed socket.
+
+    if (!isopen_) {
+        if (endpoint->getFamily() == AF_INET) {
+            socket_.open(asio::ip::tcp::v4());
+        }
+        else {
+            socket_.open(asio::ip::tcp::v6());
+        }
+        isopen_ = true;
+
+        // TODO: Complete TCPSocket::open()
+
+    }
+    return (false);
+}
+
+// Send a message.  Should never do this if the socket is not open, so throw
+// an exception if this is the case.
+
+template <typename C> void
+TCPSocket<C>::asyncSend(const void* data, size_t length,
+    const IOEndpoint* endpoint, C& callback)
+{
+    if (isopen_) {
+
+        // Upconvert to a TCPEndpoint.  We need to do this because although
+        // IOEndpoint is the base class of TCPEndpoint and TCPEndpoint, it
+        // doing cont contain a method for getting at the underlying endpoint
+        // type - those are in the derived class and the two classes differ on
+        // return type.
+
+        assert(endpoint->getProtocol() == IPPROTO_TCP);
+        const TCPEndpoint* tcp_endpoint =
+            static_cast<const TCPEndpoint*>(endpoint);
+        std::cerr << "TCPSocket::asyncSend(): sending to " <<
+            tcp_endpoint->getAddress().toText() <<
+            ", port " << tcp_endpoint->getPort() << "\n";
+
+        // TODO: Complete TCPSocket::asyncSend()
+
+    } else {
+        isc_throw(SocketNotOpen,
+            "attempt to send on a TCP socket that is not open");
+    }
+}
+
+// Receive a message. Note that the "cumulative" argument is ignored - every TCP
+// receive is put into the buffer beginning at the start - there is no concept
+// receiving a subsequent part of a message.  Same critera as before concerning
+// the need for the socket to be open.
+
+template <typename C> void
+TCPSocket<C>::asyncReceive(void* data, size_t length, size_t,
+    IOEndpoint* endpoint, C& callback)
+{
+    if (isopen_) {
+
+        // Upconvert the endpoint again.
+        assert(endpoint->getProtocol() == IPPROTO_TCP);
+        const TCPEndpoint* tcp_endpoint =
+            static_cast<const TCPEndpoint*>(endpoint);
+        std::cerr << "TCPSocket::asyncReceive(): receiving from " <<
+            tcp_endpoint->getAddress().toText() <<
+            ", port " << tcp_endpoint->getPort() << "\n";
+
+        // TODO: Complete TCPSocket::asyncReceive()
+
+    } else {
+        isc_throw(SocketNotOpen,
+            "attempt to receive from a TCP socket that is not open");
+    }
+}
+
+// Cancel I/O on the socket.  No-op if the socket is not open.
+template <typename C> void
+TCPSocket<C>::cancel() {
+    if (isopen_) {
+        socket_.cancel();
+    }
+}
+
+// Close the socket down.  Can only do this if the socket is open and we are
+// managing it ourself.
+
+template <typename C> void
+TCPSocket<C>::close() {
+    if (isopen_ && socket_ptr_) {
+        socket_.close();
+        isopen_ = false;
+    }
+}
+
+} // namespace asiolink
 
 
-}      // namespace asiolink
 #endif // __TCP_SOCKET_H
 #endif // __TCP_SOCKET_H

+ 16 - 8
src/lib/asiolink/tests/Makefile.am

@@ -15,24 +15,32 @@ CLEANFILES = *.gcno *.gcda
 TESTS =
 TESTS =
 if HAVE_GTEST
 if HAVE_GTEST
 TESTS += run_unittests
 TESTS += run_unittests
-run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
+run_unittests_SOURCES  = run_unittests.cc
+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 += 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_address_unittest.cc
+run_unittests_SOURCES += io_endpoint_unittest.cc
+run_unittests_SOURCES += io_fetch_unittest.cc
+run_unittests_SOURCES += io_socket_unittest.cc
 run_unittests_SOURCES += io_service_unittest.cc
 run_unittests_SOURCES += io_service_unittest.cc
 run_unittests_SOURCES += interval_timer_unittest.cc
 run_unittests_SOURCES += interval_timer_unittest.cc
 run_unittests_SOURCES += recursive_query_unittest.cc
 run_unittests_SOURCES += recursive_query_unittest.cc
-run_unittests_SOURCES += run_unittests.cc
+run_unittests_SOURCES += udp_endpoint_unittest.cc
+run_unittests_SOURCES += udp_socket_unittest.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_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/dns/libdns++.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/exceptions/libexceptions.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/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
+run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
 # B10_CXXFLAGS)
 # B10_CXXFLAGS)
 run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 run_unittests_CXXFLAGS = $(AM_CXXFLAGS)

+ 6 - 2
src/lib/asiolink/tests/interval_timer_unittest.cc

@@ -15,6 +15,7 @@
 #include <config.h>
 #include <config.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
+#include <asio.hpp>
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
 
 
 #include <boost/date_time/posix_time/posix_time_types.hpp>
 #include <boost/date_time/posix_time/posix_time_types.hpp>
@@ -32,7 +33,9 @@ using namespace asiolink;
 // or not.
 // or not.
 class IntervalTimerTest : public ::testing::Test {
 class IntervalTimerTest : public ::testing::Test {
 protected:
 protected:
-    IntervalTimerTest() : io_service_() {}
+    IntervalTimerTest() :
+        io_service_(), timer_called_(false), timer_cancel_success_(false)
+    {}
     ~IntervalTimerTest() {}
     ~IntervalTimerTest() {}
     class TimerCallBack : public std::unary_function<void, void> {
     class TimerCallBack : public std::unary_function<void, void> {
     public:
     public:
@@ -63,7 +66,8 @@ protected:
         TimerCallBackCancelDeleter(IntervalTimerTest* test_obj,
         TimerCallBackCancelDeleter(IntervalTimerTest* test_obj,
                                    IntervalTimer* timer,
                                    IntervalTimer* timer,
                                    TimerCallBackCounter& counter)
                                    TimerCallBackCounter& counter)
-            : test_obj_(test_obj), timer_(timer), counter_(counter), count_(0)
+            : test_obj_(test_obj), timer_(timer), counter_(counter), count_(0),
+              prev_counter_(-1)
         {}
         {}
         void operator()() {
         void operator()() {
             ++count_;
             ++count_;

+ 7 - 1
src/lib/asiolink/tests/ioaddress_unittest.cc

@@ -15,7 +15,8 @@
 #include <config.h>
 #include <config.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
-#include <asiolink/asiolink.h>
+#include <asiolink/io_error.h>
+#include <asiolink/io_address.h>
 
 
 using namespace asiolink;
 using namespace asiolink;
 
 
@@ -55,3 +56,8 @@ TEST(IOAddressTest, Equality) {
     EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("192.0.2.3"));
     EXPECT_TRUE(IOAddress("2001:db8::1234") != IOAddress("192.0.2.3"));
     EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("192.0.2.3"));
     EXPECT_FALSE(IOAddress("2001:db8::1234") == IOAddress("192.0.2.3"));
 }
 }
+
+TEST(IOAddressTest, Family) {
+    EXPECT_EQ(AF_INET, IOAddress("192.0.2.1").getFamily());
+    EXPECT_EQ(AF_INET6, IOAddress("2001:0DB8:0:0::0012").getFamily());
+}

+ 2 - 1
src/lib/asiolink/tests/ioendpoint_unittest.cc

@@ -15,7 +15,8 @@
 #include <config.h>
 #include <config.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
-#include <asiolink/asiolink.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_error.h>
 
 
 using namespace asiolink;
 using namespace asiolink;
 
 

+ 191 - 0
src/lib/asiolink/tests/io_fetch_unittest.cc

@@ -0,0 +1,191 @@
+// 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 <gtest/gtest.h>
+#include <boost/bind.hpp>
+#include <cstdlib>
+#include <string>
+#include <vector>
+
+#include <string.h>
+
+#include <asio.hpp>
+
+#include <dns/buffer.h>
+#include <dns/question.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/name.h>
+#include <dns/rcode.h>
+
+#include <asiolink/io_fetch.h>
+#include <asiolink/io_service.h>
+
+using namespace asio;
+using namespace isc::dns;
+using asio::ip::udp;
+
+namespace asiolink {
+
+const asio::ip::address TEST_HOST(asio::ip::address::from_string("127.0.0.1"));
+const uint16_t TEST_PORT(5301);
+// FIXME Shouldn't we send something that is real message?
+const char TEST_DATA[] = "TEST DATA";
+
+/// \brief Test fixture for the asiolink::IOFetch.
+class IOFetchTest : public virtual ::testing::Test, public virtual IOFetch::Callback
+{
+public:
+    IOService       service_;       ///< Service to run the query
+    IOFetch::Result expected_;      ///< Expected result of the callback
+    bool            run_;           ///< Did the callback run already?
+    Question        question_;      ///< What to ask
+    OutputBufferPtr buff_;          ///< Buffer to hold result
+    IOFetch         udp_fetch_;     ///< For UDP query test
+    //IOFetch         tcp_fetch_;     ///< For TCP query test
+
+    // The next member is the buffer iin which the "server" (implemented by the
+    // response handler method) receives the question sent by the fetch object.
+    std::vector<char>      server_buff_;  ///< Server buffer
+
+    /// \brief Constructor
+    IOFetchTest() :
+        service_(),
+        expected_(IOFetch::NOTSET),
+        run_(false),
+        question_(Name("example.net"), RRClass::IN(), RRType::A()),
+        buff_(new OutputBuffer(512)),
+        udp_fetch_(IPPROTO_UDP, service_, question_, IOAddress(TEST_HOST),
+                   TEST_PORT, buff_, this, 100),
+        server_buff_(512)
+        // tcp_fetch_(service_, question_, IOAddress(TEST_HOST), TEST_PORT,
+        //    buff_, this, 100, IPPROTO_UDP)
+        { }
+
+    /// \brief Fetch completion callback
+    ///
+    /// This is the callback's operator() method which is called when the fetch
+    /// is complete.  Check that the data received is the wire format of the
+    /// question, then send back an arbitrary response.
+    void operator()(IOFetch::Result result) {
+        EXPECT_EQ(expected_, result);   // Check correct result returned
+        EXPECT_FALSE(run_);             // Check it is run only once
+        run_ = true;                    // Note success
+        service_.stop();                // ... and exit run loop
+    }
+
+    /// \brief Response handler, pretending to be remote DNS server
+    ///
+    /// This checks that the data sent is what we expected to receive, and
+    /// sends back a test answer.
+    void respond(udp::endpoint* remote, udp::socket* socket,
+            asio::error_code ec = asio::error_code(), size_t length = 0) {
+
+        // Construct the data buffer for question we expect to receive.
+        OutputBuffer msgbuf(512);
+        Message msg(Message::RENDER);
+        msg.setQid(0);
+        msg.setOpcode(Opcode::QUERY());
+        msg.setRcode(Rcode::NOERROR());
+        msg.setHeaderFlag(Message::HEADERFLAG_RD);
+        msg.addQuestion(question_);
+        MessageRenderer renderer(msgbuf);
+        msg.toWire(renderer);
+
+        // The QID in the incoming data is random so set it to 0 for the
+        // data comparison check. (It was set to 0 when the buffer containing
+        // the expected data was constructed above.)
+        server_buff_[0] = 0;
+        server_buff_[1] = 0;
+
+        // Check that lengths are identical.
+        EXPECT_EQ(msgbuf.getLength(), length);
+        EXPECT_TRUE(memcmp(msgbuf.getData(), &server_buff_[0], length) == 0);
+
+        // ... and return a message back.
+        socket->send_to(asio::buffer(TEST_DATA, sizeof TEST_DATA), *remote);
+    }
+};
+
+
+/// Test that when we run the query and stop it after it was run,
+/// it returns "stopped" correctly.
+///
+/// That is why stop() is posted to the service_ as well instead
+/// of calling it.
+TEST_F(IOFetchTest, UdpStop) {
+    expected_ = IOFetch::STOPPED;
+
+    // Post the query
+    service_.get_io_service().post(udp_fetch_);
+
+    // Post query_.stop() (yes, the boost::bind thing is just
+    // query_.stop()).
+    service_.get_io_service().post(
+       boost::bind(&IOFetch::stop, udp_fetch_, IOFetch::STOPPED));
+
+    // Run both of them.  run() returns when everything in the I/O service
+    // queue has completed.
+    service_.run();
+    EXPECT_TRUE(run_);
+}
+
+// Test that when we queue the query to service_ and call stop() before it gets
+// executed, it acts sanely as well (eg. has the same result as running stop()
+// after - calls the callback).
+TEST_F(IOFetchTest, UdpPrematureStop) {
+    expected_ = IOFetch::STOPPED;
+
+    // Stop before it is started
+    udp_fetch_.stop();
+    service_.get_io_service().post(udp_fetch_);
+
+    service_.run();
+    EXPECT_TRUE(run_);
+}
+
+// Test that it will timeout when no answer arrives.
+TEST_F(IOFetchTest, UdpTimeout) {
+    expected_ = IOFetch::TIME_OUT;
+
+    service_.get_io_service().post(udp_fetch_);
+    service_.run();
+    EXPECT_TRUE(run_);
+}
+
+// Test that it will succeed when we fake an answer and stores the same data we
+// send.  This is done through a real socket on the loopback address.
+TEST_F(IOFetchTest, UdpReceive) {
+    expected_ = IOFetch::SUCCESS;
+
+    udp::socket socket(service_.get_io_service(), udp::v4());
+    socket.set_option(socket_base::reuse_address(true));
+    socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
+
+    udp::endpoint remote;
+    socket.async_receive_from(asio::buffer(server_buff_),
+        remote,
+        boost::bind(&IOFetchTest::respond, this, &remote, &socket, _1, _2));
+    service_.get_io_service().post(udp_fetch_);
+    service_.run();
+
+    socket.close();
+
+    EXPECT_TRUE(run_);
+    ASSERT_EQ(sizeof TEST_DATA, buff_->getLength());
+    EXPECT_EQ(0, memcmp(TEST_DATA, buff_->getData(), sizeof TEST_DATA));
+}
+
+} // namespace asiolink

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

@@ -15,6 +15,7 @@
 #include <config.h>
 #include <config.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
+#include <asio.hpp>
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
 
 
 using namespace asiolink;
 using namespace asiolink;

+ 4 - 1
src/lib/asiolink/tests/iosocket_unittest.cc

@@ -15,7 +15,10 @@
 #include <config.h>
 #include <config.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
-#include <asiolink/asiolink.h>
+#include <netinet/in.h>
+
+#include <asio.hpp>
+#include <asiolink/io_socket.h>
 
 
 using namespace asiolink;
 using namespace asiolink;
 
 

+ 10 - 5
src/lib/asiolink/tests/recursive_query_unittest.cc

@@ -41,8 +41,13 @@
 // if we include asio.hpp unless we specify a special compiler option.
 // if we include asio.hpp unless we specify a special compiler option.
 // 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/recursive_query.h>
 #include <asiolink/io_socket.h>
 #include <asiolink/io_socket.h>
+#include <asiolink/io_service.h>
+#include <asiolink/io_message.h>
+#include <asiolink/io_error.h>
+#include <asiolink/dns_lookup.h>
+#include <asiolink/simple_callback.h>
 
 
 using isc::UnitTestUtil;
 using isc::UnitTestUtil;
 using namespace std;
 using namespace std;
@@ -274,6 +279,7 @@ protected:
                             DNSLookup* lookup = NULL,
                             DNSLookup* lookup = NULL,
                             DNSAnswer* answer = NULL) :
                             DNSAnswer* answer = NULL) :
             io_(io_service),
             io_(io_service),
+            done_(false),
             message_(new Message(Message::PARSE)),
             message_(new Message(Message::PARSE)),
             answer_message_(new Message(Message::RENDER)),
             answer_message_(new Message(Message::RENDER)),
             respbuf_(new OutputBuffer(0)),
             respbuf_(new OutputBuffer(0)),
@@ -407,7 +413,8 @@ protected:
 };
 };
 
 
 RecursiveQueryTest::RecursiveQueryTest() :
 RecursiveQueryTest::RecursiveQueryTest() :
-    dns_service_(NULL), callback_(NULL), sock_(-1), res_(NULL)
+    dns_service_(NULL), callback_(NULL), callback_protocol_(0),
+    callback_native_(-1), sock_(-1), res_(NULL)
 {
 {
     io_service_ = new IOService();
     io_service_ = new IOService();
     setDNSService(true, true);
     setDNSService(true, true);
@@ -480,9 +487,7 @@ TEST_F(RecursiveQueryTest, v4AddServer) {
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
 }
 }
 
 
-TEST_F(RecursiveQueryTest, DISABLED_clearServers) {
-    // FIXME: Enable when clearServers actually close the sockets
-    //    See #388
+TEST_F(RecursiveQueryTest, clearServers) {
     setDNSService();
     setDNSService();
     dns_service_->clearServers();
     dns_service_->clearServers();
 
 

+ 55 - 0
src/lib/asiolink/tests/udp_endpoint_unittest.cc

@@ -0,0 +1,55 @@
+// 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 <string>
+
+#include <gtest/gtest.h>
+
+#include <asio.hpp>
+#include <asiolink/io_address.h>
+#include <asiolink/udp_endpoint.h>
+
+using namespace asiolink;
+using namespace std;
+
+// This test checks that the endpoint can manage its own internal
+// asio::ip::udp::endpoint object.
+
+TEST(UDPEndpointTest, v4Address) {
+    const string test_address("192.0.2.1");
+    const unsigned short test_port = 5301;
+
+    IOAddress address(test_address);
+    UDPEndpoint endpoint(address, test_port);
+
+    EXPECT_TRUE(address == endpoint.getAddress());
+    EXPECT_EQ(test_port, endpoint.getPort());
+    EXPECT_EQ(IPPROTO_UDP, endpoint.getProtocol());
+    EXPECT_EQ(AF_INET, endpoint.getFamily());
+}
+
+TEST(UDPEndpointTest, v6Address) {
+    const string test_address("2001:db8::1235");
+    const unsigned short test_port = 5302;
+
+    IOAddress address(test_address);
+    UDPEndpoint endpoint(address, test_port);
+
+    EXPECT_TRUE(address == endpoint.getAddress());
+    EXPECT_EQ(test_port, endpoint.getPort());
+    EXPECT_EQ(IPPROTO_UDP, endpoint.getProtocol());
+    EXPECT_EQ(AF_INET6, endpoint.getFamily());
+}

+ 0 - 145
src/lib/asiolink/tests/udp_query_unittest.cc

@@ -1,145 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <gtest/gtest.h>
-#include <asio.hpp>
-#include <boost/bind.hpp>
-#include <cstdlib>
-
-#include <dns/question.h>
-
-#include <asiolink/udp_query.h>
-
-using namespace asio;
-using namespace isc::dns;
-using asio::ip::udp;
-
-namespace {
-
-const asio::ip::address TEST_HOST(asio::ip::address::from_string("127.0.0.1"));
-const uint16_t TEST_PORT(5301);
-// FIXME Shouldn't we send something that is real message?
-const char TEST_DATA[] = "TEST DATA";
-
-// Test fixture for the asiolink::UDPQuery.
-class UDPQueryTest : public ::testing::Test,
-    public asiolink::UDPQuery::Callback
-{
-    public:
-        // Expected result of the callback
-        asiolink::UDPQuery::Result expected_;
-        // Did the callback run already?
-        bool run_;
-        // We use an io_service to run the query
-        io_service service_;
-        // Something to ask
-        Question question_;
-        // Buffer where the UDPQuery will store response
-        OutputBufferPtr buffer_;
-        // The query we are testing
-        asiolink::UDPQuery query_;
-
-        UDPQueryTest() :
-            run_(false),
-            question_(Name("example.net"), RRClass::IN(), RRType::A()),
-            buffer_(new OutputBuffer(512)),
-            query_(service_, question_, asiolink::IOAddress(TEST_HOST),
-                TEST_PORT, buffer_, this, 100)
-        { }
-
-        // This is the callback's (), so it can be called.
-        void operator()(asiolink::UDPQuery::Result result) {
-            // We check the query returns the correct result
-            EXPECT_EQ(expected_, result);
-            // Check it is called only once
-            EXPECT_FALSE(run_);
-            // And mark the callback was called
-            run_ = true;
-        }
-        // A response handler, pretending to be remote DNS server
-        void respond(udp::endpoint* remote, udp::socket* socket) {
-            // Some data came, just send something back.
-            socket->send_to(asio::buffer(TEST_DATA, sizeof TEST_DATA),
-                *remote);
-            socket->close();
-        }
-};
-
-/*
- * Test that when we run the query and stop it after it was run,
- * it returns "stopped" correctly.
- *
- * That is why stop() is posted to the service_ as well instead
- * of calling it.
- */
-TEST_F(UDPQueryTest, stop) {
-    expected_ = asiolink::UDPQuery::STOPPED;
-    // Post the query
-    service_.post(query_);
-    // Post query_.stop() (yes, the boost::bind thing is just
-    // query_.stop()).
-    service_.post(boost::bind(&asiolink::UDPQuery::stop, query_,
-        asiolink::UDPQuery::STOPPED));
-    // Run both of them
-    service_.run();
-    EXPECT_TRUE(run_);
-}
-
-/*
- * Test that when we queue the query to service_ and call stop()
- * before it gets executed, it acts sanely as well (eg. has the
- * same result as running stop() after - calls the callback).
- */
-TEST_F(UDPQueryTest, prematureStop) {
-    expected_ = asiolink::UDPQuery::STOPPED;
-    // Stop before it is started
-    query_.stop();
-    service_.post(query_);
-    service_.run();
-    EXPECT_TRUE(run_);
-}
-
-/*
- * Test that it will timeout when no answer will arrive.
- */
-TEST_F(UDPQueryTest, timeout) {
-    expected_ = asiolink::UDPQuery::TIME_OUT;
-    service_.post(query_);
-    service_.run();
-    EXPECT_TRUE(run_);
-}
-
-/*
- * Test that it will succeed when we fake an answer and
- * stores the same data we send.
- *
- * This is done through a real socket on loopback address.
- */
-TEST_F(UDPQueryTest, receive) {
-    expected_ = asiolink::UDPQuery::SUCCESS;
-    udp::socket socket(service_, udp::v4());
-    socket.set_option(socket_base::reuse_address(true));
-    socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
-    char inbuff[512];
-    udp::endpoint remote;
-    socket.async_receive_from(asio::buffer(inbuff, 512), remote, boost::bind(
-        &UDPQueryTest::respond, this, &remote, &socket));
-    service_.post(query_);
-    service_.run();
-    EXPECT_TRUE(run_);
-    ASSERT_EQ(sizeof TEST_DATA, buffer_->getLength());
-    EXPECT_EQ(0, memcmp(TEST_DATA, buffer_->getData(), sizeof TEST_DATA));
-}
-
-}

+ 287 - 0
src/lib/asiolink/tests/udp_socket_unittest.cc

@@ -0,0 +1,287 @@
+// 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.
+
+// 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.
+
+
+/// \brief Test of UDPSocket
+///
+/// Tests the fuctionality of a UDPSocket by working through an open-send-
+/// receive-close sequence and checking that the asynchronous notifications
+/// work.
+
+#include <string>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstddef>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <asio.hpp>
+
+#include <asiolink/io_service.h>
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_socket.h>
+
+using namespace asio;
+using namespace asiolink;
+using namespace std;
+
+namespace {
+
+const char SERVER_ADDRESS[] = "127.0.0.1";
+const unsigned short SERVER_PORT = 5301;
+
+// TODO: Shouldn't we send something that is real message?
+const char OUTBOUND_DATA[] = "Data sent from client to server";
+const char INBOUND_DATA[] = "Returned data from server to client";
+}
+
+///
+/// An instance of this object is passed to the asynchronous I/O functions
+/// and the operator() method is called when when an asynchronous I/O
+/// completes.  The arguments to the completion callback are stored for later
+/// retrieval.
+class UDPCallback {
+public:
+
+    struct PrivateData {
+        PrivateData() :
+            error_code_(), length_(0), called_(false), name_("")
+        {}
+
+        asio::error_code    error_code_;    ///< Completion error code
+        size_t              length_;        ///< Number of bytes transferred
+        bool                called_;        ///< Set true when callback called
+        std::string         name_;          ///< Which of the objects this is
+    };
+
+    /// \brief Constructor
+    ///
+    /// Constructs the object.  It also creates the data member pointed to by
+    /// a shared pointer.  When used as a callback object, this is copied as it
+    /// is passed into the asynchronous function.  This means that there are two
+    /// objects and inspecting the one we passed in does not tell us anything.
+    ///
+    /// Therefore we use a boost::shared_ptr.  When the object is copied, the
+    /// shared pointer is copied, which leaves both objects pointing to the same
+    /// data.
+    ///
+    /// \param which Which of the two callback objects this is
+    UDPCallback(std::string which) : ptr_(new PrivateData())
+    {
+        setName(which);
+    }
+
+    /// \brief Destructor
+    ///
+    /// No code needed, destroying the shared pointer destroys the private data.
+    virtual ~UDPCallback()
+    {}
+
+    /// \brief Callback Function
+    ///
+    /// Called when an asynchronous I/O completes, this stores the
+    /// completion error code and the number of bytes transferred.
+    ///
+    /// \param ec I/O completion error code passed to callback function.
+    /// \param length Number of bytes transferred
+    virtual void operator()(asio::error_code ec, size_t length = 0) {
+        ptr_->error_code_ = ec;
+        setLength(length);
+        setCalled(true);
+    }
+
+    /// \brief Get I/O completion error code
+    int getCode() {
+        return (ptr_->error_code_.value());
+    }
+
+    /// \brief Set I/O completion code
+    ///
+    /// \param code New value of completion code
+    void setCode(int code) {
+        ptr_->error_code_ = asio::error_code(code, asio::error_code().category());
+    }
+
+    /// \brief Get number of bytes transferred in I/O
+    size_t getLength() const {
+        return (ptr_->length_);
+    }
+
+    /// \brief Set number of bytes transferred in I/O
+    ///
+    /// \param length New value of length parameter
+    void setLength(size_t length) {
+        ptr_->length_ = length;
+    }
+
+    /// \brief Get flag to say when callback was called
+    bool getCalled() const {
+        return (ptr_->called_);
+    }
+
+    /// \brief Set flag to say when callback was called
+    ///
+    /// \param called New value of called parameter
+    void setCalled(bool called) {
+        ptr_->called_ = called;
+    }
+
+    /// \brief Return instance of callback name
+    std::string getName() const {
+        return (ptr_->name_);
+    }
+
+    /// \brief Set callback name
+    ///
+    /// \param name New value of the callback name
+    void setName(const std::string& name) {
+        ptr_->name_ = name;
+    }
+
+private:
+    boost::shared_ptr<PrivateData>  ptr_;   ///< Pointer to private data
+};
+
+// TODO: Need to add a test to check the cancel() method
+
+// Tests the operation of a UDPSocket by opening it, sending an asynchronous
+// message to a server, receiving an asynchronous message from the server and
+// closing.
+TEST(UDPSocket, SequenceTest) {
+
+    // Common objects.
+    IOService   service;                    // Service object for async control
+
+    // Server
+    IOAddress   server_address(SERVER_ADDRESS); // Address of target server
+    UDPCallback server_cb("Server");        // Server callback
+    UDPEndpoint server_endpoint(            // Endpoint describing server
+        server_address, SERVER_PORT);
+    UDPEndpoint server_remote_endpoint;     // Address where server received message from
+
+    // The client - the UDPSocket being tested
+    UDPSocket<UDPCallback>  client(service);// Socket under test
+    UDPCallback client_cb("Client");        // Async I/O callback function
+    UDPEndpoint client_remote_endpoint;     // Where client receives message from
+    size_t      client_cumulative = 0;      // Cumulative data received
+
+    // The server - with which the client communicates.  For convenience, we
+    // use the same io_service, and use the endpoint object created for
+    // the client to send to as the endpoint object in the constructor.
+    asio::ip::udp::socket server(service.get_io_service(),
+        server_endpoint.getASIOEndpoint());
+    server.set_option(socket_base::reuse_address(true));
+
+    // Assertion to ensure that the server buffer is large enough
+    char data[UDPSocket<UDPCallback>::MAX_SIZE];
+    ASSERT_GT(sizeof(data), sizeof(OUTBOUND_DATA));
+
+    // Open the client socket - the operation should be synchronous
+    EXPECT_FALSE(client.open(&server_endpoint, client_cb));
+
+    // Issue read on the server.  Completion callback should not have run.
+    server_cb.setCalled(false);
+    server_cb.setCode(42); // Answer to Life, the Universe and Everything!
+    server.async_receive_from(buffer(data, sizeof(data)),
+        server_remote_endpoint.getASIOEndpoint(), server_cb);
+    EXPECT_FALSE(server_cb.getCalled());
+
+    // Write something to the server using the client - the callback should not
+    // be called until we call the io_service.run() method.
+    client_cb.setCalled(false);
+    client_cb.setCode(7);  // Arbitrary number
+    client.asyncSend(OUTBOUND_DATA, sizeof(OUTBOUND_DATA), &server_endpoint, client_cb);
+    EXPECT_FALSE(client_cb.getCalled());
+
+    // Execute the two callbacks.
+    service.run_one();
+    service.run_one();
+
+    EXPECT_TRUE(client_cb.getCalled());
+    EXPECT_EQ(0, client_cb.getCode());
+    EXPECT_EQ(sizeof(OUTBOUND_DATA), client_cb.getLength());
+
+    EXPECT_TRUE(server_cb.getCalled());
+    EXPECT_EQ(0, server_cb.getCode());
+    EXPECT_EQ(sizeof(OUTBOUND_DATA), server_cb.getLength());
+
+    EXPECT_TRUE(equal(&data[0], &data[server_cb.getLength() - 1], OUTBOUND_DATA));
+
+    // Now return data from the server to the client.  Issue the read on the
+    // client.
+    client_cb.setLength(12345);             // Arbitrary number
+    client_cb.setCalled(false);
+    client_cb.setCode(32);                  // Arbitrary number
+    client.asyncReceive(data, sizeof(data), client_cumulative,
+        &client_remote_endpoint, client_cb);
+
+    // Issue the write on the server side to the source of the data it received.
+    server_cb.setLength(22345);             // Arbitrary number
+    server_cb.setCalled(false);
+    server_cb.setCode(232);                 // Arbitrary number
+    server.async_send_to(buffer(INBOUND_DATA, sizeof(INBOUND_DATA)),
+        server_remote_endpoint.getASIOEndpoint(), server_cb);
+
+    // Expect the two callbacks to run
+    service.run_one();
+    service.run_one();
+
+    EXPECT_TRUE(client_cb.getCalled());
+    EXPECT_EQ(0, client_cb.getCode());
+    EXPECT_EQ(sizeof(INBOUND_DATA), client_cb.getLength());
+
+    EXPECT_TRUE(server_cb.getCalled());
+    EXPECT_EQ(0, server_cb.getCode());
+    EXPECT_EQ(sizeof(INBOUND_DATA), server_cb.getLength());
+
+    EXPECT_TRUE(equal(&data[0], &data[server_cb.getLength() - 1], INBOUND_DATA));
+
+    // Check that the address/port received by the client corresponds to the
+    // address and port the server is listening on.
+    EXPECT_TRUE(server_address == client_remote_endpoint.getAddress());
+    EXPECT_EQ(SERVER_PORT, client_remote_endpoint.getPort());
+
+    // Finally, check that the receive received a complete buffer's worth of data.
+    EXPECT_TRUE(client.receiveComplete(&data[0], client_cb.getLength(),
+        client_cumulative));
+    EXPECT_EQ(client_cb.getLength(), client_cumulative);
+
+    // Close client and server.
+    EXPECT_NO_THROW(client.close());
+    EXPECT_NO_THROW(server.close());
+}

+ 21 - 8
src/lib/asiolink/udp_endpoint.h

@@ -33,6 +33,16 @@ public:
     /// \name Constructors and Destructor.
     /// \name Constructors and Destructor.
     ///
     ///
     //@{
     //@{
+
+    /// \brief Default Constructor
+    ///
+    /// Creates an internal endpoint.  This is expected to be set by some
+    /// external call.
+    UDPEndpoint() :
+        asio_endpoint_placeholder_(new asio::ip::udp::endpoint()),
+        asio_endpoint_(*asio_endpoint_placeholder_)
+    {}
+
     /// \brief Constructor from a pair of address and port.
     /// \brief Constructor from a pair of address and port.
     ///
     ///
     /// \param address The IP address of the endpoint.
     /// \param address The IP address of the endpoint.
@@ -50,27 +60,27 @@ public:
     /// corresponding ASIO class, \c udp::endpoint.
     /// corresponding ASIO class, \c udp::endpoint.
     ///
     ///
     /// \param asio_endpoint The ASIO representation of the UDP endpoint.
     /// \param asio_endpoint The ASIO representation of the UDP endpoint.
-    UDPEndpoint(const asio::ip::udp::endpoint& asio_endpoint) :
+    UDPEndpoint(asio::ip::udp::endpoint& asio_endpoint) :
         asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
         asio_endpoint_placeholder_(NULL), asio_endpoint_(asio_endpoint)
     {}
     {}
 
 
     /// \brief The destructor.
     /// \brief The destructor.
-    ~UDPEndpoint() { delete asio_endpoint_placeholder_; }
+    virtual ~UDPEndpoint() { delete asio_endpoint_placeholder_; }
     //@}
     //@}
 
 
-    inline IOAddress getAddress() const {
+    virtual IOAddress getAddress() const {
         return (asio_endpoint_.address());
         return (asio_endpoint_.address());
     }
     }
 
 
-    inline uint16_t getPort() const {
+    virtual uint16_t getPort() const {
         return (asio_endpoint_.port());
         return (asio_endpoint_.port());
     }
     }
 
 
-    inline short getProtocol() const {
+    virtual short getProtocol() const {
         return (asio_endpoint_.protocol().protocol());
         return (asio_endpoint_.protocol().protocol());
     }
     }
 
 
-    inline short getFamily() const {
+    virtual short getFamily() const {
         return (asio_endpoint_.protocol().family());
         return (asio_endpoint_.protocol().family());
     }
     }
 
 
@@ -79,10 +89,13 @@ public:
     inline const asio::ip::udp::endpoint& getASIOEndpoint() const {
     inline const asio::ip::udp::endpoint& getASIOEndpoint() const {
         return (asio_endpoint_);
         return (asio_endpoint_);
     }
     }
+    inline asio::ip::udp::endpoint& getASIOEndpoint() {
+        return (asio_endpoint_);
+    }
 
 
 private:
 private:
-    const asio::ip::udp::endpoint* asio_endpoint_placeholder_;
-    const asio::ip::udp::endpoint& asio_endpoint_;
+    asio::ip::udp::endpoint* asio_endpoint_placeholder_;
+    asio::ip::udp::endpoint& asio_endpoint_;
 };
 };
 
 
 }      // namespace asiolink
 }      // namespace asiolink

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

@@ -1,189 +0,0 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <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

+ 41 - 10
src/lib/asiolink/udp_server.cc

@@ -12,25 +12,26 @@
 // 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 <config.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
 
 
 #include <boost/shared_array.hpp>
 #include <boost/shared_array.hpp>
 
 
-// unistd is needed for asio.hpp with SunStudio
-#include <unistd.h>
-
-#include <asio.hpp>
+#include <config.h>
 
 
 #include <log/dummylog.h>
 #include <log/dummylog.h>
 
 
+#include <asio.hpp>
+#include <asiolink/dummy_io_cb.h>
 #include <asiolink/udp_endpoint.h>
 #include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_server.h>
 #include <asiolink/udp_socket.h>
 #include <asiolink/udp_socket.h>
 
 
-#include <asiolink/udp_server.h>
+#include <dns/opcode.h>
 
 
 using namespace asio;
 using namespace asio;
 using asio::ip::udp;
 using asio::ip::udp;
-using asio::ip::tcp;
 using isc::log::dlog;
 using isc::log::dlog;
 
 
 using namespace std;
 using namespace std;
@@ -54,8 +55,9 @@ struct UDPServer::Data {
      */
      */
     Data(io_service& io_service, const ip::address& addr, const uint16_t port,
     Data(io_service& io_service, const ip::address& addr, const uint16_t port,
         SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
         SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
-        io_(io_service), done_(false), checkin_callback_(checkin),
-        lookup_callback_(lookup), answer_callback_(answer)
+        io_(io_service), done_(false), stopped_by_hand_(false),
+        checkin_callback_(checkin),lookup_callback_(lookup),
+        answer_callback_(answer)
     {
     {
         // We must use different instantiations for v4 and v6;
         // We must use different instantiations for v4 and v6;
         // otherwise ASIO will bind to both
         // otherwise ASIO will bind to both
@@ -77,6 +79,7 @@ struct UDPServer::Data {
      */
      */
     Data(const Data& other) :
     Data(const Data& other) :
         io_(other.io_), socket_(other.socket_), done_(false),
         io_(other.io_), socket_(other.socket_), done_(false),
+        stopped_by_hand_(false),
         checkin_callback_(other.checkin_callback_),
         checkin_callback_(other.checkin_callback_),
         lookup_callback_(other.lookup_callback_),
         lookup_callback_(other.lookup_callback_),
         answer_callback_(other.answer_callback_)
         answer_callback_(other.answer_callback_)
@@ -140,6 +143,9 @@ struct UDPServer::Data {
     size_t bytes_;
     size_t bytes_;
     bool done_;
     bool done_;
 
 
+    //whether user explicitly stop the server
+    bool stopped_by_hand_;
+
     // Callback functions provided by the caller
     // Callback functions provided by the caller
     const SimpleCallback* checkin_callback_;
     const SimpleCallback* checkin_callback_;
     const DNSLookup* lookup_callback_;
     const DNSLookup* lookup_callback_;
@@ -167,6 +173,12 @@ UDPServer::operator()(error_code ec, size_t length) {
     /// a switch statement, inline variable declarations are not
     /// a switch statement, inline variable declarations are not
     /// permitted.  Certain variables used below can be declared here.
     /// permitted.  Certain variables used below can be declared here.
 
 
+    /// if user stopped the server, we won't enter the coroutine body
+    /// just return
+    if (data_->stopped_by_hand_) {
+        return;
+    }
+
     CORO_REENTER (this) {
     CORO_REENTER (this) {
         do {
         do {
             /*
             /*
@@ -206,7 +218,16 @@ UDPServer::operator()(error_code ec, size_t length) {
         // that would quickly generate an IOMessage object without
         // that would quickly generate an IOMessage object without
         // all these calls to "new".)
         // all these calls to "new".)
         data_->peer_.reset(new UDPEndpoint(*data_->sender_));
         data_->peer_.reset(new UDPEndpoint(*data_->sender_));
-        data_->iosock_.reset(new UDPSocket(*data_->socket_));
+
+        // The UDP socket class has been extended with asynchronous functions
+        // and takes as a template parameter a completion callback class.  As
+        // UDPServer does not use these extended functions (only those defined
+        // in the IOSocket base class) - but needs a UDPSocket to get hold of
+        // the underlying Boost UDP socket - DummyIOCallback is used.  This
+        // provides the appropriate operator() but is otherwise functionless.
+        data_->iosock_.reset(
+            new UDPSocket<DummyIOCallback>(*data_->socket_));
+
         data_->io_message_.reset(new IOMessage(data_->data_.get(),
         data_->io_message_.reset(new IOMessage(data_->data_.get(),
             data_->bytes_, *data_->iosock_, *data_->peer_));
             data_->bytes_, *data_->iosock_, *data_->peer_));
 
 
@@ -267,6 +288,16 @@ UDPServer::asyncLookup() {
         data_->query_message_, data_->answer_message_, data_->respbuf_, this);
         data_->query_message_, data_->answer_message_, data_->respbuf_, this);
 }
 }
 
 
+/// Stop the UDPServer
+void
+UDPServer::stop() {
+    //server should not be stopped twice
+    if (data_->stopped_by_hand_)
+        return;
+    data_->stopped_by_hand_ = true;
+    data_->socket_->close();
+}
+
 /// Post this coroutine on the ASIO service queue so that it will
 /// Post this coroutine on the ASIO service queue so that it will
 /// resume processing where it left off.  The 'done' parameter indicates
 /// resume processing where it left off.  The 'done' parameter indicates
 /// whether there is an answer to return to the client.
 /// whether there is an answer to return to the client.

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

@@ -58,6 +58,10 @@ public:
     /// \brief Calls the lookup callback
     /// \brief Calls the lookup callback
     void asyncLookup();
     void asyncLookup();
 
 
+    /// \brief Stop the running server
+    /// \note once the server stopped, it can't restart
+    void stop();
+
     /// \brief Resume operation
     /// \brief Resume operation
     ///
     ///
     /// \param done Set this to true if the lookup action is done and
     /// \param done Set this to true if the lookup action is done and

+ 239 - 11
src/lib/asiolink/udp_socket.h

@@ -19,30 +19,258 @@
 #error "asio.hpp must be included before including this, see asiolink.h as to why"
 #error "asio.hpp must be included before including this, see asiolink.h as to why"
 #endif
 #endif
 
 
-#include <asiolink/io_socket.h>
+#include <log/dummylog.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
+
+#include <cstddef>
+
+#include <config.h>
+
+
+#include <asiolink/io_asio_socket.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_service.h>
+#include <asiolink/udp_endpoint.h>
 
 
 namespace asiolink {
 namespace asiolink {
 
 
-/// \brief The \c UDPSocket class is a concrete derived class of
-/// \c IOSocket that represents a UDP socket.
+/// \brief The \c UDPSocket class is a concrete derived class of \c IOAsioSocket
+/// that represents a UDP socket.
 ///
 ///
-/// Other notes about \c TCPSocket applies to this class, too.
-class UDPSocket : public IOSocket {
+/// \param C Callback type
+template <typename C>
+class UDPSocket : public IOAsioSocket<C> {
 private:
 private:
-    UDPSocket(const UDPSocket& source);
-    UDPSocket& operator=(const UDPSocket& source);
+    /// \brief Class is non-copyable
+    UDPSocket(const UDPSocket&);
+    UDPSocket& operator=(const UDPSocket&);
+
 public:
 public:
+    enum {
+        MAX_SIZE = 4096         // Send and receive size
+    };
+    
     /// \brief Constructor from an ASIO UDP socket.
     /// \brief Constructor from an ASIO UDP socket.
     ///
     ///
-    /// \param socket The ASIO representation of the UDP socket.
-    UDPSocket(asio::ip::udp::socket& socket) : socket_(socket) {}
+    /// \param socket The ASIO representation of the UDP socket.  It
+    /// is assumed that the caller will open and close the socket, so
+    /// these operations are a no-op for that socket.
+    UDPSocket(asio::ip::udp::socket& socket);
+
+    /// \brief Constructor
+    ///
+    /// Used when the UDPSocket is being asked to manage its own internal
+    /// socket.  It is assumed that open() and close() will not be used.
+    ///
+    /// \param service I/O Service object used to manage the socket.
+    UDPSocket(IOService& service);
+
+    /// \brief Destructor
+    virtual ~UDPSocket();
 
 
     virtual int getNative() const { return (socket_.native()); }
     virtual int getNative() const { return (socket_.native()); }
     virtual int getProtocol() const { return (IPPROTO_UDP); }
     virtual int getProtocol() const { return (IPPROTO_UDP); }
 
 
+    /// \brief Open Socket
+    ///
+    /// Opens the UDP socket.  In the model for transport-layer agnostic I/O,
+    /// an "open" operation includes a connection to the remote end (which
+    /// may take time).  This does not happen for UDP, so the method returns
+    /// "false" to indicate that the operation completed synchronously.
+    ///
+    /// \param endpoint Endpoint to which the socket will connect to.
+    /// \param callback Unused.
+    ///
+    /// \return false to indicate that the "operation" completed synchronously.
+    virtual bool open(const IOEndpoint* endpoint, C&);
+
+    /// \brief Send Asynchronously
+    ///
+    /// This corresponds to async_send_to() for UDP sockets and async_send()
+    /// for TCP.  In both cases an endpoint argument is supplied indicating the
+    /// target of the send - this is ignored for TCP.
+    ///
+    /// \param data Data to send
+    /// \param length Length of data to send
+    /// \param endpoint Target of the send
+    /// \param callback Callback object.
+    virtual void asyncSend(const void* data, size_t length,
+        const IOEndpoint* endpoint, C& callback);
+
+    /// \brief Receive Asynchronously
+    ///
+    /// This correstponds to async_receive_from() for UDP sockets and
+    /// async_receive() for TCP.  In both cases, an endpoint argument is
+    /// supplied to receive the source of the communication.  For TCP it will
+    /// be filled in with details of the connection.
+    ///
+    /// \param data Buffer to receive incoming message
+    /// \param length Length of the data buffer
+    /// \param cumulative Amount of data that should already be in the buffer.
+    /// (This is ignored - every UPD receive fills the buffer from the start.)
+    /// \param endpoint Source of the communication
+    /// \param callback Callback object
+    virtual void asyncReceive(void* data, size_t length, size_t cumulative,
+        IOEndpoint* endpoint, C& callback);
+
+    /// \brief Checks if the data received is complete.
+    ///
+    /// As all the data is received in one I/O, so this is, this is effectively
+    /// a no-op (although it does update the amount of data received).
+    ///
+    /// \param data Data buffer containing data to date.  (This is ignored
+    /// for UDP receives.)
+    /// \param length Amount of data received in last asynchronous I/O
+    /// \param cumulative On input, amount of data received before the last
+    /// I/O.  On output, the total amount of data received to date.
+    ///
+    /// \return true if the receive is complete, false if another receive is
+    /// needed.
+    virtual bool receiveComplete(void*, size_t length, size_t& cumulative) {
+        cumulative = length;
+        return (true);
+    }
+
+    /// \brief Cancel I/O On Socket
+    virtual void cancel();
+
+    /// \brief Close socket
+    virtual void close();
+
+
 private:
 private:
-    asio::ip::udp::socket& socket_;
+    // Two variables to hold the socket - a socket and a pointer to it.  This
+    // handles the case where a socket is passed to the UDPSocket on
+    // construction, or where it is asked to manage its own socket.
+    asio::ip::udp::socket*      socket_ptr_;    ///< Pointer to own socket
+    asio::ip::udp::socket&      socket_;        ///< Socket
+    bool                        isopen_;        ///< true when socket is open
 };
 };
 
 
-}      // namespace asiolink
+// Constructor - caller manages socket
+
+template <typename C>
+UDPSocket<C>::UDPSocket(asio::ip::udp::socket& socket) :
+    socket_ptr_(NULL), socket_(socket), isopen_(true)
+{
+}
+
+// Constructor - create socket on the fly
+
+template <typename C>
+UDPSocket<C>::UDPSocket(IOService& service) :
+    socket_ptr_(new asio::ip::udp::socket(service.get_io_service())),
+    socket_(*socket_ptr_), isopen_(false)
+{
+}
+
+// Destructor.  Only delete the socket if we are managing it.
+
+template <typename C>
+UDPSocket<C>::~UDPSocket()
+{
+    delete socket_ptr_;
+}
+
+// Open the socket.  Throws an error on failure
+// TODO: Make the open more resilient
+
+template <typename C> bool
+UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
+
+    // Ignore opens on already-open socket.  Don't throw a failure because
+    // of uncertainties as to what precedes whan when using asynchronous I/O.
+    // At also allows us a treat a passed-in socket as a self-managed socket.
+
+    if (!isopen_) {
+        if (endpoint->getFamily() == AF_INET) {
+            socket_.open(asio::ip::udp::v4());
+        }
+        else {
+            socket_.open(asio::ip::udp::v6());
+        }
+        isopen_ = true;
+
+        // Ensure it can send and receive 4K buffers.
+        socket_.set_option(asio::socket_base::send_buffer_size(MAX_SIZE));
+        socket_.set_option(asio::socket_base::receive_buffer_size(MAX_SIZE));
+    ;
+        // Allow reuse of an existing port/address
+        socket_.set_option(asio::socket_base::reuse_address(true));
+    }
+    return (false);
+}
+
+// Send a message.  Should never do this if the socket is not open, so throw
+// an exception if this is the case.
+
+template <typename C> void
+UDPSocket<C>::asyncSend(const void* data, size_t length,
+    const IOEndpoint* endpoint, C& callback)
+{
+    if (isopen_) {
+
+        // Upconvert to a UDPEndpoint.  We need to do this because although
+        // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
+        // doing cont contain a method for getting at the underlying endpoint
+        // type - those are in the derived class and the two classes differ on
+        // return type.
+
+        assert(endpoint->getProtocol() == IPPROTO_UDP);
+        const UDPEndpoint* udp_endpoint =
+            static_cast<const UDPEndpoint*>(endpoint);
+        socket_.async_send_to(asio::buffer(data, length),
+            udp_endpoint->getASIOEndpoint(), callback);
+    } else {
+        isc_throw(SocketNotOpen,
+            "attempt to send on a UDP socket that is not open");
+    }
+}
+
+// Receive a message. Note that the "cumulative" argument is ignored - every UDP
+// receive is put into the buffer beginning at the start - there is no concept
+// receiving a subsequent part of a message.  Same critera as before concerning
+// the need for the socket to be open.
+
+template <typename C> void
+UDPSocket<C>::asyncReceive(void* data, size_t length, size_t,
+    IOEndpoint* endpoint, C& callback)
+{
+    if (isopen_) {
+
+        // Upconvert the endpoint again.
+        assert(endpoint->getProtocol() == IPPROTO_UDP);
+        UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
+
+        socket_.async_receive_from(asio::buffer(data, length),
+            udp_endpoint->getASIOEndpoint(), callback);
+    } else {
+        isc_throw(SocketNotOpen,
+            "attempt to receive from a UDP socket that is not open");
+    }
+}
+
+// Cancel I/O on the socket.  No-op if the socket is not open.
+template <typename C> void
+UDPSocket<C>::cancel() {
+    if (isopen_) {
+        socket_.cancel();
+    }
+}
+
+// Close the socket down.  Can only do this if the socket is open and we are
+// managing it ourself.
+
+template <typename C> void
+UDPSocket<C>::close() {
+    if (isopen_ && socket_ptr_) {
+        socket_.close();
+        isopen_ = false;
+    }
+}
+
+} // namespace asiolink
+
 #endif // __UDP_SOCKET_H
 #endif // __UDP_SOCKET_H

+ 11 - 8
src/lib/bench/tests/benchmark_unittest.cc

@@ -12,7 +12,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 <unistd.h>             // for usleep
+#include <time.h>               // for nanosleep
 
 
 #include <bench/benchmark.h>
 #include <bench/benchmark.h>
 
 
@@ -26,16 +26,17 @@ namespace {
 // number of iterations.
 // number of iterations.
 class TestBenchMark {
 class TestBenchMark {
 public:
 public:
-    TestBenchMark(const int sub_iterations, const int sleep_time) :
+    TestBenchMark(const int sub_iterations,
+                  const struct timespec& sleep_time) :
         sub_iterations_(sub_iterations), sleep_time_(sleep_time),
         sub_iterations_(sub_iterations), sleep_time_(sleep_time),
         setup_completed_(false), teardown_completed_(false)
         setup_completed_(false), teardown_completed_(false)
     {}
     {}
     unsigned int run() {
     unsigned int run() {
-        usleep(sleep_time_);
+        nanosleep(&sleep_time_, NULL);
         return (sub_iterations_);
         return (sub_iterations_);
     }
     }
     const int sub_iterations_;
     const int sub_iterations_;
-    const int sleep_time_;
+    const struct timespec sleep_time_;
     bool setup_completed_;
     bool setup_completed_;
     bool teardown_completed_;
     bool teardown_completed_;
 };
 };
@@ -67,6 +68,7 @@ TEST(BenchMarkTest, run) {
     // use some uncommon iterations for testing purpose:
     // use some uncommon iterations for testing purpose:
     const int sub_iterations = 23;
     const int sub_iterations = 23;
     const int sleep_time = 50000; // will sleep for 50ms
     const int sleep_time = 50000; // will sleep for 50ms
+    const struct timespec sleep_timespec = { 0, sleep_time * 1000 };
     // we cannot expect particular accuracy on the measured duration, so
     // we cannot expect particular accuracy on the measured duration, so
     // we'll include some conservative margin (25%) and perform range
     // we'll include some conservative margin (25%) and perform range
     // comparison below.
     // comparison below.
@@ -75,12 +77,12 @@ TEST(BenchMarkTest, run) {
 
 
     // Prerequisite check: since the tests in this case may depend on subtle
     // Prerequisite check: since the tests in this case may depend on subtle
     // timing, it may result in false positives.  There are reportedly systems
     // timing, it may result in false positives.  There are reportedly systems
-    // where usleep() doesn't work as this test expects.  So we check the
+    // where sleeping doesn't work as this test expects.  So we check the
     // conditions before the tests, and if it fails skip the tests at the
     // conditions before the tests, and if it fails skip the tests at the
     // risk of overlooking possible bugs.
     // risk of overlooking possible bugs.
     struct timeval check_begin, check_end;
     struct timeval check_begin, check_end;
     gettimeofday(&check_begin, NULL);
     gettimeofday(&check_begin, NULL);
-    usleep(sleep_time);
+    nanosleep(&sleep_timespec, 0);
     gettimeofday(&check_end, NULL);
     gettimeofday(&check_end, NULL);
     check_end.tv_sec -= check_begin.tv_sec;
     check_end.tv_sec -= check_begin.tv_sec;
     if (check_end.tv_usec >= check_begin.tv_usec) {
     if (check_end.tv_usec >= check_begin.tv_usec) {
@@ -97,7 +99,7 @@ TEST(BenchMarkTest, run) {
         return;
         return;
     }
     }
 
 
-    TestBenchMark test_bench(sub_iterations, sleep_time);
+    TestBenchMark test_bench(sub_iterations, sleep_timespec);
     BenchMark<TestBenchMark> bench(1, test_bench, false);
     BenchMark<TestBenchMark> bench(1, test_bench, false);
     // Check pre-test conditions.
     // Check pre-test conditions.
     EXPECT_FALSE(test_bench.setup_completed_);
     EXPECT_FALSE(test_bench.setup_completed_);
@@ -130,7 +132,8 @@ TEST(BenchMarkTest, run) {
 TEST(BenchMarkTest, runWithNoIteration) {
 TEST(BenchMarkTest, runWithNoIteration) {
     // we'll lie on the number of iteration (0).  it will result in
     // we'll lie on the number of iteration (0).  it will result in
     // meaningless result, but at least it shouldn't crash.
     // meaningless result, but at least it shouldn't crash.
-    TestBenchMark test_bench(0, 0);
+    const struct timespec null_timespec = { 0, 0 };
+    TestBenchMark test_bench(0, null_timespec);
     BenchMark<TestBenchMark> bench(1, test_bench, false);
     BenchMark<TestBenchMark> bench(1, test_bench, false);
     bench.run();
     bench.run();
     EXPECT_EQ(0, bench.getIteration());
     EXPECT_EQ(0, bench.getIteration());

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

@@ -55,7 +55,7 @@ const char* const LoadQueryTest::DATA_DIR = TEST_DATA_DIR;
 class QueryInserter {
 class QueryInserter {
 public:
 public:
     QueryInserter(stringstream& stream) : stream_(stream) {}
     QueryInserter(stringstream& stream) : stream_(stream) {}
-    void operator()(const QueryParam& query) {
+    void operator()(const QueryParam& query) const {
         stream_ << query.first << " " << query.second << endl;
         stream_ << query.first << " " << query.second << endl;
     }
     }
 private:
 private:

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

@@ -74,6 +74,7 @@ MessageCache::update(const Message& msg) {
     return (message_table_.add(msg_entry, entry_key, true));
     return (message_table_.add(msg_entry, entry_key, true));
 }
 }
 
 
+#if 0
 void
 void
 MessageCache::dump(const std::string&) {
 MessageCache::dump(const std::string&) {
     //TODO
     //TODO
@@ -89,6 +90,7 @@ MessageCache::resize(uint32_t) {
     //TODO
     //TODO
     return (true);
     return (true);
 }
 }
+#endif
 
 
 } // namespace cache
 } // namespace cache
 } // namespace isc
 } // namespace isc

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

@@ -57,6 +57,7 @@ public:
     /// directly.
     /// directly.
     bool update(const isc::dns::Message& msg);
     bool update(const isc::dns::Message& msg);
 
 
+#if 0
     /// \brief Dump the message cache to specified file.
     /// \brief Dump the message cache to specified file.
     /// \todo It should can be dumped to one configured database.
     /// \todo It should can be dumped to one configured database.
     void dump(const std::string& file_name);
     void dump(const std::string& file_name);
@@ -67,6 +68,7 @@ public:
 
 
     /// \brief Resize the size of message cache in runtime.
     /// \brief Resize the size of message cache in runtime.
     bool resize(uint32_t size);
     bool resize(uint32_t size);
+#endif
 
 
 protected:
 protected:
     /// \brief Get the hash key for the message entry in the cache.
     /// \brief Get the hash key for the message entry in the cache.

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

@@ -83,6 +83,7 @@ RRsetCache::update(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) {
     }
     }
 }
 }
 
 
+#if 0
 void
 void
 RRsetCache::dump(const std::string&) {
 RRsetCache::dump(const std::string&) {
     //TODO
     //TODO
@@ -98,6 +99,7 @@ RRsetCache::resize(uint32_t) {
     //TODO
     //TODO
     return (true);
     return (true);
 }
 }
+#endif
 
 
 } // namespace cache
 } // namespace cache
 } // namespace isc
 } // namespace isc

+ 3 - 1
src/lib/cache/rrset_cache.h

@@ -15,7 +15,7 @@
 #ifndef __RRSET_CACHE_H
 #ifndef __RRSET_CACHE_H
 #define __RRSET_CACHE_H
 #define __RRSET_CACHE_H
 
 
-#include <rrset_entry.h>
+#include <cache/rrset_entry.h>
 #include <nsas/hash_table.h>
 #include <nsas/hash_table.h>
 #include <nsas/lru_list.h>
 #include <nsas/lru_list.h>
 
 
@@ -70,6 +70,7 @@ public:
     RRsetEntryPtr update(const isc::dns::RRset& rrset,
     RRsetEntryPtr update(const isc::dns::RRset& rrset,
                          const RRsetTrustLevel& level);
                          const RRsetTrustLevel& level);
 
 
+#if 0
     /// \brief Dump the rrset cache to specified file.
     /// \brief Dump the rrset cache to specified file.
     ///
     ///
     /// \param file_name The file to write to
     /// \param file_name The file to write to
@@ -89,6 +90,7 @@ public:
     /// \param The size to resize to
     /// \param The size to resize to
     /// \return true
     /// \return true
     bool resize(uint32_t size);
     bool resize(uint32_t size);
+#endif
 
 
 private:
 private:
     uint16_t class_; // The class of the rrset cache.
     uint16_t class_; // The class of the rrset cache.

+ 1 - 1
src/lib/cc/tests/session_unittests.cc

@@ -74,7 +74,7 @@ public:
     }
     }
 
 
     void
     void
-    acceptHandler(const asio::error_code&) {
+    acceptHandler(const asio::error_code&) const {
     }
     }
 
 
     void
     void

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

@@ -170,10 +170,10 @@ ModuleCCSession::readModuleSpecification(const std::string& filename) {
 
 
     try {
     try {
         module_spec = moduleSpecFromFile(file, true);
         module_spec = moduleSpecFromFile(file, true);
-    } catch (JSONError pe) {
+    } catch (const JSONError& pe) {
         cout << "Error parsing module specification file: " << pe.what() << endl;
         cout << "Error parsing module specification file: " << pe.what() << endl;
         exit(1);
         exit(1);
-    } catch (ModuleSpecError dde) {
+    } catch (const ModuleSpecError& dde) {
         cout << "Error reading module specification file: " << dde.what() << endl;
         cout << "Error reading module specification file: " << dde.what() << endl;
         exit(1);
         exit(1);
     }
     }

+ 1 - 1
src/lib/config/module_spec.cc

@@ -121,7 +121,7 @@ void
 check_module_specification(ConstElementPtr def) {
 check_module_specification(ConstElementPtr def) {
     try {
     try {
         check_data_specification(def);
         check_data_specification(def);
-    } catch (TypeError te) {
+    } catch (const TypeError& te) {
         throw ModuleSpecError(te.what());
         throw ModuleSpecError(te.what());
     }
     }
 }
 }

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

@@ -31,7 +31,7 @@ using namespace std;
 
 
 namespace {
 namespace {
 std::string
 std::string
-ccspecfile(const std::string name) {
+ccspecfile(const std::string& name) {
     return (std::string(TEST_DATA_PATH) + "/" + name);
     return (std::string(TEST_DATA_PATH) + "/" + name);
 }
 }
 
 

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

@@ -23,7 +23,7 @@
 using namespace isc::data;
 using namespace isc::data;
 using namespace isc::config;
 using namespace isc::config;
 
 
-std::string specfile(const std::string name) {
+std::string specfile(const std::string& name) {
     return (std::string(TEST_DATA_PATH) + "/" + name);
     return (std::string(TEST_DATA_PATH) + "/" + name);
 }
 }
 
 
@@ -36,7 +36,7 @@ moduleSpecError(const std::string& file,
     EXPECT_THROW(moduleSpecFromFile(specfile(file)), ModuleSpecError);
     EXPECT_THROW(moduleSpecFromFile(specfile(file)), ModuleSpecError);
     try {
     try {
         ModuleSpec dd = moduleSpecFromFile(specfile(file));
         ModuleSpec dd = moduleSpecFromFile(specfile(file));
-    } catch (ModuleSpecError dde) {
+    } catch (const ModuleSpecError& dde) {
         std::string ddew = dde.what();
         std::string ddew = dde.what();
         EXPECT_EQ(error1 + error2 + error3, ddew);
         EXPECT_EQ(error1 + error2 + error3, ddew);
     }
     }

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

@@ -1157,7 +1157,7 @@ MetaDataSrc::addDataSrc(ConstDataSrcPtr data_src) {
 void
 void
 MetaDataSrc::removeDataSrc(ConstDataSrcPtr data_src) {
 MetaDataSrc::removeDataSrc(ConstDataSrcPtr data_src) {
     std::vector<ConstDataSrcPtr>::iterator it, itr;
     std::vector<ConstDataSrcPtr>::iterator it, itr;
-    for (it = data_sources.begin(); it != data_sources.end(); it++) {
+    for (it = data_sources.begin(); it != data_sources.end(); ++it) {
         if (*it == data_src) {
         if (*it == data_src) {
             itr = it;
             itr = it;
         }
         }

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

@@ -472,7 +472,7 @@ struct MemoryZone::MemoryZoneImpl {
                      * Otherwise, why would the DOMAINFLAG_WILD be there if
                      * Otherwise, why would the DOMAINFLAG_WILD be there if
                      * there was no wildcard under it?
                      * there was no wildcard under it?
                      */
                      */
-                    assert(result = DomainTree::EXACTMATCH);
+                    assert(result == DomainTree::EXACTMATCH);
                     /*
                     /*
                      * We have the wildcard node now. Jump below the switch,
                      * We have the wildcard node now. Jump below the switch,
                      * where handling of the common (exact-match) case is.
                      * where handling of the common (exact-match) case is.
@@ -515,7 +515,7 @@ struct MemoryZone::MemoryZoneImpl {
         if (target != NULL && !node->getData()->empty()) {
         if (target != NULL && !node->getData()->empty()) {
             // Empty domain will be handled as NXRRSET by normal processing
             // Empty domain will be handled as NXRRSET by normal processing
             for (found = node->getData()->begin();
             for (found = node->getData()->begin();
-                 found != node->getData()->end(); found++)
+                 found != node->getData()->end(); ++found)
             {
             {
                 target->addRRset(
                 target->addRRset(
                     boost::const_pointer_cast<RRset>(prepareRRset(name,
                     boost::const_pointer_cast<RRset>(prepareRRset(name,

+ 1 - 1
src/lib/datasrc/tests/rbtree_unittest.cc

@@ -56,7 +56,7 @@ const size_t Name::MAX_LABELS;
 namespace {
 namespace {
 class RBTreeTest : public::testing::Test {
 class RBTreeTest : public::testing::Test {
 protected:
 protected:
-    RBTreeTest() : rbtree_expose_empty_node(true) {
+    RBTreeTest() : rbtree_expose_empty_node(true), crbtnode(NULL) {
         const char* const domain_names[] = {
         const char* const domain_names[] = {
             "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
             "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
             "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f"};
             "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f"};

+ 1 - 3
src/lib/dns/message.cc

@@ -113,10 +113,8 @@ public:
     vector<RRsetPtr> rrsets_[NUM_SECTIONS];
     vector<RRsetPtr> rrsets_[NUM_SECTIONS];
     ConstEDNSPtr edns_;
     ConstEDNSPtr edns_;
 
 
-#ifdef notyet
     // tsig/sig0: TODO
     // tsig/sig0: TODO
-    RRsetsSorter* sorter_;
-#endif
+    // RRsetsSorter* sorter_; : TODO
 
 
     void init();
     void init();
     void setOpcode(const Opcode& opcode);
     void setOpcode(const Opcode& opcode);

+ 1 - 2
src/lib/dns/rdata/generic/nsec3_50.cc

@@ -117,11 +117,10 @@ NSEC3::NSEC3(const string& nsec3_str) :
     memset(bitmap, 0, sizeof(bitmap));
     memset(bitmap, 0, sizeof(bitmap));
     do { 
     do { 
         string type;
         string type;
-        int code;
         iss >> type;
         iss >> type;
         if (type.length() != 0) {
         if (type.length() != 0) {
             try {
             try {
-                code = RRType(type).getCode();
+                const int code = RRType(type).getCode();
                 bitmap[code / 8] |= (0x80 >> (code % 8));
                 bitmap[code / 8] |= (0x80 >> (code % 8));
             } catch (...) {
             } catch (...) {
                 isc_throw(InvalidRdataText, "Invalid RRtype in NSEC3");
                 isc_throw(InvalidRdataText, "Invalid RRtype in NSEC3");

+ 1 - 2
src/lib/dns/rdata/generic/nsec_47.cc

@@ -63,10 +63,9 @@ NSEC::NSEC(const string& nsec_str) :
     memset(bitmap, 0, sizeof(bitmap));
     memset(bitmap, 0, sizeof(bitmap));
     do { 
     do { 
         string type;
         string type;
-        int code;
         iss >> type;
         iss >> type;
         try {
         try {
-            code = RRType(type).getCode();
+            const int code = RRType(type).getCode();
             bitmap[code / 8] |= (0x80 >> (code % 8));
             bitmap[code / 8] |= (0x80 >> (code % 8));
         } catch (...) {
         } catch (...) {
             isc_throw(InvalidRdataText, "Invalid RRtype in NSEC");
             isc_throw(InvalidRdataText, "Invalid RRtype in NSEC");

+ 1 - 1
src/lib/dns/tests/name_unittest.cc

@@ -290,7 +290,7 @@ TEST_F(NameTest, assignment) {
 
 
     // Self assignment
     // Self assignment
     copy = copy;
     copy = copy;
-    EXPECT_EQ(copy, example_name);
+    EXPECT_EQ(example_name, copy);
 }
 }
 
 
 TEST_F(NameTest, toText) {
 TEST_F(NameTest, toText) {

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

@@ -2,7 +2,6 @@ SUBDIRS = . compiler tests
 
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CPPFLAGS += $(BOOST_INCLUDES)
-# AM_CPPFLAGS += $(LOG4CXX_INCLUDES)
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
 
 
 CLEANFILES = *.gcno *.gcda
 CLEANFILES = *.gcno *.gcda

+ 1 - 1
src/lib/log/strutil.cc

@@ -62,7 +62,7 @@ trim(const string& instring) {
 // another dependency on a Boost library.
 // another dependency on a Boost library.
 
 
 vector<string>
 vector<string>
-tokens(const std::string text, const std::string& delim) {
+tokens(const std::string& text, const std::string& delim) {
     vector<string> result;
     vector<string> result;
 
 
     // Search for the first non-delimiter character
     // Search for the first non-delimiter character

+ 1 - 1
src/lib/log/strutil.h

@@ -71,7 +71,7 @@ std::string trim(const std::string& instring);
 /// \param delim Delimiter characters
 /// \param delim Delimiter characters
 ///
 ///
 /// \return Vector of tokens.
 /// \return Vector of tokens.
-std::vector<std::string> tokens(const std::string text,
+std::vector<std::string> tokens(const std::string& text,
         const std::string& delim = std::string(" \t\n"));
         const std::string& delim = std::string(" \t\n"));
 
 
 
 

+ 0 - 0
src/lib/nsas/Makefile.am


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