Browse Source

Rename the resolver and change some terminology.
This is trac470.
No changelog entry -- this modifies previous changelog entries.
(Never was released with old name.)


git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@4165 e5f2f494-b856-4b98-b285-d166d9295462

Jeremy C. Reed 14 years ago
parent
commit
9a4f1e5f8a

+ 5 - 5
ChangeLog

@@ -62,21 +62,21 @@
 	(Trac #202, svn r3967)
 
   135.  [func]      each
-	Add b10-recurse. This is an example recursive server that
+	Add b10-resolver. This is an example recursive server that
 	currently does forwarding only and no caching.
 	(Trac #327, svn r3903)
 
   134.  [func]      vorner
-	b10-recurse supports timeouts and retries in forwarder mode.
+	b10-resolver supports timeouts and retries in forwarder mode.
 	(Trac #401, svn r3660)
 
   133.  [func]      vorner
 	New temporary logging function available in isc::log. It is used by
-	b10-recurse.
+	b10-resolver.
 	(Trac #393, r3602)
 
   132.  [func]      vorner
-	The b10-recurse is configured through config manager.
+	The b10-resolver is configured through config manager.
 	It has "listen_on" and "forward_addresses" options.
 	(Trac #389, r3448)
 
@@ -139,7 +139,7 @@ bind10-devel-20101201 released on December 01, 2010
   122.  [func]		stephen
 	src/bin/bind10: Added configuration options to Boss to determine
 	whether to start the authoritative server, recursive server (or
-	both). A dummy recursor has been provided for test purposes.
+	both). A dummy program has been provided for test purposes.
 	(Trac #412, svn r3676)
 
   121.  [func]		jinmei

+ 1 - 1
README

@@ -15,7 +15,7 @@ five year plan are described here:
 
 This release includes the bind10 master process, b10-msgq message
 bus, b10-auth authoritative DNS server (with SQLite3 backend),
-b10-recurse forwarding DNS server, b10-cmdctl remote control daemon,
+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

+ 5 - 5
configure.ac

@@ -577,8 +577,8 @@ AC_CONFIG_FILES([Makefile
                  src/bin/auth/Makefile
                  src/bin/auth/tests/Makefile
                  src/bin/auth/benchmarks/Makefile
-                 src/bin/recurse/Makefile
-                 src/bin/recurse/tests/Makefile
+                 src/bin/resolver/Makefile
+                 src/bin/resolver/tests/Makefile
                  src/bin/xfrin/Makefile
                  src/bin/xfrin/tests/Makefile
                  src/bin/xfrout/Makefile
@@ -652,8 +652,8 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/xfrout/xfrout.spec.pre
            src/bin/xfrout/tests/xfrout_test
            src/bin/xfrout/run_b10-xfrout.sh
-           src/bin/recurse/recurse.spec.pre
-           src/bin/recurse/spec_config.h.pre
+           src/bin/resolver/resolver.spec.pre
+           src/bin/resolver/spec_config.h.pre
            src/bin/zonemgr/zonemgr.py
            src/bin/zonemgr/zonemgr.spec.pre
            src/bin/zonemgr/tests/zonemgr_test
@@ -696,7 +696,7 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
            chmod +x src/bin/xfrin/run_b10-xfrin.sh
            chmod +x src/bin/xfrout/run_b10-xfrout.sh
-           chmod +x src/bin/recurse/run_b10-recurse.sh
+           chmod +x src/bin/resolver/run_b10-resolver.sh
            chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
            chmod +x src/bin/stats/tests/stats_test
            chmod +x src/bin/stats/run_b10-stats.sh

+ 1 - 1
src/bin/Makefile.am

@@ -1,4 +1,4 @@
 SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
-	usermgr zonemgr stats tests recurse
+	usermgr zonemgr stats tests resolver
 
 check-recursive: all-recursive

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

@@ -185,7 +185,7 @@ public:
     /// control commands and configuration updates.
     void setConfigSession(isc::config::ModuleCCSession* config_session);
 
-    /// \brief Assign an ASIO IO Service queue to this Recursor object
+    /// \brief Assign an ASIO IO Service queue to this Resolver object
     void setIOService(asiolink::IOService& ios) { io_service_ = &ios; }
 
     /// \brief Return this object's ASIO IO Service queue

+ 17 - 17
src/bin/bind10/bind10.py.in

@@ -208,13 +208,13 @@ class BoB:
         self.dns_port = dns_port
         self.forward = forward
         if forward:
-            self.recursive = True
+            self.resolver = True
         else:
-            self.recursive = False
+            self.resolver = False
         self.cc_session = None
         self.ccs = None
         self.cfg_start_auth = True
-        self.cfg_start_recurse = False
+        self.cfg_start_resolver = False
         self.curproc = None
         self.dead_processes = {}
         self.msgq_socket_file = msgq_socket_file
@@ -278,13 +278,13 @@ class BoB:
 
         config_data = self.ccs.get_full_config()
         self.cfg_start_auth = config_data.get("start_auth")
-        self.cfg_start_recurse = config_data.get("start_recurse")
+        self.cfg_start_resolver = config_data.get("start_resolver")
 
         if self.verbose:
             sys.stdout.write("[bind10] - start_auth: %s\n" %
                 str(self.cfg_start_auth))
-            sys.stdout.write("[bind10] - start_recurse: %s\n" %
-                str(self.cfg_start_recurse))
+            sys.stdout.write("[bind10] - start_resolver: %s\n" %
+                str(self.cfg_start_resolver))
 
     def log_starting(self, process, port = None, address = None):
         """
@@ -423,13 +423,13 @@ class BoB:
             Start the Authoritative server
         """
         # XXX: this must be read from the configuration manager in the future
-        if self.recursive:
-            dns_prog = 'b10-recurse'
+        if self.resolver:
+            dns_prog = 'b10-resolver'
         else:
             dns_prog = 'b10-auth'
         dnsargs = [dns_prog]
-        if not self.recursive:
-            # The recursive uses configuration manager for these
+        if not self.resolver:
+            # The resolver uses configuration manager for these
             dnsargs += ['-p', str(self.dns_port)]
             if self.address:
                 dnsargs += ['-a', str(self.address)]
@@ -444,22 +444,22 @@ class BoB:
         self.start_process("b10-auth", dnsargs, c_channel_env,
             self.dns_port, self.address)
 
-    def start_recurse(self, c_channel_env):
+    def start_resolver(self, c_channel_env):
         """
             Start the Resolver.  At present, all these arguments and switches
             are pure speculation.  As with the auth daemon, they should be
             read from the configuration database.
         """
-        self.curproc = "b10-recurse"
+        self.curproc = "b10-resolver"
         # XXX: this must be read from the configuration manager in the future
-        resargs = ['b10-recurse']
+        resargs = ['b10-resolver']
         if self.uid:
             resargs += ['-u', str(self.uid)]
         if self.verbose:
             resargs += ['-v']
 
         # ... and start
-        self.start_process("b10-recurse", resargs, c_channel_env)
+        self.start_process("b10-resolver", resargs, c_channel_env)
 
     def start_xfrout(self, c_channel_env):
         self.start_simple("b10-xfrout", c_channel_env)
@@ -496,8 +496,8 @@ class BoB:
             self.start_auth(c_channel_env)
 
         # ... and resolver (if selected):
-        if self.cfg_start_recurse:
-            self.start_recurse(c_channel_env)
+        if self.cfg_start_resolver:
+            self.start_resolver(c_channel_env)
 
         # Everything after the main components can run as non-root.
         # TODO: this is only temporary - once the privileged socket creator is
@@ -557,7 +557,7 @@ class BoB:
         self.cc_session.group_sendmsg(cmd, 'Cmdctl', 'Cmdctl')
         self.cc_session.group_sendmsg(cmd, "ConfigManager", "ConfigManager")
         self.cc_session.group_sendmsg(cmd, "Auth", "Auth")
-        self.cc_session.group_sendmsg(cmd, "Recurse", "Recurse")
+        self.cc_session.group_sendmsg(cmd, "Resolver", "Resolver")
         self.cc_session.group_sendmsg(cmd, "Xfrout", "Xfrout")
         self.cc_session.group_sendmsg(cmd, "Xfrin", "Xfrin")
         self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr")

+ 1 - 1
src/bin/bind10/bob.spec

@@ -10,7 +10,7 @@
         "item_default": true
       },
       {
-        "item_name": "start_recurse",
+        "item_name": "start_resolver",
         "item_type": "boolean",
         "item_optional": false,
         "item_default": false

+ 1 - 1
src/bin/bind10/run_bind10.sh.in

@@ -20,7 +20,7 @@ export PYTHON_EXEC
 
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 
-PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/recurse:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:$PATH
+PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:$PATH
 export PATH
 
 PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs

+ 20 - 20
src/bin/bind10/tests/bind10_test.py

@@ -89,7 +89,7 @@ class TestBoB(unittest.TestCase):
         self.assertEqual(bob.username, None)
         self.assertEqual(bob.nocache, False)
         self.assertEqual(bob.cfg_start_auth, True)
-        self.assertEqual(bob.cfg_start_recurse, False)
+        self.assertEqual(bob.cfg_start_resolver, False)
 
     def test_init_alternate_socket(self):
         bob = BoB("alt_socket_file")
@@ -106,7 +106,7 @@ class TestBoB(unittest.TestCase):
         self.assertEqual(bob.username, None)
         self.assertEqual(bob.nocache, False)
         self.assertEqual(bob.cfg_start_auth, True)
-        self.assertEqual(bob.cfg_start_recurse, False)
+        self.assertEqual(bob.cfg_start_resolver, False)
 
     def test_init_alternate_dns_port(self):
         bob = BoB(None, 9999)
@@ -123,7 +123,7 @@ class TestBoB(unittest.TestCase):
         self.assertEqual(bob.username, None)
         self.assertEqual(bob.nocache, False)
         self.assertEqual(bob.cfg_start_auth, True)
-        self.assertEqual(bob.cfg_start_recurse, False)
+        self.assertEqual(bob.cfg_start_resolver, False)
 
     def test_init_alternate_address(self):
         bob = BoB(None, 1234, IPAddr('127.127.127.127'))
@@ -140,7 +140,7 @@ class TestBoB(unittest.TestCase):
         self.assertEqual(bob.username, None)
         self.assertEqual(bob.nocache, False)
         self.assertEqual(bob.cfg_start_auth, True)
-        self.assertEqual(bob.cfg_start_recurse, False)
+        self.assertEqual(bob.cfg_start_resolver, False)
 
 # Class for testing the Bob.start_all_processes() method call.
 #
@@ -157,7 +157,7 @@ class StartAllProcessesBob(BoB):
         self.cfgmgr = False
         self.ccsession = False
         self.auth = False
-        self.recurse = False
+        self.resolver = False
         self.xfrout = False
         self.xfrin = False
         self.zonemgr = False
@@ -180,8 +180,8 @@ class StartAllProcessesBob(BoB):
     def start_auth(self, c_channel_env):
         self.auth = True
 
-    def start_recurse(self, c_channel_env):
-        self.recurse = True
+    def start_resolver(self, c_channel_env):
+        self.resolver = True
 
     def start_xfrout(self, c_channel_env):
         self.xfrout = True
@@ -206,14 +206,14 @@ class TestStartAllProcessesBob(unittest.TestCase):
         self.assertEqual(bob.cfgmgr, False)
         self.assertEqual(bob.ccsession, False)
         self.assertEqual(bob.auth, False)
-        self.assertEqual(bob.recurse, 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)
 
-    # Checks the processes started when starting neither auth nor recurse
+    # Checks the processes started when starting neither auth nor resolver
     # is specified.
     def test_start_none(self):
         # Created Bob and ensure initialization correct
@@ -223,7 +223,7 @@ class TestStartAllProcessesBob(unittest.TestCase):
         # Start processes and check what was started
         c_channel_env = {}
         bob.cfg_start_auth = False
-        bob.cfg_start_recurse = False
+        bob.cfg_start_resolver = False
 
         bob.start_all_processes(c_channel_env)
 
@@ -231,7 +231,7 @@ class TestStartAllProcessesBob(unittest.TestCase):
         self.assertEqual(bob.cfgmgr, True)
         self.assertEqual(bob.ccsession, True)
         self.assertEqual(bob.auth, False)
-        self.assertEqual(bob.recurse, False)
+        self.assertEqual(bob.resolver, False)
         self.assertEqual(bob.xfrout, False)
         self.assertEqual(bob.xfrin, False)
         self.assertEqual(bob.zonemgr, False)
@@ -247,7 +247,7 @@ class TestStartAllProcessesBob(unittest.TestCase):
         # Start processes and check what was started
         c_channel_env = {}
         bob.cfg_start_auth = True
-        bob.cfg_start_recurse = False
+        bob.cfg_start_resolver = False
 
         bob.start_all_processes(c_channel_env)
 
@@ -255,15 +255,15 @@ class TestStartAllProcessesBob(unittest.TestCase):
         self.assertEqual(bob.cfgmgr, True)
         self.assertEqual(bob.ccsession, True)
         self.assertEqual(bob.auth, True)
-        self.assertEqual(bob.recurse, False)
+        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)
 
-    # Checks the processes started when starting only the recurse process
-    def test_start_recurse(self):
+    # Checks the processes started when starting only the resolver process
+    def test_start_resolver(self):
         # Created Bob and ensure initialization correct
         bob = StartAllProcessesBob()
         self.check_preconditions(bob)
@@ -271,7 +271,7 @@ class TestStartAllProcessesBob(unittest.TestCase):
         # Start processes and check what was started
         c_channel_env = {}
         bob.cfg_start_auth = False
-        bob.cfg_start_recurse = True
+        bob.cfg_start_resolver = True
 
         bob.start_all_processes(c_channel_env)
 
@@ -279,14 +279,14 @@ class TestStartAllProcessesBob(unittest.TestCase):
         self.assertEqual(bob.cfgmgr, True)
         self.assertEqual(bob.ccsession, True)
         self.assertEqual(bob.auth, False)
-        self.assertEqual(bob.recurse, True)
+        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)
 
-    # Checks the processes started when starting both auth and recurse process
+    # Checks the processes started when starting both auth and resolver process
     def test_start_both(self):
         # Created Bob and ensure initialization correct
         bob = StartAllProcessesBob()
@@ -295,7 +295,7 @@ class TestStartAllProcessesBob(unittest.TestCase):
         # Start processes and check what was started
         c_channel_env = {}
         bob.cfg_start_auth = True
-        bob.cfg_start_recurse = True
+        bob.cfg_start_resolver = True
 
         bob.start_all_processes(c_channel_env)
 
@@ -303,7 +303,7 @@ class TestStartAllProcessesBob(unittest.TestCase):
         self.assertEqual(bob.cfgmgr, True)
         self.assertEqual(bob.ccsession, True)
         self.assertEqual(bob.auth, True)
-        self.assertEqual(bob.recurse, True)
+        self.assertEqual(bob.resolver, True)
         self.assertEqual(bob.xfrout, True)
         self.assertEqual(bob.xfrin, True)
         self.assertEqual(bob.zonemgr, True)

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

@@ -0,0 +1,57 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cc -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/asiolink
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+CLEANFILES = *.gcno *.gcda resolver.spec spec_config.h
+
+man_MANS = b10-resolver.8
+EXTRA_DIST = $(man_MANS) b10-resolver.xml
+
+if ENABLE_MAN
+
+b10-resolver.8: b10-resolver.xml
+	xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-resolver.xml
+
+endif
+
+resolver.spec: resolver.spec.pre
+	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" resolver.spec.pre >$@
+
+spec_config.h: spec_config.h.pre
+	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" spec_config.h.pre >$@
+
+BUILT_SOURCES = spec_config.h 
+pkglibexec_PROGRAMS = b10-resolver
+b10_resolver_SOURCES = resolver.cc resolver.h
+b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/change_user.h
+b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/common.h
+b10_resolver_SOURCES += main.cc
+b10_resolver_LDADD =  $(top_builddir)/src/lib/dns/libdns++.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+b10_resolver_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+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/log/liblog.la
+b10_resolver_LDADD += $(top_builddir)/src/bin/auth/change_user.o
+b10_resolver_LDFLAGS = -pthread
+
+# TODO: config.h.in is wrong because doesn't honor pkgdatadir
+# and can't use @datadir@ because doesn't expand default ${prefix}
+b10_resolverdir = $(DESTDIR)$(pkgdatadir)
+b10_resolver_DATA = resolver.spec
+

+ 95 - 0
src/bin/resolver/b10-resolver.8

@@ -0,0 +1,95 @@
+'\" t
+.\"     Title: b10-resolver
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\"      Date: January 3, 2011
+.\"    Manual: BIND10
+.\"    Source: BIND10
+.\"  Language: English
+.\"
+.TH "B10\-RESOLVER" "8" "January 3, 2011" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-resolver \- Recursive DNS server
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-resolver\fR\ 'u
+\fBb10\-resolver\fR [\fB\-u\ \fR\fB\fIusername\fR\fR] [\fB\-v\fR]
+.SH "DESCRIPTION"
+.PP
+The
+\fBb10\-resolver\fR
+daemon provides the BIND 10 recursive DNS server\&. Normally it is started by the
+\fBbind10\fR(8)
+boss process\&.
+.PP
+This daemon communicates with other BIND 10 components over a
+\fBb10-msgq\fR(8)
+C\-Channel connection\&. If this connection is not established,
+\fBb10\-resolver\fR
+will exit\&.
+.PP
+It also receives its configurations from
+\fBb10-cfgmgr\fR(8)\&. Currently no configuration commands are defined\&.
+.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"
+.PP
+The arguments are as follows:
+.PP
+\fB\-u \fR\fB\fIusername\fR\fR
+.RS 4
+The user name of the
+\fBb10\-resolver\fR
+daemon\&. If specified, the daemon changes the process owner to the specified user\&. The
+\fIusername\fR
+must be either a valid numeric user ID or a valid user name\&. By default the daemon runs as the user who invokes it\&.
+.RE
+.PP
+\fB\-v\fR
+.RS 4
+Enabled verbose mode\&. This enables diagnostic messages to STDERR\&.
+.RE
+.SH "FILES"
+.PP
+None\&.
+.SH "SEE ALSO"
+.PP
+
+\fBb10-cfgmgr\fR(8),
+\fBb10-cmdctl\fR(8),
+\fBb10-msgq\fR(8),
+\fBbind10\fR(8),
+BIND 10 Guide\&.
+.SH "HISTORY"
+.PP
+The
+\fBb10\-resolver\fR
+daemon was first coded in September 2010\&.
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+.br

+ 152 - 0
src/bin/resolver/b10-resolver.xml

@@ -0,0 +1,152 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	       [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<!-- $Id$ -->
+<refentry>
+
+  <refentryinfo>
+    <date>January 3, 2011</date>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>b10-resolver</refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo>BIND10</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>b10-resolver</refname>
+    <refpurpose>Recursive DNS server</refpurpose>
+  </refnamediv>
+
+  <docinfo>
+    <copyright>
+      <year>2010</year>
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+    </copyright>
+  </docinfo>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>b10-resolver</command>
+      <arg><option>-u <replaceable>username</replaceable></option></arg>
+      <arg><option>-v</option></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+    <para>The <command>b10-resolver</command> daemon provides the BIND 10
+      recursive DNS server.  Normally it is started by the
+      <citerefentry><refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      boss process.
+    </para>
+
+    <para>
+      This daemon communicates with other BIND 10 components over a
+      <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      C-Channel connection.  If this connection is not established,
+      <command>b10-resolver</command> will exit.
+    </para>
+
+    <para>
+      It also receives its configurations from
+<citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+      Currently no configuration commands are defined.
+    </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.
+    </para></note>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <para>The arguments are as follows:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><option>-u <replaceable>username</replaceable></option></term>
+        <listitem>
+	  <para>
+	    The user name of the <command>b10-resolver</command> daemon.
+	    If specified, the daemon changes the process owner to the
+	    specified user.
+	    The <replaceable>username</replaceable> must be either a
+	    valid numeric user ID or a valid user name.
+	    By default the daemon runs as the user who invokes it.
+	  </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-v</option></term>
+        <listitem><para>
+          Enabled verbose mode. This enables diagnostic messages to
+          STDERR.
+        </para></listitem>
+      </varlistentry>
+
+    </variablelist>
+
+  </refsect1>
+
+  <refsect1>
+    <title>FILES</title>
+    <para>
+      None.
+    </para>
+<!-- TODO: this is not correct yet. -->
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+        <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citetitle>BIND 10 Guide</citetitle>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>HISTORY</title>
+    <para>
+      The <command>b10-resolver</command> daemon was first coded in
+      September 2010.
+    </para>
+  </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->

+ 181 - 0
src/bin/resolver/main.cc

@@ -0,0 +1,181 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <string>
+#include <iostream>
+
+#include <boost/foreach.hpp>
+
+#include <asiolink/asiolink.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/buffer.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+
+#include <cc/session.h>
+#include <cc/data.h>
+#include <config/ccsession.h>
+
+#include <xfr/xfrout_client.h>
+
+#include <auth/change_user.h>
+#include <auth/common.h>
+
+#include <resolver/spec_config.h>
+#include <resolver/resolver.h>
+
+#include <log/dummylog.h>
+
+using namespace std;
+using namespace isc::cc;
+using namespace isc::config;
+using namespace isc::data;
+using isc::log::dlog;
+using namespace asiolink;
+
+namespace {
+
+// Default port current 5300 for testing purposes
+static const string PROGRAM = "Resolver";
+
+IOService io_service;
+static Resolver *resolver;
+
+ConstElementPtr
+my_config_handler(ConstElementPtr new_config) {
+    return (resolver->updateConfig(new_config));
+}
+
+ConstElementPtr
+my_command_handler(const string& command, ConstElementPtr args) {
+    ConstElementPtr answer = createAnswer();
+
+    if (command == "print_message") {
+        cout << args << endl;
+        /* let's add that message to our answer as well */
+        answer = createAnswer(0, args);
+    } else if (command == "shutdown") {
+        io_service.stop();
+    }
+
+    return (answer);
+}
+
+void
+usage() {
+    cerr << "Usage:  b10-resolver [-u user] [-v]" << endl;
+    cerr << "\t-u: change process UID to the specified user" << endl;
+    cerr << "\t-v: verbose output" << endl;
+    exit(1);
+}
+} // end of anonymous namespace
+
+int
+main(int argc, char* argv[]) {
+    isc::log::dprefix = "b10-resolver";
+    int ch;
+    const char* uid = NULL;
+
+    while ((ch = getopt(argc, argv, "u:v")) != -1) {
+        switch (ch) {
+        case 'u':
+            uid = optarg;
+            break;
+        case 'v':
+            isc::log::denabled = true;
+            break;
+        case '?':
+        default:
+            usage();
+        }
+    }
+
+    if (argc - optind > 0) {
+        usage();
+    }
+
+    if (isc::log::denabled) { // Show the command line
+        string cmdline("Command line:");
+        for (int i = 0; i < argc; ++ i) {
+            cmdline = cmdline + " " + argv[i];
+        }
+        dlog(cmdline);
+    }
+
+    int ret = 0;
+
+    Session* cc_session = NULL;
+    ModuleCCSession* config_session = NULL;
+    try {
+        string specfile;
+        if (getenv("B10_FROM_BUILD")) {
+            specfile = string(getenv("B10_FROM_BUILD")) +
+                "/src/bin/resolver/resolver.spec";
+        } else {
+            specfile = string(RESOLVER_SPECFILE_LOCATION);
+        }
+
+        resolver = new Resolver();
+        dlog("Server created.");
+
+        SimpleCallback* checkin = resolver->getCheckinProvider();
+        DNSLookup* lookup = resolver->getDNSLookupProvider();
+        DNSAnswer* answer = resolver->getDNSAnswerProvider();
+
+        DNSService dns_service(io_service, checkin, lookup, answer);
+
+        resolver->setDNSService(dns_service);
+        dlog("IOService created.");
+
+        cc_session = new Session(io_service.get_io_service());
+        dlog("Configuration session channel created.");
+
+        config_session = new ModuleCCSession(specfile, *cc_session,
+                                             my_config_handler,
+                                             my_command_handler);
+        dlog("Configuration channel established.");
+
+        // FIXME: This does not belong here, but inside Boss
+        if (uid != NULL) {
+            changeUser(uid);
+        }
+
+        resolver->setConfigSession(config_session);
+        dlog("Config loaded");
+
+        dlog("Server started.");
+        io_service.run();
+    } catch (const std::exception& ex) {
+        dlog(string("Server failed: ") + ex.what());
+        ret = 1;
+    }
+
+    delete config_session;
+    delete cc_session;
+    delete resolver;
+
+    return (ret);
+}

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

@@ -0,0 +1,610 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <config.h>
+
+#include <netinet/in.h>
+
+#include <algorithm>
+#include <vector>
+#include <cassert>
+
+#include <asiolink/asiolink.h>
+#include <asiolink/ioaddress.h>
+
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <config/ccsession.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/buffer.h>
+#include <dns/exceptions.h>
+#include <dns/name.h>
+#include <dns/question.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+
+#include <log/dummylog.h>
+
+#include <resolver/resolver.h>
+
+using namespace std;
+
+using namespace isc;
+using namespace isc::dns;
+using namespace isc::data;
+using namespace isc::config;
+using isc::log::dlog;
+using namespace asiolink;
+
+typedef pair<string, uint16_t> addr_t;
+
+class ResolverImpl {
+private:
+    // prohibit copy
+    ResolverImpl(const ResolverImpl& source);
+    ResolverImpl& operator=(const ResolverImpl& source);
+public:
+    ResolverImpl() :
+        config_session_(NULL),
+        rec_query_(NULL)
+    {}
+
+    ~ResolverImpl() {
+        queryShutdown();
+    }
+
+    void querySetup(DNSService& dnss) {
+        assert(!rec_query_); // queryShutdown must be called first
+        dlog("Query setup");
+        rec_query_ = new RecursiveQuery(dnss, upstream_);
+    }
+
+    void queryShutdown() {
+        dlog("Query shutdown");
+        delete rec_query_;
+        rec_query_ = NULL;
+    }
+
+    void setForwardAddresses(const vector<addr_t>& upstream,
+        DNSService *dnss)
+    {
+        queryShutdown();
+        upstream_ = upstream;
+        if (dnss) {
+            if (upstream_.empty()) {
+                dlog("Asked to do full recursive, but not implemented yet. "
+                    "I'll do nothing.");
+            } else {
+                dlog("Setting forward addresses:");
+                BOOST_FOREACH(const addr_t& address, upstream) {
+                    dlog(" " + address.first + ":" +
+                        boost::lexical_cast<string>(address.second));
+                }
+                querySetup(*dnss);
+            }
+        }
+    }
+
+    void processNormalQuery(const Question& question, MessagePtr message,
+                            OutputBufferPtr buffer,
+                            DNSServer* server);
+
+    /// Currently non-configurable, but will be.
+    static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
+
+    /// These members are public because Resolver accesses them directly.
+    ModuleCCSession* config_session_;
+    /// Addresses of the forward nameserver
+    vector<addr_t> upstream_;
+    /// Addresses we listen on
+    vector<addr_t> listen_;
+
+    /// Time in milliseconds, to timeout
+    int timeout_;
+    /// Number of retries after timeout
+    unsigned retries_;
+
+private:
+
+    /// Object to handle upstream queries
+    RecursiveQuery* rec_query_;
+};
+
+/*
+ * std::for_each has a broken interface. It makes no sense in a language
+ * without lambda functions/closures. These two classes emulate the lambda
+ * functions so for_each can be used.
+ */
+class QuestionInserter {
+public:
+    QuestionInserter(MessagePtr message) : message_(message) {}
+    void operator()(const QuestionPtr question) {
+        dlog(string("Adding question ") + question->getName().toText() +
+            " to message");
+        message_->addQuestion(question);
+    }
+    MessagePtr message_;
+};
+
+class SectionInserter {
+public:
+    SectionInserter(MessagePtr message, const Message::Section sect) :
+        message_(message), section_(sect)
+    {}
+    void operator()(const RRsetPtr rrset) {
+        //dlog("Adding RRSet to message section " +
+        //    boost::lexical_cast<string>(section_));
+        message_->addRRset(section_, rrset, true);
+    }
+    MessagePtr message_;
+    const Message::Section section_;
+};
+
+void
+makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
+                 const Rcode& rcode)
+{
+    // extract the parameters that should be kept.
+    // XXX: with the current implementation, it's not easy to set EDNS0
+    // depending on whether the query had it.  So we'll simply omit it.
+    const qid_t qid = message->getQid();
+    const bool rd = message->getHeaderFlag(Message::HEADERFLAG_RD);
+    const bool cd = message->getHeaderFlag(Message::HEADERFLAG_CD);
+    const Opcode& opcode = message->getOpcode();
+    vector<QuestionPtr> questions;
+
+    // If this is an error to a query or notify, we should also copy the
+    // question section.
+    if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
+        questions.assign(message->beginQuestion(), message->endQuestion());
+    }
+
+    message->clear(Message::RENDER);
+    message->setQid(qid);
+    message->setOpcode(opcode);
+    message->setHeaderFlag(Message::HEADERFLAG_QR);
+    if (rd) {
+        message->setHeaderFlag(Message::HEADERFLAG_RD);
+    }
+    if (cd) {
+        message->setHeaderFlag(Message::HEADERFLAG_CD);
+    }
+    for_each(questions.begin(), questions.end(), QuestionInserter(message));
+    message->setRcode(rcode);
+    MessageRenderer renderer(*buffer);
+    message->toWire(renderer);
+
+    dlog(string("Sending an error response (") +
+        boost::lexical_cast<string>(renderer.getLength()) + " bytes):\n" +
+        message->toText());
+}
+
+// This is a derived class of \c DNSLookup, to serve as a
+// callback in the asiolink module.  It calls
+// Resolver::processMessage() on a single DNS message.
+class MessageLookup : public DNSLookup {
+public:
+    MessageLookup(Resolver* srv) : server_(srv) {}
+
+    // \brief Handle the DNS Lookup
+    virtual void operator()(const IOMessage& io_message, MessagePtr message,
+                            OutputBufferPtr buffer, DNSServer* server) const
+    {
+        server_->processMessage(io_message, message, buffer, server);
+    }
+private:
+    Resolver* server_;
+};
+
+// This is a derived class of \c DNSAnswer, to serve as a
+// callback in the asiolink module.  It takes a completed
+// set of answer data from the DNS lookup and assembles it
+// into a wire-format response.
+class MessageAnswer : public DNSAnswer {
+public:
+    virtual void operator()(const IOMessage& io_message,
+                            MessagePtr message,
+                            OutputBufferPtr buffer) const
+    {
+        const qid_t qid = message->getQid();
+        const bool rd = message->getHeaderFlag(Message::HEADERFLAG_RD);
+        const bool cd = message->getHeaderFlag(Message::HEADERFLAG_CD);
+        const Opcode& opcode = message->getOpcode();
+        const Rcode& rcode = message->getRcode();
+        vector<QuestionPtr> questions;
+        questions.assign(message->beginQuestion(), message->endQuestion());
+
+        message->clear(Message::RENDER);
+        message->setQid(qid);
+        message->setOpcode(opcode);
+        message->setRcode(rcode);
+
+        message->setHeaderFlag(Message::HEADERFLAG_QR);
+        message->setHeaderFlag(Message::HEADERFLAG_RA);
+        if (rd) {
+            message->setHeaderFlag(Message::HEADERFLAG_RD);
+        }
+        if (cd) {
+            message->setHeaderFlag(Message::HEADERFLAG_CD);
+        }
+
+
+        // Copy the question section.
+        for_each(questions.begin(), questions.end(), QuestionInserter(message));
+
+        // If the buffer already has an answer in it, copy RRsets from
+        // that into the new message, then clear the buffer and render
+        // the new message into it.
+        if (buffer->getLength() != 0) {
+            try {
+                Message incoming(Message::PARSE);
+                InputBuffer ibuf(buffer->getData(), buffer->getLength());
+                incoming.fromWire(ibuf);
+                for_each(incoming.beginSection(Message::SECTION_ANSWER),
+                         incoming.endSection(Message::SECTION_ANSWER),
+                         SectionInserter(message, Message::SECTION_ANSWER));
+                for_each(incoming.beginSection(Message::SECTION_AUTHORITY),
+                         incoming.endSection(Message::SECTION_AUTHORITY),
+                         SectionInserter(message, Message::SECTION_AUTHORITY));
+                for_each(incoming.beginSection(Message::SECTION_ADDITIONAL),
+                         incoming.endSection(Message::SECTION_ADDITIONAL),
+                         SectionInserter(message, Message::SECTION_ADDITIONAL));
+            } catch (const Exception& ex) {
+                // Incoming message couldn't be read, we just SERVFAIL
+                message->setRcode(Rcode::SERVFAIL());
+            }
+        }
+
+        // Now we can clear the buffer and render the new message into it
+        buffer->clear();
+        MessageRenderer renderer(*buffer);
+
+        if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
+            ConstEDNSPtr edns(message->getEDNS());
+            renderer.setLengthLimit(edns ? edns->getUDPSize() :
+                Message::DEFAULT_MAX_UDPSIZE);
+        } else {
+            renderer.setLengthLimit(65535);
+        }
+
+        message->toWire(renderer);
+
+        dlog(string("sending a response (") +
+            boost::lexical_cast<string>(renderer.getLength()) + "bytes): \n" +
+            message->toText());
+    }
+};
+
+// This is a derived class of \c SimpleCallback, to serve
+// as a callback in the asiolink module.  It checks for queued
+// configuration messages, and executes them if found.
+class ConfigCheck : public SimpleCallback {
+public:
+    ConfigCheck(Resolver* srv) : server_(srv) {}
+    virtual void operator()(const IOMessage&) const {
+        if (server_->getConfigSession()->hasQueuedMsgs()) {
+            server_->getConfigSession()->checkCommand();
+        }
+    }
+private:
+    Resolver* server_;
+};
+
+Resolver::Resolver() :
+    impl_(new ResolverImpl()),
+    checkin_(new ConfigCheck(this)),
+    dns_lookup_(new MessageLookup(this)),
+    dns_answer_(new MessageAnswer)
+{}
+
+Resolver::~Resolver() {
+    delete impl_;
+    delete checkin_;
+    delete dns_lookup_;
+    delete dns_answer_;
+    dlog("Deleting the Resolver");
+}
+
+void
+Resolver::setDNSService(asiolink::DNSService& dnss) {
+    impl_->queryShutdown();
+    impl_->querySetup(dnss);
+    dnss_ = &dnss;
+}
+
+void
+Resolver::setConfigSession(ModuleCCSession* config_session) {
+    impl_->config_session_ = config_session;
+}
+
+ModuleCCSession*
+Resolver::getConfigSession() const {
+    return (impl_->config_session_);
+}
+
+void
+Resolver::processMessage(const IOMessage& io_message, MessagePtr message,
+                        OutputBufferPtr buffer, DNSServer* server)
+{
+    dlog("Got a DNS message");
+    InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
+    // First, check the header part.  If we fail even for the base header,
+    // just drop the message.
+    try {
+        message->parseHeader(request_buffer);
+
+        // Ignore all responses.
+        if (message->getHeaderFlag(Message::HEADERFLAG_QR)) {
+            dlog("Received unexpected response, ignoring");
+            server->resume(false);
+            return;
+        }
+    } catch (const Exception& ex) {
+        dlog(string("DNS packet exception: ") + ex.what());
+        server->resume(false);
+        return;
+    }
+
+    // Parse the message.  On failure, return an appropriate error.
+    try {
+        message->fromWire(request_buffer);
+    } catch (const DNSProtocolError& error) {
+        dlog(string("returning ") + error.getRcode().toText() + ": " + 
+            error.what());
+        makeErrorMessage(message, buffer, error.getRcode());
+        server->resume(true);
+        return;
+    } catch (const Exception& ex) {
+        dlog(string("returning SERVFAIL: ") + ex.what());
+        makeErrorMessage(message, buffer, Rcode::SERVFAIL());
+        server->resume(true);
+        return;
+    } // other exceptions will be handled at a higher layer.
+
+    dlog("received a message:\n" + message->toText());
+
+    // Perform further protocol-level validation.
+    bool sendAnswer = true;
+    if (message->getOpcode() == Opcode::NOTIFY()) {
+        makeErrorMessage(message, buffer, Rcode::NOTAUTH());
+        dlog("Notify arrived, but we are not authoritative");
+    } else if (message->getOpcode() != Opcode::QUERY()) {
+        dlog("Unsupported opcode (got: " + message->getOpcode().toText() +
+            ", expected: " + Opcode::QUERY().toText());
+        makeErrorMessage(message, buffer, Rcode::NOTIMP());
+    } else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
+        dlog("The query contained " +
+            boost::lexical_cast<string>(message->getRRCount(
+            Message::SECTION_QUESTION) + " questions, exactly one expected"));
+        makeErrorMessage(message, buffer, Rcode::FORMERR());
+    } else {
+        ConstQuestionPtr question = *message->beginQuestion();
+        const RRType &qtype = question->getType();
+        if (qtype == RRType::AXFR()) {
+            if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
+                makeErrorMessage(message, buffer, Rcode::FORMERR());
+            } else {
+                makeErrorMessage(message, buffer, Rcode::NOTIMP());
+            }
+        } else if (qtype == RRType::IXFR()) {
+            makeErrorMessage(message, buffer, Rcode::NOTIMP());
+        } else {
+            // The RecursiveQuery object will post the "resume" event to the
+            // DNSServer when an answer arrives, so we don't have to do it now.
+            sendAnswer = false;
+            impl_->processNormalQuery(*question, message, buffer, server);
+        }
+    }
+
+    if (sendAnswer) {
+        server->resume(true);
+    }
+}
+
+void
+ResolverImpl::processNormalQuery(const Question& question, MessagePtr message,
+                                 OutputBufferPtr buffer, DNSServer* server)
+{
+    dlog("Processing normal query");
+    ConstEDNSPtr edns(message->getEDNS());
+    const bool dnssec_ok = edns && edns->getDNSSECAwareness();
+
+    message->makeResponse();
+    message->setHeaderFlag(Message::HEADERFLAG_RA);
+    message->setRcode(Rcode::NOERROR());
+    if (edns) {
+        EDNSPtr edns_response(new EDNS());
+        edns_response->setDNSSECAwareness(dnssec_ok);
+        edns_response->setUDPSize(ResolverImpl::DEFAULT_LOCAL_UDPSIZE);
+        message->setEDNS(edns_response);
+    }
+    rec_query_->sendQuery(question, buffer, server);
+}
+
+namespace {
+
+vector<addr_t>
+parseAddresses(ConstElementPtr addresses) {
+    vector<addr_t> result;
+    if (addresses) {
+        if (addresses->getType() == Element::list) {
+            for (size_t i(0); i < addresses->size(); ++ i) {
+                ConstElementPtr addrPair(addresses->get(i));
+                ConstElementPtr addr(addrPair->get("address"));
+                ConstElementPtr port(addrPair->get("port"));
+                if (!addr || ! port) {
+                    isc_throw(BadValue, "Address must contain both the IP"
+                        "address and port");
+                }
+                try {
+                    IOAddress(addr->stringValue());
+                    if (port->intValue() < 0 ||
+                        port->intValue() > 0xffff) {
+                        isc_throw(BadValue, "Bad port value (" <<
+                            port->intValue() << ")");
+                    }
+                    result.push_back(addr_t(addr->stringValue(),
+                        port->intValue()));
+                }
+                catch (const TypeError &e) { // Better error message
+                    isc_throw(TypeError,
+                        "Address must be a string and port an integer");
+                }
+            }
+        } else if (addresses->getType() != Element::null) {
+            isc_throw(TypeError,
+                "forward_addresses config element must be a list");
+        }
+    }
+    return (result);
+}
+
+}
+
+ConstElementPtr
+Resolver::updateConfig(ConstElementPtr config) {
+    dlog("New config comes: " + config->toWire());
+
+    try {
+        // Parse forward_addresses
+        ConstElementPtr forwardAddressesE(config->get("forward_addresses"));
+        vector<addr_t> forwardAddresses(parseAddresses(forwardAddressesE));
+        ConstElementPtr listenAddressesE(config->get("listen_on"));
+        vector<addr_t> listenAddresses(parseAddresses(listenAddressesE));
+        bool set_timeouts(false);
+        int timeout = impl_->timeout_;
+        unsigned retries = impl_->retries_;
+        ConstElementPtr timeoutE(config->get("timeout")),
+            retriesE(config->get("retries"));
+        if (timeoutE) {
+            // It should be safe to just get it, the config manager should
+            // check for us
+            timeout = timeoutE->intValue();
+            if (timeout < -1) {
+                isc_throw(BadValue, "Timeout too small");
+            }
+            set_timeouts = true;
+        }
+        if (retriesE) {
+            if (retriesE->intValue() < 0) {
+                isc_throw(BadValue, "Negative number of retries");
+            }
+            retries = retriesE->intValue();
+            set_timeouts = true;
+        }
+        // Everything OK, so commit the changes
+        // listenAddresses can fail to bind, so try them first
+        if (listenAddressesE) {
+            setListenAddresses(listenAddresses);
+        }
+        if (forwardAddressesE) {
+            setForwardAddresses(forwardAddresses);
+        }
+        if (set_timeouts) {
+            setTimeouts(timeout, retries);
+        }
+        return (isc::config::createAnswer());
+    } catch (const isc::Exception& error) {
+        dlog(string("error in config: ") + error.what());
+        return (isc::config::createAnswer(1, error.what()));
+    }
+}
+
+void
+Resolver::setForwardAddresses(const vector<addr_t>& addresses)
+{
+    impl_->setForwardAddresses(addresses, dnss_);
+}
+
+bool
+Resolver::isForwarding() const {
+    return (!impl_->upstream_.empty());
+}
+
+vector<addr_t>
+Resolver::getForwardAddresses() const {
+    return (impl_->upstream_);
+}
+
+namespace {
+
+void
+setAddresses(DNSService *service, const vector<addr_t>& addresses) {
+    service->clearServers();
+    BOOST_FOREACH(const addr_t &address, addresses) {
+        service->addServer(address.second, address.first);
+    }
+}
+
+}
+
+void
+Resolver::setListenAddresses(const vector<addr_t>& addresses) {
+    try {
+        dlog("Setting listen addresses:");
+        BOOST_FOREACH(const addr_t& addr, addresses) {
+            dlog(" " + addr.first + ":" +
+                        boost::lexical_cast<string>(addr.second));
+        }
+        setAddresses(dnss_, addresses);
+        impl_->listen_ = addresses;
+    }
+    catch (const exception& e) {
+        /*
+         * We couldn't set it. So return it back. If that fails as well,
+         * we have a problem.
+         *
+         * If that fails, bad luck, but we are useless anyway, so just die
+         * and let boss start us again.
+         */
+        dlog(string("Unable to set new address: ") + e.what());
+        try {
+            setAddresses(dnss_, impl_->listen_);
+        }
+        catch (const exception& e2) {
+            dlog(string("Unable to recover from error;"));
+            dlog(string("Rollback failed with: ") + e2.what());
+            abort();
+        }
+        throw e; // Let it fly a little bit further
+    }
+}
+
+void
+Resolver::setTimeouts(int timeout, unsigned retries) {
+    dlog("Setting timeout to " + boost::lexical_cast<string>(timeout) +
+        " and retry count to " + boost::lexical_cast<string>(retries));
+    impl_->timeout_ = timeout;
+    impl_->retries_ = retries;
+    impl_->queryShutdown();
+    impl_->querySetup(*dnss_);
+}
+pair<int, unsigned>
+Resolver::getTimeouts() const {
+    return (pair<int, unsigned>(impl_->timeout_, impl_->retries_));
+}
+
+vector<addr_t>
+Resolver::getListenAddresses() const {
+    return (impl_->listen_);
+}

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

@@ -0,0 +1,151 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __RESOLVER_H
+#define __RESOLVER_H 1
+
+#include <string>
+#include <vector>
+#include <utility>
+
+#include <cc/data.h>
+#include <config/ccsession.h>
+
+#include <asiolink/asiolink.h>
+
+class ResolverImpl;
+
+/**
+ * \short The recursive nameserver.
+ *
+ * It is a concreate class implementing recursive DNS server protocol
+ * processing. It is responsible for handling incoming DNS requests. It parses
+ * them, passes them deeper into the resolving machinery and then creates the
+ * answer. It doesn't really know about chasing referrals and similar, it
+ * simply plugs the parts that know into the network handling code.
+ */
+class Resolver {
+    ///
+    /// \name Constructors, Assignment Operator and Destructor.
+    ///
+    /// Note: The copy constructor and the assignment operator are
+    /// intentionally defined as private.
+    //@{
+private:
+    Resolver(const Resolver& source);
+    Resolver& operator=(const Resolver& source);
+public:
+    /// The constructor.
+    Resolver();
+    ~Resolver();
+    //@}
+
+    /// \brief Process an incoming DNS message, then signal 'server' to resume 
+    ///
+    /// A DNS query (or other message) has been received by a \c DNSServer
+    /// object.  Find an answer, then post the \c DNSServer object on the
+    /// I/O service queue and return.  When the server resumes, it can
+    /// send the reply.
+    ///
+    /// \param io_message The raw message received
+    /// \param message Pointer to the \c Message object
+    /// \param buffer Pointer to an \c OutputBuffer for the resposne
+    /// \param server Pointer to the \c DNSServer
+    void processMessage(const asiolink::IOMessage& io_message,
+                        isc::dns::MessagePtr message,
+                        isc::dns::OutputBufferPtr buffer,
+                        asiolink::DNSServer* server);
+
+    /// \brief Set and get the config session
+    isc::config::ModuleCCSession* getConfigSession() const;
+    void setConfigSession(isc::config::ModuleCCSession* config_session);
+
+    /// \brief Handle commands from the config session
+    isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
+
+    /// \brief Assign an ASIO IO Service queue to this Resolver object
+    void setDNSService(asiolink::DNSService& dnss);
+
+    /// \brief Return this object's ASIO IO Service queue
+    asiolink::DNSService& getDNSService() const { return (*dnss_); }
+
+    /// \brief Return pointer to the DNS Lookup callback function
+    asiolink::DNSLookup* getDNSLookupProvider() { return (dns_lookup_); }
+
+    /// \brief Return pointer to the DNS Answer callback function
+    asiolink::DNSAnswer* getDNSAnswerProvider() { return (dns_answer_); }
+
+    /// \brief Return pointer to the Checkin callback function
+    asiolink::SimpleCallback* getCheckinProvider() { return (checkin_); }
+
+    /**
+     * \brief Specify the list of upstream servers.
+     *
+     * Specify the list off addresses of upstream servers to forward queries
+     * to. If the list is empty, this server is set to full recursive mode.
+     * If it is non-empty, it switches to forwarder.
+     *
+     * @param addresses The list of addresses to use (each one is the address
+     * and port pair).
+     */
+    void setForwardAddresses(const std::vector<std::pair<std::string,
+        uint16_t> >& addresses);
+    /**
+     * \short Get list of upstream addresses.
+     *
+     * \see setForwardAddresses.
+     */
+    std::vector<std::pair<std::string, uint16_t> > getForwardAddresses() const;
+    /// Return if we are in forwarding mode (if not, we are in fully recursive)
+    bool isForwarding() const;
+
+    /**
+     * Set and get the addresses we listen on.
+     */
+    void setListenAddresses(const std::vector<std::pair<std::string,
+        uint16_t> >& addresses);
+    std::vector<std::pair<std::string, uint16_t> > getListenAddresses() const;
+
+    /**
+     * \short Set options related to timeouts.
+     *
+     * This sets the time of timeout and number of retries.
+     * \param timeout The time in milliseconds. The value -1 disables timeouts.
+     * \param retries The number of retries (0 means try the first time only,
+     *     do not retry).
+     */
+    void setTimeouts(int timeout = -1, unsigned retries = 0);
+
+    /**
+     * \short Get info about timeouts.
+     *
+     * \returns Timeout and retries (as described in setTimeouts).
+     */
+    std::pair<int, unsigned> getTimeouts() const;
+
+private:
+    ResolverImpl* impl_;
+    asiolink::DNSService* dnss_;
+    asiolink::SimpleCallback* checkin_;
+    asiolink::DNSLookup* dns_lookup_;
+    asiolink::DNSAnswer* dns_answer_;
+};
+
+#endif // __RESOLVER_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 89 - 0
src/bin/resolver/resolver.spec.pre.in

@@ -0,0 +1,89 @@
+{
+  "module_spec": {
+    "module_name": "Resolver",
+    "module_description": "Recursive service",
+    "config_data": [
+      {
+        "item_name": "timeout",
+        "item_type": "integer",
+        "item_optional": False,
+        "item_default": 2000
+      },
+      {
+        "item_name": "retries",
+        "item_type": "integer",
+        "item_optional": False,
+        "item_default": 0
+      },
+      {
+        "item_name": "forward_addresses",
+        "item_type": "list",
+        "item_optional": True,
+        "item_default": [],
+        "list_item_spec" : {
+          "item_name": "address",
+          "item_type": "map",
+          "item_optional": False,
+          "item_default": {},
+          "map_item_spec": [
+            {
+              "item_name": "address",
+              "item_type": "string",
+              "item_optional": False,
+              "item_default": "::1"
+            },
+            {
+              "item_name": "port",
+              "item_type": "integer",
+              "item_optional": False,
+              "item_default": 53
+            }
+          ]
+        }
+      },
+      {
+        "item_name": "listen_on",
+        "item_type": "list",
+        "item_optional": False,
+        "item_default": [
+          {
+            "address": "::1",
+            "port": 5300
+          },
+          {
+            "address": "127.0.0.1",
+            "port": 5300
+          },
+        ],
+        "list_item_spec": {
+          "item_name": "address",
+          "item_type": "map",
+          "item_optional": False,
+          "item_default": {},
+          "map_item_spec": [
+            {
+              "item_name": "address",
+              "item_type": "string",
+              "item_optional": False,
+              "item_default": "::1"
+            },
+            {
+              "item_name": "port",
+              "item_type": "integer",
+              "item_optional": False,
+              "item_default": 5300
+            }
+          ]
+        }
+      }
+    ],
+    "commands": [
+      {
+        "command_name": "shutdown",
+        "command_description": "Shut down recursive DNS server",
+        "command_args": []
+      }
+    ]
+  }
+}
+

+ 15 - 0
src/bin/resolver/spec_config.h.pre.in

@@ -0,0 +1,15 @@
+// Copyright (C) 2009  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.
+
+#define RESOLVER_SPECFILE_LOCATION "@prefix@/share/@PACKAGE@/resolver.spec"

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

@@ -0,0 +1,40 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(top_srcdir)/src/lib/testutils/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
+run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
+run_unittests_SOURCES += ../resolver.h ../resolver.cc
+run_unittests_SOURCES += resolver_unittest.cc
+run_unittests_SOURCES += resolver_config_unittest.cc
+run_unittests_SOURCES += run_unittests.cc
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(SQLITE_LIBS)
+run_unittests_LDADD +=  $(top_builddir)/src/lib/datasrc/libdatasrc.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+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/log/liblog.la
+endif
+
+noinst_PROGRAMS = $(TESTS)

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

@@ -0,0 +1,235 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <resolver/resolver.h>
+#include <testutils/srv_unittest.h>
+
+namespace {
+class ResolverConfig : public ::testing::Test {
+    public:
+        IOService ios;
+        DNSService dnss;
+        Resolver server;
+        ResolverConfig() :
+            dnss(ios, NULL, NULL, NULL)
+        {
+            server.setDNSService(dnss);
+        }
+        void invalidTest(const string &JOSN);
+};
+
+TEST_F(ResolverConfig, forwardAddresses) {
+    // Default value should be fully recursive
+    EXPECT_TRUE(server.getForwardAddresses().empty());
+    EXPECT_FALSE(server.isForwarding());
+
+    // Try putting there some addresses
+    vector<pair<string, uint16_t> > addresses;
+    addresses.push_back(pair<string, uint16_t>(DEFAULT_REMOTE_ADDRESS, 53));
+    addresses.push_back(pair<string, uint16_t>("::1", 53));
+    server.setForwardAddresses(addresses);
+    EXPECT_EQ(2, server.getForwardAddresses().size());
+    EXPECT_EQ("::1", server.getForwardAddresses()[1].first);
+    EXPECT_TRUE(server.isForwarding());
+
+    // Is it independent from what we do with the vector later?
+    addresses.clear();
+    EXPECT_EQ(2, server.getForwardAddresses().size());
+
+    // Did it return to fully recursive?
+    server.setForwardAddresses(addresses);
+    EXPECT_TRUE(server.getForwardAddresses().empty());
+    EXPECT_FALSE(server.isForwarding());
+}
+
+TEST_F(ResolverConfig, forwardAddressConfig) {
+    // Try putting there some address
+    ElementPtr config(Element::fromJSON("{"
+        "\"forward_addresses\": ["
+        "   {"
+        "       \"address\": \"192.0.2.1\","
+        "       \"port\": 53"
+        "   }"
+        "]"
+        "}"));
+    ConstElementPtr result(server.updateConfig(config));
+    EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
+    EXPECT_TRUE(server.isForwarding());
+    ASSERT_EQ(1, server.getForwardAddresses().size());
+    EXPECT_EQ("192.0.2.1", server.getForwardAddresses()[0].first);
+    EXPECT_EQ(53, server.getForwardAddresses()[0].second);
+
+    // And then remove all addresses
+    config = Element::fromJSON("{"
+        "\"forward_addresses\": null"
+        "}");
+    result = server.updateConfig(config);
+    EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
+    EXPECT_FALSE(server.isForwarding());
+    EXPECT_EQ(0, server.getForwardAddresses().size());
+}
+
+void
+ResolverConfig::invalidTest(const string &JOSN) {
+    ElementPtr config(Element::fromJSON(JOSN));
+    EXPECT_FALSE(server.updateConfig(config)->equals(
+        *isc::config::createAnswer())) << "Accepted config " << JOSN << endl;
+}
+
+TEST_F(ResolverConfig, invalidForwardAddresses) {
+    // Try torturing it with some invalid inputs
+    invalidTest("{"
+        "\"forward_addresses\": \"error\""
+        "}");
+    invalidTest("{"
+        "\"forward_addresses\": [{}]"
+        "}");
+    invalidTest("{"
+        "\"forward_addresses\": [{"
+        "   \"port\": 1.5,"
+        "   \"address\": \"192.0.2.1\""
+        "}]}");
+    invalidTest("{"
+        "\"forward_addresses\": [{"
+        "   \"port\": -5,"
+        "   \"address\": \"192.0.2.1\""
+        "}]}");
+    invalidTest("{"
+        "\"forward_addresses\": [{"
+        "   \"port\": 53,"
+        "   \"address\": \"bad_address\""
+        "}]}");
+}
+
+TEST_F(ResolverConfig, listenAddresses) {
+    // Default value should be fully recursive
+    EXPECT_TRUE(server.getListenAddresses().empty());
+
+    // Try putting there some addresses
+    vector<pair<string, uint16_t> > addresses;
+    addresses.push_back(pair<string, uint16_t>("127.0.0.1", 5300));
+    addresses.push_back(pair<string, uint16_t>("::1", 5300));
+    server.setListenAddresses(addresses);
+    EXPECT_EQ(2, server.getListenAddresses().size());
+    EXPECT_EQ("::1", server.getListenAddresses()[1].first);
+
+    // Is it independent from what we do with the vector later?
+    addresses.clear();
+    EXPECT_EQ(2, server.getListenAddresses().size());
+
+    // Did it return to fully recursive?
+    server.setListenAddresses(addresses);
+    EXPECT_TRUE(server.getListenAddresses().empty());
+}
+
+TEST_F(ResolverConfig, DISABLED_listenAddressConfig) {
+    // Try putting there some address
+    ElementPtr config(Element::fromJSON("{"
+        "\"listen_on\": ["
+        "   {"
+        "       \"address\": \"127.0.0.1\","
+        "       \"port\": 5300"
+        "   }"
+        "]"
+        "}"));
+    ConstElementPtr result(server.updateConfig(config));
+    EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
+    ASSERT_EQ(1, server.getListenAddresses().size());
+    EXPECT_EQ("127.0.0.1", server.getListenAddresses()[0].first);
+    EXPECT_EQ(5300, server.getListenAddresses()[0].second);
+
+    // As this is example address, the machine should not have it on
+    // any interface
+    // FIXME: This test aborts, because it tries to rollback and
+    //     it is impossible, since the sockets are not closed.
+    //     Once #388 is solved, enable this test.
+    config = Element::fromJSON("{"
+        "\"listen_on\": ["
+        "   {"
+        "       \"address\": \"192.0.2.0\","
+        "       \"port\": 5300"
+        "   }"
+        "]"
+        "}");
+    result = server.updateConfig(config);
+    EXPECT_FALSE(result->equals(*isc::config::createAnswer()));
+    ASSERT_EQ(1, server.getListenAddresses().size());
+    EXPECT_EQ("127.0.0.1", server.getListenAddresses()[0].first);
+    EXPECT_EQ(5300, server.getListenAddresses()[0].second);
+}
+
+TEST_F(ResolverConfig, invalidListenAddresses) {
+    // Try torturing it with some invalid inputs
+    invalidTest("{"
+        "\"listen_on\": \"error\""
+        "}");
+    invalidTest("{"
+        "\"listen_on\": [{}]"
+        "}");
+    invalidTest("{"
+        "\"listen_on\": [{"
+        "   \"port\": 1.5,"
+        "   \"address\": \"192.0.2.1\""
+        "}]}");
+    invalidTest("{"
+        "\"listen_on\": [{"
+        "   \"port\": -5,"
+        "   \"address\": \"192.0.2.1\""
+        "}]}");
+    invalidTest("{"
+        "\"listen_on\": [{"
+        "   \"port\": 53,"
+        "   \"address\": \"bad_address\""
+        "}]}");
+}
+
+// Just test it sets and gets the values correctly
+TEST_F(ResolverConfig, timeouts) {
+    server.setTimeouts(0, 1);
+    EXPECT_EQ(0, server.getTimeouts().first);
+    EXPECT_EQ(1, server.getTimeouts().second);
+    server.setTimeouts();
+    EXPECT_EQ(-1, server.getTimeouts().first);
+    EXPECT_EQ(0, server.getTimeouts().second);
+}
+
+TEST_F(ResolverConfig, timeoutsConfig) {
+    ElementPtr config = Element::fromJSON("{"
+            "\"timeout\": 1000,"
+            "\"retries\": 3"
+            "}");
+    ConstElementPtr result(server.updateConfig(config));
+    EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
+    EXPECT_EQ(1000, server.getTimeouts().first);
+    EXPECT_EQ(3, server.getTimeouts().second);
+}
+
+TEST_F(ResolverConfig, invalidTimeoutsConfig) {
+    invalidTest("{"
+        "\"timeout\": \"error\""
+        "}");
+    invalidTest("{"
+        "\"timeout\": -2"
+        "}");
+    invalidTest("{"
+        "\"retries\": \"error\""
+        "}");
+    invalidTest("{"
+        "\"retries\": -1"
+        "}");
+}
+
+}

+ 97 - 0
src/bin/resolver/tests/resolver_unittest.cc

@@ -0,0 +1,97 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <resolver/resolver.h>
+#include <testutils/srv_unittest.h>
+
+namespace {
+const char* const TEST_PORT = "53535";
+
+class ResolverTest : public SrvTestBase{
+protected:
+    ResolverTest() : server(){}
+    Resolver server;
+};
+
+// Unsupported requests.  Should result in NOTIMP.
+TEST_F(ResolverTest, unsupportedRequest) {
+    UNSUPPORTED_REQUEST_TEST;
+}
+
+// Multiple questions.  Should result in FORMERR.
+TEST_F(ResolverTest, multiQuestion) {
+    MULTI_QUESTION_TEST; 
+}
+
+// Incoming data doesn't even contain the complete header.  Must be silently
+// dropped.
+TEST_F(ResolverTest, shortMessage) {
+    SHORT_MESSAGE_TEST;
+}
+
+// Response messages.  Must be silently dropped, whether it's a valid response
+// or malformed or could otherwise cause a protocol error.
+TEST_F(ResolverTest, response) {
+    RESPONSE_TEST;
+}
+
+// Query with a broken question
+TEST_F(ResolverTest, shortQuestion) {
+    SHORT_QUESTION_TEST;
+}
+
+// Query with a broken answer section
+TEST_F(ResolverTest, shortAnswer) {
+    SHORT_ANSWER_TEST;
+}
+
+// Query with unsupported version of EDNS.
+TEST_F(ResolverTest, ednsBadVers) {
+    EDNS_BADVERS_TEST;
+}
+
+TEST_F(ResolverTest, AXFROverUDP) {
+    AXFR_OVER_UDP_TEST;
+}
+
+TEST_F(ResolverTest, AXFRFail) {
+    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+                                       Name("example.com"), RRClass::IN(),
+                                       RRType::AXFR());
+    createRequestPacket(request_message, IPPROTO_TCP);
+    // AXFR is not implemented and should always send NOTIMP.
+    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    EXPECT_TRUE(dnsserv.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOTIMP(), opcode.getCode(),
+                QR_FLAG, 1, 0, 0, 0);
+}
+
+TEST_F(ResolverTest, notifyFail) {
+    // Notify should always return NOTAUTH
+    request_message.clear(Message::RENDER);
+    request_message.setOpcode(Opcode::NOTIFY());
+    request_message.setRcode(Rcode::NOERROR());
+    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+    request_message.setQid(default_qid);
+    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+    createRequestPacket(request_message, IPPROTO_UDP);
+    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    EXPECT_TRUE(dnsserv.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOTAUTH(),
+                Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
+}
+
+}

+ 26 - 0
src/bin/resolver/tests/run_unittests.cc

@@ -0,0 +1,26 @@
+// Copyright (C) 2009  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 <dns/tests/unittest_util.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+    isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
+    isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
+
+    return (RUN_ALL_TESTS());
+}

+ 1 - 1
src/lib/asiolink/README

@@ -17,7 +17,7 @@ including:
     leaving it in place elsewhere.
 
 Currently, the asiolink library only supports DNS servers (i.e., b10-auth
-and b10-recurse).  The plan is to make it more generic and allow it to
+and b10-resolver).  The plan is to make it more generic and allow it to
 support other modules as well.
 
 Some of the classes defined here--for example, IOSocket, IOEndpoint,

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

@@ -77,7 +77,7 @@ class io_service;
 ///
 /// Notes to developers:
 /// Currently the wrapper interface is fairly specific to use by a
-/// DNS server, i.e., b10-auth or b10-recurse.  But the plan is to
+/// DNS server, i.e., b10-auth or b10-resolver.  But the plan is to
 /// generalize it and have other modules use it as well.
 ///
 /// One obvious drawback of this approach is performance overhead

+ 1 - 1
src/lib/nsas/README

@@ -2,6 +2,6 @@ For an overview of the Nameserver Address Store, see the requirements and design
 documents at http://bind10.isc.org/wiki/Resolver.
 
 At the time of writing (19 October 2010), the file asiolink.h is present in this
-directory only for the purposes of development.  When the recursor's
+directory only for the purposes of development.  When the resolver's
 asynchronous I/O code has been finished, this will be removed and the NSAS will
 use the "real" code.

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

@@ -24,7 +24,7 @@ namespace asiolink {
 
 /// \brief IO Address Dummy Class
 ///
-/// As part of ther recursor, Evan has written the asiolink.h file, which
+/// As part of ther resolver, Evan has written the asiolink.h file, which
 /// encapsulates some of the boost::asio classes.  Until these are checked
 /// into trunk and merged with this branch, these dummy classes should fulfill
 /// their function.